| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- package com.ruoyi.app.order;
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.ruoyi.common.core.domain.entity.SysDictData;
- import com.ruoyi.common.exception.ServiceException;
- import com.ruoyi.common.utils.DictUtils;
- import com.ruoyi.common.utils.MessageUtils;
- import com.ruoyi.system.domain.InfoUser;
- import com.ruoyi.system.domain.PosOrder;
- import com.ruoyi.system.domain.flash.FlashDeliveryOrder;
- import com.ruoyi.system.mapper.flash.FlashDeliveryOrderMapper;
- import com.ruoyi.system.service.IInfoUserService;
- import com.ruoyi.system.service.IPosOrderService;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.stereotype.Service;
- import java.util.List;
- import static com.ruoyi.system.domain.flash.FlashDeliveryStatus.ACCEPTED;
- import static com.ruoyi.system.domain.flash.FlashDeliveryStatus.PICKED_UP;
- /**
- * 骑手接单统一策略:在骑手级锁内检查账号资格、配送类型、进行中任务容量和急送独占。
- * 调用方必须先取得 {@link RiderDeliveryLockService} 的同一骑手锁,避免并发请求同时绕过容量检查。
- */
- @Service
- public class RiderDeliveryAcceptanceService {
- static final String FOOD_LIMIT_DICT = "sys_rider_food_active_order_limit";
- static final String FLASH_LIMIT_DICT = "sys_rider_flash_active_order_limit";
- static final String TOTAL_LIMIT_DICT = "sys_rider_total_active_order_limit";
- private static final String URGENT = "URGENT";
- private static final int DEFAULT_LIMIT = 1;
- private static final int MAX_LIMIT = 20;
- private static final Logger log = LoggerFactory.getLogger(RiderDeliveryAcceptanceService.class);
- private final IInfoUserService riders;
- private final IPosOrderService foodOrders;
- private final FlashDeliveryOrderMapper flashOrders;
- public RiderDeliveryAcceptanceService(IInfoUserService riders,
- IPosOrderService foodOrders,
- FlashDeliveryOrderMapper flashOrders) {
- this.riders = riders;
- this.foodOrders = foodOrders;
- this.flashOrders = flashOrders;
- }
- /** 校验骑手能否再承接一笔外卖;进行中外卖和跨类型总量均受字典上限控制。 */
- public void assertCanAcceptFood(Long riderId) {
- requireEligibleRider(riderId, "FOOD");
- ActiveTasks active = activeTasks(riderId);
- if (active.urgentFlash() > 0) throw exclusiveConflict();
- if (active.food() >= configuredLimit(FOOD_LIMIT_DICT)) {
- throw new ServiceException(MessageUtils.message("rider.accept.food.limit.reached"));
- }
- assertTotalCapacity(active);
- }
- /** 校验骑手能否承接闪送;加急闪送保持跨外卖、闪送的一对一独占。 */
- public void assertCanAcceptFlash(Long riderId, String deliveryType) {
- requireEligibleRider(riderId, "FLASH");
- ActiveTasks active = activeTasks(riderId);
- if (URGENT.equals(deliveryType)) {
- if (active.food() > 0 || active.flash() > 0) throw exclusiveConflict();
- return;
- }
- if (active.urgentFlash() > 0) throw exclusiveConflict();
- if (active.flash() >= configuredLimit(FLASH_LIMIT_DICT)) {
- throw new ServiceException(MessageUtils.message("rider.accept.flash.limit.reached"));
- }
- assertTotalCapacity(active);
- }
- /**
- * 统一账号资格口径。空配送类型仍沿用 InfoUser 的历史兼容规则,但停用、未审核和离线账号均不得接单。
- */
- private InfoUser requireEligibleRider(Long riderId, String deliveryType) {
- InfoUser rider = riderId == null ? null : riders.getById(riderId);
- if (rider == null || !"2".equals(rider.getUserType())) {
- throw new ServiceException(MessageUtils.message("rider.operation.role.required"));
- }
- if (!"1".equals(rider.getAuditStatus())) {
- throw new ServiceException(MessageUtils.message("no.user.state.no.audit"));
- }
- if (!"0".equals(rider.getStatus())) {
- throw new ServiceException(MessageUtils.message("no.user.stop"));
- }
- if (!"0".equals(rider.getOffline())) {
- throw new ServiceException(MessageUtils.message("rider.accept.offline"));
- }
- if (!rider.supportsDeliveryType(deliveryType)) {
- String key = "FOOD".equals(deliveryType)
- ? "no.user.delivery.type.not.enabled"
- : "flash.delivery.rider.type.not.enabled";
- throw new ServiceException(MessageUtils.message(key));
- }
- return rider;
- }
- /** 一次读取两类进行中任务,所有容量判断使用同一数据库快照口径。 */
- private ActiveTasks activeTasks(Long riderId) {
- QueryWrapper<PosOrder> foodQuery = new QueryWrapper<PosOrder>()
- .eq("qs_id", riderId)
- .in("delivery_status", 1, 2)
- .in("state", 0, 1, 2)
- .eq("after_sale_status", 0);
- QueryWrapper<FlashDeliveryOrder> flashQuery = new QueryWrapper<FlashDeliveryOrder>()
- .eq("rider_id", riderId)
- .in("status", ACCEPTED, PICKED_UP);
- QueryWrapper<FlashDeliveryOrder> urgentQuery = new QueryWrapper<FlashDeliveryOrder>()
- .eq("rider_id", riderId)
- .in("status", ACCEPTED, PICKED_UP)
- .eq("delivery_type", URGENT);
- return new ActiveTasks(safe(foodOrders.count(foodQuery)),
- safe(flashOrders.selectCount(flashQuery)), safe(flashOrders.selectCount(urgentQuery)));
- }
- private void assertTotalCapacity(ActiveTasks active) {
- if (active.food() + active.flash() >= configuredLimit(TOTAL_LIMIT_DICT)) {
- throw new ServiceException(MessageUtils.message("rider.accept.total.limit.reached"));
- }
- }
- /**
- * 容量字典只接受 1 至 20 的整数。缺失、空值、越界、解析或缓存异常一律保守回退为 1,绝不放宽为不限单。
- */
- private int configuredLimit(String dictType) {
- List<SysDictData> values;
- try {
- values = DictUtils.getDictCache(dictType);
- } catch (RuntimeException exception) {
- log.warn("Failed to read rider acceptance limit, fallback to 1, dictType={}", dictType, exception);
- return DEFAULT_LIMIT;
- }
- if (values == null || values.isEmpty()) return DEFAULT_LIMIT;
- String raw = values.get(0).getDictValue();
- if (raw == null || !raw.trim().matches("[0-9]+")) return DEFAULT_LIMIT;
- try {
- int value = Integer.parseInt(raw.trim());
- return value >= 1 && value <= MAX_LIMIT ? value : DEFAULT_LIMIT;
- } catch (NumberFormatException ignored) {
- return DEFAULT_LIMIT;
- }
- }
- private long safe(Long value) {
- return value == null ? 0L : value;
- }
- private ServiceException exclusiveConflict() {
- return new ServiceException(MessageUtils.message("flash.delivery.rider.exclusive.conflict"));
- }
- private record ActiveTasks(long food, long flash, long urgentFlash) { }
- }
|