Przeglądaj źródła

对齐闪送费用与物品信息设计

拆分帮送帮取与普通加急配送维度,统一分时段运价。明确比例加急费、最低加急费、骑手小费、预约时段匹配和创建报价校验,并补齐数量、总重量及规格字段。
qmj 4 godzin temu
rodzic
commit
44048e4a8b

+ 15 - 12
specs/024-flash-delivery/contracts/api.md

@@ -6,9 +6,10 @@
 - 平台权限:`flash:pricing:list/edit`、`flash:order:list/query/cancel/complete`。
 - 响应沿用 `AjaxResult`:`{"code":200,"msg":"...","data":...}`;分页 data 为 `records,total,current,size`。
 - 金额为整数 TWD;路线距离为米,运价配置距离为公里。
-- `serviceType`:`HELP_SEND`、`HELP_PICKUP`、`URGENT`。
+- `serviceType`:`HELP_SEND`、`HELP_PICKUP`。
+- `deliveryType`:`NORMAL`、`URGENT`。
 - `packageType`:`DOCUMENT`、`GIFT`、`CLOTHING`、`BEAUTY`、`DAILY_NECESSITIES`、`FOOD_INGREDIENTS`、`ELECTRONICS`、`SMALL_APPLIANCE`、`OTHER`。
-- `packageSize`:`SMALL`(不超过 5kg)、`MEDIUM`(不超过 12kg)、`LARGE`(不超过 20kg);不提交精确重量
+- 物品信息提交 `packageType`、正整数 `quantity`、大于 0 且不超过 20 的两位小数 `totalWeightKg` 及最长 255 字符的可选 `specification`;`packageSize` 由服务端根据总重量生成
 - `deliveryMode`:`NOW`、`SCHEDULED`。预约起止时间使用 ISO 日期时间,时段固定 30 分钟且开始时间不晚于三天后。
 - `status`:`WAITING_ACCEPTANCE`、`ACCEPTED`、`PICKED_UP`、`DELIVERED`、`COMPLETED`、`CANCELLED`。
 
@@ -30,17 +31,19 @@
 
 | 方法 | 路径 | 请求/说明 |
 |---|---|---|
-| GET | `/system/flashDelivery/home` | 返回当前时间存在运价时段的服务及摘要 `serviceType,pricingId,startTime,endTime,startingDistance,startingFare,distance,freight,pricingVersion,currency`;不返回内部更新人、时间或地图密钥 |
-| POST | `/system/flashDelivery/quote` | `serviceType,packageType,packageSize,pickup,delivery` |
-| POST | `/system/flashDelivery/orders` | 报价请求字段 + `clientRequestId,deliveryMode,scheduledPickupStartAt?,scheduledPickupEndAt?,pinRequired?,senderImageUrls?,userNote?`;服务端重算并幂等创建 |
+| GET | `/system/flashDelivery/home` | 返回 `serviceTypes=[HELP_SEND,HELP_PICKUP]`、`deliveryTypes=[NORMAL,URGENT]` 及当前时刻命中的统一运价摘要;不返回内部更新人、时间或地图密钥 |
+| POST | `/system/flashDelivery/quote` | `serviceType,deliveryType,deliveryMode,scheduledPickupStartAt?,scheduledPickupEndAt?,packageType,quantity,totalWeightKg,specification?,tipAmount,pickup,delivery`;立即订单按当前时刻、预约订单按预约开始时刻匹配运价 |
+| POST | `/system/flashDelivery/orders` | 报价请求字段 + `clientRequestId,pricingId,pricingVersion,quotedBaseDeliveryFee,quotedDistanceFee,quotedUrgentFee,quotedAmount,pinRequired?,senderImageUrls?,userNote?`;服务端复算并逐项校验后幂等创建 |
 | GET | `/system/flashDelivery/orders` | `page,size,role?`;`role=sender/receiver`,省略为 `sender`;按创建时间倒序返回当前用户作为寄件人或已绑定收件人的订单摘要 |
 | GET | `/system/flashDelivery/orders/{id}` | 订单创建人或创建时已绑定收件人的 App 订单详情;`data` 直接为订单字段,不返回原始状态日志 |
 | POST | `/system/flashDelivery/orders/{id}/cancel` | `{"reason":"行程变化"}`;仅订单创建人且状态为待接/已接 |
 | POST | `/system/flashDelivery/orders/{id}/confirmReceipt` | 无 body;订单创建人或已绑定收件人可对 DELIVERED 订单确认签收 |
 
-用户列表摘要字段固定为:`id,orderNo,serviceType,status,packageType,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,pickupAddress,pickupDetailAddress,deliveryAddress,deliveryDetailAddress,estimatedDurationSeconds,amount,currency,deliveredAt,createTime`。列表不返回联系人、电话、精确坐标、照片、日志或计价审计字段。`receiver` 不是按当前手机号动态查询:订单只在创建时唯一匹配已注册普通用户并固化 `receiverUserId`,未匹配订单以后不追溯认领。
+用户列表摘要字段固定为:`id,orderNo,serviceType,deliveryType,status,packageType,quantity,totalWeightKg,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,pickupAddress,pickupDetailAddress,deliveryAddress,deliveryDetailAddress,estimatedDurationSeconds,baseDeliveryFee,urgentFee,tipAmount,amount,currency,deliveredAt,createTime`。列表不返回联系人、电话、精确坐标、照片、日志或内部计价审计字段。`receiver` 不是按当前手机号动态查询:订单只在创建时唯一匹配已注册普通用户并固化 `receiverUserId`,未匹配订单以后不追溯认领。
 
-报价响应 data:`serviceType,pricingId,startTime,endTime,distanceMeters,distanceSource,estimatedDurationSeconds?,startingDistance,startingFare,distance,freight,billableDistance,distanceFee,amount,currency,pricingVersion`。`billableDistance` 为按外卖规则处理 0.5 公里边界后的计费公里数,`distanceFee` 与 `amount` 为整数 TWD。地址快照字段为联系人、电话、市/区、交付方式、地址、详细地址和经纬度。客户端传入的金额/距离不会被使用。用户创建和详情响应的 `data` 直接为订单详情字段,照片分别为 `senderImageUrls,pickupImageUrls,deliveryImageUrls`,不返回原始状态日志;`deliveryPinCode` 仅在启用时向订单用户返回,用户仅在 `ACCEPTED`、`PICKED_UP` 阶段获得骑手位置。
+报价响应 data:`serviceType,deliveryType,deliveryMode,scheduledPickupStartAt?,scheduledPickupEndAt?,pricingId,startTime,endTime,distanceMeters,distanceSource,estimatedDurationSeconds?,startingDistance,startingFare,distance,freight,billableDistance,distanceFee,baseDeliveryFee,urgentRate,minimumUrgentFee,urgentFee,tipAmount,amount,currency,pricingVersion`。`billableDistance` 为按外卖规则处理 0.5 公里边界后的计费公里数;全部费用为整数 TWD,`urgentRate` 为百分比。`baseDeliveryFee=startingFare+distanceFee`;普通配送 `urgentFee=0`;加急配送 `urgentFee=max(minimumUrgentFee,roundHalfUp(baseDeliveryFee*urgentRate/100))`;`amount=baseDeliveryFee+urgentFee+tipAmount`。
+
+创建时服务端重新获取路线、按配送方式匹配运价并复算费用。`pricingId`、`pricingVersion`、`quotedBaseDeliveryFee`、`quotedDistanceFee`、`quotedUrgentFee`、`quotedAmount` 任一不一致时不创建订单,返回国际化“报价已变化,请重新确认”及完整最新报价。地址快照字段为联系人、电话、市/区、交付方式、地址、详细地址和经纬度。用户创建和详情响应的 `data` 直接为订单详情字段,照片分别为 `senderImageUrls,pickupImageUrls,deliveryImageUrls`,不返回原始状态日志;`deliveryPinCode` 仅在启用时向订单参与用户返回,用户仅在 `ACCEPTED`、`PICKED_UP` 阶段获得骑手位置。
 
 ## 骑手端
 
