本文件记录 2026-08-31 已经逐项确认的设计决策,作为后续 plan.md、tasks.md 和接口契约的输入。
蓝湖“闪送”分组共 7 个页面,均已通过蓝湖 MCP 读取图片、设计标注、颜色与图层:
最初的蓝湖 7 页明确展示三种入口、取件与收件地址、联系人和电话、地址簿、起步价格及下单入口;后续用户端/骑手端原型明确包裹类别、数量、总重量、体积/规格、预约配送、交付 PIN、寄件图片以及基础配送、距离加价、加急和骑手小费明细。此前把精确重量和件数排除在范围外是错误判断,2026-09-07 起按设计稿补齐。
采用独立闪送订单模块,不复用 pos_order 或 taxi_order。
用户端 / 骑手端 / 平台端
|
v
ruoyi-admin
- 用户、骑手、平台 Controller
- 报价编排与地图路线适配器
- 闪送状态流转与自动完成任务
- 通知编排
|
v
ruoyi-system
- 闪送领域实体、Mapper、Service
- 计价配置、配送凭证、状态日志
- 共享地址所有权数据访问
外部地图 HTTP 集成位于 ruoyi-admin,保持 ruoyi-admin -> ruoyi-system 的模块依赖方向。SQL 只写入 updatesql/sql.md,由开发者手动执行。
GET /system/flashDelivery/homePOST /system/flashDelivery/quotePOST /system/flashDelivery/ordersGET /system/flashDelivery/ordersGET /system/flashDelivery/orders/{id}POST /system/flashDelivery/orders/{id}/cancelPOST /system/flashDelivery/orders/{id}/confirmReceiptGET /system/flashDelivery/rider/ordersGET /system/flashDelivery/rider/orders/{id}POST /system/flashDelivery/rider/orders/{id}/acceptPOST /system/flashDelivery/rider/orders/{id}/pickupPOST /system/flashDelivery/rider/orders/{id}/deliverGET /system/flashDelivery/admin/pricingPOST /system/flashDelivery/admin/pricingPUT /system/flashDelivery/admin/pricing/{id}DELETE /system/flashDelivery/admin/pricing/{id}GET /system/flashDelivery/admin/ordersGET /system/flashDelivery/admin/orders/{id}POST /system/flashDelivery/admin/orders/{id}/cancelPOST /system/flashDelivery/admin/orders/{id}/complete平台管理前端沿用 foodie-admin-vue 的 Vue 2、Element UI、动态菜单和权限指令,不额外引入 UI 或状态管理依赖:
src/api/flashDelivery/index.js 集中封装平台接口,页面不直接拼接请求。v-hasPermi 控制可见性,后端权限与状态机仍是最终安全边界。flashDelivery 对象中,key 集合保持一致。GET /system/address/getaddressGET /system/address/getaddressxqPOST /system/address/addressDELETE /system/address/{id}POST /system/address/{id}/top地址接口继续由现有收货与闪送场景共用,但所有用户接口都必须携带 token;后端按当前登录用户限制详情、保存、删除和置顶范围。
flash_delivery_order保存订单号、客户端请求号、用户、骑手、帮送/帮取业务场景、普通/加急配送等级、包裹类别、数量、总重量、规格、服务端生成的重量档、配送方式、预约时段、交付 PIN、取件与收件信息快照、路线距离、距离来源、预计时长、基础配送费、距离费、加急费、小费、总金额、匹配时段完整计价快照、币种、计价配置版本、备注、状态、并发版本号、关键操作时间与取消信息。
flash_delivery_pricing保存一套供帮送和帮取共享、全局互不重叠的运价时段;每个时段保存开始时间、结束时间、起送距离、起送价格、计价距离、计价金额、加急比例、最低加急费、配置版本、修改人和修改时间。保存即生效,删除即失效,不设置启停字段。
flash_delivery_order_image保存订单、凭证类型、图片 URL、排序、操作人类型、操作人和创建时间。凭证类型包括 SENDER、PICKUP 与 DELIVERY。
flash_delivery_order_log保存变更前状态、变更后状态、操作人类型、操作人 ID、原因和时间。
info_address增加置顶标记和置顶时间;列表按置顶时间与既有排序规则返回。
目标计价时刻 = NOW ? 当前报价时刻 : scheduledPickupStartAt
当前配置 = 按目标计价时刻匹配唯一统一运价时段
超出公里 = max(0, 路线距离公里 - 起送距离)
计费公里 = 超出公里 < 0.5 ? 0 : 超出公里 < 1 ? 1 : 超出公里
里程费用 = 计费公里 * (计价金额 / 计价距离)
普通配送费 = 起送价格 + 四舍五入到整数元的里程费用
加急费 = NORMAL ? 0 : max(最低加急费, 四舍五入(普通配送费 * 加急比例 / 100))
订单金额 = 普通配送费 + 加急费 + 骑手小费
普通计价规则与现有外卖订单一致,但闪送使用自己的统一时段配置表。起送价格和计价金额为正整数新台币;距离、比例与除法过程使用十进制定点数,里程费用和比例加急费采用 HALF_UP 四舍五入到整数元,最终价格不显示小数,也不执行外卖旧实现中金额达到 1000 后的千位特殊取整。立即订单按当前时刻、预约订单按预约开始时刻匹配时段;目标时刻没有匹配时段时拒绝报价和创建。路线预计时长继续返回和保存,仅用于履约展示,不参与计价。报价优先使用地图路线距离,失败时降级为直线距离;创建订单时服务端必须重新匹配时段、计算价格并核对客户端回传报价,完全一致后才能固化配置快照。
WAITING_ACCEPTANCE
|
| 骑手原子抢单
v
ACCEPTED
|
| 订单骑手 + 取件图片
v
PICKED_UP
|
| 订单骑手 + 送达图片
v
DELIVERED
|
| 用户确认 / 24小时自动完成 / 平台完成
v
COMPLETED
WAITING_ACCEPTANCE 和 ACCEPTED 允许订单用户取消。PICKED_UP 和 DELIVERED 只有平台能够介入取消。任一允许取消状态进入 CANCELLED。COMPLETED 与 CANCELLED 为终态。
order_id + user_id 校验。order_id + rider_id + expected_status 校验。address_id + user_id 校验,客户端 userId 不参与归属判断。@RequestBody、@RequestParam、@PathVariable 和 @RequestHeader,不接收 Map。支付、退款、分账、骑手收入、代购、垫付、商品金额、自动派单、动态附近骑手数、预计接单时间以及用户端、骑手端和商家端页面均不在本期范围。物品数量、总重量、体积/规格和骑手小费属于本期接口范围。
采用单一骑手列表接口,查询参数与现有骑手外卖订单列表保持一致。该方案让 App 的两类配送列表共享 page、size、tab、longitude、latitude 的调用方式,同时由服务端完成页签到状态的映射。原 /available 与 /mine 拆分接口、scene 与 serviceType 组合筛选不再保留。
没有采用“保留两个接口只改参数名”,因为 App 仍需维护两套请求和响应;也没有采用“列表继续返回完整详情对象”,因为列表页面只需要卡片字段,完整订单信息已有独立详情接口。
GET /system/flashDelivery/orders?page=1&size=10status、scene 或 serviceType,页面无需为了简单卡片组装筛选条件。GET /system/flashDelivery/rider/orders?page=1&size=10&tab=newTask&longitude=121.5&latitude=25.0newTask 的附近范围、取件点距离和排序,其他页签忽略坐标。newTask -> WAITING_ACCEPTANCE;toPickup -> ACCEPTED;delivering -> PICKED_UP;completed -> DELIVERED + COMPLETED;cancelled -> CANCELLED。除 newTask 外均只查询当前骑手本人。refund 页签。newTask 分页响应额外返回附近任务总数与最高订单金额,用于原型顶部摘要;不返回已移除的尖峰倍率,也不将订单金额表述为尚未实现的骑手收入。data 直接返回订单详情字段,不再使用 {order,images,logs} 包装。senderImageUrls、pickupImageUrls、deliveryImageUrls,避免 App 再按通用图片记录的类型分组。status、acceptedAt、pickedUpAt、deliveredAt、completedAt 和 cancelledAt 展示进度。平台详情继续保留 {order,images,logs} 供审计。data 顶层形状保持不变。/available、/mine 路由或旧参数兼容层。page、size 或 tab 非法时返回国际化业务错误;坐标必须成对出现并通过经纬度范围校验。WAITING_ACCEPTANCE,蓝湖中的待支付、去支付和支付倒计时不进入接口。deliveryType=URGENT 按 1 对 1 专送实现骑手独占;serviceType 只表示帮送或帮取业务场景。采用创建时快照方案。创建订单时将收件手机号去除首尾/内部空白、+、- 和圆括号但保留国家码数字,并以同样规则匹配当时已注册的普通用户:恰好匹配一名时保存其用户 ID 到 receiver_user_id;没有匹配或出现重复匹配时保存空值。该字段创建后不可因注册、手机号换绑或地址簿变化而自动改写。
没有采用每次查询时按当前手机号动态匹配,因为手机号回收或换绑会让新持有人读取历史订单;也不实现注册后追溯认领,因为这需要独立的手机号归属证明、认领审计和撤销流程,超出本次范围。
用户列表增加 role=sender/receiver,省略时保持原有 sender 行为。寄件人与绑定收件人都能查看详情和交付 PIN、确认签收;取消仍只允许订单创建人。列表和详情权限直接使用固化用户 ID,不使用请求中的手机号判断。
所有骑手接单入口先获取同一 Redisson 骑手锁 lock:delivery:rider:{riderId},再查询该骑手的有效外卖和闪送任务。抢 deliveryType=URGENT 订单时要求不存在未送达外卖及 ACCEPTED/PICKED_UP 闪送;抢普通外卖或 deliveryType=NORMAL 闪送时,如果已有 ACCEPTED/PICKED_UP 急送则拒绝。急送进入 DELIVERED、COMPLETED 或 CANCELLED 后解除独占。
同一骑手的外卖和闪送接单必须共享这把分布式锁,确保并发请求不能分别在两张订单表中同时通过“无冲突”检查。锁使用 Redisson 看门狗续期,不指定固定租期,并通过事务同步回调在提交或回滚完成后释放;获取超时、线程中断或 Redis 异常均拒绝本次接单。数据库订单条件更新继续负责防止不同骑手抢中同一订单。
serviceType 只区分 HELP_SEND 和 HELP_PICKUP;deliveryType 独立区分 NORMAL 和 URGENT;deliveryMode 继续区分 NOW 和 SCHEDULED。帮送和帮取共享同一套运价,只有 deliveryType=URGENT 启用加急计价和骑手独占。
平台每个时间段配置 startTime、endTime、startingDistance、startingFare、distance、freight、urgentRate 和 minimumUrgentFee。所有时间段全局互斥,不再按 serviceType 分组。立即订单按报价时刻匹配,预约订单按 scheduledPickupStartAt 匹配;日期只用于确定预约有效性,时间段按业务当地时间的时分命中。
普通配送费先沿用现有起送与超距规则计算:baseDeliveryFee = startingFare + distanceFee。普通配送 urgentFee=0;加急配送使用 urgentFee = max(minimumUrgentFee, roundHalfUp(baseDeliveryFee × urgentRate / 100))。骑手小费 tipAmount 是用户输入的独立非负整数 TWD,不参与加急费计算;最终 amount = baseDeliveryFee + urgentFee + tipAmount。
报价响应返回配置 ID/版本、距离及来源、普通运价明细、baseDeliveryFee、urgentRate、minimumUrgentFee、urgentFee、tipAmount 和 amount。创建订单时客户端回传配置 ID/版本以及 baseDeliveryFee、distanceFee、urgentFee、amount;服务端重新取路线、匹配时间段并计算费用,逐项精确一致才创建。任何配置或费用变化都不得静默创建订单,响应必须携带最新报价供 App 重新确认。
请求保存 packageType、quantity、totalWeightKg 和可选 specification。数量必须是正整数;总重量精确到两位小数且大于 0、不超过 20kg;规格说明去除首尾空格后最长 255 字符。packageSize 不再由客户端提交,而由服务端按总重量生成并作为订单快照保留给骑手与平台使用。
费用字段是报价和订单应付金额快照,不代表已经完成资金扣款。订单创建后仍直接进入 WAITING_ACCEPTANCE,不增加待支付状态、支付倒计时、支付回调或退款流程;骑手小费也只作为订单费用明细和接单参考展示。