# 数据库调整设计 ## 1. 调整目标 基于现有 `数据库.sql`,补齐开发总纲中新增或明确的能力: - 订单状态新增 `cancel_pending` - 订单固定提成金额 - 订单取消申请与取消前状态记录 - 产品分类独立维护 - 报表分类映射配置 - 欠款生成模式配置 - 阿里云 OSS 文件字段补充 - 司机任务手动创建/分配需要的字段补充 - 工厂下发确认记录 ## 2. 状态与枚举调整 ### 2.1 订单状态 `sales_order.order_status` 继续使用 `varchar(32)`,业务层限制枚举。 | 状态值 | 说明 | | --- | --- | | `draft` | 草稿 | | `pending_approve` | 待审核 | | `approved` | 已通过,待生成/确认下发工厂文本 | | `rejected` | 已退回 | | `pending_factory` | 已确认下发工厂,待工厂处理 | | `pending_driver` | 待司机接单 | | `accepted` | 已接单 | | `picked_up` | 已揽货 | | `delivered` | 已送达 | | `production` | 生产中 | | `shipped` | 已发货 | | `completed` | 已完成 | | `settled` | 已结算 | | `cancel_pending` | 取消申请中 | | `canceled` | 已取消 | ### 2.2 欠款生成模式 使用 `system_config` 保存。 | config_key | config_value | 说明 | | --- | --- | --- | | `arrears_generate_mode` | `delivered` | 默认,送达后生成欠款 | | `arrears_generate_mode` | `shipped` | 发货后生成欠款 | ## 3. 表结构调整清单 ### 3.1 `sales_order` 调整 新增字段: | 字段 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `commission_amount` | decimal(18,2) | 0.00 | 订单固定提成金额 | | `cancel_requested_by` | bigint | null | 取消申请人 | | `cancel_requested_at` | datetime | null | 取消申请时间 | | `cancel_reason` | varchar(255) | null | 取消原因 | | `cancel_opinion` | varchar(1000) | null | 取消说明 | | `cancel_previous_status` | varchar(32) | null | 进入 `cancel_pending` 前状态 | | `supplier_text_confirmed_at` | datetime | null | 确认下发工厂时间 | | `supplier_text_confirmed_by` | bigint | null | 确认下发人 | 索引: | 索引 | 字段 | 说明 | | --- | --- | --- | | `idx_sales_order_cancel_requested_by` | `cancel_requested_by` | 取消申请查询 | | `idx_sales_order_supplier_confirmed_at` | `supplier_text_confirmed_at` | 下发确认查询 | ### 3.2 新增 `product_category` 用于管理员维护产品分类,不再将工业品/日用品写死。 | 字段 | 类型 | 约束 | 说明 | | --- | --- | --- | --- | | `id` | bigint | PK | 主键 | | `category_name` | varchar(64) | not null | 分类名称 | | `category_code` | varchar(64) | not null unique | 分类编码 | | `sort_no` | int | default 0 | 排序 | | `status` | tinyint | default 1 | 1 启用,0 停用 | | `remark` | varchar(500) | null | 备注 | | `created_at` | datetime | default current_timestamp | 创建时间 | | `updated_at` | datetime | on update current_timestamp | 更新时间 | | `deleted` | tinyint | default 0 | 逻辑删除 | ### 3.3 `product` 调整 新增字段: | 字段 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `category_id` | bigint | null | 产品分类 ID | 保留 `category` 快照/冗余字段,方便历史兼容和展示。 索引: | 索引 | 字段 | | --- | --- | | `idx_product_category_id` | `category_id` | ### 3.4 新增 `order_supplier_text_log` 记录下发工厂文本生成与确认历史。 | 字段 | 类型 | 约束 | 说明 | | --- | --- | --- | --- | | `id` | bigint | PK | 主键 | | `order_id` | bigint | not null | 订单 ID | | `supplier_id` | bigint | not null | 工厂/供应商 ID | | `template_type` | varchar(64) | null | 模板类型 | | `text_content` | longtext | not null | 下发文本 | | `confirmed` | tinyint | default 0 | 是否确认下发 | | `confirmed_by` | bigint | null | 确认人 | | `confirmed_at` | datetime | null | 确认时间 | | `created_by` | bigint | null | 生成人 | | `created_at` | datetime | default current_timestamp | 创建时间 | 索引: | 索引 | 字段 | | --- | --- | | `idx_supplier_text_order_id` | `order_id` | | `idx_supplier_text_supplier_id` | `supplier_id` | ### 3.5 `logistics_task` 调整 现有字段基本满足手动创建司机任务。建议补充: | 字段 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `created_by` | bigint | null | 任务创建人 | | `canceled_at` | datetime | null | 任务取消时间 | | `canceled_by` | bigint | null | 任务取消人 | | `cancel_reason` | varchar(500) | null | 任务取消原因 | ### 3.6 `file_attachment` 调整 为阿里云 OSS 图片、视频和附件补充元数据。 | 字段 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `storage_provider` | varchar(32) | `aliyun_oss` | 存储服务商 | | `bucket_name` | varchar(128) | null | OSS Bucket | | `object_key` | varchar(1000) | null | OSS Object Key | | `content_type` | varchar(128) | null | MIME 类型 | | `file_ext` | varchar(32) | null | 扩展名 | | `file_category` | varchar(32) | null | image/video/document/export/import | 索引: | 索引 | 字段 | | --- | --- | | `idx_file_attachment_category` | `file_category` | | `idx_file_attachment_object_key` | `object_key` | ### 3.7 `customer_arrears` 调整 补充结算关闭字段: | 字段 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `generated_mode` | varchar(32) | null | `shipped` 或 `delivered` | | `settled_at` | datetime | null | 结算时间 | | `settled_by` | bigint | null | 结算操作人 | | `closed_at` | datetime | null | 关闭时间 | ### 3.8 `system_config` 初始化配置 建议初始化以下配置: | config_key | config_value | config_name | | --- | --- | --- | | `arrears_generate_mode` | `delivered` | 欠款生成模式 | | `logistics_timeout_hours` | `48` | 物流超时小时数 | | `inactive_customer_days` | `90` | 沉默客户周期 | | `inactive_customer_amount_threshold` | `1000` | 沉默客户金额阈值 | | `report_category_mapping` | `{}` | 报表分类映射 | | `aliyun_oss_bucket` | 空 | 阿里云 OSS Bucket | | `aliyun_oss_endpoint` | 空 | 阿里云 OSS Endpoint | | `aliyun_ai_region` | 空 | 阿里云 AI/OCR 地域 | 敏感凭证不写入数据库明文字段,使用环境变量或安全配置。 ## 4. 建议迁移 SQL 以下 SQL 是基于现有 `数据库.sql` 的增量调整示例,实际执行应通过 Alembic 迁移管理。 ```sql CREATE TABLE IF NOT EXISTS `product_category` ( `id` bigint NOT NULL COMMENT '主键', `category_name` varchar(64) NOT NULL COMMENT '分类名称', `category_code` varchar(64) NOT NULL COMMENT '分类编码', `sort_no` int NOT NULL DEFAULT 0 COMMENT '排序号', `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1启用 0停用', `remark` varchar(500) DEFAULT NULL COMMENT '备注', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` tinyint NOT NULL DEFAULT 0 COMMENT '逻辑删除:0否 1是', PRIMARY KEY (`id`), UNIQUE KEY `uk_product_category_code` (`category_code`), KEY `idx_product_category_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品分类表'; ALTER TABLE `product` ADD COLUMN `category_id` bigint DEFAULT NULL COMMENT '产品分类ID' AFTER `category`, ADD KEY `idx_product_category_id` (`category_id`); ALTER TABLE `sales_order` ADD COLUMN `commission_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '订单固定提成金额' AFTER `profit_rate`, ADD COLUMN `cancel_requested_by` bigint DEFAULT NULL COMMENT '取消申请人' AFTER `canceled_at`, ADD COLUMN `cancel_requested_at` datetime DEFAULT NULL COMMENT '取消申请时间' AFTER `cancel_requested_by`, ADD COLUMN `cancel_reason` varchar(255) DEFAULT NULL COMMENT '取消原因' AFTER `cancel_requested_at`, ADD COLUMN `cancel_opinion` varchar(1000) DEFAULT NULL COMMENT '取消说明' AFTER `cancel_reason`, ADD COLUMN `cancel_previous_status` varchar(32) DEFAULT NULL COMMENT '取消前状态' AFTER `cancel_opinion`, ADD COLUMN `supplier_text_confirmed_at` datetime DEFAULT NULL COMMENT '确认下发工厂时间' AFTER `cancel_previous_status`, ADD COLUMN `supplier_text_confirmed_by` bigint DEFAULT NULL COMMENT '确认下发人' AFTER `supplier_text_confirmed_at`, ADD KEY `idx_sales_order_cancel_requested_by` (`cancel_requested_by`), ADD KEY `idx_sales_order_supplier_confirmed_at` (`supplier_text_confirmed_at`); CREATE TABLE IF NOT EXISTS `order_supplier_text_log` ( `id` bigint NOT NULL COMMENT '主键', `order_id` bigint NOT NULL COMMENT '订单ID', `supplier_id` bigint NOT NULL COMMENT '工厂/供应商ID', `template_type` varchar(64) DEFAULT NULL COMMENT '模板类型', `text_content` longtext NOT NULL COMMENT '下发文本', `confirmed` tinyint NOT NULL DEFAULT 0 COMMENT '是否确认下发:0否 1是', `confirmed_by` bigint DEFAULT NULL COMMENT '确认人', `confirmed_at` datetime DEFAULT NULL COMMENT '确认时间', `created_by` bigint DEFAULT NULL COMMENT '生成人', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_supplier_text_order_id` (`order_id`), KEY `idx_supplier_text_supplier_id` (`supplier_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工厂下发文本记录表'; ALTER TABLE `logistics_task` ADD COLUMN `created_by` bigint DEFAULT NULL COMMENT '任务创建人' AFTER `factory_id`, ADD COLUMN `canceled_at` datetime DEFAULT NULL COMMENT '任务取消时间' AFTER `delivered_at`, ADD COLUMN `canceled_by` bigint DEFAULT NULL COMMENT '任务取消人' AFTER `canceled_at`, ADD COLUMN `cancel_reason` varchar(500) DEFAULT NULL COMMENT '任务取消原因' AFTER `canceled_by`; ALTER TABLE `file_attachment` ADD COLUMN `storage_provider` varchar(32) NOT NULL DEFAULT 'aliyun_oss' COMMENT '存储服务商' AFTER `file_size`, ADD COLUMN `bucket_name` varchar(128) DEFAULT NULL COMMENT 'OSS Bucket' AFTER `storage_provider`, ADD COLUMN `object_key` varchar(1000) DEFAULT NULL COMMENT 'OSS Object Key' AFTER `bucket_name`, ADD COLUMN `content_type` varchar(128) DEFAULT NULL COMMENT 'MIME类型' AFTER `object_key`, ADD COLUMN `file_ext` varchar(32) DEFAULT NULL COMMENT '扩展名' AFTER `content_type`, ADD COLUMN `file_category` varchar(32) DEFAULT NULL COMMENT '文件类别' AFTER `file_ext`, ADD KEY `idx_file_attachment_category` (`file_category`), ADD KEY `idx_file_attachment_object_key` (`object_key`(191)); ALTER TABLE `customer_arrears` ADD COLUMN `generated_mode` varchar(32) DEFAULT NULL COMMENT '欠款生成模式:shipped/delivered' AFTER `status`, ADD COLUMN `settled_at` datetime DEFAULT NULL COMMENT '结算时间' AFTER `reminded_at`, ADD COLUMN `settled_by` bigint DEFAULT NULL COMMENT '结算操作人' AFTER `settled_at`, ADD COLUMN `closed_at` datetime DEFAULT NULL COMMENT '关闭时间' AFTER `settled_by`; ``` ## 5. 初始化数据建议 ### 5.1 产品分类 ```sql INSERT INTO `product_category` (`id`, `category_name`, `category_code`, `sort_no`, `status`) VALUES (1, '工业品', 'industry', 1, 1), (2, '日用品', 'daily', 2, 1); ``` ### 5.2 系统配置 ```sql INSERT INTO `system_config` (`id`, `config_key`, `config_value`, `config_name`, `remark`, `status`) VALUES (1001, 'arrears_generate_mode', 'delivered', '欠款生成模式', 'shipped=发货后记账,delivered=送达后记账', 1), (1002, 'logistics_timeout_hours', '48', '物流超时小时数', '首个物流节点超过该小时数无流转提醒', 1), (1003, 'inactive_customer_days', '90', '沉默客户周期', '超过该天数未下单触发检查', 1), (1004, 'inactive_customer_amount_threshold', '1000', '沉默客户金额阈值', '低于该金额不提醒', 1), (1005, 'report_category_mapping', '{}', '报表分类映射', '按产品分类配置统计口径', 1); ``` ## 6. 数据一致性规则 | 场景 | 规则 | | --- | --- | | 客户去重 | `crm_customer.customer_name + mobile` 唯一 | | 产品快照 | 订单明细保存名称、规格、单位快照,历史订单不随产品变化 | | 取消审批 | 进入 `cancel_pending` 时保存 `cancel_previous_status`,拒绝取消后恢复 | | 下发确认 | 确认下发时写 `order_supplier_text_log`,订单状态变为 `pending_factory` | | 欠款生成 | 按 `arrears_generate_mode` 在 `shipped` 或 `delivered` 状态生成 | | 附件存储 | OSS 上传成功后必须写入 `file_attachment` | | 审计日志 | 所有关键写操作必须写入 `audit_log` | ## 7. Alembic 迁移要求 - 每次表结构变更必须生成独立 migration 文件。 - migration 文件必须包含 upgrade 和 downgrade。 - 初始化数据可以单独 migration,避免和结构变更混在一起。 - 生产环境禁止直接手工改表,必须通过迁移执行。 - 敏感配置如 OSS AccessKey、阿里云模型凭证不得写入 migration。