@@ -56,7 +59,7 @@
 
 骑手页签状态映射:`newTask=WAITING_ACCEPTANCE`、`toPickup=ACCEPTED`、`delivering=PICKED_UP`、`completed=DELIVERED+COMPLETED`、`cancelled=CANCELLED`。除 `newTask` 外只返回当前骑手本人任务;闪送没有 `refund` 页签。
 
-骑手列表摘要字段固定为:`id,orderNo,serviceType,status,packageType,packageSize,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,pinRequired,pickupAddress,pickupDetailAddress,deliveryAddress,deliveryDetailAddress,pickupDistanceMeters,distanceMeters,estimatedDurationSeconds,amount,currency,createTime`。`newTask` 分页 data 在 `records,total,current,size` 外增加 `nearbyTaskCount,highestOrderAmount`;不返回尖峰倍率或骑手收入字段。
+骑手列表摘要字段固定为:`id,orderNo,serviceType,deliveryType,status,packageType,quantity,totalWeightKg,specification,packageSize,deliveryMode,scheduledPickupStartAt,scheduledPickupEndAt,pinRequired,pickupAddress,pickupDetailAddress,deliveryAddress,deliveryDetailAddress,pickupDistanceMeters,distanceMeters,estimatedDurationSeconds,baseDeliveryFee,urgentFee,tipAmount,amount,currency,createTime`。`newTask` 分页 data 在 `records,total,current,size` 外增加 `nearbyTaskCount,highestOrderAmount`;不返回加急比例、最低加急费或骑手收入字段。
 
 骑手接单前可按原型查看完整取送文字地址,但不返回用户 ID、联系人、电话、实际 PIN、精确经纬度、备注、幂等请求号、履约凭证或日志。抢单成功后才向中单骑手返回完整联系方式、坐标和有权限查看的照片数组。骑手详情在接单前后使用同一个直接 DTO,不返回 `{order,images,logs}` 包装,也不要求 App 判断 `data.order`。
 
@@ -64,8 +67,8 @@
 
 | 方法 | 路径 | 请求/说明 |
 |---|---|---|
-| GET | `/system/flashDelivery/admin/pricing` | 返回全部运价时段,按服务类型和开始时间排序 |
-| POST | `/system/flashDelivery/admin/pricing` | `serviceType,startTime,endTime,startingDistance,startingFare,distance,freight`;保存即生效 |
+| GET | `/system/flashDelivery/admin/pricing` | 返回帮送和帮取共享的全部统一运价时段,按开始时间排序 |
+| POST | `/system/flashDelivery/admin/pricing` | `startTime,endTime,startingDistance,startingFare,distance,freight,urgentRate,minimumUrgentFee`;保存即生效 |
 | PUT | `/system/flashDelivery/admin/pricing/{id}` | 与新增相同的完整请求体;版本原子加一,并发覆盖返回业务错误 |
 | DELETE | `/system/flashDelivery/admin/pricing/{id}` | 删除运价时段并立即停止用于报价 |
 | GET | `/system/flashDelivery/admin/orders` | 分页;可按状态、类型、订单号、用户、骑手筛选 |
@@ -73,6 +76,6 @@
 | POST | `/system/flashDelivery/admin/orders/{id}/cancel` | `reason` 必填;非终态可取消 |
 | POST | `/system/flashDelivery/admin/orders/{id}/complete` | 无 body;仅 DELIVERED |
 
-本期没有支付、退款、取消费、退回费、结算、骑手收入、代购/垫付、精确重量、件数、违禁品电子确认、骑手放弃或自动派单字段和接口。所有闪送金额保持整数 TWD;创建订单后直接待接单,不存在待支付状态。`URGENT` 为 1 对 1 独占配送,相关外卖与闪送接单入口必须执行跨订单独占检查。
+本期没有支付、退款、取消费、退回费、结算、骑手收入、代购/垫付、商品金额、违禁品电子确认、骑手放弃或自动派单字段和接口。所有闪送金额保持整数 TWD;创建订单后直接待接单,不存在待支付状态。`deliveryType=URGENT` 为 1 对 1 独占配送,相关外卖与闪送接单入口必须执行跨订单独占检查。骑手小费仅为订单费用明细,不代表已付款。
 
-运价时段使用业务当地时间和 `HH:mm` 格式,采用开始时间包含、结束时间不包含的匹配语义;`24:00` 只允许作为结束时间,跨午夜时段必须拆成两个配置。同一服务类型的时间段不得重叠。起送距离和计价距离必须为正数,起送价格和计价金额必须为正整数;当前时间没有匹配时段时,用户首页不返回该服务,报价和创建接口返回“当前时段暂无可用运价”。
+运价时段使用业务当地时间和 `HH:mm` 格式,采用开始时间包含、结束时间不包含的匹配语义;`24:00` 只允许作为结束时间,跨午夜时段必须拆成两个配置。全部时间段不得重叠。起送距离和计价距离必须为正数,起送价格和计价金额必须为正整数,加急比例和最低加急费必须为非负数;立即订单按当前时刻、预约订单按预约开始时刻匹配,目标时刻没有匹配时段时报“对应取件时段暂无可用运价”。

+ 3 - 3
specs/024-flash-delivery/data-model.md

@@ -2,17 +2,17 @@
 
 ## `flash_delivery_order`
 
