Преглед на файлове

Merge branch 'test' into test-202609v2

# Conflicts:
#	.claude/homunculus/observations.jsonl
qmj преди 7 часа
родител
ревизия
8c60118e8b
променени са 29 файла, в които са добавени 481 реда и са изтрити 2 реда
  1. 158 0
      .claude/homunculus/observations.jsonl
  2. 7 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryAdminController.java
  3. 2 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAddressChangeRequest.java
  4. 5 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAddressConfirmRequest.java
  5. 9 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAddressView.java
  6. 3 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAdminOrderDetailView.java
  7. 3 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryHomeView.java
  8. 5 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryOrderDetailView.java
  9. 33 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryOrderView.java
  10. 6 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryPriceBreakdown.java
  11. 25 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryQuoteView.java
  12. 27 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderListView.java
  13. 6 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderPageView.java
  14. 6 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderSummaryView.java
  15. 12 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryServiceView.java
  16. 23 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryUserOrderListView.java
  17. 1 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/route/FlashDeliveryRouteService.java
  18. 1 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/route/GoogleRoutesDistanceProvider.java
  19. 1 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/route/RouteDistanceProvider.java
  20. 77 2
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationService.java
  21. 8 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryPricingCalculator.java
  22. 1 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryStateMachine.java
  23. 4 0
      ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/InfoAddressBookService.java
  24. 25 0
      ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryOrder.java
  25. 6 0
      ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryOrderImage.java
  26. 7 0
      ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryOrderLog.java
  27. 12 0
      ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryPricing.java
  28. 6 0
      ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryStatus.java
  29. 2 0
      ruoyi-system/src/main/java/com/ruoyi/system/mapper/flash/FlashDeliveryPricingMapper.java

Файловите разлики са ограничени, защото са твърде много
+ 158 - 0
.claude/homunculus/observations.jsonl


+ 7 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/controller/FlashDeliveryAdminController.java

@@ -30,12 +30,14 @@ public class FlashDeliveryAdminController extends BaseController {
         this.service = service;
     }
 
+    /** 查询全部运价配置列表。 */
     @PreAuthorize("@ss.hasPermi('flash:pricing:list')")
     @GetMapping("/pricing")
     public AjaxResult pricing() {
         return success(service.adminPricing());
     }
 
+    /** 新增时段运价配置。 */
     @PreAuthorize("@ss.hasPermi('flash:pricing:edit')")
     @Log(title = "闪送时段运价", businessType = BusinessType.INSERT)
     @PostMapping("/pricing")
@@ -43,6 +45,7 @@ public class FlashDeliveryAdminController extends BaseController {
         return success(service.createPricing(SecurityUtils.getUserId(), request));
     }
 
+    /** 修改时段运价配置,按版本乐观锁更新。 */
     @PreAuthorize("@ss.hasPermi('flash:pricing:edit')")
     @Log(title = "闪送时段运价", businessType = BusinessType.UPDATE)
     @PutMapping("/pricing/{id}")
@@ -51,6 +54,7 @@ public class FlashDeliveryAdminController extends BaseController {
         return success(service.updatePricing(SecurityUtils.getUserId(), id, request));
     }
 
+    /** 删除时段运价配置。 */
     @PreAuthorize("@ss.hasPermi('flash:pricing:edit')")
     @Log(title = "闪送时段运价", businessType = BusinessType.DELETE)
     @DeleteMapping("/pricing/{id}")
@@ -82,12 +86,14 @@ public class FlashDeliveryAdminController extends BaseController {
         return success(service.adminOrders(pageNum, pageSize, status, serviceType, orderNo, userId, riderId));
     }
 
+    /** 平台订单审计详情:订单完整实体、凭证图片与状态日志。 */
     @PreAuthorize("@ss.hasPermi('flash:order:query')")
     @GetMapping("/orders/{id}")
     public AjaxResult detail(@PathVariable Long id) {
         return success(service.adminDetail(id));
     }
 
+    /** 平台取消订单,必须填写取消原因。 */
     @PreAuthorize("@ss.hasPermi('flash:order:cancel')")
     @Log(title = "闪送订单取消", businessType = BusinessType.UPDATE)
     @PostMapping("/orders/{id}/cancel")
@@ -96,6 +102,7 @@ public class FlashDeliveryAdminController extends BaseController {
         return success();
     }
 
+    /** 平台手动将已送达订单置为已完成。 */
     @PreAuthorize("@ss.hasPermi('flash:order:complete')")
     @Log(title = "闪送订单完成", businessType = BusinessType.UPDATE)
     @PostMapping("/orders/{id}/complete")

+ 2 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAddressChangeRequest.java

@@ -7,6 +7,8 @@ import lombok.Data;
 public class FlashDeliveryAddressChangeRequest {
     /** 用户订单详情或地址报价返回的订单版本,必填。 */
     private Integer orderVersion;
+    /** 新取件地址,需完整提交,未修改时也原样回传。 */
     private FlashDeliveryAddressRequest pickup;
+    /** 新收件地址,需完整提交,未修改时也原样回传。 */
     private FlashDeliveryAddressRequest delivery;
 }

+ 5 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAddressConfirmRequest.java

@@ -9,9 +9,14 @@ import lombok.EqualsAndHashCode;
 public class FlashDeliveryAddressConfirmRequest extends FlashDeliveryAddressChangeRequest {
     /** 用户订单详情的 id,必填正整数。 */
     private Long orderId;
+    /** 确认时回传的报价距离(米),与服务端重算结果一致才允许保存。 */
     private Integer quotedDistanceMeters;
+    /** 确认时回传的报价基础配送费(TWD)。 */
     private Long quotedBaseDeliveryFee;
+    /** 确认时回传的报价距离费(TWD)。 */
     private Long quotedDistanceFee;
+    /** 确认时回传的报价加急费(TWD)。 */
     private Long quotedUrgentFee;
+    /** 确认时回传的报价总金额(TWD)。 */
     private Long quotedAmount;
 }

+ 9 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAddressView.java

@@ -7,13 +7,22 @@ import java.math.BigDecimal;
 /** 订单用户或中单骑手可见的取送地址业务快照。 */
 @Data
 public class FlashDeliveryAddressView {
+    /** 联系人姓名,骑手接单前不返回。 */
     private String name;
+    /** 联系电话,骑手接单前不返回。 */
     private String phone;
+    /** 完整主地址。 */
     private String address;
+    /** 门牌、楼层等详细地址。 */
     private String addressDetail;
+    /** 城市。 */
     private String city;
+    /** 行政区。 */
     private String area;
+    /** 交接方式或联系说明。 */
     private String handoffMethod;
+    /** 地址经度,骑手接单前不返回。 */
     private BigDecimal longitude;
+    /** 地址纬度,骑手接单前不返回。 */
     private BigDecimal latitude;
 }

+ 3 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryAdminOrderDetailView.java

@@ -10,7 +10,10 @@ import java.util.List;
 /** 平台审计详情,平台权限通过后才返回完整持久化字段。 */
 @Data
 public class FlashDeliveryAdminOrderDetailView {
+    /** 订单完整持久化实体。 */
     private FlashDeliveryOrder order;
+    /** 全部凭证图片。 */
     private List<FlashDeliveryOrderImage> images;
+    /** 状态变更日志。 */
     private List<FlashDeliveryOrderLog> logs;
 }

+ 3 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryHomeView.java

@@ -7,7 +7,10 @@ import java.util.List;
 /** 用户闪送首页响应,仅包含当前启用的公开服务摘要。 */
 @Data
 public class FlashDeliveryHomeView {
+    /** 开放的业务场景列表(HELP_SEND、HELP_PICKUP)。 */
     private List<String> serviceTypes;
+    /** 开放的配送等级列表(NORMAL、URGENT)。 */
     private List<String> deliveryTypes;
+    /** 当前时段各服务的运价摘要,无可用运价时为空列表。 */
     private List<FlashDeliveryServiceView> services;
 }

+ 5 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryOrderDetailView.java

