Files
TakeoutSaaS.Docs/Document/03_数据库设计.md
2026-01-29 01:58:15 +00:00

22 KiB
Raw Blame History

外卖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 通用字段

所有表都包含以下字段:

  • idUUID主键
  • created_atTIMESTAMP创建时间
  • updated_atTIMESTAMP更新时间
  • deleted_atTIMESTAMP软删除时间可选
  • tenant_idUUID租户ID多租户隔离

1.3 数据类型规范

  • 金额DECIMAL(18,2)
  • 时间TIMESTAMP WITH TIME ZONE
  • 布尔BOOLEAN
  • 枚举VARCHAR 或 INTEGER
  • JSON数据JSONB

2. 核心表结构

2.1 租户管理

tenants租户表

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商家表

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门店表

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菜品分类表

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菜品表

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菜品规格表

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用户表

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用户地址表

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订单表

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订单明细表

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配送员表

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配送任务表

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支付记录表

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退款记录表

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优惠券表

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用户优惠券表

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评价表

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系统用户表

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角色表

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操作日志表

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 复合索引

-- 订单查询常用复合索引
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 地理位置索引

-- 使用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 分区策略

-- 订单表按月分区
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 物化视图

-- 商家统计物化视图
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 备份脚本示例

#!/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

# 添加迁移
dotnet ef migrations add InitialCreate --project TakeoutSaaS.Infrastructure

# 更新数据库
dotnet ef database update --project TakeoutSaaS.Infrastructure

6.2 版本控制

  • 所有数据库变更通过Migration管理
  • Migration文件纳入版本控制
  • 生产环境变更需要审核