-核心字段:`id`、唯一 `order_no`、与用户联合唯一的 `client_request_id`、创建人 `user_id`、创建时按收件手机号唯一匹配并固化的可空 `receiver_user_id`、`rider_id`、`service_type`、`status`;`package_type`、`package_size`;`delivery_mode`、预约开始/结束时间;`pin_required`、四位交付 PIN;取件/收件联系人、电话、市/区、交付方式、地址、详细地址和经纬度快照;`distance_meters`、`distance_source`、`estimated_duration_seconds`;整数 `amount`、`currency`、`pricing_id`、`pricing_version`、`pricing_start_time`、`pricing_end_time`、`starting_distance`、`starting_fare`、`distance`、`freight`、`distance_fee` 计价快照;`user_note`、`version`、关键状态时间、取消审计及创建/更新时间。
+核心字段:`id`、唯一 `order_no`、与用户联合唯一的 `client_request_id`、创建人 `user_id`、创建时按收件手机号唯一匹配并固化的可空 `receiver_user_id`、`rider_id`、帮送/帮取 `service_type`、普通/加急 `delivery_type`、`status`;`package_type`、正整数 `quantity`、两位小数 `total_weight_kg`、可空 `specification`、服务端生成的 `package_size`;`delivery_mode`、预约开始/结束时间;`pin_required`、四位交付 PIN;取件/收件联系人、电话、市/区、交付方式、地址、详细地址和经纬度快照;`distance_meters`、`distance_source`、`estimated_duration_seconds`;整数 `amount`、`currency`、`pricing_id`、`pricing_version`、`pricing_start_time`、`pricing_end_time`、`starting_distance`、`starting_fare`、`distance`、`freight`、`distance_fee`、`base_delivery_fee`、`urgent_rate`、`minimum_urgent_fee`、`urgent_fee`、`tip_amount` 计价快照;`user_note`、`version`、关键状态时间、取消审计及创建/更新时间。
 
 索引:订单号唯一、用户幂等号唯一、待抢排序、创建人列表、收件人列表、骑手列表、自动完成扫描。`receiver_user_id` 只保存创建当时唯一匹配的普通用户;空值和历史订单不补写,用户手机号变更也不回写。
 
 ## `flash_delivery_pricing`
 
-每个服务类型可保存多个运价时段。字段为 `service_type`、`start_time`、`end_time`、`starting_distance`、整数 `starting_fare`、`distance`、整数 `freight`、`config_version`、`updated_by` 和创建/更新时间。配置保存即生效,不保存 `enabled`;同一服务类型的时间段不得重叠,索引支持按服务类型和时间匹配当前时段。有效修改后版本加一,历史订单不回写。当前时段查询不使用 `LIMIT 1` 隐藏异常数据;若匹配到多条则明确返回配置重叠错误。
+帮送与帮取共享多个统一运价时段,不再保存 `service_type`。字段为 `start_time`、`end_time`、`starting_distance`、整数 `starting_fare`、`distance`、整数 `freight`、非负两位小数百分比 `urgent_rate`、非负整数 `minimum_urgent_fee`、`config_version`、`updated_by` 和创建/更新时间。配置保存即生效,不保存 `enabled`;全部时间段不得重叠,索引支持按时间匹配目标时刻。有效修改后版本加一,历史订单不回写。时段查询不使用 `LIMIT 1` 隐藏异常数据;若匹配到多条则明确返回配置重叠错误。
 
 ## `flash_delivery_pricing_lock`
 
-以 `service_type` 为主键,固定保存 `HELP_SEND`、`HELP_PICKUP`、`URGENT` 三行。新增、修改和删除运价时,事务先通过 `SELECT ... FOR UPDATE` 锁定涉及的服务类型,再检查重叠并写入,避免并发请求同时通过“先查重、后写入”。修改跨服务类型时按服务类型字典序加锁,避免反向加锁造成死锁。
+改为只保存一行全局运价锁,例如固定主键 `GLOBAL`。新增、修改和删除运价时,事务先通过 `SELECT ... FOR UPDATE` 取得该锁,再检查全部时间段重叠并写入,避免并发请求同时通过“先查重、后写入”。
 
 ## `flash_delivery_order_image`
 

+ 39 - 12
specs/024-flash-delivery/design.md

@@ -14,7 +14,7 @@
 6. 闪送
 7. 闪送 送
 
-最初的蓝湖 7 页明确展示三种服务、取件与收件地址、联系人和电话、地址簿、起步价格及下单入口,未展示重量和类别;2026-08-24 用户端/骑手端补充原型进一步明确包裹类别、重量档、预约配送、交付 PIN、寄件图片和费用明细。两套设计均未明确支付、代购、精确重量或件数规则
+最初的蓝湖 7 页明确展示三种入口、取件与收件地址、联系人和电话、地址簿、起步价格及下单入口;后续用户端/骑手端原型明确包裹类别、数量、总重量、体积/规格、预约配送、交付 PIN、寄件图片以及基础配送、距离加价、加急和骑手小费明细。此前把精确重量和件数排除在范围外是错误判断,2026-09-07 起按设计稿补齐
 
 ## 2. 架构决策
 
@@ -74,7 +74,7 @@ ruoyi-system
 
 - “闪送管理”作为父菜单,“价格配置”和“闪送订单”作为两个子页面。
 - `src/api/flashDelivery/index.js` 集中封装平台接口,页面不直接拼接请求。
-- 价格配置页按帮送、帮取和加急送展示运价时段,新增与编辑弹窗承载时间、距离和整数金额校验,支持删除不再需要的时段;配置保存即生效,不提供启停开关。
+- 价格配置页展示帮送和帮取共享的统一运价时段,新增与编辑弹窗承载时间、距离、整数金额、加急比例和最低加急费校验,支持删除不再需要的时段;配置保存即生效,不提供启停开关。
 - 订单页以服务端分页表格为主体,详情弹窗分区展示概况、地址、费用、凭证和日志;取消原因使用独立输入弹窗,完成操作使用二次确认。
 - 操作按钮同时使用状态条件和 `v-hasPermi` 控制可见性,后端权限与状态机仍是最终安全边界。
 - 所有新增文本集中在四个前端语言文件的 `flashDelivery` 对象中,key 集合保持一致。
@@ -93,11 +93,11 @@ ruoyi-system
 
 ### 4.1 `flash_delivery_order`
 
-保存订单号、客户端请求号、用户、骑手、服务类型、包裹类别、重量档、配送方式、预约时段、交付 PIN、取件与收件信息快照、路线距离、距离来源、预计时长、报价金额、匹配时段的起送与里程计价快照、币种、计价配置版本、备注、状态、并发版本号、关键操作时间与取消信息。
+保存订单号、客户端请求号、用户、骑手、帮送/帮取业务场景、普通/加急配送等级、包裹类别、数量、总重量、规格、服务端生成的重量档、配送方式、预约时段、交付 PIN、取件与收件信息快照、路线距离、距离来源、预计时长、基础配送费、距离费、加急费、小费、总金额、匹配时段完整计价快照、币种、计价配置版本、备注、状态、并发版本号、关键操作时间与取消信息。
 
 ### 4.2 `flash_delivery_pricing`
 
-按服务类型保存多个不重叠的运价时段;每个时段保存开始时间、结束时间、起送距离、起送价格、计价距离、计价金额、配置版本、修改人和修改时间。保存即生效,删除即失效,不设置启停字段。
+保存一套供帮送和帮取共享、全局互不重叠的运价时段;每个时段保存开始时间、结束时间、起送距离、起送价格、计价距离、计价金额、加急比例、最低加急费、配置版本、修改人和修改时间。保存即生效,删除即失效,不设置启停字段。
 
 ### 4.3 `flash_delivery_order_image`
 
