plan.md 11 KB

实施计划:闪送配送服务

分支test | 日期:2026-08-31 | 规格spec.md

摘要

在既有独立闪送模块上补充包裹类别/重量档、立即或预约配送、可选 PIN 交付、寄件图片、费用明细、页面状态分组、骑手公开摘要和角色安全响应。服务端继续负责路线报价、骑手原子抢单、取件/送达图片、用户签收、24 小时自动完成和平台介入;支付、退款、结算、骑手收入与自动派单仍不在本期范围。

技术上下文

  • 语言/版本:Java 21
  • 主要依赖:Spring Boot、Spring MVC、Spring Security、MyBatis/MyBatis-Plus、Apache HttpClient 4、fastjson2
  • 存储:MySQL;迁移仅记录在 updatesql/sql.md
  • 测试:JUnit 5、Mockito、Spring MockMvc、MyBatis XML 解析测试
  • 目标平台:现有 RuoYi 多模块 REST API 服务端
  • 性能目标:列表分页;抢单和状态流转单条条件更新;地图超时后立即降级
  • 约束:保持 ruoyi-admin -> ruoyi-system;Controller 不接收 Map;App 身份只从 token 解析;业务错误全部 i18n;不执行数据库迁移
  • 范围:24 个闪送/地址端点、4 张新表、1 张地址表增量、五套语言资源、定向自动化测试

规范检查

  • 独立 flash_delivery_* 模型,不复用 pos_order/taxi_order
  • HTTP 集成只放在 ruoyi-admin
  • Controller 使用显式 DTO 和参数注解
  • 用户与骑手接口通过 token 取身份,平台接口使用明确权限
  • 金额、距离和状态由服务端计算与校验
  • 地址、订单和凭证执行所有权/角色校验
  • SQL 只写 updatesql/sql.md
  • 支付能力明确延期

源码结构

