Data Model: 商家自配送 (029)
Date: 2026-09-08 | 依据: spec.md + research.md
所有 DDL 写入 updatesql/sql.md(标注日期+用途),不直接执行(项目规则)。
表变更
1. pos_store(现有表,加 2 列)
| 列 |
类型 |
说明 |
self_delivery_enabled |
TINYINT(1) NOT NULL DEFAULT 0 |
自配送开关(配置主体=该门店;夜市主门店行开启=整个夜市生效) |
self_delivery_mode |
TINYINT(1) NULL |
时段模式:NULL/0=未设置,1=全天,2=自定义(按星期);仅 enabled=1 时有意义 |
-- 2026-09-08 029 商家自配送:门店配置
ALTER TABLE pos_store ADD COLUMN self_delivery_enabled TINYINT(1) NOT NULL DEFAULT 0 COMMENT '自配送开关(夜市主门店行开启=旗下摊位统一生效)';
ALTER TABLE pos_store ADD COLUMN self_delivery_mode TINYINT(1) DEFAULT NULL COMMENT '自配送时段模式:1=全天,2=自定义按星期';
2. pos_store_self_delivery_hours(新表,镜像 operating_hours)
| 列 |
类型 |
说明 |
id |
BIGINT PK AUTO_INCREMENT |
|
md_id |
INT NOT NULL |
配置主体门店 id(普通门店=自身;夜市=夜市主门店行 id,不是摊位 id) |
day_of_week |
TINYINT NOT NULL |
ISO 星期:1=周一 … 7=周日(对齐 OperatingHours 现有取值) |
start_time |
VARCHAR(5) NOT NULL |
HH:mm |
end_time |
VARCHAR(5) NOT NULL |
HH:mm,必须 > start_time(同段内不允许跨天) |
约束:UNIQUE (md_id, day_of_week, start_time)。某天无记录 = 该天无自配送(对齐营业时段"未配即不营业"语义)。
-- 2026-09-08 029 商家自配送:时段表
CREATE TABLE pos_store_self_delivery_hours (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
md_id INT NOT NULL COMMENT '配置主体门店id(夜市场景为夜市主门店行)',
day_of_week TINYINT NOT NULL COMMENT 'ISO星期:1=周一..7=周日',
start_time VARCHAR(5) NOT NULL COMMENT '开始时刻 HH:mm(含)',
end_time VARCHAR(5) NOT NULL COMMENT '结束时刻 HH:mm(不含)',
PRIMARY KEY (id),
UNIQUE KEY uk_sd_hours (md_id, day_of_week, start_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='门店自配送时段(按星期)';
3. pos_order(现有表,加 1 列)
| 列 |
类型 |
说明 |
self_delivery |
TINYINT(1) NOT NULL DEFAULT 0 |
下单判定快照:1=商家自配送(不进骑手池、运费归 sh_id)。终身不变,存量单默认 0 |
-- 2026-09-08 029 商家自配送:订单判定快照
ALTER TABLE pos_order ADD COLUMN self_delivery TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1=商家自配送(下单时快照,不进骑手池,运费归sh_id)';
4. user_billing(现有表,无 DDL,启用新 type 值)
- 现有用途:type="0" 分成(商家/骑手,dedup 键 userId+type+ddId)、type="3" 到付用户账单。
- type="5" 固定为自配送运费入账。原商家端“金额解冻”业务已停止,页面筛选和展示均以“自配送运费”为准;不新增账单类型,避免与商品分成 type="0" 的去重键冲突。
- 字段赋值:
user_id=pos_order.sh_id(普通商家=商家、夜市摊位单=夜市主,见 research F3)、amount=freight 全额、divvy=0、divvy_rate=0、user_type="1"、md_id、dd_id、state="0"、illustrate=自配送运费(i18n)。dedup 键沿用 (userId, type, ddId)——type 不同不与商品分成冲突。
- 到付自配送单:送达时用户账单(type="3")
payment_id=sh_id(镜像骑手送达的 qsId 归因,research F13)。
实体/Mapper 变更(Java)
| 层 |
文件 |
变更 |
| Entity |
ruoyi-system/.../domain/PosStore.java |
+selfDeliveryEnabled, +selfDeliveryMode |
| Entity |
ruoyi-system/.../domain/PosOrder.java |
+selfDelivery |
| Entity(新) |
ruoyi-system/.../domain/PosStoreSelfDeliveryHours.java |
新实体(MyBatis-Plus 注解风格对齐 OperatingHours) |
| Mapper(新) |
ruoyi-system/.../mapper/PosStoreSelfDeliveryHoursMapper.java |
MyBatis-Plus BaseMapper,无需 XML(简单 CRUD) |
| Service(新) |
ruoyi-admin/.../order/SelfDeliveryService.java |
配置解析(含摊位→夜市主门店解析)、时段判定入口、运费入账;仅依赖 system 实体+服务,符合模块边界 |
| Util(新) |
ruoyi-admin/.../utils/SelfDeliveryUtil.java |
纯函数:matches(hours, enabled, mode, target),[start,end) 语义,可单测 |
状态机(自配送外送单,type=0)
state: 0 待接单 ──商家接单──> 1 已接单 ──商家出餐──> 2 已出餐 ──确认送达──> 3 已完成
deliveryStatus: 0 ────────────────────────────────(可选:开始配送──> 2 配送中)──────> 3 已送达
- 商家接单/出餐:
requireRiderAssigned 对 self_delivery=1 放行(现状 F2 会拦)
- "开始配送"(可选):state=2 且 deliveryStatus=0 → deliveryStatus=2;重复操作拒绝
- "确认送达":state=2 且 deliveryStatus∈{0,2} → 原子置 deliveryStatus=3 + state=3 + sdTime=now;随后 completeSideEffects(商品分成 + 自配送运费入账 + 到付归因)
- 满足管理端不变量(research F7):delivery=3 ⇒ state=3
- 售后/取消:沿用现有规则,无骑手环节
校验规则(配置保存)
- 自定义模式必须至少一段时段(否则拒绝)
- 每段
end_time > start_time(字符串比较即可,HH:mm 定长)
- day_of_week ∈ [1,7];start/end 匹配
^([01]\d|2[0-3]):[0-5]\d$
- 同一星期内时段不得重叠(相邻衔接允许:前段结束=后段开始不算重叠)
- 同 md_id + 星期 + start 唯一(数据库约束兜底)
- 保存为整体覆盖式(先删 md_id 全部再插入,与营业时段保存一致)