@@ -114,14 +114,17 @@ ruoyi-system
 ## 5. 计价与距离
 
 ```text
-当前配置 = 按服务类型和当前时间匹配唯一运价时段
+目标计价时刻 = NOW ? 当前报价时刻 : scheduledPickupStartAt
+当前配置 = 按目标计价时刻匹配唯一统一运价时段
 超出公里 = max(0, 路线距离公里 - 起送距离)
 计费公里 = 超出公里 < 0.5 ? 0 : 超出公里 < 1 ? 1 : 超出公里
 里程费用 = 计费公里 * (计价金额 / 计价距离)
-价格 = 起送价格 + 四舍五入到整数元的里程费用
+普通配送费 = 起送价格 + 四舍五入到整数元的里程费用
+加急费 = NORMAL ? 0 : max(最低加急费, 四舍五入(普通配送费 * 加急比例 / 100))
+订单金额 = 普通配送费 + 加急费 + 骑手小费
 ```
 
-计价规则与现有外卖订单一致,但闪送使用自己的时段配置表。起送价格和计价金额为正整数新台币;距离与除法过程使用十进制定点数,里程费用采用 `HALF_UP` 四舍五入到整数元,最终价格不显示小数,也不执行外卖旧实现中金额达到 1000 后的千位特殊取整。帮送、帮取和加急送分别拥有多个互不重叠的时段;当前时间没有匹配时段时拒绝报价和创建。路线预计时长继续返回和保存,仅用于履约展示,不参与计价。报价优先使用地图路线距离,失败时降级为直线距离;创建订单时服务端必须重新匹配时段、计算价格并固化配置快照。
+普通计价规则与现有外卖订单一致,但闪送使用自己的统一时段配置表。起送价格和计价金额为正整数新台币;距离、比例与除法过程使用十进制定点数,里程费用和比例加急费采用 `HALF_UP` 四舍五入到整数元,最终价格不显示小数,也不执行外卖旧实现中金额达到 1000 后的千位特殊取整。立即订单按当前时刻、预约订单按预约开始时刻匹配时段;目标时刻没有匹配时段时拒绝报价和创建。路线预计时长继续返回和保存,仅用于履约展示,不参与计价。报价优先使用地图路线距离,失败时降级为直线距离;创建订单时服务端必须重新匹配时段、计算价格并核对客户端回传报价,完全一致后才能固化配置快照。
 
 ## 6. 状态机
 
@@ -160,7 +163,7 @@ COMPLETED
 
 ## 8. 测试方向
 
-- 时段匹配、重叠拒绝、0.5 公里边界、整数金额取整和配置版本测试。
+- 统一时段匹配、预约取件时段匹配、重叠拒绝、0.5 公里边界、整数金额取整、比例加急费、最低加急费、小费和配置版本测试。
 - 地图路线成功、失败及直线降级测试。
 - 地址和订单跨用户访问测试。
 - 多骑手并发抢单测试。
@@ -175,7 +178,7 @@ COMPLETED
 
 ## 9. 明确延期内容
 
-支付、退款、分账、骑手收入、代购、垫付、商品金额、精确重量、件数、自动派单、动态附近骑手数、预计接单时间以及用户端、骑手端和商家端页面均不在本期范围。
+支付、退款、分账、骑手收入、代购、垫付、商品金额、自动派单、动态附近骑手数、预计接单时间以及用户端、骑手端和商家端页面均不在本期范围。物品数量、总重量、体积/规格和骑手小费属于本期接口范围。
 
 ## 10. App 订单列表精简增量(2026-09-01)
 
@@ -198,7 +201,7 @@ COMPLETED
 - 参数名、默认分页和坐标用途与骑手外卖订单列表保持一致;经纬度用于 `newTask` 的附近范围、取件点距离和排序,其他页签忽略坐标。
 - `newTask -> WAITING_ACCEPTANCE`;`toPickup -> ACCEPTED`;`delivering -> PICKED_UP`;`completed -> DELIVERED + COMPLETED`;`cancelled -> CANCELLED`。除 `newTask` 外均只查询当前骑手本人。
 - 闪送没有退款流程,因此不接受外卖列表的 `refund` 页签。
-- 列表使用统一卡片摘要 DTO,包含订单标识、服务类型、状态、包裹类别/重量档、配送方式/预约时段、是否需要 PIN、完整取送文字地址、取件点距离、路线距离、预计时长、订单金额和创建时间。
+- 列表使用统一卡片摘要 DTO,包含订单标识、业务场景、配送等级、状态、包裹类别/数量/总重量/重量档、配送方式/预约时段、是否需要 PIN、完整取送文字地址、取件点距离、路线距离、预计时长、费用明细、订单金额和创建时间。
 - `newTask` 分页响应额外返回附近任务总数与最高订单金额,用于原型顶部摘要;不返回已移除的尖峰倍率,也不将订单金额表述为尚未实现的骑手收入。
 - 接单前不返回联系人、电话、实际 PIN、精确坐标、用户备注、图片凭证、状态日志、用户 ID、幂等号或计价审计字段。接单成功后,详情接口仅向中单骑手返回完整履约信息。
 
@@ -222,7 +225,7 @@ COMPLETED
 - 闪送不接入支付;订单创建后直接进入 `WAITING_ACCEPTANCE`,蓝湖中的待支付、去支付和支付倒计时不进入接口。
 - 金额继续使用整数 TWD,蓝湖小数价格作为视觉占位处理。
 - 不实现动态附近骑手数或预计接单分钟数,用户端文案统一为“发布后等待附近骑手接单”。
-- `URGENT` 按 1 对 1 专送实现骑手独占,不再只表示另一套运价
+- `deliveryType=URGENT` 按 1 对 1 专送实现骑手独占;`serviceType` 只表示帮送或帮取业务场景
 
 ### 11.2 “我收的”身份绑定
 
@@ -234,6 +237,30 @@ COMPLETED
 
 ### 11.3 急送独占与并发
 
-所有骑手接单入口先获取同一 Redisson 骑手锁 `lock:delivery:rider:{riderId}`,再查询该骑手的有效外卖和闪送任务。抢急送时要求不存在未送达外卖及 `ACCEPTED/PICKED_UP` 闪送;抢普通外卖或普通闪送时,如果已有 `ACCEPTED/PICKED_UP` 急送则拒绝。急送进入 `DELIVERED`、`COMPLETED` 或 `CANCELLED` 后解除独占。
+所有骑手接单入口先获取同一 Redisson 骑手锁 `lock:delivery:rider:{riderId}`,再查询该骑手的有效外卖和闪送任务。抢 `deliveryType=URGENT` 订单时要求不存在未送达外卖及 `ACCEPTED/PICKED_UP` 闪送;抢普通外卖或 `deliveryType=NORMAL` 闪送时,如果已有 `ACCEPTED/PICKED_UP` 急送则拒绝。急送进入 `DELIVERED`、`COMPLETED` 或 `CANCELLED` 后解除独占。
 
 同一骑手的外卖和闪送接单必须共享这把分布式锁,确保并发请求不能分别在两张订单表中同时通过“无冲突”检查。锁使用 Redisson 看门狗续期,不指定固定租期,并通过事务同步回调在提交或回滚完成后释放;获取超时、线程中断或 Redis 异常均拒绝本次接单。数据库订单条件更新继续负责防止不同骑手抢中同一订单。