@@ -13,11 +13,16 @@ public class FlashDeliveryOrderDetailView extends FlashDeliveryOrderView {
     /** 用户编辑待接单订单使用的版本;骑手详情不返回。 */
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private Integer orderVersion;
+    /** 寄件图片 URL 列表,用户下单时上传,可为空。 */
     private List<String> senderImageUrls = List.of();
+    /** 取件凭证图片 URL 列表,骑手上传。 */
     private List<String> pickupImageUrls = List.of();
+    /** 送达凭证图片 URL 列表,骑手上传。 */
     private List<String> deliveryImageUrls = List.of();
+    /** 骑手公开资料,未接单时为空。 */
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private FlashDeliveryRiderSummaryView rider;
+    /** 交付 PIN,仅启用 PIN 的用户详情返回,骑手端始终为空。 */
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String deliveryPinCode;
 }

+ 33 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryOrderView.java

@@ -7,39 +7,72 @@ import java.util.Date;
 /** 用户和中单骑手共用的业务订单视图,不包含持久化及身份内部字段。 */
 @Data
 public class FlashDeliveryOrderView {
+    /** 订单 ID。 */
     private Long id;
+    /** 订单号。 */
     private String orderNo;
+    /** 业务场景:HELP_SEND=帮送、HELP_PICKUP=帮取。 */
     private String serviceType;
+    /** 配送等级:NORMAL=普通、URGENT=加急。 */
     private String deliveryType;
+    /** 订单状态,取值见 FlashDeliveryStatus。 */
     private String status;
+    /** 物品类别(DOCUMENT、GIFT 等 9 种)。 */
     private String packageType;
+    /** 物品数量。 */
     private Integer quantity;
+    /** 重量范围(UP_TO_5_KG 等 4 档)。 */
     private String weightRange;
+    /** 物品规格说明。 */
     private String specification;
+    /** 取件方式:NOW=立即、SCHEDULED=预约。 */
     private String deliveryMode;
+    /** 预约取件时间窗开始时间,立即单为空。 */
     private Date scheduledPickupStartAt;
+    /** 预约取件时间窗结束时间,立即单为空。 */
     private Date scheduledPickupEndAt;
+    /** 是否启用交付 PIN。 */
     private Boolean pinRequired;
+    /** 取件地址快照,骑手接单前联系人、电话和坐标被脱敏。 */
     private FlashDeliveryAddressView pickup;
+    /** 收件地址快照,骑手接单前联系人、电话和坐标被脱敏。 */
     private FlashDeliveryAddressView delivery;
+    /** 服务端计算的路线距离(米)。 */
     private Integer distanceMeters;
+    /** 距离来源:ROUTE=地图路线、STRAIGHT_LINE=降级直线。 */
     private String distanceSource;
+    /** 预计配送时长(秒)。 */
     private Integer estimatedDurationSeconds;
+    /** 基础配送费(TWD),已含距离费。 */
     private Long baseDeliveryFee;
+    /** 距离附加费(TWD)。 */
     private Long distanceFee;
+    /** 加急费(TWD),普通配送为 0。 */
     private Long urgentFee;
+    /** 小费(TWD)。 */
     private Long tipAmount;
+    /** 订单总金额(TWD)。 */
     private Long amount;
+    /** 币种。 */
     private String currency;
     /** 支付方式:4=现金、6=线下转账;款项直达骑手,用户与骑手详情均返回。 */
     private String payType;
+    /** 用户备注,骑手接单前不返回。 */
     private String userNote;
+    /** 骑手接单时间。 */
     private Date acceptedAt;
+    /** 骑手取件时间。 */
     private Date pickedUpAt;
+    /** 骑手送达时间。 */
     private Date deliveredAt;
+    /** 订单完成时间。 */
     private Date completedAt;
+    /** 订单取消时间。 */
     private Date cancelledAt;
+    /** 取消原因。 */
     private String cancelReason;
+    /** 创建时间。 */
     private Date createTime;
+    /** 最后更新时间。 */
     private Date updateTime;
 }

+ 6 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryPriceBreakdown.java

@@ -7,10 +7,16 @@ import java.math.BigDecimal;
 /** 按外卖运费规则计算的闪送费用明细。 */
 @Data
 public class FlashDeliveryPriceBreakdown {
+    /** 计费距离(公里):超出起步距离部分的计费公里数,不足 0.5 公里为 0、0.5 至 1 公里记 1 公里、1 公里以上按实际公里数。 */
     private BigDecimal billableDistance;
+    /** 距离附加费(TWD)。 */
     private Long distanceFee;
+    /** 基础配送费(TWD),等于起步价加距离费。 */
     private Long baseDeliveryFee;
+    /** 加急费(TWD),普通配送为 0。 */
     private Long urgentFee;
+    /** 小费(TWD)。 */
     private Long tipAmount;
+    /** 总金额(TWD),等于基础配送费、加急费、小费之和。 */
     private Long amount;
 }

+ 25 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryQuoteView.java

@@ -10,29 +10,54 @@ public class FlashDeliveryQuoteView {
     /** 仅修改订单地址的报价返回,确认保存时原样回传。 */
     @com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
     private Integer orderVersion;
+    /** 业务场景(回显请求值):HELP_SEND=帮送、HELP_PICKUP=帮取。 */
     private String serviceType;
+    /** 配送等级(回显请求值):NORMAL=普通、URGENT=加急。 */
     private String deliveryType;
+    /** 取件方式(归一化后):NOW=立即、SCHEDULED=预约。 */
     private String deliveryMode;
+    /** 预约取件时间窗开始时间,立即单为空。 */
     private java.util.Date scheduledPickupStartAt;
+    /** 预约取件时间窗结束时间,立即单为空。 */
     private java.util.Date scheduledPickupEndAt;
+    /** 命中的运价配置 ID,创建订单时需回传。 */
     private Long pricingId;
+    /** 命中运价的时段开始(HH:mm)。 */
     private String startTime;
+    /** 命中运价的时段结束(HH:mm)。 */
     private String endTime;
+    /** 服务端计算的路线距离(米)。 */
     private Integer distanceMeters;
+    /** 距离来源:ROUTE=地图路线、STRAIGHT_LINE=降级直线。 */
     private String distanceSource;
+    /** 预计配送时长(秒),直线降级时为空。 */
     private Integer estimatedDurationSeconds;
+    /** 起步距离(公里)。 */
     private BigDecimal startingDistance;
+    /** 起步价(TWD)。 */
     private Long startingFare;
+    /** 距离计价步长(公里)。 */
     private BigDecimal distance;
+    /** 每个步长收取的运费(TWD)。 */
     private Long freight;
+    /** 计费距离(公里):超出起步距离部分的计费公里数,不足 0.5 公里为 0、0.5 至 1 公里记 1 公里、1 公里以上按实际公里数。 */
     private BigDecimal billableDistance;
+    /** 距离附加费(TWD),已包含在基础配送费中。 */
     private Long distanceFee;
+    /** 基础配送费(TWD),等于起步价加距离费。 */
     private Long baseDeliveryFee;
+    /** 加急费比例(百分比)。 */
     private BigDecimal urgentRate;
+    /** 最低加急费(TWD)。 */
     private Long minimumUrgentFee;
+    /** 加急费(TWD),普通配送为 0。 */
     private Long urgentFee;
+    /** 小费(TWD)。 */
     private Long tipAmount;
+    /** 订单总金额(TWD),等于基础配送费、加急费、小费之和。 */
     private Long amount;
+    /** 币种,固定 TWD。 */
     private String currency;
+    /** 运价配置版本,创建订单时需回传。 */
     private Integer pricingVersion;
 }

+ 27 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderListView.java

