Files
TakeoutSaaS.AdminApi/0_Document/03_数据库设计.md

642 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 外卖SaaS系统 - 数据库设计
## 1. 数据库设计原则
### 1.1 命名规范
- **表名**:小写字母,下划线分隔,复数形式(如:`orders`, `order_items`
- **字段名**:小写字母,下划线分隔(如:`created_at`, `total_amount`
- **主键**:统一使用 `id`,类型为 UUID
- **外键**`表名_id`(如:`order_id`, `merchant_id`
- **索引**`idx_表名_字段名`(如:`idx_orders_merchant_id`
### 1.2 通用字段
所有表都包含以下字段:
- `id`UUID主键
- `created_at`TIMESTAMP创建时间
- `updated_at`TIMESTAMP更新时间
- `deleted_at`TIMESTAMP软删除时间可选
- `tenant_id`UUID租户ID多租户隔离
### 1.3 数据类型规范
- **金额**DECIMAL(18,2)
- **时间**TIMESTAMP WITH TIME ZONE
- **布尔**BOOLEAN
- **枚举**VARCHAR 或 INTEGER
- **JSON数据**JSONB
## 2. 核心表结构
### 2.1 租户管理
#### tenants租户表
```sql
CREATE TABLE tenants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL,
code VARCHAR(50) UNIQUE NOT NULL,
contact_name VARCHAR(50),
contact_phone VARCHAR(20),
contact_email VARCHAR(100),
status INTEGER NOT NULL DEFAULT 1, -- 1:正常 2:冻结 3:过期
subscription_plan VARCHAR(50), -- 订阅套餐
subscription_start_date TIMESTAMP WITH TIME ZONE,
subscription_end_date TIMESTAMP WITH TIME ZONE,
max_merchants INTEGER DEFAULT 10, -- 最大商家数
max_orders_per_day INTEGER DEFAULT 1000, -- 每日订单限额
settings JSONB, -- 租户配置
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_tenants_code ON tenants(code);
CREATE INDEX idx_tenants_status ON tenants(status);
```
### 2.2 商家管理
#### merchants商家表
```sql
CREATE TABLE merchants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
name VARCHAR(100) NOT NULL,
logo_url VARCHAR(500),
description TEXT,
contact_phone VARCHAR(20),
contact_person VARCHAR(50),
business_license VARCHAR(100), -- 营业执照号
status INTEGER NOT NULL DEFAULT 1, -- 1:正常 2:休息 3:停业
rating DECIMAL(3,2) DEFAULT 0, -- 评分
total_sales INTEGER DEFAULT 0, -- 总销量
settings JSONB, -- 商家配置
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_merchants_tenant_id ON merchants(tenant_id);
CREATE INDEX idx_merchants_status ON merchants(status);
```
#### merchant_stores门店表
```sql
CREATE TABLE merchant_stores (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
merchant_id UUID NOT NULL REFERENCES merchants(id),
name VARCHAR(100) NOT NULL,
address VARCHAR(500) NOT NULL,
latitude DECIMAL(10,7), -- 纬度
longitude DECIMAL(10,7), -- 经度
phone VARCHAR(20),
business_hours JSONB, -- 营业时间 {"monday": {"open": "09:00", "close": "22:00"}}
delivery_range INTEGER DEFAULT 3000, -- 配送范围(米)
min_order_amount DECIMAL(18,2) DEFAULT 0, -- 起送价
delivery_fee DECIMAL(18,2) DEFAULT 0, -- 配送费
status INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_merchant_stores_merchant_id ON merchant_stores(merchant_id);
CREATE INDEX idx_merchant_stores_location ON merchant_stores USING GIST(point(longitude, latitude));
```
### 2.3 菜品管理
#### categories菜品分类表
```sql
CREATE TABLE categories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
merchant_id UUID NOT NULL REFERENCES merchants(id),
name VARCHAR(50) NOT NULL,
sort_order INTEGER DEFAULT 0,
status INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_categories_merchant_id ON categories(merchant_id);
```
#### dishes菜品表
```sql
CREATE TABLE dishes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
merchant_id UUID NOT NULL REFERENCES merchants(id),
category_id UUID REFERENCES categories(id),
name VARCHAR(100) NOT NULL,
description TEXT,
image_url VARCHAR(500),
price DECIMAL(18,2) NOT NULL,
original_price DECIMAL(18,2), -- 原价
unit VARCHAR(20) DEFAULT '', -- 单位
stock INTEGER, -- 库存NULL表示不限
sales_count INTEGER DEFAULT 0, -- 销量
rating DECIMAL(3,2) DEFAULT 0, -- 评分
sort_order INTEGER DEFAULT 0,
status INTEGER NOT NULL DEFAULT 1, -- 1:上架 2:下架
tags JSONB, -- 标签 ["热销", "新品"]
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_dishes_merchant_id ON dishes(merchant_id);
CREATE INDEX idx_dishes_category_id ON dishes(category_id);
CREATE INDEX idx_dishes_status ON dishes(status);
```
#### dish_specs菜品规格表
```sql
CREATE TABLE dish_specs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
dish_id UUID NOT NULL REFERENCES dishes(id),
name VARCHAR(50) NOT NULL, -- 规格名称(如:大份、小份)
price DECIMAL(18,2) NOT NULL,
stock INTEGER,
status INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_dish_specs_dish_id ON dish_specs(dish_id);
```
### 2.4 用户管理
#### users用户表
```sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
phone VARCHAR(20) UNIQUE NOT NULL,
nickname VARCHAR(50),
avatar_url VARCHAR(500),
gender INTEGER, -- 0:未知 1:男 2:女
birthday DATE,
balance DECIMAL(18,2) DEFAULT 0, -- 余额
points INTEGER DEFAULT 0, -- 积分
status INTEGER NOT NULL DEFAULT 1,
last_login_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_users_phone ON users(phone);
```
#### user_addresses用户地址表
```sql
CREATE TABLE user_addresses (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
contact_name VARCHAR(50) NOT NULL,
contact_phone VARCHAR(20) NOT NULL,
province VARCHAR(50),
city VARCHAR(50),
district VARCHAR(50),
address VARCHAR(500) NOT NULL,
house_number VARCHAR(50), -- 门牌号
latitude DECIMAL(10,7),
longitude DECIMAL(10,7),
is_default BOOLEAN DEFAULT FALSE,
label VARCHAR(20), -- 标签:家、公司等
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_user_addresses_user_id ON user_addresses(user_id);
```
### 2.5 订单管理
#### orders订单表
```sql
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
order_no VARCHAR(50) UNIQUE NOT NULL, -- 订单号
merchant_id UUID NOT NULL REFERENCES merchants(id),
store_id UUID NOT NULL REFERENCES merchant_stores(id),
user_id UUID NOT NULL REFERENCES users(id),
-- 收货信息
delivery_address VARCHAR(500) NOT NULL,
delivery_latitude DECIMAL(10,7),
delivery_longitude DECIMAL(10,7),
contact_name VARCHAR(50) NOT NULL,
contact_phone VARCHAR(20) NOT NULL,
-- 金额信息
dish_amount DECIMAL(18,2) NOT NULL, -- 菜品金额
delivery_fee DECIMAL(18,2) DEFAULT 0, -- 配送费
package_fee DECIMAL(18,2) DEFAULT 0, -- 打包费
discount_amount DECIMAL(18,2) DEFAULT 0, -- 优惠金额
total_amount DECIMAL(18,2) NOT NULL, -- 总金额
actual_amount DECIMAL(18,2) NOT NULL, -- 实付金额
-- 订单状态
status INTEGER NOT NULL DEFAULT 1, -- 1:待支付 2:待接单 3:制作中 4:待配送 5:配送中 6:已完成 7:已取消
payment_status INTEGER DEFAULT 0, -- 0:未支付 1:已支付 2:已退款
payment_method VARCHAR(20), -- 支付方式
payment_time TIMESTAMP WITH TIME ZONE,
-- 时间信息
estimated_delivery_time TIMESTAMP WITH TIME ZONE, -- 预计送达时间
accepted_at TIMESTAMP WITH TIME ZONE, -- 接单时间
cooking_at TIMESTAMP WITH TIME ZONE, -- 开始制作时间
delivered_at TIMESTAMP WITH TIME ZONE, -- 送达时间
completed_at TIMESTAMP WITH TIME ZONE, -- 完成时间
cancelled_at TIMESTAMP WITH TIME ZONE, -- 取消时间
remark TEXT, -- 备注
cancel_reason TEXT, -- 取消原因
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_orders_tenant_id ON orders(tenant_id);
CREATE INDEX idx_orders_order_no ON orders(order_no);
CREATE INDEX idx_orders_merchant_id ON orders(merchant_id);
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_created_at ON orders(created_at);
```
#### order_items订单明细表
```sql
CREATE TABLE order_items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
order_id UUID NOT NULL REFERENCES orders(id),
dish_id UUID NOT NULL REFERENCES dishes(id),
dish_name VARCHAR(100) NOT NULL, -- 冗余字段,防止菜品被删除
dish_image_url VARCHAR(500),
spec_id UUID REFERENCES dish_specs(id),
spec_name VARCHAR(50),
price DECIMAL(18,2) NOT NULL, -- 单价
quantity INTEGER NOT NULL, -- 数量
amount DECIMAL(18,2) NOT NULL, -- 小计
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_order_items_dish_id ON order_items(dish_id);
```
### 2.6 配送管理
#### delivery_drivers配送员表
```sql
CREATE TABLE delivery_drivers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
merchant_id UUID REFERENCES merchants(id), -- NULL表示平台配送员
name VARCHAR(50) NOT NULL,
phone VARCHAR(20) UNIQUE NOT NULL,
id_card VARCHAR(18), -- 身份证号
vehicle_type VARCHAR(20), -- 车辆类型:电动车、摩托车
vehicle_number VARCHAR(20), -- 车牌号
status INTEGER NOT NULL DEFAULT 1, -- 1:空闲 2:配送中 3:休息 4:离线
current_latitude DECIMAL(10,7), -- 当前位置
current_longitude DECIMAL(10,7),
rating DECIMAL(3,2) DEFAULT 0,
total_deliveries INTEGER DEFAULT 0, -- 总配送单数
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_delivery_drivers_merchant_id ON delivery_drivers(merchant_id);
CREATE INDEX idx_delivery_drivers_status ON delivery_drivers(status);
```
#### delivery_tasks配送任务表
```sql
CREATE TABLE delivery_tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
order_id UUID NOT NULL REFERENCES orders(id),
driver_id UUID REFERENCES delivery_drivers(id),
pickup_address VARCHAR(500) NOT NULL, -- 取餐地址
pickup_latitude DECIMAL(10,7),
pickup_longitude DECIMAL(10,7),
delivery_address VARCHAR(500) NOT NULL, -- 送餐地址
delivery_latitude DECIMAL(10,7),
delivery_longitude DECIMAL(10,7),
distance INTEGER, -- 配送距离(米)
estimated_time INTEGER, -- 预计时长(分钟)
status INTEGER NOT NULL DEFAULT 1, -- 1:待分配 2:待取餐 3:配送中 4:已送达 5:异常
assigned_at TIMESTAMP WITH TIME ZONE, -- 分配时间
picked_at TIMESTAMP WITH TIME ZONE, -- 取餐时间
delivered_at TIMESTAMP WITH TIME ZONE, -- 送达时间
delivery_fee DECIMAL(18,2) DEFAULT 0, -- 配送费
remark TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_delivery_tasks_order_id ON delivery_tasks(order_id);
CREATE INDEX idx_delivery_tasks_driver_id ON delivery_tasks(driver_id);
CREATE INDEX idx_delivery_tasks_status ON delivery_tasks(status);
```
### 2.7 支付管理
#### payments支付记录表
```sql
CREATE TABLE payments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
order_id UUID NOT NULL REFERENCES orders(id),
user_id UUID NOT NULL REFERENCES users(id),
payment_no VARCHAR(50) UNIQUE NOT NULL, -- 支付单号
payment_method VARCHAR(20) NOT NULL, -- 支付方式wechat、alipay、balance
amount DECIMAL(18,2) NOT NULL,
status INTEGER NOT NULL DEFAULT 0, -- 0:待支付 1:支付中 2:成功 3:失败 4:已退款
third_party_no VARCHAR(100), -- 第三方支付单号
paid_at TIMESTAMP WITH TIME ZONE,
callback_data JSONB, -- 回调数据
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_payments_order_id ON payments(order_id);
CREATE INDEX idx_payments_payment_no ON payments(payment_no);
CREATE INDEX idx_payments_user_id ON payments(user_id);
```
#### refunds退款记录表
```sql
CREATE TABLE refunds (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
order_id UUID NOT NULL REFERENCES orders(id),
payment_id UUID NOT NULL REFERENCES payments(id),
refund_no VARCHAR(50) UNIQUE NOT NULL,
amount DECIMAL(18,2) NOT NULL,
reason TEXT,
status INTEGER NOT NULL DEFAULT 0, -- 0:待审核 1:退款中 2:成功 3:失败
third_party_no VARCHAR(100),
refunded_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_refunds_order_id ON refunds(order_id);
CREATE INDEX idx_refunds_payment_id ON refunds(payment_id);
```
### 2.8 营销管理
#### coupons优惠券表
```sql
CREATE TABLE coupons (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
merchant_id UUID REFERENCES merchants(id), -- NULL表示平台券
name VARCHAR(100) NOT NULL,
type INTEGER NOT NULL, -- 1:满减券 2:折扣券 3:代金券
discount_type INTEGER NOT NULL, -- 1:固定金额 2:百分比
discount_value DECIMAL(18,2) NOT NULL, -- 优惠值
min_order_amount DECIMAL(18,2) DEFAULT 0, -- 最低消费
max_discount_amount DECIMAL(18,2), -- 最大优惠金额(折扣券用)
total_quantity INTEGER NOT NULL, -- 总数量
received_quantity INTEGER DEFAULT 0, -- 已领取数量
used_quantity INTEGER DEFAULT 0, -- 已使用数量
valid_start_time TIMESTAMP WITH TIME ZONE NOT NULL,
valid_end_time TIMESTAMP WITH TIME ZONE NOT NULL,
status INTEGER NOT NULL DEFAULT 1, -- 1:正常 2:停用
description TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_coupons_merchant_id ON coupons(merchant_id);
CREATE INDEX idx_coupons_status ON coupons(status);
```
#### user_coupons用户优惠券表
```sql
CREATE TABLE user_coupons (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
coupon_id UUID NOT NULL REFERENCES coupons(id),
status INTEGER NOT NULL DEFAULT 1, -- 1:未使用 2:已使用 3:已过期
used_order_id UUID REFERENCES orders(id),
received_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
used_at TIMESTAMP WITH TIME ZONE,
expired_at TIMESTAMP WITH TIME ZONE NOT NULL
);
CREATE INDEX idx_user_coupons_user_id ON user_coupons(user_id);
CREATE INDEX idx_user_coupons_coupon_id ON user_coupons(coupon_id);
CREATE INDEX idx_user_coupons_status ON user_coupons(status);
```
### 2.9 评价管理
#### reviews评价表
```sql
CREATE TABLE reviews (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
order_id UUID NOT NULL REFERENCES orders(id),
user_id UUID NOT NULL REFERENCES users(id),
merchant_id UUID NOT NULL REFERENCES merchants(id),
rating INTEGER NOT NULL, -- 评分 1-5
taste_rating INTEGER, -- 口味评分
package_rating INTEGER, -- 包装评分
delivery_rating INTEGER, -- 配送评分
content TEXT,
images JSONB, -- 评价图片
is_anonymous BOOLEAN DEFAULT FALSE,
reply_content TEXT, -- 商家回复
reply_at TIMESTAMP WITH TIME ZONE,
status INTEGER NOT NULL DEFAULT 1, -- 1:正常 2:隐藏
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_reviews_order_id ON reviews(order_id);
CREATE INDEX idx_reviews_user_id ON reviews(user_id);
CREATE INDEX idx_reviews_merchant_id ON reviews(merchant_id);
```
### 2.10 系统管理
#### system_users系统用户表
```sql
CREATE TABLE system_users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID REFERENCES tenants(id), -- NULL表示平台管理员
merchant_id UUID REFERENCES merchants(id), -- NULL表示租户管理员
username VARCHAR(50) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
real_name VARCHAR(50),
phone VARCHAR(20),
email VARCHAR(100),
role_id UUID REFERENCES roles(id),
status INTEGER NOT NULL DEFAULT 1,
last_login_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_system_users_username ON system_users(username);
CREATE INDEX idx_system_users_tenant_id ON system_users(tenant_id);
```
#### roles角色表
```sql
CREATE TABLE roles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID REFERENCES tenants(id),
name VARCHAR(50) NOT NULL,
code VARCHAR(50) NOT NULL,
description TEXT,
permissions JSONB, -- 权限列表
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE INDEX idx_roles_tenant_id ON roles(tenant_id);
```
#### operation_logs操作日志表
```sql
CREATE TABLE operation_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID REFERENCES tenants(id),
user_id UUID,
user_type VARCHAR(20), -- system_user, merchant_user, customer
module VARCHAR(50), -- 模块
action VARCHAR(50), -- 操作
description TEXT,
ip_address VARCHAR(50),
user_agent TEXT,
request_data JSONB,
response_data JSONB,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_operation_logs_tenant_id ON operation_logs(tenant_id);
CREATE INDEX idx_operation_logs_user_id ON operation_logs(user_id);
CREATE INDEX idx_operation_logs_created_at ON operation_logs(created_at);
```
## 3. 数据库索引策略
### 3.1 主键索引
- 所有表使用UUID作为主键自动创建主键索引
### 3.2 外键索引
- 所有外键字段创建索引,提升关联查询性能
### 3.3 业务索引
- 订单号、支付单号等唯一业务字段创建唯一索引
- 状态字段创建普通索引
- 时间字段created_at创建索引支持时间范围查询
### 3.4 复合索引
```sql
-- 订单查询常用复合索引
CREATE INDEX idx_orders_merchant_status_created ON orders(merchant_id, status, created_at DESC);
-- 用户订单查询
CREATE INDEX idx_orders_user_status_created ON orders(user_id, status, created_at DESC);
```
### 3.5 地理位置索引
```sql
-- 使用PostGIS扩展支持地理位置查询
CREATE EXTENSION IF NOT EXISTS postgis;
-- 门店位置索引
CREATE INDEX idx_merchant_stores_location ON merchant_stores
USING GIST(ST_MakePoint(longitude, latitude));
```
## 4. 数据库优化
### 4.1 分区策略
```sql
-- 订单表按月分区
CREATE TABLE orders_2024_01 PARTITION OF orders
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
CREATE TABLE orders_2024_02 PARTITION OF orders
FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');
```
### 4.2 物化视图
```sql
-- 商家统计物化视图
CREATE MATERIALIZED VIEW merchant_statistics AS
SELECT
m.id as merchant_id,
m.name,
COUNT(DISTINCT o.id) as total_orders,
SUM(o.actual_amount) as total_revenue,
AVG(r.rating) as avg_rating
FROM merchants m
LEFT JOIN orders o ON m.id = o.merchant_id AND o.status = 6
LEFT JOIN reviews r ON m.id = r.merchant_id
GROUP BY m.id, m.name;
CREATE UNIQUE INDEX ON merchant_statistics(merchant_id);
```
### 4.3 查询优化建议
- 避免SELECT *,只查询需要的字段
- 使用EXPLAIN分析查询计划
- 合理使用JOIN避免过多关联
- 大数据量查询使用分页
- 使用prepared statement防止SQL注入
## 5. 数据备份策略
### 5.1 备份方案
- **全量备份**每天凌晨2点执行
- **增量备份**每4小时执行一次
- **WAL归档**实时归档支持PITR
### 5.2 备份脚本示例
```bash
#!/bin/bash
# 全量备份
pg_dump -h localhost -U postgres -d takeout_saas -F c -f /backup/full_$(date +%Y%m%d).dump
# 保留最近30天的备份
find /backup -name "full_*.dump" -mtime +30 -delete
```
## 6. 数据迁移
### 6.1 EF Core Migrations
```bash
# 添加迁移
dotnet ef migrations add InitialCreate --project TakeoutSaaS.Infrastructure
# 更新数据库
dotnet ef database update --project TakeoutSaaS.Infrastructure
```
### 6.2 版本控制
- 所有数据库变更通过Migration管理
- Migration文件纳入版本控制
- 生产环境变更需要审核