+
+## 12. 2026-09-07 费用与物品信息对齐增量
+
+### 12.1 正交业务维度
+
+`serviceType` 只区分 `HELP_SEND` 和 `HELP_PICKUP`;`deliveryType` 独立区分 `NORMAL` 和 `URGENT`;`deliveryMode` 继续区分 `NOW` 和 `SCHEDULED`。帮送和帮取共享同一套运价,只有 `deliveryType=URGENT` 启用加急计价和骑手独占。
+
+### 12.2 统一分时段运价
+
+平台每个时间段配置 `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`。
+
+### 12.3 报价回传与创建校验
+
+报价响应返回配置 ID/版本、距离及来源、普通运价明细、`baseDeliveryFee`、`urgentRate`、`minimumUrgentFee`、`urgentFee`、`tipAmount` 和 `amount`。创建订单时客户端回传配置 ID/版本以及 `baseDeliveryFee`、`distanceFee`、`urgentFee`、`amount`;服务端重新取路线、匹配时间段并计算费用,逐项精确一致才创建。任何配置或费用变化都不得静默创建订单,响应必须携带最新报价供 App 重新确认。
+
+### 12.4 物品信息
+
+请求保存 `packageType`、`quantity`、`totalWeightKg` 和可选 `specification`。数量必须是正整数;总重量精确到两位小数且大于 0、不超过 20kg;规格说明去除首尾空格后最长 255 字符。`packageSize` 不再由客户端提交,而由服务端按总重量生成并作为订单快照保留给骑手与平台使用。
+
+### 12.5 非支付边界
+
+费用字段是报价和订单应付金额快照,不代表已经完成资金扣款。订单创建后仍直接进入 `WAITING_ACCEPTANCE`,不增加待支付状态、支付倒计时、支付回调或退款流程;骑手小费也只作为订单费用明细和接单参考展示。

+ 37 - 31
specs/024-flash-delivery/spec.md

@@ -4,30 +4,33 @@
 
 **创建日期**:2026-08-31
 
-**状态**:平台前端与外卖同规则时段运价调整已验证;App 订单列表与详情精简已提交,定向测试与模块构建通过
+**状态**:既有功能已验证;2026-09-07 设计稿费用与物品信息对齐增量已确认,待实施
 
-**输入**:基于蓝湖“闪送”分组 7 个设计页面、2026-08-24 用户端/骑手端补充原型及 2026-09-03 更新的 19 张用户端设计稿,实现帮送、帮取、加急送的完整非支付业务闭环,包括共享地址簿、路线报价、包裹信息、立即/预约配送、可选 PIN 交付、创建订单、寄件人/收件人订单查询、骑手主动抢单、1 对 1 急送独占、取件、送达、用户签收、平台配置与介入。
+**输入**:基于蓝湖“闪送”分组 7 个设计页面、2026-08-24 用户端/骑手端补充原型及 2026-09-03 更新的 19 张用户端设计稿,实现帮送/帮取业务场景、普通/1 对 1 加急配送、共享地址簿、路线报价、完整物品信息、立即/预约配送、费用明细与报价校验、可选 PIN 交付、创建订单、寄件人/收件人订单查询、骑手主动抢单、急送独占、取件、送达、用户签收、平台配置与介入的非支付业务闭环
 
 ## 用户场景与测试
 
 ### 用户故事 1:用户获取报价并发布闪送订单(优先级:P1)
 
-用户选择帮送、帮取或加急送,填写取件与收件地址后获取服务端报价,并发布一笔等待骑手接单的闪送订单。
+用户选择帮送或帮取、普通或 1 对 1 加急配送,填写取送地址、物品信息和可选骑手小费后获取服务端报价,并回传报价明细发布一笔等待骑手接单的闪送订单。
 
 **优先级原因**:报价与发布订单是闪送服务成立的基础,没有该能力就无法形成可配送任务。
 
-**独立测试**:为同一组取件和收件坐标分别选择三种服务,验证系统按服务类型和当前时间匹配运价时段,使用与外卖订单一致的起送与里程规则返回整数新台币报价,并在创建订单时重新计算价格、保存地址与计价快照且进入待接单状态
+**独立测试**:为同一组取件和收件坐标分别选择帮送/帮取及普通/加急配送,验证两种业务场景共享运价,立即订单按当前时刻、预约订单按预约开始时刻匹配唯一运价时段;系统使用起送与里程规则计算普通配送费,以当前时段的加急比例和最低加急费计算加急费,再加入用户小费返回整数新台币明细;创建订单时前端回传报价,后端复算并逐项校验后保存物品、地址、路线和计价快照
 
 **验收场景**:
 
-1. **假如** 当前时间命中所选服务类型的运价时段,**当** 用户请求报价,**那么** 系统使用该时段的起送距离、起送价格、计价距离和计价金额计算报价。
+1. **假如** 立即订单的当前时刻或预约订单的预约开始时刻命中唯一运价时段,**当** 用户请求报价,**那么** 帮送与帮取均使用该时段的起送距离、起送价格、计价距离、计价金额、加急比例和最低加急费计算报价。
 2. **假如** 路线距离超过起送距离不足 0.5 公里、达到 0.5 公里但不足 1 公里或超过 1 公里,**当** 用户请求报价,**那么** 系统分别按 0、1 公里或实际超出距离计算里程费用,并将新台币金额四舍五入为整数元。
 3. **假如** 地图路线服务正常,**当** 用户请求报价,**那么** 系统使用路线距离并标记距离来源为路线服务。
 4. **假如** 地图路线服务超时或失败,**当** 用户请求报价,**那么** 系统使用经纬度直线距离降级报价并明确标记距离来源。
-5. **假如** 客户端提交伪造金额或距离,**当** 用户创建订单,**那么** 系统忽略客户端金额与距离并在服务端重新报价。
+5. **假如** 客户端创建订单时回传的配置版本、基础配送费、距离费、加急费或总金额与服务端最新复算结果不一致,**当** 系统校验报价,**那么** 系统不得创建订单,并返回报价已变化的业务结果及最新报价。
 6. **假如** 同一用户以相同客户端请求号重复创建订单,**当** 请求被重复处理,**那么** 系统返回同一订单且不产生重复订单。
 7. **假如** 用户选择预约配送,**当** 创建订单,**那么** 系统保存 30 分钟取件时段,预约开始前订单不进入骑手可抢列表。
 8. **假如** 路线距离超过 40 公里,**当** 用户报价或创建订单,**那么** 系统拒绝请求。
+9. **假如** 用户选择普通配送,**当** 系统报价,**那么** 加急费必须为 0;用户选择加急配送时,加急费必须为普通配送费乘当前时段加急比例并与最低加急费取较大值。
+10. **假如** 用户填写物品类别、数量、总重量和规格,**当** 报价或创建订单,**那么** 系统校验数量为正整数、总重量大于 0 且不超过 20kg,并由总重量生成内部重量档。
+11. **假如** 用户填写骑手小费,**当** 系统报价或创建订单,**那么** 小费作为独立非负整数 TWD 明细参与总金额,但不改变基础配送费或加急费。
 
 ---
 
