# 实施计划：闪送配送服务

**分支**：`test` | **日期**：2026-08-31 | **规格**：[spec.md](./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 张地址表增量、五套语言资源、定向自动化测试

## 规范检查

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

## 源码结构

```text
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/
```

地址簿沿用 `InfoAddress`、`IInfoAddressService` 和 `InfoAddressController`，增加请求 DTO、置顶字段、按用户条件查询/更新及所有权测试。

## 核心实现决策

1. `FlashDeliveryApplicationService` 是事务边界；报价只读，创建和每个状态操作同时写订单与日志。
2. 抢单执行带 `status='WAITING_ACCEPTANCE' AND rider_id IS NULL` 的单条 UPDATE；受影响行数不是 1 即已被抢走。
3. 其余状态变更携带订单归属、操作者归属和预期状态条件，阻止并发覆盖；`version` 字段随每次更新递增，仅作审计参考，不作为更新条件。
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 测试、模块构建与差异检查。

## 验证策略

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

```powershell
$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.records` 和 `data.total`，不得套用若依传统 `rows/total` 响应。
- 操作成功后重新读取服务端数据；操作失败不提前修改本地订单状态或价格。

### 文件结构

```text
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,riderId`；`normalizePage` 从 `response.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、生产构建和两个仓库差异检查。

### 验证命令

```powershell
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
```

## 外卖同规则时段运价增量（2026-09-01）

本节替代上方“平台管理前端增量”中的单行价格配置接口、七字段表单和两位小数计价描述；订单管理与平台介入设计继续有效。

### 目标与架构

**目标**：将闪送计价改为与现有外卖订单一致的“当前时段 + 起送距离/价格 + 计价距离/金额”规则，移除最低价、时长费、尖峰倍率和启停状态，并让平台在独立闪送入口维护三种服务的多个运价时段。

**架构**：`flash_delivery_pricing` 继续作为闪送独立配置表，但从每服务单行改为每服务多时段；`flash_delivery_pricing_lock` 为三种服务各保存一行事务锁。`FlashDeliveryPricingMapper` 在数据库当前时间下返回全部匹配时段、提供重叠计数并用 `SELECT ... FOR UPDATE` 串行化同服务写入；应用服务要求当前时段唯一，异常多行时显式失败。`FlashDeliveryPricingCalculator` 只负责距离边界和整数 TWD 计算；应用服务负责 CRUD、版本、订单快照和业务错误。平台前端复用现有价格页与权限，改为时段新增、修改和删除。

**技术栈**：JDK 21、Spring Boot、MyBatis-Plus/MyBatis、MySQL、JUnit 5/Mockito；Vue 2.6、Element UI 2.15、Vue i18n 8、Node `node:test`。

### 全局约束

- 不新建分支或 worktree，后端与平台前端都在当前 `test` 分支工作。
- 不直接执行数据库迁移；删除测试阶段旧计价数据和字段的 SQL 只追加到 `updatesql/sql.md`。
- 路线预计时长继续保存和展示，但不得参与计价。
- 起送价格、计价金额、里程费用和最终金额均为整数 TWD；计算结果使用 `RoundingMode.HALF_UP` 取整到 1 元，不做千位特殊取整。
- 同一服务类型的时段使用开始包含、结束不包含语义且不得重叠；写操作先取得服务类型数据库行锁再查重，保存即生效，删除即失效，不提供 `enabled`。
- Controller 使用明确 DTO 和显式注解，平台配置接口继续使用 `flash:pricing:list/edit`。
- 前端新增可见文本同步简中、繁中、英文和越南文，文件保留 CRLF，不增加 npm 依赖。
- 只修改闪送功能所需文件，不触碰工作区原有脏文件。

### 文件职责

```text
foodie_server/
├── ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/
│   ├── FlashDeliveryPricing.java          # 时段运价实体
│   └── FlashDeliveryOrder.java            # 整数金额与运价快照
├── ruoyi-system/src/main/java/com/ruoyi/system/mapper/flash/
│   └── FlashDeliveryPricingMapper.java    # 当前时段与重叠查询
├── ruoyi-system/src/main/resources/mapper/flash/
│   └── FlashDeliveryOrderMapper.xml       # 新订单快照字段映射
├── ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/
│   ├── controller/FlashDeliveryAdminController.java
│   ├── dto/FlashDeliveryPricingRequest.java
│   ├── dto/FlashDeliveryPriceBreakdown.java
│   ├── dto/FlashDeliveryQuoteView.java
│   ├── dto/FlashDeliveryServiceView.java
│   └── service/{FlashDeliveryApplicationService,FlashDeliveryPricingCalculator}.java
├── ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/
│   ├── controller/FlashDeliveryControllerContractTest.java
│   └── service/{FlashDeliveryApplicationServiceTest,FlashDeliveryPricingCalculatorTest}.java
├── ruoyi-admin/src/main/resources/i18n/messages_{zh_CN,zh_TW,en_US,vi}.properties
└── updatesql/sql.md

foodie-admin-vue/
├── src/api/flashDelivery/{contracts,index}.js
├── src/views/flashDelivery/shared.js
├── src/views/flashDelivery/pricing/index.vue
├── src/views/flashDelivery/orders/OrderDetail.vue
├── src/api/language/language.{zh_CN,zh_TW,en_US,vi}.js
└── tests/flash-delivery.test.cjs
```

### 后端接口与数据流

1. `FlashDeliveryPricing` 使用 `startTime,endTime,startingDistance,startingFare,distance,freight`；距离为正十进制公里，金额为正 `Long`。
2. `selectCurrent(serviceType)` 使用数据库 `CURTIME()` 和 `start_time <= CURTIME() AND end_time > CURTIME()` 返回全部匹配时段；找不到时返回无可用运价错误，超过一条时返回配置重叠错误，不用 `LIMIT 1` 掩盖异常。
3. `countOverlaps(serviceType,startTime,endTime,excludedId)` 使用 `start_time < endTime AND end_time > startTime` 检测重叠。新增、修改和删除在事务中先锁定 `flash_delivery_pricing_lock` 对应服务行；跨服务修改按字典序锁定旧、新服务，再校验并写入。
4. 计价器签名改为 `calculateBreakdown(FlashDeliveryPricing pricing, int distanceMeters)`。超出公里小于 `0.5` 时计费公里为 `0`，小于 `1` 时为 `1`，否则使用实际超出公里；`distanceFee` 四舍五入为 `Long`，`amount=startingFare+distanceFee`。
5. `GET /admin/pricing` 返回全部时段；`POST /admin/pricing` 新增；`PUT /admin/pricing/{id}` 按版本更新；`DELETE /admin/pricing/{id}` 删除。新增、修改和删除都使用 `flash:pricing:edit`。
6. `home` 对三种服务分别读取当前时段，只返回有配置的服务；`quote/create` 每次重新读取当前时段并计算。订单固化 `pricingId,pricingVersion,pricingStartTime,pricingEndTime,startingDistance,startingFare,distance,freight,distanceFee`。
7. 报价与公开服务 DTO 移除旧计价字段，保留 `estimatedDurationSeconds`；金额 DTO 和订单实体改为整数 `Long`。

### 平台前端交互

- 价格页表格按服务类型、开始时间排序，列出起止时间、起送距离/价格、计价距离/金额、版本和更新时间。
- “新增时段”打开空表单；“编辑”回填完整配置；“删除”二次确认后请求服务端并刷新列表。
- 表单校验开始时间早于结束时间、距离为正数、金额为正整数，并用当前列表预检同服务时段重叠；服务端仍执行最终重叠校验。
- 页面不显示启停、最低价、每分钟价格或尖峰倍率。配置保存后立即生效，无匹配时段时由后端阻止报价。
- 订单详情展示匹配时段、起送距离/价格、计价距离/金额、里程费用和整数总额；预计时长仍显示在路线信息中。

### TDD 批次

1. 先改 `FlashDeliveryPricingCalculatorTest`，覆盖起送内、超出 `0.49/0.50/0.99/1.20` 公里、非 1 公里计价距离、`.5` 金额四舍五入及非法配置；确认旧实现编译或断言失败。
2. 再改应用服务和 Controller 契约测试，覆盖当前时段读取、无时段、重叠新增/修改、版本冲突、删除、整数快照和新路由；确认旧接口失败。
3. 实现实体、并发锁表、Mapper、DTO、计算器、应用服务、Controller 和四语言后端错误，使定向后端测试转绿。
4. 先改 Node 测试，要求新增/修改/删除请求、六字段载荷、时段/整数校验、页面权限和四语言新 key，并确认旧前端失败。
5. 实现前端请求、纯函数、价格页、订单详情与四语言，使 Node 测试转绿。
6. 追加开发阶段重建 SQL，删除旧闪送测试表并创建运价锁、时段配置及新订单快照最终结构；不执行 SQL。

### 验证命令

```powershell
Set-Location E:\QtwCode\foodie\foodie_server
$env:JAVA_HOME='C:\Users\qmj\.jdks\graalvm-jdk-21.0.7'
$env:PATH="$env:JAVA_HOME\bin;$env:PATH"
mvn -pl ruoyi-admin -am -Dtest='FlashDeliveryPricingCalculatorTest,FlashDeliveryApplicationServiceTest,FlashDeliveryControllerContractTest,FlashDeliveryOrderViewTest,FlashDeliveryRiderOrderListViewTest,FlashDeliveryI18nContractTest' -Dsurefire.failIfNoSpecifiedTests=false test
mvn -pl ruoyi-admin -am -DskipTests package
git diff --check

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

# 闪送 App 订单列表与详情精简 Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** 将用户端和骑手端闪送订单接口收敛为与页面一致的轻量列表与直接详情响应，并让骑手列表复用外卖订单的分页、页签和位置参数。

**Architecture:** Controller 只暴露新的显式查询参数；`FlashDeliveryApplicationService` 在数据库分页前完成用户归属、骑手页签、预约可抢和附近距离条件。列表分别使用用户/骑手卡片 DTO，App 详情使用继承订单业务字段的直接 DTO；平台审计详情继续独立保留实体、图片和日志。

**Tech Stack:** JDK 21、Spring Boot、MyBatis-Plus、MySQL、JUnit 5、Mockito、Lombok。

## Global Constraints

- 不新建分支或 worktree，在当前 `test` 分支连续实施。
- 不保留开发测试阶段的 `/orders/available`、`/orders/mine`、`scene`、`status`、`serviceType` 兼容入口。
- 用户和骑手身份只从 `@RequestHeader String token` 解析；查询参数全部显式使用 `@RequestParam`。
- App 列表和详情禁止返回实体、幂等号、内部用户 ID、计价审计字段或原始状态日志。
- 骑手接单前显示完整取送文字地址，但不返回联系人、电话、实际 PIN、精确坐标或履约图片。
- 后端业务错误同步 `messages.properties`、`messages_zh_CN.properties`、`messages_zh_TW.properties`、`messages_en_US.properties` 和 `messages_vi.properties`。
- 不执行数据库迁移；本次接口精简不需要表结构变更。

---

### Task 1: 固定 Controller 与 DTO 契约

**Files:**
- Modify: `ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryControllerContractTest.java`
- Delete: `ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAvailableOrderViewTest.java`
- Create: `ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderListViewTest.java`
- Modify: `ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryOrderViewTest.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryUserOrderListView.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderListView.java`
- Create: `ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderPageView.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryOrderDetailView.java`
- Delete: `ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAvailableOrderView.java`
- Delete: `ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryImageView.java`
- Delete: `ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryLogView.java`

**Interfaces:**
- Produces: `FlashDeliveryUserOrderListView` for user cards.
- Produces: `FlashDeliveryRiderOrderListView` for all rider tabs.
- Produces: `FlashDeliveryRiderOrderPageView(records,total,current,size,nearbyTaskCount,highestOrderAmount)`.
- Produces: direct `FlashDeliveryOrderDetailView extends FlashDeliveryOrderView` with `senderImageUrls,pickupImageUrls,deliveryImageUrls,rider,deliveryPinCode`.

- [ ] **Step 1: Write failing reflection and whitelist tests**

```java
Method userOrders = FlashDeliveryUserController.class.getMethod(
        "orders", String.class, int.class, int.class);
Method riderOrders = FlashDeliveryRiderController.class.getMethod(
        "orders", String.class, int.class, int.class, String.class,
        BigDecimal.class, BigDecimal.class);
assertThrows(NoSuchFieldException.class,
        () -> FlashDeliveryOrderDetailView.class.getDeclaredField("order"));
assertTrue(riderFields.containsAll(Set.of("pickupAddress", "deliveryAddress",
        "pickupDistanceMeters", "pinRequired")));
```

- [ ] **Step 2: Run tests and verify the old contract fails**

```powershell
$env:JAVA_HOME='C:\Users\qmj\.jdks\graalvm-jdk-21.0.7'
$env:PATH="$env:JAVA_HOME\bin;$env:PATH"
mvn -pl ruoyi-admin -am -Dtest='FlashDeliveryControllerContractTest,FlashDeliveryOrderViewTest,FlashDeliveryRiderOrderListViewTest' -Dsurefire.failIfNoSpecifiedTests=false test
```

Expected: compilation or reflection failure because the old controllers and wrapper DTO still exist.

- [ ] **Step 3: Add the minimal DTO types**

```java
@Data
public class FlashDeliveryOrderDetailView extends FlashDeliveryOrderView {
    private List<String> senderImageUrls = List.of();
    private List<String> pickupImageUrls = List.of();
    private List<String> deliveryImageUrls = List.of();
    private FlashDeliveryRiderSummaryView rider;
    private String deliveryPinCode;
}
```

`FlashDeliveryUserOrderListView` 固定卡片字段为订单标识、服务/包裹、状态、配送方式/预约时段、取送文字地址、预计时长、金额、送达时间和创建时间。`FlashDeliveryRiderOrderListView` 固定增加重量档、`pinRequired`、`pickupDistanceMeters` 和路线距离。分页 DTO 只使用上述六个分页/摘要属性。

- [ ] **Step 4: Run DTO tests**

```powershell
mvn -pl ruoyi-admin -am -Dtest='FlashDeliveryControllerContractTest,FlashDeliveryOrderViewTest,FlashDeliveryRiderOrderListViewTest' -Dsurefire.failIfNoSpecifiedTests=false test
```

Expected: DTO whitelist tests pass; controller reflection test remains red until Task 2.

### Task 2: 实现轻量列表、统一骑手页签和直接详情

**Files:**
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryUserController.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryRiderController.java`
- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationService.java`
- Modify: `ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationServiceTest.java`
- Modify: `ruoyi-admin/src/main/resources/i18n/messages.properties`
- Modify: `ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties`
- Modify: `ruoyi-admin/src/main/resources/i18n/messages_zh_TW.properties`
- Modify: `ruoyi-admin/src/main/resources/i18n/messages_en_US.properties`
- Modify: `ruoyi-admin/src/main/resources/i18n/messages_vi.properties`

**Interfaces:**
- Consumes: Task 1 DTOs.
- Produces: `userOrders(Long userId,int page,int size)`.
- Produces: `riderOrders(Long riderId,int page,int size,String tab,BigDecimal longitude,BigDecimal latitude)`.
- Produces: `riderDetail(Long riderId,Long orderId)` returning `FlashDeliveryOrderDetailView`, never `Object`.

- [ ] **Step 1: Write failing service tests**

```java
var userPage = fixture.service.userOrders(7L, 1, 10);
var newTasks = fixture.service.riderOrders(8L, 1, 10, "newTask",
        new BigDecimal("121.5"), new BigDecimal("25.0"));
assertEquals("台北市中山區南京東路二段 100 號",
        newTasks.getRecords().get(0).getPickupAddress());
assertNull(fixture.service.riderDetail(8L, 10L).getPickup().getPhone());
verify(fixture.logMapper, never()).selectList(any());
```

Tests must also capture SQL segments for all five tab mappings, reject `refund`/unknown tabs, reject half/malformed coordinates, keep scheduled orders hidden before their start, restrict non-new tabs to `rider_id`, and verify direct photo arrays grouped by `SENDER/PICKUP/DELIVERY`.

- [ ] **Step 2: Run service and controller tests and verify failure**

```powershell
mvn -pl ruoyi-admin -am -Dtest='FlashDeliveryApplicationServiceTest,FlashDeliveryControllerContractTest,FlashDeliveryOrderViewTest,FlashDeliveryRiderOrderListViewTest' -Dsurefire.failIfNoSpecifiedTests=false test
```

Expected: failure on old method signatures, routes, scene filtering and wrapped detail response.

- [ ] **Step 3: Implement controllers and query mapping**

```java
@GetMapping("/orders")
public AjaxResult orders(@RequestHeader String token,
        @RequestParam(defaultValue = "1") int page,
        @RequestParam(defaultValue = "10") int size,
        @RequestParam(defaultValue = "newTask") String tab,
        @RequestParam(required = false) BigDecimal longitude,
        @RequestParam(required = false) BigDecimal latitude) {
    return success(service.riderOrders(userId(token), page, size, tab, longitude, latitude));
}
```

User Controller uses only `page,size`. Service maps `newTask/toPickup/delivering/completed/cancelled` before `selectPage`; `newTask` applies `WAITING_ACCEPTANCE`、`rider_id IS NULL`、预约开始时间条件 and, when coordinates exist, the same `sys_qs_newtask_distance` radius and distance sorting used by `PosOrderQsOprateController.orderList`.

- [ ] **Step 4: Implement list mapping and direct detail**

```java
private FlashDeliveryOrderDetailView participantDetail(FlashDeliveryOrder order,
        boolean userView, boolean protectedRiderView) {
    FlashDeliveryOrderDetailView view = detailOrderView(order, !protectedRiderView);
    if (!protectedRiderView) groupImageUrls(view, selectImages(order.getId()));
    if (userView) {
        view.setDeliveryPinCode(Boolean.TRUE.equals(order.getPinRequired())
                ? order.getDeliveryPinCode() : null);
        view.setRider(riderSummary(order));
    }
    return view;
}
```

Do not query `FlashDeliveryOrderLogMapper` while building App details. Pre-accept address views copy `city,area,handoffMethod,address,addressDetail` only; assigned rider/user detail additionally copies contact and exact coordinates. `newTask` page summary uses filtered page total as `nearbyTaskCount` and a same-condition maximum amount query as `highestOrderAmount`.

- [ ] **Step 5: Add localized parameter errors and run targeted tests**

Add `flash.delivery.tab.invalid` and `flash.delivery.coordinates.invalid` to all five message bundles, then run:

```powershell
mvn -pl ruoyi-admin -am -Dtest='FlashDeliveryApplicationServiceTest,FlashDeliveryControllerContractTest,FlashDeliveryOrderViewTest,FlashDeliveryRiderOrderListViewTest' -Dsurefire.failIfNoSpecifiedTests=false test
```

Expected: PASS.

### Task 3: 更新契约文档并统一验证

**Files:**
- Modify: `docs/flash-delivery-app-api.md`
- Modify: `specs/024-flash-delivery/quickstart.md`
- Modify: `specs/024-flash-delivery/tasks.md`

**Interfaces:**
- Consumes: the implemented Controller routes and DTO field names from Tasks 1–2.
- Produces: App integration examples that exactly match production code.

- [ ] **Step 1: Replace obsolete request and response examples**

Document only:

```text
GET /system/flashDelivery/orders?page=1&size=10
GET /system/flashDelivery/rider/orders?page=1&size=10&tab=newTask&longitude=121.5&latitude=25.0
```

Remove `/available`、`/mine`、`scene`、`serviceType` list filters, approximate coordinates, `{order,images,logs}` App wrappers and App raw log examples. Keep platform audit response unchanged.

- [ ] **Step 2: Run all affected tests and package**

```powershell
$env:JAVA_HOME='C:\Users\qmj\.jdks\graalvm-jdk-21.0.7'
$env:PATH="$env:JAVA_HOME\bin;$env:PATH"
mvn -pl ruoyi-admin -am -Dtest='FlashDeliveryApplicationServiceTest,FlashDeliveryControllerContractTest,FlashDeliveryOrderViewTest,FlashDeliveryRiderOrderListViewTest,FlashDeliveryI18nContractTest' -Dsurefire.failIfNoSpecifiedTests=false test
mvn -pl ruoyi-admin -am -DskipTests package
git diff --check
```

Expected: all targeted tests pass, package succeeds and diff check is clean.

- [ ] **Step 3: Audit scope and commit**

```powershell
git diff --name-only
$flashFiles = @(
  'ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryUserController.java',
  'ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryRiderController.java',
  'ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationService.java',
  'ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryUserOrderListView.java',
  'ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderListView.java',
  'ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderPageView.java',
  'ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryOrderDetailView.java',
  'ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAvailableOrderView.java',
  'ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryImageView.java',
  'ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryLogView.java',
  'ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryControllerContractTest.java',
  'ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAvailableOrderViewTest.java',
  'ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderListViewTest.java',
  'ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryOrderViewTest.java',
  'ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationServiceTest.java',
  'ruoyi-admin/src/main/resources/i18n/messages.properties',
  'ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties',
  'ruoyi-admin/src/main/resources/i18n/messages_zh_TW.properties',
  'ruoyi-admin/src/main/resources/i18n/messages_en_US.properties',
  'ruoyi-admin/src/main/resources/i18n/messages_vi.properties',
  'docs/flash-delivery-app-api.md',
  'specs/024-flash-delivery/quickstart.md',
  'specs/024-flash-delivery/tasks.md'
)
git add -- $flashFiles
git diff --cached --check
git diff --cached --name-only
git commit -m "精简闪送 App 订单接口"
git push origin test
```

The staged list must not contain `.claude/homunculus/observations.jsonl`、`.tmp/` or `docs/merchant-subaccount-api.md`.

## 新版蓝湖稿增量计划（2026-09-04）

详细文件级执行步骤见 `implementation-plan-2026-09-04.md`。

### 目标

保持闪送非支付和整数 TWD 现状，新增创建时收件账号绑定与“我收的”查询，并将 `URGENT` 落实为跨外卖、闪送任务的骑手独占。附近骑手人数和接单分钟数不进入接口，用户端统一展示“发布后等待附近骑手接单”。

### 数据与身份

1. `flash_delivery_order` 增加可空 `receiver_user_id BIGINT` 和 `(receiver_user_id, create_time)` 索引。
2. 创建订单时将 `delivery.phone` 去除空白、`+`、`-` 和圆括号但保留国家码数字，并以相同规则只查询已注册普通用户；唯一匹配时固化用户 ID，无匹配或多匹配时留空。
3. 不扫描或回填历史订单，不因用户注册、注销或手机号换绑修改 `receiver_user_id`。
4. 用户列表新增可选 `role`，默认 `sender`；`receiver` 使用 `receiver_user_id` 在数据库分页前过滤。
5. 详情和确认签收允许创建人或绑定收件人；取消只允许创建人。

### 急送独占

1. 新增共享骑手接单锁服务，使用 Redisson 锁键 `lock:delivery:rider:{riderId}`；闪送接单和当前有效外卖接单入口在事务内首先获取同一把锁。
2. 抢 `URGENT` 前检查该骑手没有未送达外卖，也没有 `ACCEPTED/PICKED_UP` 闪送。
3. 抢其他任务前检查该骑手没有 `ACCEPTED/PICKED_UP` 的 `URGENT` 闪送。
4. 使用不指定固定租期的 `tryLock(waitTime, unit)` 交由 Redisson 看门狗续期，并通过事务同步回调在事务完成后释放；获取失败或 Redis 异常时拒绝接单。
5. 独占冲突使用统一国际化业务错误；数据库条件更新仍作为订单被其他骑手抢走的最终并发保护。

### 测试与交付

先写并运行失败测试，再实现生产代码。测试覆盖手机号唯一/零个/多个匹配、幂等创建、角色分页、参与者权限、取消边界、急送双向互斥、终态释放和同骑手并发接单。数据库变更只追加到 `updatesql/sql.md`，不直接执行。最后使用 JDK 21 运行定向测试和模块构建，并执行 `git diff --check`。

## 设计稿费用与物品信息对齐计划（2026-09-07）

> **执行要求**：在当前会话按测试驱动方式依次完成下列任务；每个任务先运行指定测试确认旧实现失败，再实现最小改动并重新运行。数据库 SQL 只追加到 `updatesql/sql.md`，不得执行。

**目标**：拆分帮送/帮取业务场景与普通/加急配送等级，让两种业务场景共享统一分时段运价，按普通配送费比例和最低金额计算加急费，支持骑手小费、预约时段计价、客户端报价回传校验及设计稿中的完整物品信息。

**架构**：`flash_delivery_pricing` 改为全局时间段配置，计价器负责纯函数费用计算，应用服务负责选择立即或预约计价时刻、路线编排和报价一致性校验。订单持久化完整物品及费用快照；`delivery_type` 成为急送独占的唯一判断字段。平台管理端只维护一组时间段，不再展示三类服务价格。

**技术栈**：Java 21、Spring Boot、MyBatis-Plus/MyBatis XML、MySQL、JUnit 5、Mockito、Vue 2、Element UI、Node 测试。

### 全局约束

- Controller 继续使用明确 DTO、`@RequestBody`、`@RequestParam`、`@PathVariable` 和 `@RequestHeader String token`，不接收 Map。
- 所有 TWD 费用为整数 `Long`；比例和重量使用 `BigDecimal`，计算使用 `RoundingMode.HALF_UP`。
- `serviceType` 只接受 `HELP_SEND/HELP_PICKUP`，`deliveryType` 只接受 `NORMAL/URGENT`，`deliveryMode` 只接受 `NOW/SCHEDULED`。
- 立即订单按报价时刻、预约订单按 `scheduledPickupStartAt` 的业务当地时分匹配统一运价。
- 不接入支付、退款或结算；小费仅作为应付费用快照。
- 平台新增可见文本同步简中、繁中、英文和越南文；前端文件保持 CRLF。
- 不执行数据库迁移，只把可人工执行 SQL 追加到 `updatesql/sql.md`。

### Task 1：计价模型与数据层

**文件：**

- 修改：`ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryPricing.java`
- 修改：`ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryOrder.java`
- 修改：`ruoyi-system/src/main/java/com/ruoyi/system/mapper/flash/FlashDeliveryPricingMapper.java`
- 修改：`ruoyi-system/src/main/resources/mapper/flash/FlashDeliveryOrderMapper.xml`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryPriceBreakdown.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryPricingCalculator.java`
- 测试：`ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryPricingCalculatorTest.java`
- 测试：`ruoyi-system/src/test/java/com/ruoyi/system/mapper/flash/FlashDeliveryPricingMapperContractTest.java`
- 测试：`ruoyi-system/src/test/java/com/ruoyi/system/mapper/flash/FlashDeliveryMapperXmlTest.java`

**接口：**

- `FlashDeliveryPricingCalculator.calculateBreakdown(FlashDeliveryPricing pricing, int distanceMeters, String deliveryType, Long tipAmount)` 返回 `distanceFee/baseDeliveryFee/urgentFee/tipAmount/amount`。
- `FlashDeliveryPricingMapper.selectAtTime(String targetTime)` 返回目标 `HH:mm` 命中的全部配置；`countOverlapping(String startTime,String endTime,Long excludedId)` 全局查重；`lockPricing()` 获取单行全局锁。
- `FlashDeliveryOrder` 新增 `deliveryType,quantity,totalWeightKg,specification,baseDeliveryFee,urgentRate,minimumUrgentFee,urgentFee,tipAmount`。

- [ ] 先扩展计价器和 Mapper 契约测试，覆盖普通加急费为 0、比例费高于最低值、最低值兜底、HALF_UP、小费相加、全局时段查询/查重和订单字段映射，并运行测试确认旧实现失败。
- [ ] 修改实体、Mapper 和纯计价器；删除运价配置的 `serviceType` 依赖，使上述测试通过。
- [ ] 运行：`mvn -pl ruoyi-admin -am -Dtest='FlashDeliveryPricingCalculatorTest,FlashDeliveryPricingMapperContractTest,FlashDeliveryMapperXmlTest' -Dsurefire.failIfNoSpecifiedTests=false test`。

### Task 2：报价、创建校验与物品信息

**文件：**

- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryQuoteRequest.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryCreateRequest.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryQuoteView.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryHomeView.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryOrderView.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryUserOrderListView.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderListView.java`
- 新增：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/exception/FlashDeliveryQuoteChangedException.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationService.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryUserController.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/order/RiderDeliveryExclusivityService.java`
- 测试：`ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationServiceTest.java`
- 测试：`ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryControllerContractTest.java`
- 测试：`ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryOrderViewTest.java`
- 测试：`ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderListViewTest.java`

**接口：**

- 报价请求新增 `deliveryType,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,quantity,totalWeightKg,specification,tipAmount`，移除客户端 `packageSize`。
- 创建请求新增 `pricingId,pricingVersion,quotedBaseDeliveryFee,quotedDistanceFee,quotedUrgentFee,quotedAmount`。
- 报价和订单视图新增业务、物品与费用明细；`packageSize` 由服务端按 `<=5/<=12/<=20kg` 生成。
- 报价变化异常携带 `FlashDeliveryQuoteView latestQuote`；Controller 返回国际化错误和 `data=latestQuote`。

- [ ] 先扩展应用服务、Controller 和 DTO 测试，覆盖业务/配送维度校验、完整物品校验、立即与预约时段匹配、报价费用字段、创建逐项一致、配置或金额变化拒绝并返回最新报价、订单费用快照和 `deliveryType=URGENT` 独占，并确认旧实现失败。
- [ ] 实现请求/响应 DTO、目标计价时刻选择、统一运价查询、报价复算比较、重量档生成、快照保存和报价变化错误响应。
- [ ] 将所有急送独占查询从 `service_type='URGENT'` 改为 `delivery_type='URGENT'`，保持既有骑手锁和状态边界不变。
- [ ] 运行：`mvn -pl ruoyi-admin -am -Dtest='FlashDeliveryApplicationServiceTest,FlashDeliveryControllerContractTest,FlashDeliveryOrderViewTest,FlashDeliveryRiderOrderListViewTest' -Dsurefire.failIfNoSpecifiedTests=false test`。

### Task 3：平台配置接口和管理页面

**文件：**

- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryPricingRequest.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationService.java`
- 修改：`ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryAdminController.java`
- 修改：`E:/QtwCode/foodie/foodie-admin-vue/src/api/flashDelivery/contracts.js`
- 修改：`E:/QtwCode/foodie/foodie-admin-vue/src/views/flashDelivery/shared.js`
- 修改：`E:/QtwCode/foodie/foodie-admin-vue/src/views/flashDelivery/pricing/index.vue`
- 修改：`E:/QtwCode/foodie/foodie-admin-vue/src/views/flashDelivery/orders/OrderDetail.vue`
- 修改：`E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.zh.js`
- 修改：`E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.tw.js`
- 修改：`E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.en.js`
- 修改：`E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.vi.js`
- 测试：`E:/QtwCode/foodie/foodie-admin-vue/tests/flash-delivery.test.cjs`

**接口：**

- 平台运价请求为 `startTime,endTime,startingDistance,startingFare,distance,freight,urgentRate,minimumUrgentFee,configVersion?`，不再含 `serviceType`。
- 列表按 `startTime` 排序，所有时段全局重叠校验；平台订单详情展示业务场景、配送等级、物品和五项费用。

- [ ] 先修改后端平台契约测试和前端定向测试，确认旧三服务配置结构失败。
- [ ] 实现统一时段 CRUD、全局锁和字段校验；更新平台价格表单、订单详情及四语言文案。
- [ ] 运行后端平台定向测试和 `npm run test:flash-delivery`，再对修改的前端文件运行 ESLint。

### Task 4：SQL、App 文档和规格任务状态

**文件：**

- 修改：`updatesql/sql.md`
- 修改：`docs/flash-delivery-app-api.md`
- 修改：`specs/024-flash-delivery/quickstart.md`
- 修改：`specs/024-flash-delivery/tasks.md`
- 修改：五个 `ruoyi-admin/src/main/resources/i18n/messages*.properties`
- 测试：`ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryI18nContractTest.java`

- [ ] 在 `updatesql/sql.md` 追加 2026-09-07 人工迁移脚本：统一运价时段、全局锁、订单业务/物品/费用字段和相关索引；不得连接或修改数据库。
- [ ] 重写 App 文档的枚举、首页、报价、创建、列表、详情和费用示例，明确报价变化响应及非支付边界。
- [ ] 添加物品、配送等级、报价变化和运价字段错误的五语言 key，并运行 i18n 契约测试。
- [ ] 更新 `tasks.md` 实际完成状态，不提前勾选未运行验证的任务。

### Task 5：统一验证与提交

- [ ] 临时设置 `JAVA_HOME=C:\Users\qmj\.jdks\graalvm-jdk-21.0.7`，运行闪送、外卖独占和 i18n 定向测试。
- [ ] 运行 `mvn -pl ruoyi-admin -am -DskipTests package`。
- [ ] 在平台前端运行 `npm run test:flash-delivery`、修改文件 ESLint 和 `npm run build:prod`。
- [ ] 运行两个仓库的 `git diff --check`，核对编码、换行和暂存文件清单。
- [ ] 后端与平台前端分别形成中文独立提交；最终报告两个 SHA、实际检查结果和任何未执行项。
