210 lines
4.3 KiB
Markdown
210 lines
4.3 KiB
Markdown
|
|
# 数据库规则
|
|||
|
|
|
|||
|
|
> **入口**: [_index.md](_index.md)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 表命名规范
|
|||
|
|
|
|||
|
|
### 1.1 基本规则
|
|||
|
|
|
|||
|
|
| 规则 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| **表前缀** | 所有表必须以 `cf_` 开头 |
|
|||
|
|
| **命名格式** | `cf_{模块}_{实体}` 如 `cf_product`, `cf_order_item` |
|
|||
|
|
| **禁止** | 无前缀、其他前缀 |
|
|||
|
|
|
|||
|
|
### 1.2 示例
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- ✅ 正确
|
|||
|
|
CREATE TABLE cf_product (...);
|
|||
|
|
CREATE TABLE cf_order_item (...);
|
|||
|
|
CREATE TABLE cf_inventory_log (...);
|
|||
|
|
|
|||
|
|
-- ❌ 错误
|
|||
|
|
CREATE TABLE product (...);
|
|||
|
|
CREATE TABLE order_items (...);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 字段类型规范
|
|||
|
|
|
|||
|
|
### 2.1 金额字段
|
|||
|
|
|
|||
|
|
| 规则 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| **类型** | `decimal(10,2)` |
|
|||
|
|
| **禁止** | `float`, `double` |
|
|||
|
|
| **原因** | 精度问题会导致金额计算错误 |
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- ✅ 正确
|
|||
|
|
price DECIMAL(10,2) NOT NULL,
|
|||
|
|
total_amount DECIMAL(10,2) NOT NULL,
|
|||
|
|
|
|||
|
|
-- ❌ 错误
|
|||
|
|
price FLOAT,
|
|||
|
|
total_amount DOUBLE,
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2.2 物理属性单位
|
|||
|
|
|
|||
|
|
| 属性 | 单位 | 字段类型 |
|
|||
|
|
|------|------|---------|
|
|||
|
|
| 长度 | cm | `decimal(10,2)` |
|
|||
|
|
| 重量 | kg | `decimal(10,2)` |
|
|||
|
|
| 体积 | m³ | `decimal(10,4)` |
|
|||
|
|
|
|||
|
|
### 2.3 JSON 字段
|
|||
|
|
|
|||
|
|
| 规则 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| **存储** | JSON 字段入库前序列化 |
|
|||
|
|
| **读取** | 出库后解析 |
|
|||
|
|
| **适用** | images, skus, attributes, params 等 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 数据完整性
|
|||
|
|
|
|||
|
|
### 3.1 唯一约束
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 商品表:平台+商品ID唯一
|
|||
|
|
ALTER TABLE cf_product
|
|||
|
|
ADD UNIQUE KEY uk_platform_product (platform, product_id);
|
|||
|
|
|
|||
|
|
-- 店铺表:租户+店铺ID唯一
|
|||
|
|
ALTER TABLE cf_shop
|
|||
|
|
ADD UNIQUE KEY uk_tenant_shop (tenant_id, shop_id);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 幂等性保证
|
|||
|
|
|
|||
|
|
所有建表语句必须使用 `db.schema.hasTable` 前置校验:
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// ✅ 正确
|
|||
|
|
if (!(await db.schema.hasTable('cf_product'))) {
|
|||
|
|
await db.schema.createTable('cf_product', (table) => {
|
|||
|
|
table.increments('id').primary();
|
|||
|
|
// ...
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ❌ 错误
|
|||
|
|
await db.schema.createTable('cf_product', (table) => {
|
|||
|
|
// 可能重复创建导致错误
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 索引规范
|
|||
|
|
|
|||
|
|
### 4.1 必须索引
|
|||
|
|
|
|||
|
|
| 字段类型 | 索引要求 |
|
|||
|
|
|---------|---------|
|
|||
|
|
| `tenant_id` | 必须索引 |
|
|||
|
|
| `shop_id` | 必须索引 |
|
|||
|
|
| `created_at` | 必须索引 |
|
|||
|
|
| 状态字段 | 按查询频率决定 |
|
|||
|
|
| 外键 | 必须索引 |
|
|||
|
|
|
|||
|
|
### 4.2 复合索引顺序
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 按查询频率和选择性排序
|
|||
|
|
-- 高选择性在前,常用查询条件在前
|
|||
|
|
CREATE INDEX idx_tenant_shop_status ON cf_order (tenant_id, shop_id, status);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.3 索引验证
|
|||
|
|
|
|||
|
|
复杂查询必须通过 `EXPLAIN` 校验:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
EXPLAIN SELECT * FROM cf_order WHERE tenant_id = 'xxx' AND status = 'PENDING';
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 禁止操作
|
|||
|
|
|
|||
|
|
| 操作 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| `DROP TABLE` | 禁止在代码中执行 |
|
|||
|
|
| `TRUNCATE` | 禁止在代码中执行 |
|
|||
|
|
| `DELETE` 全表 | 必须带 WHERE 条件 |
|
|||
|
|
| 直接修改生产数据 | 必须通过迁移脚本 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 迁移规范
|
|||
|
|
|
|||
|
|
### 6.1 迁移文件命名
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
{timestamp}_{action}_{table}.ts
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
20260322000001_create_cf_product.ts
|
|||
|
|
20260322000002_add_price_index_to_cf_product.ts
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6.2 迁移模板
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
import { Knex } from 'knex';
|
|||
|
|
|
|||
|
|
export async function up(knex: Knex): Promise<void> {
|
|||
|
|
if (!(await knex.schema.hasTable('cf_product'))) {
|
|||
|
|
await knex.schema.createTable('cf_product', (table) => {
|
|||
|
|
table.increments('id').primary();
|
|||
|
|
table.string('tenant_id', 50).notNullable();
|
|||
|
|
table.string('shop_id', 50).notNullable();
|
|||
|
|
table.string('product_id', 100).notNullable();
|
|||
|
|
table.string('platform', 20).notNullable();
|
|||
|
|
table.decimal('price', 10, 2).notNullable();
|
|||
|
|
table.timestamps(true, true);
|
|||
|
|
|
|||
|
|
table.index(['tenant_id', 'shop_id']);
|
|||
|
|
table.unique(['platform', 'product_id']);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export async function down(knex: Knex): Promise<void> {
|
|||
|
|
await knex.schema.dropTableIfExists('cf_product');
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. 五元组字段
|
|||
|
|
|
|||
|
|
所有业务表必须包含以下追踪字段:
|
|||
|
|
|
|||
|
|
| 字段 | 类型 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `tenant_id` | VARCHAR(50) | 租户ID |
|
|||
|
|
| `shop_id` | VARCHAR(50) | 店铺ID |
|
|||
|
|
| `task_id` | VARCHAR(50) | 任务ID(可选) |
|
|||
|
|
| `trace_id` | VARCHAR(50) | 链路追踪ID |
|
|||
|
|
| `business_type` | VARCHAR(10) | TOC/TOB |
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
tenant_id VARCHAR(50) NOT NULL,
|
|||
|
|
shop_id VARCHAR(50) NOT NULL,
|
|||
|
|
task_id VARCHAR(50),
|
|||
|
|
trace_id VARCHAR(50) NOT NULL,
|
|||
|
|
business_type VARCHAR(10) NOT NULL,
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
*最后更新: 2026-03-22*
|