@@ -97,16 +100,16 @@
 
 ### 用户故事 5:平台管理时段运价并介入异常订单(优先级:P2)
 
-平台管理员为三种闪送服务分别维护多个运价时段,查询全部闪送订单,并在取件后的异常场景中取消或完成订单。
+平台管理员维护一套供帮送和帮取共享的分时段运价,查询全部闪送订单,并在取件后的异常场景中取消或完成订单。
 
 **优先级原因**:价格必须可运营调整,取件后的订单又必须具备受控的人工处置入口。
 
-**独立测试**:管理员新增、修改和删除加急送运价时段,验证重叠时段被拒绝、保存后立即生效、新报价使用当前时段及最新版本;随后对已取件订单执行平台取消并检查状态日志。
+**独立测试**:管理员新增、修改和删除统一运价时段,验证重叠时段被拒绝、保存后立即生效,普通报价使用基础配送规则,加急报价额外使用该时段的加急比例和最低加急费;随后对已取件订单执行平台取消并检查状态日志。
 
 **验收场景**:
 
-1. **假如** 管理员新增或修改某一服务类型的运价时段,**当** 新请求在该时段内报价,**那么** 新报价立即使用新配置且已创建订单价格不变。
-2. **假如** 时间范围重叠、起止时间非法、距离或金额不是正数、金额不是整数或服务类型非法,**当** 管理员提交配置,**那么** 系统拒绝保存。
+1. **假如** 管理员新增或修改统一运价时段,**当** 帮送或帮取的新请求命中该时段,**那么** 新报价立即使用新配置且已创建订单价格不变。
+2. **假如** 时间范围重叠、起止时间非法、距离或金额不是正数、金额不是整数、加急比例为负数或最低加急费为负数,**当** 管理员提交配置,**那么** 系统拒绝保存。
 3. **假如** 普通用户或骑手调用平台配置或介入接口,**当** 权限校验执行,**那么** 系统拒绝操作。
 4. **假如** 订单已取件但发生异常,**当** 有权限的管理员取消订单,**那么** 系统允许取消并记录管理员、原因和时间。
 5. **假如** 订单已送达但用户无法确认,**当** 有权限的管理员确认完成,**那么** 系统完成订单并记录平台操作日志。
@@ -115,15 +118,15 @@
 
 ### 用户故事 6:平台在管理后台配置价格并处理闪送订单(优先级:P2)
 
-平台管理员从“闪送管理”菜单进入价格配置或订单管理页面,无需直接调用接口即可维护三种服务价格、筛选订单、查看完整履约资料并执行受权限控制的平台介入。
+平台管理员从“闪送管理”菜单进入价格配置或订单管理页面,无需直接调用接口即可维护统一分时段运价、筛选订单、查看完整履约资料并执行受权限控制的平台介入。
 
 **优先级原因**:后端接口已具备,但缺少平台可操作入口会导致计价只能通过接口工具维护,异常订单也无法进入日常运营流程。
 
-**独立测试**:以拥有完整闪送权限的管理员登录后台,分别完成三种服务价格读取与修改、订单组合筛选、详情查看、异常取消和已送达订单完成;再以缺少对应权限的账号验证菜单与操作按钮不可用。
+**独立测试**:以拥有完整闪送权限的管理员登录后台,完成统一分时段运价读取与修改、订单组合筛选、详情查看、异常取消和已送达订单完成;再以缺少对应权限的账号验证菜单与操作按钮不可用。
 
 **验收场景**:
 
-1. **假如** 管理员进入价格配置页面,**当** 页面加载,**那么** 系统按帮送、帮取和加急送展示全部运价时段、起送规则、计价规则及配置版本。
+1. **假如** 管理员进入价格配置页面,**当** 页面加载,**那么** 系统按开始时间展示全部统一运价时段、起送规则、普通超距规则、加急比例、最低加急费及配置版本。
 2. **假如** 管理员新增、修改或删除合法时段,**当** 操作成功,**那么** 页面刷新服务端配置;非法值、重叠时段或并发冲突必须保留编辑内容并展示错误。
 3. **假如** 管理员进入订单页面,**当** 按状态、服务类型、订单号、用户 ID 或骑手 ID 查询,**那么** 表格按服务端分页结果展示并可重置条件。
 4. **假如** 管理员查看订单详情,**当** 详情加载,**那么** 页面分区展示订单概况、取送地址、费用明细、预约与 PIN、骑手、图片凭证和状态日志。
@@ -134,7 +137,7 @@
 
 ### 用户故事 7:收件人查看订单且急送骑手保持独占(优先级:P1)
 
-已注册普通用户在他人创建订单时通过收件手机号被安全绑定为收件人,可在“我收的”查看订单详情和配送进度;骑手承接 1 对 1 急送后,在订单送达或取消前不得承接其他外卖或闪送任务。
+已注册普通用户在他人创建订单时通过收件手机号被安全绑定为收件人,可在“我收的”查看订单详情和配送进度;骑手承接 `deliveryType=URGENT` 的 1 对 1 急送后,在订单送达或取消前不得承接其他外卖或闪送任务。
 
 **独立测试**:使用已注册、未注册、重复手机号和换绑手机号创建订单,验证收件账号只在创建时唯一匹配并固化;并发发起急送与其他任务抢单,验证同一骑手只能进入一个互斥的有效接单结果。
 
@@ -152,9 +155,9 @@
 - 取件地址和收件地址相同或坐标相同,不允许创建订单。
 - 地址缺少联系人、联系电话、完整地址或有效经纬度时,不允许用于报价和创建订单。
 - 经纬度超出合法范围时拒绝请求,不调用地图服务。
-- 对应服务类型在当前时间没有匹配的运价时段时,不允许报价或创建订单,并返回“当前时段暂无可用运价”。
+- 立即订单在当前时刻、预约订单在预约开始时刻没有匹配的运价时段时,不允许报价或创建订单,并返回“对应取件时段暂无可用运价”。
 - 地图路线结果缺少有效距离时按路线失败处理并降级为直线距离。
-- 创建订单时当前配置与先前报价时不同,以创建时服务端重新计算结果为准
+- 创建订单时当前配置或路线结果与先前报价不同,拒绝创建并返回最新报价,不得静默使用新金额创建订单
 - 待接单列表仅返回待接单订单;已取消或已被抢走的订单不得继续出现在新查询结果中。
 - 待抢订单列表按原型显示完整取送文字地址,但隐藏联系人、电话、实际 PIN、精确坐标和内部字段;抢单成功后订单骑手才能读取履约所需的完整联系方式与坐标。
 - 骑手第一版不能自行放弃已抢订单,需要平台介入处理。
@@ -168,18 +171,18 @@
 
 ### 功能需求
 
-- **FR-001**:系统必须提供帮送、帮取和加急送三种服务类型
+- **FR-001**:系统必须将业务场景与配送等级分离:`serviceType` 只接受 `HELP_SEND`(帮送)和 `HELP_PICKUP`(帮取),`deliveryType` 只接受 `NORMAL`(普通配送)和 `URGENT`(1 对 1 加急配送)
 - **FR-002**:帮取只承担取件配送,不包含代购、垫付或商品金额。
