# 外卖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文件纳入版本控制
- 生产环境变更需要审核