ruoyi-system/src/main/java/com/ruoyi/system/
├── domain/flash/                 # 订单、计价、图片、日志实体
└── mapper/flash/                 # 持久化、原子抢单、条件状态流转
ruoyi-system/src/main/resources/mapper/flash/*.xml

ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/
├── controller/                   # 用户、骑手、平台 Controller
├── dto/                          # 明确请求 DTO 与响应视图
├── service/                      # 报价、状态机、权限和脱敏
├── route/                        # Google Routes 与直线降级
└── task/                         # 送达 24 小时自动完成

ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/
ruoyi-system/src/test/java/com/ruoyi/system/mapper/flash/

地址簿沿用 InfoAddressIInfoAddressServiceInfoAddressController,增加请求 DTO、置顶字段、按用户条件查询/更新及所有权测试。

核心实现决策

  1. FlashDeliveryApplicationService 是事务边界;报价只读,创建和每个状态操作同时写订单与日志。
  2. 抢单执行带 status='WAITING_ACCEPTANCE' AND rider_id IS NULL 的单条 UPDATE;受影响行数不是 1 即已被抢走。
  3. 其余状态变更携带订单、操作者、预期状态和版本条件,阻止并发覆盖。
  4. (user_id, client_request_id) 唯一键保证创建幂等;冲突时返回原订单。
  5. Google Routes API 字段掩码仅取距离与时长;异常、非 2xx、空路线或无效距离降级为 Haversine。
  6. 待抢单使用专用脱敏视图;抢单后只有订单骑手可见完整电话和详细地址。
  7. 寄件图片在创建事务中写入;取件/送达图片在状态更新事务中写入。三类凭证均校验 1 至 9 个 HTTP(S) URL,取件和送达不得为空。
  8. 预约订单沿用 WAITING_ACCEPTANCE,可抢查询和原子抢单 SQL 同时限制 scheduled_pickup_start_at <= now,不增加仅为调度使用的新状态。
  9. PIN 默认启用并由服务端生成四位数字;用户详情可见,骑手响应始终剔除,送达事务先校验 PIN 再写凭证和状态。
  10. 用户/骑手使用业务视图,平台详情保留完整审计实体;待抢视图只增加类别、重量档、配送时段、预计时长和市/区级路线信息。
  11. 自动任务扫描送达满 24 小时订单并逐条条件完成,和用户/平台并发时只允许一次成功。

补充原型增量实施批次

  1. 更新规格、API、数据模型和 updatesql/sql.md,明确旧表增量字段及索引。
  2. 先扩展计价器、Mapper XML、应用服务和 Controller 契约测试并确认红灯。
  3. 增加请求/响应 DTO、实体字段、计价明细与角色安全映射。
  4. 实现预约可抢条件、PIN 校验、寄件凭证和状态分组分页。
  5. 使用 JDK 21 运行闪送定向测试、MyBatis XML 测试、模块构建与差异检查。

验证策略

测试先行:先写测试并确认因实现缺失而失败,再添加最小生产代码。最终统一执行:

$env:JAVA_HOME='C:\Users\qmj\.jdks\graalvm-jdk-21.0.7'
$env:PATH="$env:JAVA_HOME\bin;$env:PATH"
mvn -pl ruoyi-system -am -Dtest=FlashDeliveryMapperXmlTest -Dsurefire.failIfNoSpecifiedTests=false test
mvn -pl ruoyi-admin -am -Dtest='FlashDelivery*Test,InfoAddressControllerSecurityTest' -Dsurefire.failIfNoSpecifiedTests=false test
mvn -pl ruoyi-admin -am -DskipTests package
git diff --check

复杂度跟踪

无规范违例。四张新表分别承担订单快照、可变计价、图片凭证和审计日志;应用层留在 admin 是外部 HTTP 依赖和模块边界共同决定的。

平台管理前端增量(2026-09-01)

目标与架构

目标:在 foodie-admin-vue 增加可直接运营的闪送价格配置和订单管理入口,完整消费现有六个平台 API,不修改后端接口语义。

架构:继续使用若依动态菜单加载两个 Vue 2 + Element UI 页面。API 请求集中在一个模块,状态/服务类型映射、分页归一化、操作可见性和价格校验集中在可直接由 Node 测试的纯函数模块;订单详情拆为独立展示组件,避免列表页面同时承担全部渲染职责。

技术栈:Vue 2.6、Element UI 2.15、Vue i18n 8、若依 request、动态 sys_menu、Node 内置 node:test

全局约束

  • 不新建分支或 worktree,后端与平台前端均在当前 test 分支工作。
  • 不增加 npm 依赖,不引入新的 UI 或状态管理框架。
  • 所有新增用户可见文本使用 flashDelivery.*,并同步简中、繁中、英文、越南文。
  • 前端文件保留 CRLF;只修改本功能文件,不格式化既有大文件。
  • 菜单和权限 SQL 只追加到 foodie_server/updatesql/sql.md,不直接执行数据库变更。
  • 后端分页响应读取 data.recordsdata.total,不得套用若依传统 rows/total 响应。
  • 操作成功后重新读取服务端数据;操作失败不提前修改本地订单状态或价格。

文件结构

foodie-admin-vue/
├── package.json                                      # 增加闪送定向契约测试命令
├── tests/flash-delivery.test.cjs                    # 请求配置、页面规则和四语言真实行为
└── src/
    ├── api/flashDelivery/contracts.js               # 可执行的六个平台请求配置构造器
    ├── api/flashDelivery/index.js                   # 调用若依 request 的六个平台接口
    ├── views/flashDelivery/shared.js                # 枚举、分页、价格校验、操作可见性
    ├── views/flashDelivery/pricing/index.vue        # 三种服务价格列表与编辑弹窗
    ├── views/flashDelivery/orders/index.vue         # 筛选、分页、取消和完成
    ├── views/flashDelivery/orders/OrderDetail.vue   # 完整详情、图片和日志
    └── api/language/language.{zh_CN,zh_TW,en_US,vi}.js

foodie_server/
├── specs/024-flash-delivery/{spec,design,plan,tasks}.md
└── updatesql/sql.md                                 # 父菜单、两个页面及六项权限归位

接口和页面数据流

  1. GET /system/flashDelivery/admin/pricing 返回数组;价格页只显示服务端实际存在的配置,不创建前端默认价格。
  2. PUT /system/flashDelivery/admin/pricing/{serviceType} 提交七个完整字段。弹窗先执行纯函数校验,成功响应覆盖列表对应行。
  3. GET /system/flashDelivery/admin/orders 原样提交 pageNum,pageSize,status,serviceType,orderNo,userId,riderIdnormalizePageresponse.data 提取 records,total,current,size
  4. GET /system/flashDelivery/admin/orders/{id} 返回 {order,images,logs};打开详情前先清空旧数据,失败时保持弹窗关闭。
  5. POST .../{id}/cancel 提交 {reason},只对非 COMPLETED/CANCELLED 状态显示并要求二次确认。
  6. POST .../{id}/complete 无请求体,只对 DELIVERED 状态显示并要求二次确认。

交互设计

  • 价格页使用紧凑表格展示三种服务和版本信息;“编辑”打开宽度 640px 的表单弹窗,金额两位小数、距离单位为米、倍率最小为 1。
  • 订单页顶部使用可折叠行内筛选,表格展示订单号、服务、状态、用户、骑手、距离、金额和创建时间。
  • 详情使用 960px 弹窗,按“订单概况、取送地址、费用与路线、履约与 PIN、图片凭证、状态日志”分区;图片支持预览,空集合显示空状态。
  • 状态使用 Element UI 标签,同时显示本地化文字,避免只靠颜色传达。
  • 表格设置固定操作列和横向滚动容器;页面自身不产生横向溢出。

错误处理

  • 列表和价格请求使用 finally 关闭 loading;后端统一错误拦截负责消息提示。
  • 价格校验失败由表单显示本地化字段错误,不发送请求。
  • 详情请求开始时清空旧详情,请求失败不打开弹窗。
  • 取消原因去除首尾空白后必须非空;取消/完成成功关闭操作层并刷新列表,若详情仍打开则重新读取详情。

测试驱动批次

  1. 先创建 tests/flash-delivery.test.cjs 和 npm 命令,直接执行请求配置构造器、分页、价格校验、状态操作与四语言对象;运行后必须因目标模块不存在而失败。
  2. 实现 API 请求配置、API 封装和 shared.js,使请求、分页、价格校验和状态规则测试转绿。
  3. 实现价格页面与四语言资源,使价格字段和语言一致性测试转绿。
  4. 实现订单页面、详情组件和介入操作;页面调用已经通过测试的状态操作纯函数,权限指令通过 ESLint、构建和页面检查验证。
  5. 追加幂等菜单 SQL,再统一执行定向测试、ESLint、生产构建和两个仓库差异检查。

验证命令

Set-Location E:\QtwCode\foodie\foodie-admin-vue
npm run test:flash-delivery
npx eslint src/api/flashDelivery/index.js src/views/flashDelivery tests/flash-delivery.test.cjs
npm run build:prod
git diff --check

Set-Location E:\QtwCode\foodie\foodie_server
git diff --check