-- **FR-003**:三种服务必须使用同一订单履约状态机;加急送仅在排序和计价上体现优先级
-- **FR-004**:系统必须按服务类型维护多个运价时段,每个时段保存开始时间、结束时间、起送距离、起送价格、计价距离、计价金额、配置版本与修改记录;配置保存即生效,不设置启停状态。
-- **FR-005**:报价必须先按服务类型和当前时间匹配唯一运价时段;同一服务类型的时间段不得重叠,没有匹配时段时必须拒绝报价和创建订单。
+- **FR-003**:帮送、帮取、普通配送和加急配送必须使用同一订单履约状态机;`deliveryType=URGENT` 额外启用骑手独占规则和加急计价
+- **FR-004**:系统必须维护一套供帮送和帮取共享的多个运价时段;每个时段保存开始时间、结束时间、起送距离、起送价格、计价距离、计价金额、加急比例、最低加急费、配置版本与修改记录,配置保存即生效且不设置启停状态。
+- **FR-005**:立即订单必须按报价时刻匹配唯一运价时段,预约订单必须按 `scheduledPickupStartAt` 匹配唯一运价时段;全部时间段之间不得重叠,没有匹配时段时必须拒绝报价和创建订单。
 - **FR-006**:计价必须与现有外卖订单一致:起送距离内只收起送价格;超出不足 0.5 公里不加价,超出 0.5 公里但不足 1 公里按 1 公里计,超出至少 1 公里按实际超出距离计;里程费用四舍五入到整数元后与起送价格相加,所有 TWD 金额均为整数且不再按百位或千位特殊取整。
 - **FR-007**:系统必须优先使用地图路线距离,地图失败时使用经纬度直线距离并向客户端返回距离来源。
 - **FR-008**:地图密钥必须从服务端配置读取,不得返回客户端或写入业务日志。
-- **FR-009**:创建订单时必须由服务端重新计算距离和价格,并保存地址、路线、金额和计价配置快照。
+- **FR-009**:创建订单时前端必须回传报价配置 ID、版本、基础配送费、距离费、加急费和总金额;服务端必须重新计算距离和价格并逐项精确校验,一致后才能保存地址、路线、金额和计价配置快照,不一致时不得创建订单并必须返回最新报价
 - **FR-010**:系统必须使用客户端请求号保证同一用户的订单创建幂等。
 - **FR-011**:闪送订单不得复用餐饮订单或打车订单数据模型。
-- **FR-012**:订单必须保存包裹类别和机车可载重量档,重量档为 `SMALL`(不超过 5kg)、`MEDIUM`(不超过 12kg)或 `LARGE`(不超过 20kg);不保存精确重量或件数
+- **FR-012**:订单必须保存包裹类别、正整数数量、大于 0 且不超过 20kg 的总重量及可选体积/规格说明;内部重量档由总重量生成:不超过 5kg 为 `SMALL`,超过 5kg 且不超过 12kg 为 `MEDIUM`,超过 12kg 且不超过 20kg 为 `LARGE`,不得由客户端直接指定
 - **FR-013**:订单必须支持用户备注,但备注不参与计价。
 - **FR-014**:骑手必须通过统一闪送订单列表的 `newTask` 页签主动抢单,第一阶段不实现自动派单。
 - **FR-015**:骑手闪送列表必须复用骑手外卖订单列表的查询参数名称和分页习惯:`page`、`size`、`tab`、`longitude`、`latitude`;不得再拆分为 `available`、`mine` 或叠加 `scene`、`status`、`serviceType` 筛选。
@@ -218,37 +221,40 @@
 - **FR-048**:路线距离不得超过 40 公里;超过限制时报价和创建均拒绝。
 - **FR-049**:平台管理前端必须提供“闪送管理”父菜单,并提供“价格配置”和“闪送订单”两个子页面。
 - **FR-050**:平台前端必须通过现有六个 `flash:*` 权限分别控制页面访问、价格修改、订单查看、取消和完成操作,不得仅依赖按钮隐藏代替后端鉴权。
-- **FR-051**:价格配置页面必须按服务类型展示全部时段,并支持新增、修改和删除;表单字段为 `startTime`、`endTime`、`startingDistance`、`startingFare`、`distance` 和 `freight`,同时展示只读配置版本与更新时间。
-- **FR-052**:价格表单必须校验时间范围有效、同服务时间段不重叠、距离为正数、起送价格和计价金额为正整数;配置保存即生效,删除后立即不可用于报价,操作成功后必须重新读取服务端列表。
+- **FR-051**:价格配置页面必须按开始时间展示统一运价时段,并支持新增、修改和删除;表单字段为 `startTime`、`endTime`、`startingDistance`、`startingFare`、`distance`、`freight`、`urgentRate` 和 `minimumUrgentFee`,同时展示只读配置版本与更新时间。
+- **FR-052**:价格表单必须校验时间范围有效、全部时段互不重叠、距离为正数、起送价格和计价金额为正整数、加急比例和最低加急费为非负数;配置保存即生效,删除后立即不可用于报价,操作成功后必须重新读取服务端列表。
 - **FR-053**:订单页面必须按服务端分页,并支持状态、服务类型、订单号、用户 ID 和骑手 ID 组合筛选及一键重置。
 - **FR-054**:平台订单详情必须展示订单概况、取送地址、路线与费用、预约与 PIN、骑手 ID 与履约时间、三类图片凭证及状态日志;不得要求管理员从列表字段拼接详情。
 - **FR-055**:平台取消必须要求非空原因和二次确认;平台完成必须二次确认。操作成功后刷新服务端数据,操作失败时保留当前页面并显示后端错误。
 - **FR-056**:平台前端新增的全部用户可见文本必须通过 Vue i18n 提供简体中文、繁体中文、英文和越南文,不得硬编码单一语言文本。
 - **FR-057**:平台前端必须沿用现有 Vue 2、Element UI、若依请求封装、动态菜单和 `v-hasPermi` 体系,不新增重复的状态管理或 UI 框架。
 - **FR-058**:骑手列表 `tab` 只接受 `newTask`、`toPickup`、`delivering`、`completed` 和 `cancelled`,分别映射待接单、已接单、已取件、已送达/已完成和已取消;闪送不提供外卖退款页签。
