diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/constants/CanteenDeliveryOrderStatus.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/constants/CanteenDeliveryOrderStatus.java index e4f0621c6..4d5180dad 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/constants/CanteenDeliveryOrderStatus.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/constants/CanteenDeliveryOrderStatus.java @@ -22,4 +22,8 @@ public final class CanteenDeliveryOrderStatus { * 预约单:已到单但未达「提前 N 分钟派骑手」时间点,不展示在骑手待配送列表。 */ public static final int SCHEDULED_PENDING = 5; + /** 待商家接单(支付同步后或预约单到商家通知时间后) */ + public static final int MERCHANT_PENDING = 6; + /** 已指派骑手,待骑手确认接单 */ + public static final int RIDER_PENDING_ACCEPT = 7; } diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDeliveryAcceptSettingEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDeliveryAcceptSettingEntity.java new file mode 100644 index 000000000..0a9c6ae09 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDeliveryAcceptSettingEntity.java @@ -0,0 +1,42 @@ +package com.bsd.pjcloud.canteen.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import com.bsd.pjcloud.common.core.util.TenantTable; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +@Data +@TenantTable +@TableName("canteen_delivery_accept_setting") +@EqualsAndHashCode(callSuper = true) +@Schema(description = "外送自动接单配置") +public class CanteenDeliveryAcceptSettingEntity extends Model { + + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + @Schema(description = "商户ID") + private String merchantId; + + @Schema(description = "骑手用户ID") + private Long riderUserId; + + @Schema(description = "1自动接单 0手动") + private Integer autoAccept; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + private Long tenantId; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDeliveryOrderEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDeliveryOrderEntity.java index 400c7ec36..b9b3e966f 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDeliveryOrderEntity.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenDeliveryOrderEntity.java @@ -36,6 +36,22 @@ public class CanteenDeliveryOrderEntity extends Model carList; } diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/CanteenDeliveryRiderCommissionQueryVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/CanteenDeliveryRiderCommissionQueryVO.java new file mode 100644 index 000000000..9e2472cff --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/CanteenDeliveryRiderCommissionQueryVO.java @@ -0,0 +1,23 @@ +package com.bsd.pjcloud.canteen.vo.app; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; + +@Data +@Schema(description = "骑手自定义日期佣金查询结果") +public class CanteenDeliveryRiderCommissionQueryVO { + + @Schema(description = "查询开始日期") + private LocalDate startDate; + + @Schema(description = "查询结束日期") + private LocalDate endDate; + + @Schema(description = "佣金合计(分)") + private Long totalAmt; + + @Schema(description = "有效派单数(非取消)") + private Long orderCount; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/CanteenDeliveryRiderCommissionSummaryVO.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/CanteenDeliveryRiderCommissionSummaryVO.java new file mode 100644 index 000000000..3bb6b61df --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/vo/app/CanteenDeliveryRiderCommissionSummaryVO.java @@ -0,0 +1,21 @@ +package com.bsd.pjcloud.canteen.vo.app; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "骑手佣金汇总(今日/本月,单位:分)") +public class CanteenDeliveryRiderCommissionSummaryVO { + + @Schema(description = "今日佣金合计(分)") + private Long todayAmt; + + @Schema(description = "本月佣金合计(分)") + private Long monthAmt; + + @Schema(description = "今日有效派单数(非取消)") + private Long todayOrderCount; + + @Schema(description = "本月有效派单数(非取消)") + private Long monthOrderCount; +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenDeliveryOrderController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenDeliveryOrderController.java index ebe05c3a0..b31e09fe5 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenDeliveryOrderController.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenDeliveryOrderController.java @@ -6,7 +6,12 @@ import com.bsd.pjcloud.canteen.push.CanteenDeliveryMerchantNotifyPublisher; import com.bsd.pjcloud.canteen.push.CanteenDeliveryRiderPushPublisher; import com.bsd.pjcloud.canteen.service.CanteenDeliveryOrderService; import com.bsd.pjcloud.canteen.entity.CanteenDeliveryOrderEntity; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryAutoAcceptVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryMerchantStatsMonthVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryMerchantStatsSummaryVO; import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryOrderAppVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryRiderCommissionQueryVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryRiderCommissionSummaryVO; import com.bsd.pjcloud.common.core.util.R; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.bsd.pjcloud.common.log.annotation.SysLog; @@ -22,6 +27,9 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import org.springdoc.core.annotations.ParameterObject; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; + /** * 食堂外送:订单同步 + 骑手 App */ @@ -103,6 +111,30 @@ public class CanteenDeliveryOrderController { return R.ok(canteenDeliveryOrderService.pageForRider(uid, tab, page)); } + @GetMapping("/app/commission/summary") + @Operation(summary = "骑手端-佣金汇总", description = "今日/本月佣金合计(分),按派单时间统计,已取消单不计入") + public R appCommissionSummary() { + Long uid = SecurityUtils.getUser().getId(); + return R.ok(canteenDeliveryOrderService.commissionSummaryForRider(uid)); + } + + @GetMapping("/app/commission/query") + @Operation(summary = "骑手端-自定义日期佣金", description = "startDate/endDate 格式 yyyy-MM-dd,含首尾自然日") + public R appCommissionQuery( + @RequestParam String startDate, + @RequestParam String endDate) { + try { + LocalDate start = LocalDate.parse(startDate.trim()); + LocalDate end = LocalDate.parse(endDate.trim()); + Long uid = SecurityUtils.getUser().getId(); + return R.ok(canteenDeliveryOrderService.commissionQueryForRider(uid, start, end)); + } catch (DateTimeParseException ex) { + return R.failed("日期格式应为 yyyy-MM-dd"); + } catch (IllegalArgumentException ex) { + return R.failed(ex.getMessage()); + } + } + @GetMapping("/app/{id}") @Operation(summary = "骑手端-配送单详情") public R appDetail(@PathVariable Long id) { @@ -116,14 +148,43 @@ public class CanteenDeliveryOrderController { @GetMapping("/app/mch/list") @Operation(summary = "商家端 App-外送单列表", - description = "当前登录用户部门及其下级部门关联的商户(钱包 get-by-dept-ids)下的外送单。" - + "tab:pending 进行中 | all全部 | completed 已完成 | cancelled 已取消。分页同骑手端。" - + "预约单 SCHEDULED 仅在 merchant_notify_done=1(已向商家触发通知)后返回;立即送不受限。") - public R> mchAppList(@RequestParam(defaultValue = "pending") String tab, + description = "tab: wait_accept待接单 | accepted已接单 | cancelled | completed | all | pending(兼容)") + public R> mchAppList(@RequestParam(defaultValue = "wait_accept") String tab, @ParameterObject Page page) { return canteenDeliveryOrderService.pageForMerchantApp(tab, page); } + @GetMapping("/app/mch/stats/summary") + @Operation(summary = "商家端-订单统计汇总") + public R mchStatsSummary() { + return canteenDeliveryOrderService.merchantStatsSummary(); + } + + @GetMapping("/app/mch/stats/month") + @Operation(summary = "商家端-按月统计", description = "month 格式 yyyy-MM") + public R mchStatsMonth(@RequestParam String month) { + return canteenDeliveryOrderService.merchantStatsByMonth(month); + } + + @GetMapping("/app/mch/auto-accept") + @Operation(summary = "商家端-获取自动接单配置") + public R mchGetAutoAccept() { + return canteenDeliveryOrderService.getMerchantAutoAccept(); + } + + @PutMapping("/app/mch/auto-accept") + @Operation(summary = "商家端-保存自动接单配置") + public R mchSaveAutoAccept(@RequestParam Boolean autoAccept) { + return canteenDeliveryOrderService.saveMerchantAutoAccept(autoAccept); + } + + @SysLog("商家接单") + @PostMapping("/app/mch/accept/{id}") + @Operation(summary = "商家端-手动接单") + public R mchAccept(@PathVariable Long id) { + return canteenDeliveryOrderService.merchantAcceptOrder(id); + } + @GetMapping("/app/mch/{id}") @Operation(summary = "商家端 App-外送单详情", description = "预约单未到商家通知完成前返回失败(与列表可见性一致)。") @@ -131,6 +192,28 @@ public class CanteenDeliveryOrderController { return canteenDeliveryOrderService.detailForMerchantApp(id); } + @GetMapping("/app/rider/auto-accept") + @Operation(summary = "骑手端-获取自动接单配置") + public R riderGetAutoAccept() { + Long uid = SecurityUtils.getUser().getId(); + return canteenDeliveryOrderService.getRiderAutoAccept(uid); + } + + @PutMapping("/app/rider/auto-accept") + @Operation(summary = "骑手端-保存自动接单配置") + public R riderSaveAutoAccept(@RequestParam Boolean autoAccept) { + Long uid = SecurityUtils.getUser().getId(); + return canteenDeliveryOrderService.saveRiderAutoAccept(uid, autoAccept); + } + + @SysLog("骑手接单") + @PostMapping("/app/accept/{id}") + @Operation(summary = "骑手端-手动接单") + public R riderAccept(@PathVariable Long id) { + Long uid = SecurityUtils.getUser().getId(); + return canteenDeliveryOrderService.riderAcceptOrder(uid, id); + } + @SysLog("取餐拍照") @PostMapping("/app/pickup") @Operation(summary = "骑手端-到店取餐拍照", diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenDeliveryAcceptSettingMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenDeliveryAcceptSettingMapper.java new file mode 100644 index 000000000..e5f5cbe3a --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenDeliveryAcceptSettingMapper.java @@ -0,0 +1,9 @@ +package com.bsd.pjcloud.canteen.mapper; + +import com.bsd.pjcloud.canteen.entity.CanteenDeliveryAcceptSettingEntity; +import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CanteenDeliveryAcceptSettingMapper extends PjcloudBaseMapper { +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenDeliveryOrderService.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenDeliveryOrderService.java index 352c54026..16f84befa 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenDeliveryOrderService.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenDeliveryOrderService.java @@ -5,9 +5,16 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.bsd.pjcloud.canteen.dto.delivery.CanteenDeliveryOrderSyncDTO; import com.bsd.pjcloud.canteen.dto.delivery.CanteenDeliveryPhotoDTO; import com.bsd.pjcloud.canteen.entity.CanteenDeliveryOrderEntity; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryAutoAcceptVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryMerchantStatsMonthVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryMerchantStatsSummaryVO; import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryOrderAppVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryRiderCommissionQueryVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryRiderCommissionSummaryVO; import com.bsd.pjcloud.common.core.util.R; +import java.time.LocalDate; + public interface CanteenDeliveryOrderService extends IService { /** @@ -53,4 +60,35 @@ public interface CanteenDeliveryOrderService extends IService merchantStatsSummary(); + + /** 商家端:按月统计 yyyy-MM */ + R merchantStatsByMonth(String month); + + /** 商家接单 */ + R merchantAcceptOrder(Long deliveryOrderId); + + /** 骑手接单 */ + R riderAcceptOrder(Long riderUserId, Long deliveryOrderId); + + /** 商家自动接单配置 */ + R getMerchantAutoAccept(); + + R saveMerchantAutoAccept(Boolean autoAccept); + + /** 骑手自动接单配置 */ + R getRiderAutoAccept(Long riderUserId); + + R saveRiderAutoAccept(Long riderUserId, Boolean autoAccept); + + /** 内部:尝试商家自动接单(同步/通知后) */ + void tryMerchantAutoAccept(Long deliveryOrderId); } diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDeliveryOrderServiceImpl.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDeliveryOrderServiceImpl.java index 06cf870e0..97f495785 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDeliveryOrderServiceImpl.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenDeliveryOrderServiceImpl.java @@ -18,10 +18,12 @@ import com.bsd.pjcloud.canteen.constants.CanteenDeliveryScheduleConstants; import com.bsd.pjcloud.canteen.dto.delivery.CanteenDeliveryCartItemDTO; import com.bsd.pjcloud.canteen.dto.delivery.CanteenDeliveryOrderSyncDTO; import com.bsd.pjcloud.canteen.dto.delivery.CanteenDeliveryPhotoDTO; +import com.bsd.pjcloud.canteen.entity.CanteenDeliveryAcceptSettingEntity; import com.bsd.pjcloud.canteen.entity.CanteenDeliveryAssignSeqEntity; import com.bsd.pjcloud.canteen.entity.CanteenDeliveryOrderEntity; import com.bsd.pjcloud.canteen.entity.CanteenRiderBuildingBindEntity; import com.bsd.pjcloud.canteen.entity.CanteenRiderPoolEntity; +import com.bsd.pjcloud.canteen.mapper.CanteenDeliveryAcceptSettingMapper; import com.bsd.pjcloud.canteen.mapper.CanteenDeliveryAssignSeqMapper; import com.bsd.pjcloud.canteen.mapper.CanteenDeliveryOrderMapper; import com.bsd.pjcloud.canteen.mapper.CanteenRiderBuildingBindMapper; @@ -31,11 +33,17 @@ import com.bsd.pjcloud.canteen.push.CanteenDeliveryRiderPushPublisher; import com.bsd.pjcloud.canteen.service.CanteenDeliveryOrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryAutoAcceptVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryMerchantStatsMonthVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryMerchantStatsSummaryVO; import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryOrderAppVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryRiderCommissionQueryVO; +import com.bsd.pjcloud.canteen.vo.app.CanteenDeliveryRiderCommissionSummaryVO; import com.bsd.pjcloud.common.core.util.BeanUtils; import com.bsd.pjcloud.common.core.util.R; import com.bsd.pjcloud.common.security.util.SecurityUtils; import com.bsd.pjcloud.wallet.api.feign.RemoteMchService; +import com.bsd.pjcloud.wallet.api.feign.RemoteWalletService; import com.bsd.pjcloud.wallet.api.vo.MchManageVo; import com.bsd.pjcloud.hard.api.dto.order.remote.HardUpdateTakeOutStatusDto; import com.bsd.pjcloud.hard.api.feign.RemoteHardOrderService; @@ -49,7 +57,10 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.YearMonth; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -66,6 +77,7 @@ public class CanteenDeliveryOrderServiceImpl extends ServiceImpl r = remoteWalletService.innerMchTakeOutAmt(mid); + if (r != null && r.isOk() && r.getData() != null) { + return Math.max(0, r.getData()); + } + } catch (Exception ex) { + log.warn("resolveMchTakeOutAmt merchantId={}", merchantId, ex); + } + return 0; } private List filterPoolByBuyerGender(List pool, Integer buyerGender) { @@ -381,8 +448,11 @@ public class CanteenDeliveryOrderServiceImpl extends ServiceImpllambdaQuery() .eq(CanteenDeliveryOrderEntity::getRiderUserId, riderUserId); switch (t) { + case "wait_accept" -> + q.eq(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.RIDER_PENDING_ACCEPT); case "pending" -> - q.in(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.WAIT_PICKUP, CanteenDeliveryOrderStatus.DELIVERING); + q.in(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.WAIT_PICKUP, CanteenDeliveryOrderStatus.DELIVERING) + .eq(CanteenDeliveryOrderEntity::getRiderAccepted, 1); case "completed" -> q.eq(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.COMPLETED); case "cancelled" -> q.eq(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.CANCELLED); default -> { @@ -486,10 +556,19 @@ public class CanteenDeliveryOrderServiceImpl extends ServiceImpl q.eq(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.MERCHANT_PENDING); + case "accepted" -> q.eq(CanteenDeliveryOrderEntity::getMerchantAccepted, 1) + .in(CanteenDeliveryOrderEntity::getStatus, + CanteenDeliveryOrderStatus.PENDING_ASSIGN, + CanteenDeliveryOrderStatus.WAIT_PICKUP, + CanteenDeliveryOrderStatus.RIDER_PENDING_ACCEPT, + CanteenDeliveryOrderStatus.DELIVERING, + CanteenDeliveryOrderStatus.SCHEDULED_PENDING); case "pending" -> q.in(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.PENDING_ASSIGN, CanteenDeliveryOrderStatus.SCHEDULED_PENDING, CanteenDeliveryOrderStatus.WAIT_PICKUP, + CanteenDeliveryOrderStatus.RIDER_PENDING_ACCEPT, CanteenDeliveryOrderStatus.DELIVERING); case "completed" -> q.eq(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.COMPLETED); case "cancelled" -> q.eq(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.CANCELLED); @@ -575,11 +654,15 @@ public class CanteenDeliveryOrderServiceImpl extends ServiceImpl riderDue = list(Wrappers.lambdaQuery() - .eq(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.SCHEDULED_PENDING) + .eq(CanteenDeliveryOrderEntity::getMerchantAccepted, 1) + .isNull(CanteenDeliveryOrderEntity::getRiderUserId) + .eq(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.PENDING_ASSIGN) .isNotNull(CanteenDeliveryOrderEntity::getScheduledRiderNotifyTime) .le(CanteenDeliveryOrderEntity::getScheduledRiderNotifyTime, now) .select(CanteenDeliveryOrderEntity::getId) @@ -661,8 +747,12 @@ public class CanteenDeliveryOrderServiceImpl extends ServiceImpllambdaQuery() .eq(CanteenDeliveryOrderEntity::getId, deliveryOrderId) .last("FOR UPDATE")); - if (e == null || !Objects.equals(e.getStatus(), CanteenDeliveryOrderStatus.SCHEDULED_PENDING)) { + if (e == null) { return; } if (e.getScheduledRiderNotifyTime() == null || e.getScheduledRiderNotifyTime().isAfter(LocalDateTime.now())) { return; } - assignRiderOnCreate(e); - if (e.getRiderUserId() == null) { - e.setStatus(CanteenDeliveryOrderStatus.SCHEDULED_PENDING); - updateById(e); + if (!isMerchantAccepted(e)) { return; } + if (e.getRiderUserId() != null) { + return; + } + if (e.getStatus() != CanteenDeliveryOrderStatus.PENDING_ASSIGN) { + return; + } + assignRiderOnCreate(e); updateById(e); scheduleRiderPushAfterAssign(e); } @@ -728,6 +822,388 @@ public class CanteenDeliveryOrderServiceImpl extends ServiceImpl rows = list(buildCommissionQuery(riderUserId, startInclusive, endExclusive) + .select(CanteenDeliveryOrderEntity::getRiderCommissionAmt)); + return rows.stream() + .mapToLong(e -> e.getRiderCommissionAmt() != null ? e.getRiderCommissionAmt() : 0L) + .sum(); + } + + private long countRiderCommissionOrders(Long riderUserId, LocalDateTime startInclusive, LocalDateTime endExclusive) { + if (riderUserId == null) { + return 0L; + } + return count(buildCommissionQuery(riderUserId, startInclusive, endExclusive)); + } + + private LambdaQueryWrapper buildCommissionQuery(Long riderUserId, + LocalDateTime startInclusive, + LocalDateTime endExclusive) { + return Wrappers.lambdaQuery() + .eq(CanteenDeliveryOrderEntity::getRiderUserId, riderUserId) + .isNotNull(CanteenDeliveryOrderEntity::getRiderAssignedTime) + .ge(CanteenDeliveryOrderEntity::getRiderAssignedTime, startInclusive) + .lt(CanteenDeliveryOrderEntity::getRiderAssignedTime, endExclusive) + .ne(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.CANCELLED) + .isNotNull(CanteenDeliveryOrderEntity::getRiderCommissionAmt); + } + + @Override + public R merchantStatsSummary() { + R> mchIdsR = resolveMerchantIdStringsForLoginUser(); + if (!mchIdsR.isOk()) { + return R.failed(mchIdsR.getMsg()); + } + List merchantIds = mchIdsR.getData(); + LocalDate today = LocalDate.now(); + LocalDateTime dayStart = today.atStartOfDay(); + LocalDateTime dayEnd = today.plusDays(1).atStartOfDay(); + LocalDateTime monthStart = today.withDayOfMonth(1).atStartOfDay(); + LocalDateTime monthEnd = today.withDayOfMonth(1).plusMonths(1).atStartOfDay(); + + CanteenDeliveryMerchantStatsSummaryVO vo = new CanteenDeliveryMerchantStatsSummaryVO(); + vo.setTodayOrderCount(countMerchantAcceptedOrders(merchantIds, dayStart, dayEnd)); + vo.setTodayOrderAmount(sumMerchantAcceptedAmount(merchantIds, dayStart, dayEnd)); + vo.setMonthOrderAmount(sumMerchantAcceptedAmount(merchantIds, monthStart, monthEnd)); + return R.ok(vo); + } + + @Override + public R merchantStatsByMonth(String month) { + R> mchIdsR = resolveMerchantIdStringsForLoginUser(); + if (!mchIdsR.isOk()) { + return R.failed(mchIdsR.getMsg()); + } + YearMonth ym; + try { + ym = YearMonth.parse(month.trim()); + } catch (DateTimeParseException ex) { + return R.failed("月份格式应为 yyyy-MM"); + } + LocalDateTime start = ym.atDay(1).atStartOfDay(); + LocalDateTime end = ym.plusMonths(1).atDay(1).atStartOfDay(); + CanteenDeliveryMerchantStatsMonthVO vo = new CanteenDeliveryMerchantStatsMonthVO(); + vo.setMonth(ym.toString()); + vo.setOrderCount(countMerchantAcceptedOrders(mchIdsR.getData(), start, end)); + vo.setTotalAmount(sumMerchantAcceptedAmount(mchIdsR.getData(), start, end)); + return R.ok(vo); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public R merchantAcceptOrder(Long deliveryOrderId) { + R> mchIdsR = resolveMerchantIdStringsForLoginUser(); + if (!mchIdsR.isOk()) { + return R.failed(mchIdsR.getMsg()); + } + return merchantAcceptInternal(deliveryOrderId, mchIdsR.getData()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public R riderAcceptOrder(Long riderUserId, Long deliveryOrderId) { + CanteenDeliveryOrderEntity e = getById(deliveryOrderId); + if (e == null || !Objects.equals(e.getRiderUserId(), riderUserId)) { + return R.failed("订单不存在或无权操作"); + } + if (!Objects.equals(e.getStatus(), CanteenDeliveryOrderStatus.RIDER_PENDING_ACCEPT)) { + return R.failed("当前状态不可接单"); + } + e.setRiderAccepted(1); + e.setRiderAcceptTime(LocalDateTime.now()); + e.setStatus(CanteenDeliveryOrderStatus.WAIT_PICKUP); + updateById(e); + return R.ok(); + } + + @Override + public R getMerchantAutoAccept() { + R> mchIdsR = resolveMerchantIdStringsForLoginUser(); + if (!mchIdsR.isOk()) { + return R.failed(mchIdsR.getMsg()); + } + if (CollUtil.isEmpty(mchIdsR.getData())) { + return R.failed("商户不存在"); + } + String merchantId = mchIdsR.getData().get(0); + CanteenDeliveryAutoAcceptVO vo = new CanteenDeliveryAutoAcceptVO(); + vo.setMerchantId(merchantId); + vo.setAutoAccept(isMerchantAutoAccept(merchantId)); + return R.ok(vo); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public R saveMerchantAutoAccept(Boolean autoAccept) { + R> mchIdsR = resolveMerchantIdStringsForLoginUser(); + if (!mchIdsR.isOk()) { + return R.failed(mchIdsR.getMsg()); + } + if (CollUtil.isEmpty(mchIdsR.getData())) { + return R.failed("商户不存在"); + } + String merchantId = mchIdsR.getData().get(0); + saveMerchantAutoAcceptSetting(merchantId, Boolean.TRUE.equals(autoAccept)); + if (Boolean.TRUE.equals(autoAccept)) { + autoAcceptPendingMerchantOrders(merchantId); + } + CanteenDeliveryAutoAcceptVO vo = new CanteenDeliveryAutoAcceptVO(); + vo.setMerchantId(merchantId); + vo.setAutoAccept(Boolean.TRUE.equals(autoAccept)); + return R.ok(vo); + } + + @Override + public R getRiderAutoAccept(Long riderUserId) { + CanteenDeliveryAutoAcceptVO vo = new CanteenDeliveryAutoAcceptVO(); + vo.setRiderUserId(riderUserId); + vo.setAutoAccept(isRiderAutoAccept(riderUserId)); + return R.ok(vo); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public R saveRiderAutoAccept(Long riderUserId, Boolean autoAccept) { + saveRiderAutoAcceptSetting(riderUserId, Boolean.TRUE.equals(autoAccept)); + if (Boolean.TRUE.equals(autoAccept)) { + autoAcceptPendingRiderOrders(riderUserId); + } + CanteenDeliveryAutoAcceptVO vo = new CanteenDeliveryAutoAcceptVO(); + vo.setRiderUserId(riderUserId); + vo.setAutoAccept(Boolean.TRUE.equals(autoAccept)); + return R.ok(vo); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void tryMerchantAutoAccept(Long deliveryOrderId) { + CanteenDeliveryOrderEntity e = getById(deliveryOrderId); + if (e == null || isMerchantAccepted(e)) { + return; + } + if (!Objects.equals(e.getStatus(), CanteenDeliveryOrderStatus.MERCHANT_PENDING)) { + return; + } + if (!isMerchantAutoAccept(e.getMerchantId())) { + return; + } + merchantAcceptInternal(e.getId(), List.of(e.getMerchantId())); + } + + private R merchantAcceptInternal(Long deliveryOrderId, List merchantIds) { + CanteenDeliveryOrderEntity e = getOne(Wrappers.lambdaQuery() + .eq(CanteenDeliveryOrderEntity::getId, deliveryOrderId) + .last("FOR UPDATE")); + if (e == null || StrUtil.isBlank(e.getMerchantId()) || !merchantIds.contains(e.getMerchantId())) { + return R.failed("订单不存在或无权操作"); + } + if (!merchantScheduledOrderVisibleToMerchant(e)) { + return R.failed("订单不存在或无权操作"); + } + if (isMerchantAccepted(e)) { + return R.failed("订单已接单"); + } + if (!Objects.equals(e.getStatus(), CanteenDeliveryOrderStatus.MERCHANT_PENDING)) { + return R.failed("当前状态不可接单"); + } + e.setMerchantAccepted(1); + e.setMerchantAcceptTime(LocalDateTime.now()); + assignRiderOnCreate(e); + updateById(e); + scheduleRiderPushAfterAssign(e); + return R.ok(); + } + + private void scheduleTryMerchantAutoAcceptAfterCommit(Long deliveryOrderId) { + if (deliveryOrderId == null || deliveryOrderServiceSelf == null) { + return; + } + if (TransactionSynchronizationManager.isSynchronizationActive()) { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCommit() { + deliveryOrderServiceSelf.tryMerchantAutoAccept(deliveryOrderId); + } + }); + } else { + deliveryOrderServiceSelf.tryMerchantAutoAccept(deliveryOrderId); + } + } + + private void autoAcceptPendingMerchantOrders(String merchantId) { + List pending = list(Wrappers.lambdaQuery() + .eq(CanteenDeliveryOrderEntity::getMerchantId, merchantId) + .eq(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.MERCHANT_PENDING) + .eq(CanteenDeliveryOrderEntity::getMerchantAccepted, 0) + .last("LIMIT 200")); + for (CanteenDeliveryOrderEntity row : pending) { + try { + merchantAcceptInternal(row.getId(), List.of(merchantId)); + } catch (Exception ex) { + log.warn("autoAcceptPendingMerchantOrders id={}", row.getId(), ex); + } + } + } + + private void autoAcceptPendingRiderOrders(Long riderUserId) { + List pending = list(Wrappers.lambdaQuery() + .eq(CanteenDeliveryOrderEntity::getRiderUserId, riderUserId) + .eq(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.RIDER_PENDING_ACCEPT) + .eq(CanteenDeliveryOrderEntity::getRiderAccepted, 0) + .last("LIMIT 200")); + for (CanteenDeliveryOrderEntity row : pending) { + try { + riderAcceptOrder(riderUserId, row.getId()); + } catch (Exception ex) { + log.warn("autoAcceptPendingRiderOrders id={}", row.getId(), ex); + } + } + } + + private long countMerchantAcceptedOrders(List merchantIds, LocalDateTime start, LocalDateTime end) { + if (CollUtil.isEmpty(merchantIds)) { + return 0L; + } + return count(buildMerchantStatsQuery(merchantIds, start, end)); + } + + private long sumMerchantAcceptedAmount(List merchantIds, LocalDateTime start, LocalDateTime end) { + if (CollUtil.isEmpty(merchantIds)) { + return 0L; + } + List rows = list(buildMerchantStatsQuery(merchantIds, start, end) + .select(CanteenDeliveryOrderEntity::getOrderAmount)); + return rows.stream().mapToLong(e -> e.getOrderAmount() != null ? e.getOrderAmount() : 0L).sum(); + } + + private LambdaQueryWrapper buildMerchantStatsQuery(List merchantIds, + LocalDateTime start, + LocalDateTime end) { + return Wrappers.lambdaQuery() + .in(CanteenDeliveryOrderEntity::getMerchantId, merchantIds) + .eq(CanteenDeliveryOrderEntity::getMerchantAccepted, 1) + .ne(CanteenDeliveryOrderEntity::getStatus, CanteenDeliveryOrderStatus.CANCELLED) + .isNotNull(CanteenDeliveryOrderEntity::getMerchantAcceptTime) + .ge(CanteenDeliveryOrderEntity::getMerchantAcceptTime, start) + .lt(CanteenDeliveryOrderEntity::getMerchantAcceptTime, end); + } + + private int calcOrderAmountFromCart(List carList) { + if (CollUtil.isEmpty(carList)) { + return 0; + } + int sum = 0; + for (CanteenDeliveryCartItemDTO c : carList) { + if (c == null) { + continue; + } + if (c.getTotalPrice() != null && StrUtil.isNotBlank(String.valueOf(c.getTotalPrice()))) { + try { + sum += Integer.parseInt(String.valueOf(c.getTotalPrice()).trim()); + continue; + } catch (NumberFormatException ignored) { + } + } + int price = c.getProductPrice() != null ? c.getProductPrice() : 0; + int cnt = c.getProductCount() != null ? c.getProductCount() : 0; + sum += price * cnt; + } + return Math.max(0, sum); + } + + private boolean isMerchantAccepted(CanteenDeliveryOrderEntity e) { + return e != null && Objects.equals(e.getMerchantAccepted(), 1); + } + + private boolean isMerchantAutoAccept(String merchantId) { + if (StrUtil.isBlank(merchantId)) { + return false; + } + CanteenDeliveryAcceptSettingEntity row = acceptSettingMapper.selectOne( + Wrappers.lambdaQuery() + .eq(CanteenDeliveryAcceptSettingEntity::getMerchantId, merchantId)); + return row != null && Objects.equals(row.getAutoAccept(), 1); + } + + private boolean isRiderAutoAccept(Long riderUserId) { + if (riderUserId == null) { + return false; + } + CanteenDeliveryAcceptSettingEntity row = acceptSettingMapper.selectOne( + Wrappers.lambdaQuery() + .eq(CanteenDeliveryAcceptSettingEntity::getRiderUserId, riderUserId)); + return row != null && Objects.equals(row.getAutoAccept(), 1); + } + + private void saveMerchantAutoAcceptSetting(String merchantId, boolean autoAccept) { + CanteenDeliveryAcceptSettingEntity row = acceptSettingMapper.selectOne( + Wrappers.lambdaQuery() + .eq(CanteenDeliveryAcceptSettingEntity::getMerchantId, merchantId)); + if (row == null) { + row = new CanteenDeliveryAcceptSettingEntity(); + row.setMerchantId(merchantId); + row.setAutoAccept(autoAccept ? 1 : 0); + acceptSettingMapper.insert(row); + return; + } + row.setAutoAccept(autoAccept ? 1 : 0); + acceptSettingMapper.updateById(row); + } + + private void saveRiderAutoAcceptSetting(Long riderUserId, boolean autoAccept) { + CanteenDeliveryAcceptSettingEntity row = acceptSettingMapper.selectOne( + Wrappers.lambdaQuery() + .eq(CanteenDeliveryAcceptSettingEntity::getRiderUserId, riderUserId)); + if (row == null) { + row = new CanteenDeliveryAcceptSettingEntity(); + row.setRiderUserId(riderUserId); + row.setAutoAccept(autoAccept ? 1 : 0); + acceptSettingMapper.insert(row); + return; + } + row.setAutoAccept(autoAccept ? 1 : 0); + acceptSettingMapper.updateById(row); + } + /** * 来源订单号即硬件外卖订单主键(与同步时 dto.id 一致)。非数字则跳过(非硬件单)。 * 同步失败抛异常,触发事务回滚,避免本地状态与硬件订单不一致。 diff --git a/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/feign/RemoteWalletService.java b/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/feign/RemoteWalletService.java index 739708616..e89022406 100644 --- a/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/feign/RemoteWalletService.java +++ b/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/feign/RemoteWalletService.java @@ -69,6 +69,13 @@ public interface RemoteWalletService { @GetMapping("/wallet-manage/inner/is-use-door") R innserIsUseDoor(@RequestParam Long userId); + /** + * 商户配送费(分),供食堂外送派单时快照骑手佣金 + */ + @NoToken + @GetMapping("/wallet-manage/inner/mch-take-out-amt") + R innerMchTakeOutAmt(@RequestParam("merchantId") Long merchantId); + @NoToken @PostMapping("/wallet-active/remote/open-card-list") R> remoteOpenCardList(@RequestBody WalletRemoteOpenCardListDto body); diff --git a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/WalletManageController.java b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/WalletManageController.java index 3b69fb74e..0e77be690 100644 --- a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/WalletManageController.java +++ b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/WalletManageController.java @@ -148,6 +148,20 @@ public class WalletManageController { return R.ok(true); } + @Inner + @GetMapping("/inner/mch-take-out-amt") + @Operation(summary = "内部-商户配送费(分)", description = "派单时快照骑手佣金;返回 mch_take_out_setting.mch_take_out_amt") + public R innerMchTakeOutAmt(@RequestParam Long merchantId) { + if (merchantId == null) { + return R.ok(0); + } + MchTakeOutSettingEntity setting = walletManageService.getOrCreateTakeOut(merchantId); + if (setting == null || setting.getMchTakeOutAmt() == null) { + return R.ok(0); + } + return R.ok(Math.max(0, setting.getMchTakeOutAmt())); + } + @GetMapping("/dept-change") public R deptChange(@RequestParam Long deptId, @RequestParam Boolean isDel) { walletManageService.deptChange(deptId, isDel);