@@ -7,33 +7,60 @@ import java.util.Date;
 /** 骑手端闪送任务卡片,不包含联系人、电话、精确坐标或内部字段。 */
 @Data
 public class FlashDeliveryRiderOrderListView {
+    /** 订单 ID。 */
     private Long id;
+    /** 订单号。 */
     private String orderNo;
+    /** 业务场景:HELP_SEND=帮送、HELP_PICKUP=帮取。 */
     private String serviceType;
+    /** 配送等级:NORMAL=普通、URGENT=加急。 */
     private String deliveryType;
+    /** 订单状态,取值见 FlashDeliveryStatus。 */
     private String status;
+    /** 物品类别。 */
     private String packageType;
+    /** 物品数量。 */
     private Integer quantity;
+    /** 重量范围。 */
     private String weightRange;
+    /** 物品规格说明。 */
     private String specification;
+    /** 取件方式:NOW=立即、SCHEDULED=预约。 */
     private String deliveryMode;
+    /** 预约取件时间窗开始时间,立即单为空。 */
     private Date scheduledPickupStartAt;
+    /** 预约取件时间窗结束时间,立即单为空。 */
     private Date scheduledPickupEndAt;
+    /** 送达时是否需要核对交付 PIN。 */
     private Boolean pinRequired;
+    /** 取件主地址。 */
     private String pickupAddress;
+    /** 取件详细地址。 */
     private String pickupDetailAddress;
+    /** 收件主地址。 */
     private String deliveryAddress;
+    /** 收件详细地址。 */
     private String deliveryDetailAddress;
+    /** 骑手当前位置到取件点的直线距离(米),未上传坐标时为空。 */
     private Integer pickupDistanceMeters;
+    /** 服务端计算的取送路线距离(米)。 */
     private Integer distanceMeters;
+    /** 预计配送时长(秒)。 */
     private Integer estimatedDurationSeconds;
+    /** 基础配送费(TWD),已含距离费。 */
     private Long baseDeliveryFee;
+    /** 距离附加费(TWD)。 */
     private Long distanceFee;
+    /** 加急费(TWD),普通配送为 0。 */
     private Long urgentFee;
+    /** 小费(TWD)。 */
     private Long tipAmount;
+    /** 订单总金额(TWD)。 */
     private Long amount;
+    /** 币种。 */
     private String currency;
     /** 支付方式:4=现金、6=线下转账;骑手抢单前需知道如何收钱。 */
     private String payType;
+    /** 创建时间。 */
     private Date createTime;
 }

+ 6 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderPageView.java

@@ -7,10 +7,16 @@ import java.util.List;
 /** 骑手端分页结果及新任务页顶部摘要。 */
 @Data
 public class FlashDeliveryRiderOrderPageView {
+    /** 当前页任务卡片。 */
     private List<FlashDeliveryRiderOrderListView> records = List.of();
+    /** 总条数。 */
     private long total;
+    /** 当前页码。 */
     private long current;
+    /** 每页数量。 */
     private long size;
+    /** 附近任务总数,仅 newTask 页签返回。 */
     private Long nearbyTaskCount;
+    /** 附近任务中最高订单金额(TWD),仅 newTask 页签返回。 */
     private Long highestOrderAmount;
 }

+ 6 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderSummaryView.java

@@ -7,10 +7,16 @@ import java.math.BigDecimal;
 /** 订单用户可见的骑手公开资料;位置仅在进行中订单返回。 */
 @Data
 public class FlashDeliveryRiderSummaryView {
+    /** 骑手昵称,未设置时使用用户名。 */
     private String name;
+    /** 骑手头像。 */
     private String avatar;
+    /** 骑手评分。 */
     private Double rating;
+    /** IM 用户 ID(字符串形式),用于发起聊天。 */
     private String imUserId;
+    /** 骑手实时位置经度,仅已接单或已取件状态返回。 */
     private BigDecimal longitude;
+    /** 骑手实时位置纬度,仅已接单或已取件状态返回。 */
     private BigDecimal latitude;
 }

+ 12 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryServiceView.java

@@ -7,16 +7,28 @@ import java.math.BigDecimal;
 /** 用户首页当前可用的闪送服务及运价摘要。 */
 @Data
 public class FlashDeliveryServiceView {
+    /** 业务场景:HELP_SEND=帮送、HELP_PICKUP=帮取。 */
     private String serviceType;
+    /** 运价配置 ID。 */
     private Long pricingId;
+    /** 运价时段开始(HH:mm)。 */
     private String startTime;
+    /** 运价时段结束(HH:mm)。 */
     private String endTime;
+    /** 起步距离(公里)。 */
     private BigDecimal startingDistance;
+    /** 起步价(TWD)。 */
     private Long startingFare;
+    /** 距离计价步长(公里)。 */
     private BigDecimal distance;
+    /** 每个步长收取的运费(TWD)。 */
     private Long freight;
+    /** 加急费比例(百分比)。 */
     private BigDecimal urgentRate;
+    /** 最低加急费(TWD)。 */
     private Long minimumUrgentFee;
+    /** 运价配置版本。 */
     private Integer pricingVersion;
+    /** 币种,固定 TWD。 */
     private String currency;
 }

+ 23 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryUserOrderListView.java

@@ -7,29 +7,52 @@ import java.util.Date;
 /** 用户端闪送订单卡片,仅包含列表页面所需字段。 */
 @Data
 public class FlashDeliveryUserOrderListView {
+    /** 订单 ID。 */
     private Long id;
+    /** 订单号。 */
     private String orderNo;
+    /** 业务场景:HELP_SEND=帮送、HELP_PICKUP=帮取。 */
     private String serviceType;
+    /** 配送等级:NORMAL=普通、URGENT=加急。 */
     private String deliveryType;
+    /** 订单状态,取值见 FlashDeliveryStatus。 */
     private String status;
     /** 用户编辑待接单订单使用的乐观锁版本。 */
     private Integer orderVersion;
+    /** 物品类别。 */
     private String packageType;
+    /** 物品数量。 */
     private Integer quantity;
+    /** 重量范围。 */
     private String weightRange;
+    /** 取件方式:NOW=立即、SCHEDULED=预约。 */
     private String deliveryMode;
+    /** 预约取件时间窗开始时间,立即单为空。 */
     private Date scheduledPickupStartAt;
+    /** 预约取件时间窗结束时间,立即单为空。 */
     private Date scheduledPickupEndAt;
+    /** 取件主地址。 */
     private String pickupAddress;
+    /** 取件详细地址。 */
     private String pickupDetailAddress;
+    /** 收件主地址。 */
     private String deliveryAddress;
+    /** 收件详细地址。 */
     private String deliveryDetailAddress;
+    /** 预计配送时长(秒)。 */
     private Integer estimatedDurationSeconds;
+    /** 基础配送费(TWD),已含距离费。 */
     private Long baseDeliveryFee;
+    /** 加急费(TWD),普通配送为 0。 */
     private Long urgentFee;
+    /** 小费(TWD)。 */
     private Long tipAmount;
+    /** 订单总金额(TWD)。 */
     private Long amount;
+    /** 币种。 */
     private String currency;
+    /** 送达时间。 */
     private Date deliveredAt;
+    /** 创建时间。 */
     private Date createTime;
 }

+ 1 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/route/FlashDeliveryRouteService.java

@@ -12,6 +12,7 @@ public class FlashDeliveryRouteService {
         this.provider = provider;
     }
 