-- **FR-059**:骑手 `newTask` 列表必须返回取件点距离、路线距离、预计时长、订单金额、包裹类别、重量档、配送方式、预约时段、完整取送文字地址和是否需要 PIN,并返回附近任务总数与最高订单金额;不得返回已移除的尖峰倍率或尚未实现的骑手收入。
+- **FR-059**:骑手 `newTask` 列表必须返回取件点距离、路线距离、预计时长、基础配送费、加急费、小费、订单金额、业务场景、配送等级、包裹类别、数量、总重量、规格、重量档、配送方式、预约时段、完整取送文字地址和是否需要 PIN,并返回附近任务总数与最高订单金额;不得返回内部加急比例、最低加急费或尚未实现的骑手收入。
 - **FR-060**:用户创建订单、用户详情、骑手详情和骑手接单成功响应必须使用直接且稳定的 App 订单详情结构;权限差异只影响敏感字段是否返回,不得改变 `data` 的顶层形状,也不得要求 App 通过 `data.order` 是否存在判断响应类型。
 - **FR-061**:订单新增可空 `receiver_user_id`;创建订单时将收件手机号去除首尾/内部空白、`+`、`-` 和圆括号但不改写国家码,再以剩余数字唯一匹配当时已注册且 `userType=0` 的普通用户并固化匹配结果,不执行历史订单追溯认领。
 - **FR-062**:手机号无匹配或存在多个普通用户匹配时不得绑定收件账号;订单仍可创建,且不得选择任意一名用户作为收件人。
 - **FR-063**:用户订单列表新增 `role` 参数,只接受 `sender` 和 `receiver`,省略时兼容为 `sender`;筛选必须在数据库分页前完成。
 - **FR-064**:订单创建人和已绑定收件人均可查询订单详情并确认签收;只有订单创建人可以主动取消订单。交付 PIN 对两类参与者可见。
-- **FR-065**:`URGENT` 表示 1 对 1 独占配送。骑手已有未送达外卖、`ACCEPTED` 或 `PICKED_UP` 闪送时不得接急送;骑手存在 `ACCEPTED` 或 `PICKED_UP` 急送时不得再接任何外卖或闪送。
+- **FR-065**:`deliveryType=URGENT` 表示 1 对 1 独占配送。骑手已有未送达外卖、`ACCEPTED` 或 `PICKED_UP` 闪送时不得接急送;骑手存在 `ACCEPTED` 或 `PICKED_UP` 急送时不得再接任何外卖或闪送。
 - **FR-066**:所有有效骑手接单入口必须使用同一 Redisson 锁键 `lock:delivery:rider:{riderId}`,在持锁期间检查跨外卖和闪送订单的独占条件并执行接单;锁必须在数据库事务完成后释放,获取超时或 Redis 异常时按失败关闭原则拒绝接单。
 - **FR-067**:闪送继续保持非支付业务,创建成功直接进入 `WAITING_ACCEPTANCE`,不增加待支付状态、支付倒计时、支付接口或退款流程。
 - **FR-068**:所有闪送金额继续使用整数 TWD;蓝湖稿中的小数金额仅为视觉占位,不改变接口和数据库金额类型。
 - **FR-069**:用户端不得展示固定或伪造的附近骑手人数及预计接单分钟数,统一使用“发布后等待附近骑手接单”;本期不增加实时骑手数量或接单预测接口。
+- **FR-070**:普通配送费等于起送价格与距离费之和;普通配送的加急费为 0,加急配送的加急费等于“普通配送费 × 当前时段加急比例”四舍五入到整数 TWD 后与最低加急费取较大值。
+- **FR-071**:骑手小费由用户提交,为独立的非负整数 TWD;订单总金额等于普通配送费、加急费和骑手小费之和,小费不得参与加急费计算。
+- **FR-072**:报价和订单响应必须分别返回 `baseDeliveryFee`、`distanceFee`、`urgentFee`、`tipAmount` 和 `amount`;加急报价还必须返回命中的 `urgentRate` 和 `minimumUrgentFee`,费用名称不得混用。
 
 ### 关键实体
 
-- **闪送订单**:用户发布的取件配送任务,持有地址、路线、价格、服务类型、履约状态和关键时间快照。
-- **计价配置**:某一服务类型在一个时间范围内立即生效的起送距离、起送价格、计价距离、计价金额及版本信息。
+- **闪送订单**:用户发布的取件配送任务,持有业务场景、配送等级、物品、地址、路线、费用、履约状态和关键时间快照。
+- **计价配置**:供帮送和帮取共享、在一个时间范围内立即生效的起送距离、起送价格、计价距离、计价金额、加急比例、最低加急费及版本信息。
 - **配送凭证**:用户下单时的可选寄件图片,以及骑手取件或送达时提交的必填图片记录。
 - **订单状态日志**:记录订单每次有效状态变化的操作审计信息。
 - **共享地址**:属于单一用户、可供闪送和现有收货业务共同使用的联系人与地点信息。
 
 ## 成功标准
 
-- **SC-001**:三种服务的当前时段匹配、起送距离、0.5 公里边界、实际超出距离、整数新台币取整和加急配置报价测试全部通过。
+- **SC-001**:帮送/帮取共享运价、立即/预约时段匹配、起送距离、0.5 公里边界、实际超出距离、整数新台币取整、比例加急费、最低加急费和小费报价测试全部通过。
 - **SC-002**:地图路线成功和失败降级场景均能在一次请求内返回可用报价,并准确标记距离来源。
 - **SC-003**:至少 20 个并发抢单请求针对同一订单时,数据库中恰好只有一名骑手成功绑定。
 - **SC-004**:跨用户地址和订单的详情、修改、删除、置顶、取消及签收测试拦截率达到 100%。
@@ -257,7 +263,7 @@
 - **SC-007**:送达超过 24 小时的订单能够自动完成,且并发确认不会产生重复状态日志或重复副作用。
 - **SC-008**:修改计价配置后,新订单使用新版本,历史订单保存的价格与配置快照保持不变。
 - **SC-009**:所有新增用户可见业务错误均能通过项目支持的语言资源解析,不出现硬编码单一语言结果。
-- **SC-010**:拥有对应权限的管理员能够只通过平台页面完成三种服务价格维护和订单查询、详情、取消及完成操作,核心流程无需 Postman 或数据库操作。
+- **SC-010**:拥有对应权限的管理员能够只通过平台页面完成统一分时段运价维护和订单查询、详情、取消及完成操作,核心流程无需 Postman 或数据库操作。
 - **SC-011**:价格和订单页面的接口路径、请求方法、查询参数及权限字符串契约测试全部通过,四个前端语言文件的 `flashDelivery` key 集合完全一致。
 - **SC-012**:平台前端生产构建通过,新增页面在 1280px 及以上后台常用宽度无横向页面溢出,表格窄屏时仅在表格区域滚动。
 
@@ -273,7 +279,7 @@
 ## 非目标
 
 - 不接入支付网关、付款、退款、取消费、退回费、结算或骑手收入分账。
-- 不实现代购、垫付款、商品金额、精确重量、件数或禁寄品电子确认;禁运品、货值和尺寸限制由前端展示规则说明,后端只校验本期明确的重量档和最大路线距离。
+- 不实现代购、垫付款、商品金额或禁寄品电子确认;禁运品和货值限制由前端展示规则说明,后端校验本期明确的物品数量、总重量、规格长度和最大路线距离。
 - 不实现自动派单、抢单推送筛选、骑手在线状态计算或接单时间预测。
 - 不修改餐饮订单 `pos_order` 或打车订单 `taxi_order` 的业务语义。
 - 不在本功能中实现用户端、骑手端或商家端页面;本轮前端增量仅实现平台管理端。