+    /** 计算取送两点路线距离:优先地图路线,异常或无结果时降级球面直线距离。 */
     public RouteDistance calculate(GeoPoint origin, GeoPoint destination) {
         validate(origin);
         validate(destination);

+ 1 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/route/GoogleRoutesDistanceProvider.java

@@ -26,6 +26,7 @@ public class GoogleRoutesDistanceProvider implements RouteDistanceProvider {
     private static final RequestConfig TIMEOUTS = RequestConfig.custom()
             .setConnectTimeout(2500).setConnectionRequestTimeout(2500).setSocketTimeout(3500).build();
 
+    /** 调用 Google Routes API 获取驾车路线距离与时长;密钥缺失或响应异常时抛错,由上层降级。 */
     @Override
     public RouteDistance route(BigDecimal originLatitude, BigDecimal originLongitude,
                                BigDecimal destinationLatitude, BigDecimal destinationLongitude) {

+ 1 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/route/RouteDistanceProvider.java

@@ -5,6 +5,7 @@ import java.math.BigDecimal;
 @FunctionalInterface
 /** 外部路线距离提供器,失败时由上层服务负责本地降级。 */
 public interface RouteDistanceProvider {
+    /** 按起终点计算路线距离与时长;失败直接抛异常,由上层负责降级。 */
     RouteDistance route(BigDecimal originLatitude, BigDecimal originLongitude,
                         BigDecimal destinationLatitude, BigDecimal destinationLongitude);
 }

+ 77 - 2
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationService.java

@@ -91,6 +91,7 @@ public class FlashDeliveryApplicationService {
         this.riderDeliveryExclusivityService = riderDeliveryExclusivityService;
     }
 
+    /** 闪送首页:返回开放的业务场景、配送等级,以及当前时段各服务的公开运价摘要。 */
     public FlashDeliveryHomeView home() {
         FlashDeliveryHomeView view = new FlashDeliveryHomeView();
         view.setServiceTypes(SERVICE_TYPES);
@@ -101,12 +102,14 @@ public class FlashDeliveryApplicationService {
         return view;
     }
 
+    /** 实时报价:校验请求后按服务端路线距离和命中时段的运价计算费用(立即单取当前时段、预约单取预约开始时刻),不写库。 */
     public FlashDeliveryQuoteView quote(FlashDeliveryQuoteRequest request) {
         // 报价只使用服务端路线距离和当前启用的计价配置,避免客户端篡改金额。
         QuoteContext context = calculateQuote(request);
         return quoteView(request, context);
     }
 
+    /** 将报价上下文组装为对外报价视图,含运价快照字段与费用明细。 */
     private FlashDeliveryQuoteView quoteView(FlashDeliveryQuoteRequest request, QuoteContext context) {
         FlashDeliveryQuoteView view = new FlashDeliveryQuoteView();
         view.setServiceType(request.getServiceType());
@@ -132,6 +135,11 @@ public class FlashDeliveryApplicationService {
         return view;
     }
 
+    /**
+     * 幂等创建订单:clientRequestId 与 userId 构成唯一键,重复提交直接返回已有订单。
+     * 创建前按最新条件重新报价,客户端回传报价不一致时抛出 FlashDeliveryQuoteChangedException;
+     * 成功后保存计价快照、寄件图片和初始状态日志。
+     */
     @Transactional
     public FlashDeliveryOrderDetailView create(Long userId, FlashDeliveryCreateRequest request) {
         requireUserId(userId);
@@ -214,6 +222,7 @@ public class FlashDeliveryApplicationService {
         return participantDetail(order, true, false);
     }
 
+    /** 分页查询当前用户参与的订单卡片;role=sender 按寄件人、receiver 按收件人过滤。 */
     public IPage<FlashDeliveryUserOrderListView> userOrders(Long userId, int pageNum, int pageSize, String role) {
         String selectedRole = hasText(role) ? role.trim().toLowerCase(Locale.ROOT) : "sender";
         if (!"sender".equals(selectedRole) && !"receiver".equals(selectedRole)) {
@@ -225,6 +234,7 @@ public class FlashDeliveryApplicationService {
         return mapPage(orderMapper.selectPage(page(pageNum, pageSize), query), this::userOrderListView);
     }
 
+    /** 查询本人订单详情,仅寄件人和匹配到的收件人可见。 */
     public FlashDeliveryOrderDetailView userDetail(Long userId, Long orderId) {
         FlashDeliveryOrder order = requireOrder(orderId);
         if (!isParticipant(userId, order)) throw fail("flash.delivery.order.not.found");
@@ -239,6 +249,7 @@ public class FlashDeliveryApplicationService {
         return calculateAddressQuote(order, request);
     }
 
+    /** 确认修改待接单订单地址:重算报价并与回传值比对,按版本条件更新。 */
     @Transactional
     public FlashDeliveryOrderDetailView updateAddress(Long userId, Long orderId,
                                                        FlashDeliveryAddressConfirmRequest request) {
@@ -273,6 +284,7 @@ public class FlashDeliveryApplicationService {
         return participantDetail(order, true, false);
     }
 
+    /** 待接单订单追加小费:订单总额与小费同步增加,版本条件防止并发重复加款。 */
     @Transactional
     public FlashDeliveryOrderDetailView addTip(Long userId, Long orderId, FlashDeliveryTipAddRequest request) {
         if (request == null) throw fail("flash.delivery.request.required");
@@ -302,6 +314,7 @@ public class FlashDeliveryApplicationService {
         return participantDetail(order, true, false);
     }
 
+    /** 校验并加载可编辑订单:本人、待接单、未绑定骑手且版本一致。 */
     private FlashDeliveryOrder requireEditableOrder(Long userId, Long orderId, Integer version) {
         requireUserId(userId);
         if (orderId == null || orderId <= 0) throw fail("flash.delivery.order.not.found");
@@ -315,6 +328,7 @@ public class FlashDeliveryApplicationService {
         return order;
     }
 
+    /** 地址修改报价:使用订单保存的运价快照重算,不采用当前运价,避免改地址顺带变价。 */
     private FlashDeliveryQuoteView calculateAddressQuote(FlashDeliveryOrder order,
                                                          FlashDeliveryAddressChangeRequest request) {
         validateAddress(request.getPickup());
@@ -358,6 +372,7 @@ public class FlashDeliveryApplicationService {
         return view;
     }
 
+    /** 用户取消:仅待接单或已接单(实际取件前)允许。 */
     @Transactional
     public void userCancel(Long userId, Long orderId, FlashDeliveryReasonRequest request) {
         FlashDeliveryOrder order = requireOrder(orderId);
@@ -371,6 +386,7 @@ public class FlashDeliveryApplicationService {
         writeLog(orderId, order.getStatus(), CANCELLED, "USER", userId, reason, now);
     }
 
+    /** 用户(寄件人或收件人)确认收货:已送达推进为已完成。 */
     @Transactional
     public void confirmReceipt(Long userId, Long orderId) {
         FlashDeliveryOrder order = requireOrder(orderId);
@@ -382,6 +398,11 @@ public class FlashDeliveryApplicationService {
         writeLog(orderId, DELIVERED, COMPLETED, "USER", userId, null, now);
     }
 
+    /**
+     * 骑手任务列表:按页签查询(newTask 可抢任务、toPickup 待取件、delivering 配送中、
+     * completed 已送达/已完成、cancelled 已取消)。newTask 页签额外按骑手坐标做距离过滤和就近排序,
+     * 并返回附近任务数与最高订单金额;其余页签按订单归属骑手查询。
+     */
     public FlashDeliveryRiderOrderPageView riderOrders(Long riderId, int pageNum, int pageSize,
                                                         String tab, BigDecimal longitude, BigDecimal latitude) {
         InfoUser rider = requireRider(riderId);
@@ -414,6 +435,7 @@ public class FlashDeliveryApplicationService {
         return result;
     }
 
+    /** 骑手任务详情:抢单前返回脱敏视图(隐藏联系人、电话、坐标和 PIN);接单后仅中单骑手可见完整信息。 */
     public FlashDeliveryOrderDetailView riderDetail(Long riderId, Long orderId) {
         InfoUser rider = requireRider(riderId);
         FlashDeliveryOrder order = requireOrder(orderId);
@@ -429,6 +451,7 @@ public class FlashDeliveryApplicationService {
         return participantDetail(order, false, false);
     }
 
+    /** 原子抢单:骑手锁内完成互斥校验与条件更新,同一订单仅一名骑手成功。 */
     @Transactional
     public FlashDeliveryOrderDetailView accept(Long riderId, Long orderId) {
         InfoUser rider = requireRider(riderId);
@@ -447,11 +470,13 @@ public class FlashDeliveryApplicationService {
         });
     }
 
+    /** 骑手取件:提交取件凭证,订单由已接单推进为已取件。 */
     @Transactional
     public void pickup(Long riderId, Long orderId, FlashDeliveryProofRequest request) {
         transitionWithProof(riderId, orderId, request, ACCEPTED, PICKED_UP, "PICKUP", "picked_up_at");
     }
 
+    /** 骑手送达:校验交付 PIN(启用时)并提交送达凭证,订单由已取件推进为已送达。 */
     @Transactional
     public void deliver(Long riderId, Long orderId, FlashDeliveryDeliverRequest request) {
         requireRider(riderId);
@@ -466,11 +491,13 @@ public class FlashDeliveryApplicationService {
         transitionWithProof(riderId, orderId, request, PICKED_UP, DELIVERED, "DELIVERY", "delivered_at");
     }
 
+    /** 平台运价配置列表,按时段开始时间排序。 */
     public List<FlashDeliveryPricing> adminPricing() {
         return pricingMapper.selectList(new QueryWrapper<FlashDeliveryPricing>()
                 .orderByAsc("start_time", "id"));
     }
 
+    /** 新增运价配置:校验参数合法性与时段不重叠后入库,版本号从 1 开始。 */
     @Transactional
     public FlashDeliveryPricing createPricing(Long adminId, FlashDeliveryPricingRequest request) {
         validatePricingRequest(request, false);
@@ -485,6 +512,7 @@ public class FlashDeliveryApplicationService {
         return pricing;
     }
 
+    /** 修改运价配置:按 configVersion 乐观锁条件更新,防止管理员并发互相覆盖。 */
     @Transactional
     public FlashDeliveryPricing updatePricing(Long adminId, Long pricingId,
                                                FlashDeliveryPricingRequest request) {
@@ -524,6 +552,7 @@ public class FlashDeliveryApplicationService {
         return pricing;
     }
 
+    /** 删除运价配置。 */
     @Transactional
     public void deletePricing(Long pricingId) {
         FlashDeliveryPricing preliminary = pricingId == null ? null : pricingMapper.selectById(pricingId);
@@ -535,6 +564,7 @@ public class FlashDeliveryApplicationService {
         if (pricingMapper.deleteById(pricingId) != 1) throw fail("flash.delivery.pricing.not.found");
     }
 
+    /** 平台订单分页:支持状态、业务场景、订单号模糊、寄件用户、骑手条件过滤。 */
     public IPage<FlashDeliveryOrder> adminOrders(int pageNum, int pageSize, String status,
                                                  String serviceType, String orderNo,
                                                  Long userId, Long riderId) {
@@ -548,6 +578,7 @@ public class FlashDeliveryApplicationService {
         return orderMapper.selectPage(page(pageNum, pageSize), query);
     }
 
+    /** 平台订单审计详情:返回完整订单实体、全部凭证图片和状态日志。 */
     public FlashDeliveryAdminOrderDetailView adminDetail(Long orderId) {
         FlashDeliveryOrder order = requireOrder(orderId);
         FlashDeliveryAdminOrderDetailView view = new FlashDeliveryAdminOrderDetailView();
@@ -559,6 +590,7 @@ public class FlashDeliveryApplicationService {
         return view;
     }
 
+    /** 平台取消:任何非终态订单可取消,记录操作人与原因,用于异常履约人工收口。 */
     @Transactional
     public void adminCancel(Long adminId, Long orderId, FlashDeliveryReasonRequest request) {
         FlashDeliveryOrder order = requireOrder(orderId);
@@ -571,6 +603,7 @@ public class FlashDeliveryApplicationService {
         writeLog(orderId, order.getStatus(), CANCELLED, "ADMIN", adminId, reason, now);
     }
 
+    /** 将已送达订单推进为已完成;平台手动与系统自动完成共用此入口,条件更新失败返回 false。 */
     @Transactional
     public boolean complete(Long operatorId, String operatorType, Long orderId) {
         Date now = new Date();
@@ -579,10 +612,12 @@ public class FlashDeliveryApplicationService {
         return true;
     }
 
+    /** 查询送达时间早于截止点且未完成的订单,供自动完成任务分批处理。 */
     public List<FlashDeliveryOrder> autoCompleteCandidates(Date deadline, int limit) {
         return orderMapper.selectAutoCompletable(deadline, Math.min(Math.max(limit, 1), 500));
     }
 
+    /** 报价核心:校验枚举、物品、地址与预约窗后,按时段命中运价并计算路线距离,产出报价上下文。 */
     private QuoteContext calculateQuote(FlashDeliveryQuoteRequest request) {
         if (request == null) throw fail("flash.delivery.request.required");
         validateServiceType(request.getServiceType());
@@ -613,12 +648,14 @@ public class FlashDeliveryApplicationService {
         return new QuoteContext(pricing, route, breakdown);
     }
 
+    /** 查询指定时刻命中的运价配置,未配置时直接报错。 */
     private FlashDeliveryPricing findPricingAt(String targetTime) {
         FlashDeliveryPricing pricing = findPricingAtOrNull(targetTime);
         if (pricing == null) throw fail("flash.delivery.pricing.not.available");
         return pricing;
     }
 
+    /** 查询命中运价;命中多条说明配置重叠,报错交由平台修正。 */
     private FlashDeliveryPricing findPricingAtOrNull(String targetTime) {
         List<FlashDeliveryPricing> pricing = pricingMapper.selectAtTime(targetTime);
         if (pricing == null || pricing.isEmpty()) return null;
@@ -626,6 +663,7 @@ public class FlashDeliveryApplicationService {
         return pricing.get(0);
     }
 
+    /** 校验运价请求:时间格式与区间顺序、各金额取值范围及修改时的版本号。 */
     private void validatePricingRequest(FlashDeliveryPricingRequest request, boolean requireVersion) {
         if (request == null) throw fail("flash.delivery.pricing.invalid");
         BigDecimal startingDistance = normalizeDistance(request.getStartingDistance());
@@ -647,12 +685,14 @@ public class FlashDeliveryApplicationService {
         }
     }
 
+    /** 确保新时段与既有运价不重叠,excludedId 用于修改时排除自身。 */
     private void ensureNoPricingOverlap(FlashDeliveryPricingRequest request, Long excludedId) {
         if (pricingMapper.countOverlapping(request.getStartTime(), request.getEndTime(), excludedId) > 0) {
             throw fail("flash.delivery.pricing.overlap");
         }
     }
 
+    /** 将运价请求转换为实体,距离与比例统一保留两位小数。 */
     private FlashDeliveryPricing pricingFrom(FlashDeliveryPricingRequest request) {
         FlashDeliveryPricing pricing = new FlashDeliveryPricing();
         pricing.setStartTime(request.getStartTime());
@@ -691,6 +731,7 @@ public class FlashDeliveryApplicationService {
         return value == null ? null : value.setScale(2, RoundingMode.HALF_UP);
     }
 
+    /** 骑手带凭证的状态推进:校验凭证后按骑手归属条件更新,再落图片与日志。 */
     private void transitionWithProof(Long riderId, Long orderId, FlashDeliveryProofRequest request,
                                      String expected, String next, String proofType, String timeColumn) {
         List<String> urls = validateProofUrls(request == null ? null : request.getImageUrls(), true);
@@ -719,6 +760,7 @@ public class FlashDeliveryApplicationService {
         }
     }
 
+    /** 校验凭证图片 URL:必填时 1 至 9 张,每项为合法 URI 且不超过 1000 字符。 */
     private List<String> validateProofUrls(List<String> values, boolean required) {
         if (values == null || values.isEmpty()) {
             if (!required) return List.of();
@@ -741,6 +783,7 @@ public class FlashDeliveryApplicationService {
         return urls;
     }
 
+    /** 组装用户或骑手视角详情;hideSensitiveFields 为 true 时按抢单前脱敏返回。 */
     private FlashDeliveryOrderDetailView participantDetail(FlashDeliveryOrder order,
                                                            boolean userView,
                                                            boolean hideSensitiveFields) {
@@ -757,6 +800,7 @@ public class FlashDeliveryApplicationService {
         return view;
     }
 
+    /** 订单实体转用户端订单卡片。 */
     private FlashDeliveryUserOrderListView userOrderListView(FlashDeliveryOrder order) {
         FlashDeliveryUserOrderListView view = new FlashDeliveryUserOrderListView();
         view.setId(order.getId());
@@ -786,6 +830,7 @@ public class FlashDeliveryApplicationService {
         return view;
     }
 
+    /** 订单实体转骑手端任务卡片,可附带骑手到取件点的直线距离。 */
     private FlashDeliveryRiderOrderListView riderOrderListView(FlashDeliveryOrder order,
                                                                  BigDecimal longitude, BigDecimal latitude) {
         FlashDeliveryRiderOrderListView view = new FlashDeliveryRiderOrderListView();
@@ -821,12 +866,14 @@ public class FlashDeliveryApplicationService {
         return view;
     }
 
+    /** 订单实体转详情基础视图。 */
     private FlashDeliveryOrderDetailView detailOrderView(FlashDeliveryOrder order, boolean includeSensitiveFields) {
         FlashDeliveryOrderDetailView view = new FlashDeliveryOrderDetailView();
         populateOrderView(view, order, includeSensitiveFields);
         return view;
     }
 
+    /** 按凭证类型把图片分组为寄件、取件、送达三组 URL。 */
     private void groupImageUrls(FlashDeliveryOrderDetailView view, List<FlashDeliveryOrderImage> images) {
         List<String> sender = new ArrayList<>();
         List<String> pickup = new ArrayList<>();
@@ -841,6 +888,7 @@ public class FlashDeliveryApplicationService {
         view.setDeliveryImageUrls(delivery);
     }
 
+    /** 运价实体转首页服务摘要视图。 */
     private FlashDeliveryServiceView serviceView(FlashDeliveryPricing pricing, String serviceType) {
         FlashDeliveryServiceView view = new FlashDeliveryServiceView();
         view.setServiceType(serviceType);
@@ -858,6 +906,7 @@ public class FlashDeliveryApplicationService {
         return view;
     }
 
+    /** 将订单实体的公共字段填充到视图,includeSensitiveFields 控制是否包含敏感字段。 */
     private void populateOrderView(FlashDeliveryOrderView view, FlashDeliveryOrder order,
                                    boolean includeSensitiveFields) {
         view.setId(order.getId());
@@ -896,6 +945,7 @@ public class FlashDeliveryApplicationService {
         view.setUpdateTime(order.getUpdateTime());
     }
 
+    /** 从订单实体提取取件或收件地址视图;脱敏时不返回联系人、电话和坐标。 */
     private FlashDeliveryAddressView addressView(FlashDeliveryOrder order, boolean pickup,
                                                  boolean includeSensitiveFields) {
         FlashDeliveryAddressView view = new FlashDeliveryAddressView();
@@ -919,6 +969,7 @@ public class FlashDeliveryApplicationService {
         return view;
     }
 
+    /** 组装骑手公开资料;仅已接单或已取件状态附带骑手实时位置。 */
     private FlashDeliveryRiderSummaryView riderSummary(FlashDeliveryOrder order) {
         if (order.getRiderId() == null) return null;
         InfoUser rider = userMapper.selectInfoUserByUserId(order.getRiderId());
@@ -935,6 +986,7 @@ public class FlashDeliveryApplicationService {
         return view;
     }
 
+    /** 把费用明细写入报价视图。 */
     private void applyBreakdown(FlashDeliveryQuoteView view, FlashDeliveryPriceBreakdown breakdown) {
         view.setBillableDistance(breakdown.getBillableDistance());
         view.setDistanceFee(breakdown.getDistanceFee());
@@ -944,6 +996,7 @@ public class FlashDeliveryApplicationService {
         view.setAmount(breakdown.getAmount());
     }
 
+    /** 把请求地址复制为订单取件或收件快照字段。 */
     private void copyAddress(FlashDeliveryAddressRequest source, FlashDeliveryOrder target, boolean pickup) {
         if (pickup) {
             target.setPickupName(source.getName().trim()); target.setPickupPhone(source.getPhone().trim());
@@ -960,6 +1013,7 @@ public class FlashDeliveryApplicationService {
         }
     }
 
+    /** 校验地址请求:必填项、长度限制和经纬度取值范围。 */
     private void validateAddress(FlashDeliveryAddressRequest address) {
         if (address == null || !hasText(address.getName()) || !hasText(address.getPhone())
                 || !hasText(address.getAddress()) || address.getLatitude() == null || address.getLongitude() == null
@@ -976,6 +1030,7 @@ public class FlashDeliveryApplicationService {
         }
     }
 
+    /** 收件手机号归一化后唯一匹配普通用户作为收件人;匹配零个或多个视为无收件人。 */
     private Long resolveReceiverUserId(String phone) {
         if (!hasText(phone)) return null;
         StringBuilder normalized = new StringBuilder(phone.length());
@@ -993,23 +1048,27 @@ public class FlashDeliveryApplicationService {
         return matches != null && matches.size() == 1 ? matches.get(0) : null;
     }
 
+    /** 判断用户是否为订单寄件人或匹配收件人。 */
     private boolean isParticipant(Long userId, FlashDeliveryOrder order) {
         return Objects.equals(userId, order.getUserId())
                 || Objects.equals(userId, order.getReceiverUserId());
     }
 
+    /** 校验 token 用户为骑手(userType=2)并返回其资料。 */
     private InfoUser requireRider(Long riderId) {
         InfoUser rider = riderId == null ? null : userMapper.selectInfoUserByUserId(riderId);
         if (rider == null || !"2".equals(rider.getUserType())) throw fail("flash.delivery.rider.required");
         return rider;
     }
 
+    /** 加载订单,不存在时统一按订单不存在报错。 */
     private FlashDeliveryOrder requireOrder(Long orderId) {
         FlashDeliveryOrder order = orderId == null ? null : orderMapper.selectById(orderId);
         if (order == null) throw fail("flash.delivery.order.not.found");
         return order;
     }
 
+    /** 写入状态变更审计日志。 */
     private void writeLog(Long orderId, String from, String to, String operatorType,
                           Long operatorId, String reason, Date now) {
         FlashDeliveryOrderLog log = new FlashDeliveryOrderLog();
@@ -1018,6 +1077,7 @@ public class FlashDeliveryApplicationService {
         logMapper.insert(log);
     }
 
+    /** 校验并提取取消原因。 */
     private String requiredReason(FlashDeliveryReasonRequest request) {
         if (request == null || !hasText(request.getReason()) || request.getReason().trim().length() > 500) {
             throw fail("flash.delivery.cancel.reason.required");
@@ -1035,6 +1095,7 @@ public class FlashDeliveryApplicationService {
         }
     }
 
+    /** 校验物品信息:数量、重量范围、规格长度和小费。 */
     private void validateItem(FlashDeliveryQuoteRequest request) {
         if (request.getQuantity() == null || request.getQuantity() <= 0
                 || !hasText(request.getWeightRange()) || !WEIGHT_RANGES.contains(request.getWeightRange())
@@ -1050,15 +1111,18 @@ public class FlashDeliveryApplicationService {
     private String normalizePayType(String payType) {
         if (!hasText(payType)) return PAY_TYPE_CASH;
         String trimmed = payType.trim();
-        if (PAY_TYPE_CASH.equals(trimmed) || PAY_TYPE_TRANSFER.equals(trimmed)) return trimmed;
-        throw fail("flash.delivery.pay.type.invalid");
+        return trimmed;
+//        if (PAY_TYPE_CASH.equals(trimmed) || PAY_TYPE_TRANSFER.equals(trimmed)) return trimmed;
+//        throw fail("flash.delivery.pay.type.invalid");
     }
 
+    /** 将时间转为台北时区 HH:mm,用于命中运价时段。 */
     private String pricingTargetTime(Date date) {
         return PRICING_TIME_FORMAT.format(Instant.ofEpochMilli(date.getTime())
                 .atZone(TAIPEI).toLocalTime());
     }
 
+    /** 比对客户端回传报价与服务端最新报价是否完全一致。 */
     private boolean matchesQuotedPrice(FlashDeliveryCreateRequest request, FlashDeliveryQuoteView latest) {
         return Objects.equals(request.getPricingId(), latest.getPricingId())
                 && Objects.equals(request.getPricingVersion(), latest.getPricingVersion())
@@ -1076,10 +1140,12 @@ public class FlashDeliveryApplicationService {
         if (userId == null || userId <= 0) throw fail("flash.delivery.auth.required");
     }
 
+    /** 构造分页对象并夹紧页码与每页数量(1 至 100)。 */
     private Page<FlashDeliveryOrder> page(int pageNum, int pageSize) {
         return new Page<>(Math.max(1, pageNum), Math.min(Math.max(1, pageSize), 100));
     }
 
+    /** 分页结果实体转换,保留分页元数据。 */
     private <T> IPage<T> mapPage(IPage<FlashDeliveryOrder> source,
                                  Function<FlashDeliveryOrder, T> mapper) {
         Page<T> result = new Page<>(source.getCurrent(), source.getSize(), source.getTotal());
@@ -1087,6 +1153,7 @@ public class FlashDeliveryApplicationService {
         return result;
     }
 
+    /** 按页签构建骑手任务查询:newTask 限定可抢状态与时间窗,可选距离过滤和就近排序。 */
     private QueryWrapper<FlashDeliveryOrder> riderOrdersQuery(Long riderId, String tab,
                                                                 BigDecimal longitude, BigDecimal latitude,
                                                                 boolean orderResults) {
@@ -1132,6 +1199,7 @@ public class FlashDeliveryApplicationService {
         return query;
     }
 
+    /** 校验骑手坐标:经纬度必须成对出现且在合法范围。 */
     private void validateRiderCoordinates(BigDecimal longitude, BigDecimal latitude) {
         if ((longitude == null) != (latitude == null)
                 || longitude != null && (longitude.compareTo(BigDecimal.valueOf(-180)) < 0
@@ -1142,6 +1210,7 @@ public class FlashDeliveryApplicationService {
         }
     }
 
+    /** 读取字典 sys_qs_newtask_distance 的新任务距离上限(公里);缺失或非法时不限距离。 */
     private Integer newTaskDistanceLimit() {
         try {
             List<SysDictData> values = DictUtils.getDictCache("sys_qs_newtask_distance");
@@ -1155,6 +1224,7 @@ public class FlashDeliveryApplicationService {
         return null;
     }
 
+    /** Haversine 球面直线距离(米),用于骑手列表展示距取件点的距离。 */
     private Integer distanceMeters(BigDecimal longitude, BigDecimal latitude,
                                    BigDecimal targetLongitude, BigDecimal targetLatitude) {
         if (longitude == null || targetLongitude == null || targetLatitude == null) return null;
@@ -1168,6 +1238,7 @@ public class FlashDeliveryApplicationService {
         return (int) Math.round(6_371_000D * 2D * Math.atan2(Math.sqrt(value), Math.sqrt(1D - value)));
     }
 
+    /** 从 selectObjs 聚合结果中取第一个 Long 值。 */
     private Long firstLong(List<Object> values) {
         if (values == null || values.isEmpty() || values.get(0) == null) return null;
         Object value = values.get(0);
@@ -1175,12 +1246,14 @@ public class FlashDeliveryApplicationService {
         return Long.valueOf(String.valueOf(value));
     }
 
+    /** 取件方式归一化:缺省按 NOW 处理,非法值报错。 */
     private String normalizeDeliveryMode(String value) {
         String mode = hasText(value) ? value.trim() : "NOW";
         if (!DELIVERY_MODES.contains(mode)) throw fail("flash.delivery.mode.invalid");
         return mode;
     }
 
+    /** 校验预约窗:NOW 不得携带时间;SCHEDULED 必须是未来 3 天内恰好 30 分钟的窗。 */
     private void validateSchedule(String mode, Date start, Date end, Date now) {
         if ("NOW".equals(mode)) {
             if (start != null || end != null) throw fail("flash.delivery.schedule.invalid");
@@ -1193,6 +1266,7 @@ public class FlashDeliveryApplicationService {
         }
     }
 
+    /** 判断订单当前是否开放抢单:立即单随时可抢,预约单到点开放。 */
     private boolean isAvailableAt(FlashDeliveryOrder order, Date now) {
         return "NOW".equals(order.getDeliveryMode())
                 || "SCHEDULED".equals(order.getDeliveryMode())
@@ -1214,6 +1288,7 @@ public class FlashDeliveryApplicationService {
     private String normalized(String value) { return value == null ? "" : value.trim().toLowerCase(Locale.ROOT); }
     private ServiceException fail(String key) { return new ServiceException(MessageUtils.message(key)); }
 
+    /** 报价中间结果:命中的运价配置、路线距离和费用明细。 */
     private record QuoteContext(FlashDeliveryPricing pricing, RouteDistance route,
                                 FlashDeliveryPriceBreakdown breakdown) { }
 }

+ 8 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryPricingCalculator.java

@@ -10,14 +10,21 @@ import java.math.RoundingMode;
 /** 按闪送时段运价计算整数新台币金额。 */
 @Component
 public class FlashDeliveryPricingCalculator {
+    /** 按普通配送、无小费场景计算总金额的便捷入口。 */
     public Long calculate(FlashDeliveryPricing pricing, int distanceMeters) {
         return calculateBreakdown(pricing, distanceMeters, "NORMAL", 0L).getAmount();
     }
 
+    /** 按普通配送、无小费场景计算完整费用明细。 */
     public FlashDeliveryPriceBreakdown calculateBreakdown(FlashDeliveryPricing pricing, int distanceMeters) {
         return calculateBreakdown(pricing, distanceMeters, "NORMAL", 0L);
     }
 
+    /**
+     * 费用明细计算:基础配送费 = 起步价 + 距离费;距离费按超起步距离部分、以步长折算的
+     * 每公里单价线性计价,不足 0.5 公里不计、0.5 至 1 公里按 1 公里计;加急费仅 URGENT 收取,
+     * 取最低加急费与基础配送费乘加急比例的较大者;总金额 = 基础配送费 + 加急费 + 小费,均为整数 TWD。
+     */
     public FlashDeliveryPriceBreakdown calculateBreakdown(FlashDeliveryPricing pricing, int distanceMeters,
                                                            String deliveryType, Long tipAmount) {
         if (pricing == null || distanceMeters <= 0
@@ -36,6 +43,7 @@ public class FlashDeliveryPricingCalculator {
         BigDecimal extraDistance = routeDistance.subtract(pricing.getStartingDistance()).max(BigDecimal.ZERO);
         BigDecimal chargeableDistance;
         if (extraDistance.compareTo(new BigDecimal("0.50")) < 0) {
+    // 超距不足 0.5 公里不计费;0.5 至 1 公里按 1 公里计费。
             chargeableDistance = BigDecimal.ZERO;
         } else if (extraDistance.compareTo(BigDecimal.ONE) < 0) {
             chargeableDistance = BigDecimal.ONE;

+ 1 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryStateMachine.java

@@ -9,6 +9,7 @@ import static com.ruoyi.system.domain.flash.FlashDeliveryStatus.*;
 public final class FlashDeliveryStateMachine {
     private FlashDeliveryStateMachine() { }
 
+    /** 判断状态流转是否合法:正常链路逐级推进,非终态可转已取消由平台收口。 */
     public static boolean canTransition(String from, String to) {
         return WAITING_ACCEPTANCE.equals(from) && ACCEPTED.equals(to)
                 || ACCEPTED.equals(from) && PICKED_UP.equals(to)

+ 4 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/InfoAddressBookService.java

@@ -25,6 +25,7 @@ public class InfoAddressBookService {
 
     public InfoAddressBookService(IInfoAddressService addressService) { this.addressService = addressService; }
 
+    /** 按姓名、电话、地址关键字模糊搜索本人地址簿,置顶优先。 */
     public List<InfoAddress> list(Long userId, String keyword) {
         QueryWrapper<InfoAddress> query = new QueryWrapper<InfoAddress>().eq("user_id", userId);
         if (keyword != null && !keyword.trim().isEmpty()) {
@@ -43,6 +44,7 @@ public class InfoAddressBookService {
         return address;
     }
 
+    /** 新增或更新本人地址;更新用显式 set 以支持清空可选字段。 */
     @Transactional
     public InfoAddress save(Long userId, InfoAddressRequest request) {
         validate(request);
@@ -70,12 +72,14 @@ public class InfoAddressBookService {
         return address;
     }
 
+    /** 删除本人地址。 */
     public void delete(Long userId, Long id) {
         if (!addressService.remove(new QueryWrapper<InfoAddress>().eq("id", id).eq("user_id", userId))) {
             throw fail("address.access.denied");
         }
     }
 
+    /** 置顶本人地址。 */
     public void top(Long userId, Long id) {
         detail(userId, id);
         if (!addressService.update(new UpdateWrapper<InfoAddress>().eq("id", id).eq("user_id", userId)

+ 25 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryOrder.java

@@ -13,29 +13,39 @@ import java.util.Date;
 @TableName("flash_delivery_order")
 public class FlashDeliveryOrder {
     @TableId(type = IdType.AUTO)
+    /** 主键 ID,数据库自增。 */
     private Long id;
+    /** 订单号,FD 前缀加 24 位随机大写串,用于展示与检索。 */
     private String orderNo;
     /** 客户端请求幂等号,与 userId 共同建立唯一约束。 */
     private String clientRequestId;
+    /** 下单用户(寄件人)ID。 */
     private Long userId;
     /** 创建订单时按收件手机号唯一匹配的普通用户;匹配后不再动态追溯。 */
     private Long receiverUserId;
     /** 抢单前为空,原子抢单成功后绑定且不允许骑手主动放弃。 */
     private Long riderId;
+    /** 业务场景:HELP_SEND=帮送、HELP_PICKUP=帮取。 */
     private String serviceType;
+    /** 配送等级:NORMAL=普通、URGENT=一对一加急。 */
     private String deliveryType;
+    /** 订单状态,取值见 FlashDeliveryStatus。 */
     private String status;
     /** 用户申报的包裹类别和重量范围。 */
     private String packageType;
     private Integer quantity;
     private String weightRange;
+    /** 物品体积、尺寸等规格说明。 */
     private String specification;
     /** NOW 为立即配送,SCHEDULED 为预约取件时段。 */
     private String deliveryMode;
+    /** 预约取件时间窗开始时间,仅 SCHEDULED 模式有值。 */
     private Date scheduledPickupStartAt;
+    /** 预约取件时间窗结束时间,仅 SCHEDULED 模式有值。 */
     private Date scheduledPickupEndAt;
     /** 交付 PIN 只允许订单用户和平台读取,骑手响应必须剔除。 */
     private Boolean pinRequired;
+    /** 四位交付 PIN 明文,骑手送达时向收件人核对。 */
     private String deliveryPinCode;
 
     /** 取件联系人和完整地址快照。 */
@@ -70,20 +80,35 @@ public class FlashDeliveryOrder {
     private String currency;
     /** 支付方式与主订单系统同域:4=现金、6=线下转账;闪送款项直达骑手,仅允许线下方式,下单时锁定。 */
     private String payType;
+    /** 下单时命中的运价配置 ID。 */
     private Long pricingId;
+    /** 下单时命中的运价配置版本。 */
     private Integer pricingVersion;
+    /** 命中运价的时段开始(HH:mm)。 */
     private String pricingStartTime;
+    /** 命中运价的时段结束(HH:mm)。 */
     private String pricingEndTime;
+    /** 起步距离(公里)。 */
     private BigDecimal startingDistance;
+    /** 起步价(整数 TWD)。 */
     private Long startingFare;
+    /** 距离计价步长(公里)。 */
     private BigDecimal distance;
+    /** 每个步长收取的运费(整数 TWD)。 */
     private Long freight;
+    /** 距离附加费(TWD),按超起步距离部分以每公里单价计算。 */
     private Long distanceFee;
+    /** 基础配送费(TWD),等于起步价加距离费。 */
     private Long baseDeliveryFee;
+    /** 加急费比例(百分比)。 */
     private BigDecimal urgentRate;
+    /** 最低加急费(TWD)。 */
     private Long minimumUrgentFee;
+    /** 加急费(TWD),URGENT 订单收取,普通配送为 0。 */
     private Long urgentFee;
+    /** 用户小费合计(TWD),待接单阶段可追加。 */
     private Long tipAmount;
+    /** 用户备注。 */
     private String userNote;
     /** 订单条件更新版本号,用于记录并发状态变化次数。 */
     private Integer version;

+ 6 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryOrderImage.java

@@ -12,14 +12,20 @@ import java.util.Date;
 @TableName("flash_delivery_order_image")
 public class FlashDeliveryOrderImage {
     @TableId(type = IdType.AUTO)
+    /** 主键 ID,数据库自增。 */
     private Long id;
+    /** 关联闪送订单 ID。 */
     private Long orderId;
     /** 凭证类型:SENDER(寄件)、PICKUP(取件)或 DELIVERY(送达)。 */
     private String proofType;
+    /** 图片 URL,最长 1000 个字符,允许相对路径。 */
     private String imageUrl;
+    /** 同一凭证类型内的展示顺序,按上传顺序从 0 编号。 */
     private Integer sortOrder;
     /** 操作人类型用于区分用户寄件图片和骑手履约图片。 */
     private String operatorType;
+    /** 上传人用户 ID。 */
     private Long operatorId;
+    /** 上传时间。 */
     private Date createTime;
 }

+ 7 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryOrderLog.java

@@ -12,13 +12,20 @@ import java.util.Date;
 @TableName("flash_delivery_order_log")
 public class FlashDeliveryOrderLog {
     @TableId(type = IdType.AUTO)
+    /** 主键 ID,数据库自增。 */
     private Long id;
+    /** 关联闪送订单 ID。 */
     private Long orderId;
+    /** 变更前状态,初次创建日志时为空。 */
     private String fromStatus;
+    /** 变更后状态。 */
     private String toStatus;
     /** 操作人类型:USER、RIDER、ADMIN 或 SYSTEM。 */
     private String operatorType;
+    /** 操作人用户 ID,SYSTEM 自动任务时为空。 */
     private Long operatorId;
+    /** 取消等原因说明,普通状态推进时为空。 */
     private String reason;
+    /** 日志产生时间。 */
     private Date createTime;
 }

+ 12 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryPricing.java

@@ -13,18 +13,30 @@ import java.util.Date;
 @TableName("flash_delivery_pricing")
 public class FlashDeliveryPricing {
     @TableId(type = IdType.AUTO)
+    /** 主键 ID,数据库自增。 */
     private Long id;
+    /** 时段开始时间(HH:mm,包含该时刻)。 */
     private String startTime;
+    /** 时段结束时间(HH:mm,不包含该时刻,24:00 表示收尾到全天结束)。 */
     private String endTime;
+    /** 起步距离(公里)。 */
     private BigDecimal startingDistance;
+    /** 起步价(整数 TWD)。 */
     private Long startingFare;
+    /** 距离计价步长(公里)。 */
     private BigDecimal distance;
+    /** 每个步长收取的运费(整数 TWD)。 */
     private Long freight;
+    /** 加急费比例(百分比)。 */
     private BigDecimal urgentRate;
+    /** 最低加急费(整数 TWD)。 */
     private Long minimumUrgentFee;
     /** 管理员并发更新使用的乐观版本号。 */
     private Integer configVersion;
+    /** 最后修改该配置的管理员用户 ID。 */
     private Long updatedBy;
+    /** 创建时间。 */
     private Date createTime;
+    /** 最后更新时间。 */
     private Date updateTime;
 }

+ 6 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/flash/FlashDeliveryStatus.java

@@ -2,11 +2,17 @@ package com.ruoyi.system.domain.flash;
 
 /** 闪送订单状态常量,状态流转规则由应用层状态机统一维护。 */
 public final class FlashDeliveryStatus {
+    /** 待接单:订单已创建,等待骑手抢单。 */
     public static final String WAITING_ACCEPTANCE = "WAITING_ACCEPTANCE";
+    /** 已接单:骑手已接单,正在前往取件。 */
     public static final String ACCEPTED = "ACCEPTED";
+    /** 已取件:骑手已取到物品。 */
     public static final String PICKED_UP = "PICKED_UP";
+    /** 已送达:物品已送达,等待用户确认收货。 */
     public static final String DELIVERED = "DELIVERED";
+    /** 已完成(终态):用户确认收货或超时后系统自动完成。 */
     public static final String COMPLETED = "COMPLETED";
+    /** 已取消(终态):用户或平台取消。 */
     public static final String CANCELLED = "CANCELLED";
 
     private FlashDeliveryStatus() { }

+ 2 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/flash/FlashDeliveryPricingMapper.java

@@ -9,6 +9,7 @@ import java.util.List;
 
 /** 闪送时段运价持久化接口。 */
 public interface FlashDeliveryPricingMapper extends BaseMapper<FlashDeliveryPricing> {
+    /** 查询指定时刻(HH:mm)命中的运价配置,时段为左闭右开。 */
     @Select("""
             select id, start_time, end_time, starting_distance, starting_fare,
                    distance, freight, urgent_rate, minimum_urgent_fee,
@@ -20,6 +21,7 @@ public interface FlashDeliveryPricingMapper extends BaseMapper<FlashDeliveryPric
             """)
     List<FlashDeliveryPricing> selectAtTime(@Param("targetTime") String targetTime);
 
+    /** 统计与指定时段重叠的运价数量,新增和修改时用于校验时段不重叠。 */
     @Select("""
             select count(1)
             from flash_delivery_pricing

Някои файлове не бяха показани, защото твърде много файлове са промени