server/admin/src/main/java/com/doumee/api/business/OrdersController.java
@@ -12,10 +12,7 @@ import com.doumee.dao.dto.DispatchDTO; import com.doumee.dao.dto.HandleOrderExceptionDTO; import com.doumee.dao.dto.ManualRefundDTO; import com.doumee.dao.vo.OrderDetailVO; import com.doumee.dao.vo.OrderDispatchVO; import com.doumee.dao.vo.OrdersExportVO; import com.doumee.dao.vo.OrderSummaryVO; import com.doumee.dao.vo.*; import com.doumee.service.business.OrdersService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -109,6 +106,8 @@ vo.setOverdueAmount(String.valueOf(Constants.getFormatMoney(o.getOverdueAmount()))); vo.setExceptionAmount(String.valueOf(Constants.getFormatMoney(o.getExceptionAmount()))); vo.setDeductionAmount(String.valueOf(Constants.getFormatMoney(o.getDeductionAmount()))); vo.setShopCompensationAmount(String.valueOf(Constants.getFormatMoney(o.getShopCompensationAmount()))); vo.setExceptionFee(String.valueOf(Constants.getFormatMoney(o.getExceptionFee()))); vo.setStatusDesc(o.getStatusDesc()); vo.setSettlementDesc(o.getSettlementStatus() != null ? (o.getSettlementStatus() == 1 ? "已结算" : "待结算") : ""); vo.setPayTime(o.getPayTime()); @@ -174,4 +173,11 @@ return ApiResponse.success("操作成功"); } @ApiOperation("手动退款详情") @GetMapping("/manualRefundDetail/{orderId}") @RequiresPermissions("business:orders:query") public ApiResponse<ManualRefundDetailVO> manualRefundDetail(@PathVariable Integer orderId) { return ApiResponse.success(ordersService.getManualRefundDetail(orderId)); } } server/admin/src/main/resources/application.yml
@@ -12,7 +12,7 @@ spring: profiles: active: dev active: pro # JSON返回配置 jackson: # 默认时区 server/services/src/main/java/com/doumee/biz/system/impl/OperationConfigBizImpl.java
@@ -50,9 +50,9 @@ dto.setRegisterCouponId(getValue(Constants.OP_REGISTER_COUPON_ID)); dto.setRegisterGiftCouponIds(getValue(Constants.OP_REGISTER_GIFT_COUPON_IDS)); dto.setRegisterRewardOrderCount(getValue(Constants.OP_REGISTER_REWARD_ORDER_COUNT)); dto.setRegisterRewardAmount(fenToYuan(getValue(Constants.OP_REGISTER_REWARD_AMOUNT))); dto.setRegisterRewardAmount(getValue(Constants.OP_REGISTER_REWARD_AMOUNT)); dto.setPlatformRewardOrderCount(getValue(Constants.OP_PLATFORM_REWARD_ORDER_COUNT)); dto.setPlatformRewardAmount(fenToYuan(getValue(Constants.OP_PLATFORM_REWARD_AMOUNT))); dto.setPlatformRewardAmount(getValue(Constants.OP_PLATFORM_REWARD_AMOUNT)); dto.setInvoiceMonthLimit(getValue(Constants.OP_INVOICE_MONTH_LIMIT)); return dto; } server/services/src/main/java/com/doumee/config/wx/WxPayV3Service.java
@@ -59,7 +59,7 @@ com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount(); amount.setTotal(1);//totalCents.intValue()); amount.setTotal(totalCents.intValue()); amount.setCurrency("CNY"); request.setAmount(amount); @@ -109,8 +109,8 @@ request.setNotifyUrl(notifyUrl); AmountReq amount = new AmountReq(); amount.setRefund(1L);//refundCents); amount.setTotal(1L);//totalCents); amount.setRefund(refundCents); amount.setTotal(totalCents); amount.setCurrency("CNY"); request.setAmount(amount); server/services/src/main/java/com/doumee/dao/dto/DriverOrderPageDTO.java
@@ -16,4 +16,7 @@ @ApiModelProperty(value = "订单状态筛选:null=全部;3=待取件;4=配送中;7=已完成") private Integer status; @ApiModelProperty(value = "搜索关键词(收件人/收件人电话模糊/订单号精准)") private String keyword; } server/services/src/main/java/com/doumee/dao/vo/LocationTagShopCountVO.java
@@ -24,4 +24,7 @@ @ApiModelProperty("门店数量") private Integer shopCount; @ApiModelProperty(value = "排序码", hidden = true) private Integer sortnum; } server/services/src/main/java/com/doumee/dao/vo/MyOrderDetailVO.java
@@ -76,6 +76,8 @@ private Date arriveTime; // ---- 存件门店 ---- @ApiModelProperty(value = "存件门店主键", example = "1") private Integer depositShopId; @ApiModelProperty(value = "存件门店名称") private String depositShopName; @@ -132,6 +134,9 @@ @ApiModelProperty(value = "实际支付费用(分)") private Long actualPayAmount; @ApiModelProperty(value = "优惠券抵扣金额(分)") private Long deductionAmount; // ---- 逾期 ---- //逾期状态: 0=未到店未逾期 1=未到店存在逾期 2=已到店未存在逾期 3=已到店待支付逾期 4=逾期已支付 server/services/src/main/java/com/doumee/dao/vo/MyOrderVO.java
@@ -148,4 +148,7 @@ @ApiModelProperty(value = "优惠券抵扣金额(分)") private Long deductionAmount; @ApiModelProperty(value = "是否异常订单:0=否;1=是") private Integer abnormalOrder; } server/services/src/main/java/com/doumee/dao/vo/OrderDetailVO.java
@@ -6,6 +6,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.Date; import java.util.List; /** @@ -82,4 +83,22 @@ @ApiModelProperty(value = "优惠券抵扣金额(分)") private Long deductionAmount; @ApiModelProperty(value = "评价内容") private String commentContent; @ApiModelProperty(value = "评价时间") private Date commentTime; @ApiModelProperty(value = "评价附件图片") private List<String> commentImages; @ApiModelProperty(value = "寄存门店评分") private Integer depositScore; @ApiModelProperty(value = "取件门店评分") private Integer takeScore; @ApiModelProperty(value = "司机评分") private Integer driverScore; } server/services/src/main/java/com/doumee/dao/vo/OrdersExportVO.java
@@ -47,15 +47,21 @@ @ExcelColumn(name = "优惠券折扣(元)", index = 12) private String deductionAmount; @ExcelColumn(name = "订单状态", index = 13) @ExcelColumn(name = "门店补偿费用(元)", index = 13) private String shopCompensationAmount; @ExcelColumn(name = "司机补偿费用(元)", index = 14) private String exceptionFee; @ExcelColumn(name = "订单状态", index = 15) private String statusDesc; @ExcelColumn(name = "结算状态", index = 14) @ExcelColumn(name = "结算状态", index = 16) private String settlementDesc; @ExcelColumn(name = "支付时间", index = 15, dateFormat = "yyyy-MM-dd HH:mm:ss", width = 16) @ExcelColumn(name = "支付时间", index = 17, dateFormat = "yyyy-MM-dd HH:mm:ss", width = 16) private Date payTime; @ExcelColumn(name = "创建时间", index = 16, dateFormat = "yyyy-MM-dd HH:mm:ss", width = 16) @ExcelColumn(name = "创建时间", index = 18, dateFormat = "yyyy-MM-dd HH:mm:ss", width = 16) private Date createTime; } server/services/src/main/java/com/doumee/dao/vo/ShopCenterVO.java
@@ -6,6 +6,9 @@ @Data public class ShopCenterVO { @ApiModelProperty(value = "门店主键") private Integer id; @ApiModelProperty(value = "门店头像全路径") private String fullCoverImg; server/services/src/main/java/com/doumee/service/business/MemberCouponService.java
@@ -5,6 +5,7 @@ import com.doumee.dao.business.model.MemberCoupon; import java.util.List; import java.util.Map; public interface MemberCouponService { @@ -27,4 +28,6 @@ PageData<MemberCoupon> findMemberPage(Integer memberId, Integer status, PageWrap<MemberCoupon> pageWrap); void claimCoupon(Integer memberId, Integer couponId); Map<String, Integer> findPendingCount(Integer memberId); } server/services/src/main/java/com/doumee/service/business/OrdersService.java
@@ -466,4 +466,5 @@ */ Boolean checkOperationRadius(Integer orderId, Integer userId, Integer userType, Double lng, Double lat); ManualRefundDetailVO getManualRefundDetail(Integer orderId); } server/services/src/main/java/com/doumee/service/business/impl/CouponServiceImpl.java
@@ -164,6 +164,12 @@ if (coupon.getPrice() >= coupon.getLimitPrice()) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "优惠金额必须小于满额"); } if (coupon.getLimitPrice() < 1000) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "满减金额最低10元"); } if (coupon.getLimitPrice() - coupon.getPrice() < 1000) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "满减金额必须大于扣减金额10元以上"); } if (Objects.isNull(coupon.getPushDays()) || coupon.getPushDays() < 1) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "推送后领取有效天数必须大于等于1天"); } server/services/src/main/java/com/doumee/service/business/impl/DataBoardServiceImpl.java
@@ -150,10 +150,10 @@ Map<String, Long> map = members.stream() .collect(Collectors.groupingBy(m -> sdf.format(m.getCreateTime()), Collectors.counting())); return buildTrendList(range, (date) -> { return buildTrendList(range, (key, label) -> { MemberTrendVO vo = new MemberTrendVO(); vo.setDate(date); vo.setCount(map.getOrDefault(date, 0L)); vo.setDate(label); vo.setCount(map.getOrDefault(key, 0L)); return vo; }); } @@ -169,10 +169,10 @@ Map<String, List<Orders>> grouped = orders.stream() .collect(Collectors.groupingBy(o -> sdf.format(o.getCreateTime()))); return buildTrendList(range, (date) -> { List<Orders> dayOrders = grouped.getOrDefault(date, Collections.emptyList()); return buildTrendList(range, (key, label) -> { List<Orders> dayOrders = grouped.getOrDefault(key, Collections.emptyList()); OrderTrendVO vo = new OrderTrendVO(); vo.setDate(date); vo.setDate(label); vo.setLocalCount(dayOrders.stream().filter(o -> Constants.equalsInteger(o.getType(), Constants.ZERO)).count()); vo.setRemoteCount(dayOrders.stream().filter(o -> Constants.equalsInteger(o.getType(), Constants.ONE)).count()); return vo; @@ -227,11 +227,11 @@ } } return buildTrendList(range, (date) -> { return buildTrendList(range, (key, label) -> { RevenueTrendVO vo = new RevenueTrendVO(); vo.setDate(date); long local = localOrderRevenue.getOrDefault(date, 0L); long remote = remoteOrderRevenue.getOrDefault(date, 0L); vo.setDate(label); long local = localOrderRevenue.getOrDefault(key, 0L); long remote = remoteOrderRevenue.getOrDefault(key, 0L); vo.setLocalRevenue(BigDecimal.valueOf(local).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)); vo.setRemoteRevenue(BigDecimal.valueOf(remote).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)); return vo; @@ -718,7 +718,7 @@ @FunctionalInterface private interface TrendVOBuilder<T> { T build(String date); T build(String key, String label); } private <T> List<T> buildTrendList(TrendDateRange range, TrendVOBuilder<T> builder) { @@ -732,7 +732,8 @@ Calendar end = Calendar.getInstance(); end.setTime(range.endDate); while (!loop.after(end)) { result.add(builder.build(sdf.format(loop.getTime()))); String dateStr = sdf.format(loop.getTime()); result.add(builder.build(dateStr, dateStr)); loop.add(Calendar.DAY_OF_MONTH, 1); } } else { @@ -741,8 +742,9 @@ endCal.setTime(range.endDate); int endMonth = endCal.get(Calendar.MONTH); // 0-based for (int m = 0; m <= endMonth; m++) { String label = String.format("%02d", m + 1); result.add(builder.build(label)); String key = String.format("%02d", m + 1); String label = (m + 1) + "月"; result.add(builder.build(key, label)); } } return result; server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java
@@ -246,7 +246,6 @@ IPage<DriverInfo> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity()); MPJLambdaWrapper<DriverInfo> queryWrapper = new MPJLambdaWrapper<>(); Utils.MP.blankToNull(pageWrap.getModel()); pageWrap.getModel().setDeleted(Constants.ZERO); // 司机姓名/手机号(关键字模糊查询) if (StringUtils.isNotBlank(pageWrap.getModel().getKeyword())) { @@ -282,7 +281,8 @@ queryWrapper.selectAll(DriverInfo.class) .select(" ( select ifnull(sum(r.OPT_TYPE * r.AMOUNT),0) from revenue r where r.MEMBER_TYPE = 1 and r.MEMBER_ID= t.id and r.VAILD_STATUS = 1 ) as memberAmount ") .selectAs(Category::getName,DriverInfo::getCarTypeName) .leftJoin(Category.class, Category::getId,DriverInfo::getCarType); .leftJoin(Category.class, Category::getId,DriverInfo::getCarType) .eq(DriverInfo::getDeleted, Constants.ZERO); queryWrapper.orderByDesc(DriverInfo::getId); PageData<DriverInfo> pageData = PageData.from(driverInfoMapper.selectPage(page, queryWrapper)); for (DriverInfo d : pageData.getRecords()) { @@ -560,8 +560,13 @@ driverInfoMapper.insert(newChange); saveDriverAttachments(newChange.getId(), request, now); // 标记历史的变更版本为删除 driverInfoMapper.update(new UpdateWrapper<DriverInfo>().lambda() .set(DriverInfo::getDeleted, Constants.ONE) .set(DriverInfo::getUpdateTime, now) .eq(DriverInfo::getMemberId, memberId) .eq(DriverInfo::getVersionType, Constants.ONE) .eq(DriverInfo::getDeleted, Constants.ZERO) .ne(DriverInfo::getId, newChange.getId())); } else { // auditStatus=0/2:直接更新变更版本 @@ -1881,6 +1886,9 @@ if (!driverId.equals(order.getAcceptDriver())) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "无权操作该订单"); } if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "异常订单无法进行确认送达"); } // 3. 保存送达图片 Date now = new Date(); @@ -2271,7 +2279,13 @@ .eq(status != null, Orders::getStatus, status) .eq(Orders::getDeleted, Constants.ZERO) .orderByDesc(Orders::getAcceptTime); // 关键词搜索:收件人/收件人电话模糊、订单号精准 if (StringUtils.isNotBlank(model.getKeyword())) { String kw = model.getKeyword().trim(); wrapper.and(w -> w.like(Orders::getTakeUser, kw) .or().like(Orders::getTakePhone, kw) .or().like(Orders::getCode, kw)); } IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper); List<DriverGrabOrderVO> voList = new ArrayList<>(); server/services/src/main/java/com/doumee/service/business/impl/MemberCouponServiceImpl.java
@@ -26,7 +26,9 @@ import java.util.Calendar; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @Service @@ -197,6 +199,22 @@ } @Override public Map<String, Integer> findPendingCount(Integer memberId) { Long waitClaim = memberCouponMapper.selectCount(new QueryWrapper<MemberCoupon>().lambda() .eq(MemberCoupon::getMemberId, memberId) .eq(MemberCoupon::getStatus, Constants.CouponStatus.waitClaim.getKey()) .eq(MemberCoupon::getIsdeleted, Constants.ZERO)); Long waitUse = memberCouponMapper.selectCount(new QueryWrapper<MemberCoupon>().lambda() .eq(MemberCoupon::getMemberId, memberId) .eq(MemberCoupon::getStatus, Constants.CouponStatus.claimed.getKey()) .eq(MemberCoupon::getIsdeleted, Constants.ZERO)); Map<String, Integer> result = new LinkedHashMap<>(); result.put("waitClaim", waitClaim != null ? waitClaim.intValue() : 0); result.put("waitUse", waitUse != null ? waitUse.intValue() : 0); return result; } @Override @Transactional(rollbackFor = {Exception.class, BusinessException.class}) public void claimCoupon(Integer memberId, Integer couponId) { // 查询该会员的待领取优惠券记录 @@ -207,11 +225,6 @@ .eq(MemberCoupon::getIsdeleted, Constants.ZERO)); if (mc == null) { throw new BusinessException(ResponseStatus.DATA_EMPTY); } // 校验优惠券是否有效 Coupon coupon = couponMapper.selectById(couponId); if (coupon == null || coupon.getStatus() != Constants.ZERO) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "优惠券无效"); } // 标记已领取,计算有效期 Date now = new Date(); server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java
@@ -410,9 +410,9 @@ } } // 根据openid查询当前绑定的门店 if (StringUtils.isNotBlank(member.getOpenid())) { if (Objects.nonNull(member.getLoginShopId())) { ShopInfo bindShop = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda() .eq(ShopInfo::getOpenid, member.getOpenid()) .eq(ShopInfo::getId, member.getLoginShopId()) .eq(ShopInfo::getDeleted, Constants.ZERO) .last("limit 1")); if (bindShop != null) { @@ -725,6 +725,12 @@ ); } /** * 注册满X年赠送优惠券(定时任务调用) * 规则:根据运营配置 registerCouponYears(满几年赠送)、registerCouponGiftCount(至多赠送次数)、registerCouponId(赠送优惠券ID列表), * 遍历所有普通会员,计算注册年限,每满配置年数赠送一次,累计赠送次数不超过配置上限。 * 例如:配置满2年赠送、至多3次,则注册第2/4/6年各赠送一次,共3次。 */ @Override public void giftRegisterCoupon() { // 1. 读取配置 @@ -753,11 +759,17 @@ return; } // 3. 查询所有普通会员 // 3. 数据库层面过滤:注册满configYears且未赠满的普通会员 Calendar cal = Calendar.getInstance(); cal.add(Calendar.YEAR, -configYears); Date minRegisterDate = cal.getTime(); List<Member> members = memberMapper.selectList(new QueryWrapper<Member>().lambda() .eq(Member::getDeleted, Constants.ZERO) .eq(Member::getStatus, Constants.ZERO) .eq(Member::getUserType, Constants.ZERO) .le(Member::getCreateTime, minRegisterDate) .lt(Member::getRegisterCouponGiftCount, maxGiftCount) .isNotNull(Member::getCreateTime)); Date now = new Date(); server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -133,6 +133,8 @@ @Autowired private MemberCouponMapper memberCouponMapper; @Autowired private CouponMapper couponMapper; @Autowired private AreasBiz areasBiz; @@ -662,7 +664,7 @@ .le(MemberCoupon::getLimitPrice, totalPrice) .ge(MemberCoupon::getEndDate, now) .orderByDesc(MemberCoupon::getPrice) .orderByAsc(MemberCoupon::getEndDate)); .orderByAsc(MemberCoupon::getCreateDate)); result.setAvailableCoupons(availableCoupons); if (couponId == null) { @@ -1151,6 +1153,11 @@ if (Objects.isNull(order)) { throw new BusinessException(ResponseStatus.DATA_EMPTY); } // 实付金额 = 支付金额 - 退款金额 + 逾期费用 long pay = order.getPayAmount() != null ? order.getPayAmount() : 0L; long refund = order.getRefundAmount() != null ? order.getRefundAmount() : 0L; long overdue = order.getOverdueAmount() != null ? order.getOverdueAmount() : 0L; order.setPayAmount(pay - refund + overdue); OrderDetailVO vo = new OrderDetailVO(); vo.setOrder(order); @@ -1217,6 +1224,25 @@ for (OrderItemVO v:vo.getDetailList()) { v.setTypeName(category.getName()); } } // 评价信息 List<OrderComment> comments = orderCommentMapper.selectList(new QueryWrapper<OrderComment>().lambda() .eq(OrderComment::getOrderId, id) .eq(OrderComment::getDeleted, Constants.ZERO)); if (CollectionUtils.isNotEmpty(comments)) { for (OrderComment c : comments) { if (Constants.equalsInteger(c.getTargetType(), Constants.ONE)) { vo.setDepositScore(c.getScore()); } else if (Constants.equalsInteger(c.getTargetType(), Constants.TWO)) { vo.setTakeScore(c.getScore()); } else if (Constants.equalsInteger(c.getTargetType(), Constants.THREE)) { vo.setDriverScore(c.getScore()); } } vo.setCommentContent(comments.get(0).getContent()); vo.setCommentTime(comments.get(0).getCreateTime()); vo.setCommentImages(getFileUrls(id, Constants.FileType.COMMENT_ATTACH.getKey(), imgPrefix)); } // 取消/退款状态时查询退款记录 @@ -1655,7 +1681,8 @@ cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); wrapper.ge(Orders::getFinishTime, cal.getTime()); wrapper.ge(Orders::getFinishTime, cal.getTime()) .eq(Orders::getStatus, Constants.OrderStatus.finished.getKey()); } } // 关键词搜索:收件人/收件人电话模糊、订单号精准 @@ -1737,6 +1764,8 @@ fillOverdueStatus(vo, o, details); // 优惠券抵扣金额 vo.setDeductionAmount(o.getDeductionAmount()); // 异常订单标识 vo.setAbnormalOrder(o.getExceptionStatus()); // 可开票金额(支付金额 - 退款金额) if (model != null && model.getInvoiceStatus() != null && Constants.equalsInteger(model.getInvoiceStatus(), Constants.ONE)) { long payAmt = o.getPayAmount() != null ? o.getPayAmount() : 0L; @@ -1815,7 +1844,7 @@ .or(w3-> w3.eq(Orders::getType, Constants.ONE).eq(Orders::getDepositShopId, shopId) .eq(Orders::getStatus, Constants.OrderStatus.waitDeposit.getStatus())) .or(w2 -> w2.eq(Orders::getType, Constants.ONE).eq(Orders::getTakeShopId, shopId) .eq(Orders::getStatus, Constants.OrderStatus.arrived.getStatus()))) .in(Orders::getStatus, Constants.OrderStatus.arrived.getStatus(),Constants.OrderStatus.delivering.getStatus()))) ); } else { wrapper.and(w -> w.eq(Orders::getDepositShopId, shopId).or().eq(Orders::getTakeShopId, shopId)); @@ -1975,6 +2004,7 @@ vo.setUrgentAmount(order.getUrgentAmount()); vo.setIsUrgent(order.getIsUrgent()); vo.setActualPayAmount(Constants.equalsInteger(order.getPayStatus(), Constants.ONE)?order.getPayAmount():order.getEstimatedAmount()); vo.setDeductionAmount(order.getDeductionAmount()); // 标记 vo.setExceptionStatus(order.getExceptionStatus()); @@ -1988,13 +2018,12 @@ vo.setPayCountdownMs(calcPayCountdownMs(order)); } //序号 vo.setSortnum(Constants.formatIntegerNum(order.getDepositShopId())+"-"+order.getId()); if(order.getTakeShopId()!=null){ String dateStr = new SimpleDateFormat("dd").format(order.getPayTime() != null ? order.getPayTime() : new Date()); String autoNumStr = String.format("%03d", order.getAutoNum() != null ? order.getAutoNum() : 0); String sort = order.getTakeShopId() + "-" + dateStr + "-" + autoNumStr; vo.setSortnumTake(sort); } // vo.setSortnum(Constants.formatIntegerNum(order.getDepositShopId())+"-"+order.getId()); String dateStr = new SimpleDateFormat("dd").format(order.getPayTime() != null ? order.getPayTime() : new Date()); String autoNumStr = String.format("%03d", order.getAutoNum() != null ? order.getAutoNum() : 0); String sort = order.getDepositShopId() + "-" + dateStr + "-" + autoNumStr; vo.setSortnum(sort); vo.setDepositShopId(order.getDepositShopId()); // 存件门店 if (order.getDepositShopId() != null) { ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId()); @@ -2282,16 +2311,71 @@ * 保存取消订单操作日志 */ private void restoreCoupon(Orders order) { // if (order.getCouponId() == null || order.getDeductionAmount() == null || order.getDeductionAmount() <= 0) { // return; // } // memberCouponMapper.update(new UpdateWrapper<MemberCoupon>().lambda() // .set(MemberCoupon::getStatus, Constants.CouponStatus.claimed.getKey()) // .set(MemberCoupon::getUseDate, null) // .set(MemberCoupon::getOrderId, null) // .eq(MemberCoupon::getId, order.getCouponId()) // .eq(MemberCoupon::getOrderId, order.getId()) // .eq(MemberCoupon::getStatus, Constants.CouponStatus.used.getKey())); if (order.getCouponId() == null || order.getDeductionAmount() == null || order.getDeductionAmount() <= 0) { return; } memberCouponMapper.update(new UpdateWrapper<MemberCoupon>().lambda() .set(MemberCoupon::getStatus, Constants.CouponStatus.claimed.getKey()) .set(MemberCoupon::getUseDate, null) .set(MemberCoupon::getOrderId, null) .eq(MemberCoupon::getId, order.getCouponId()) .eq(MemberCoupon::getOrderId, order.getId()) .eq(MemberCoupon::getStatus, Constants.CouponStatus.used.getKey())); } private void giftOrderCoupon(Integer memberId) { String orderCountStr = operationConfigBiz.getConfig().getOrderCouponOrderCount(); String giftCountStr = operationConfigBiz.getConfig().getOrderCouponGiftCount(); String couponIdsStr = operationConfigBiz.getConfig().getOrderCouponId(); if (StringUtils.isAnyBlank(orderCountStr, giftCountStr, couponIdsStr)) return; int orderCount = Integer.parseInt(orderCountStr); int maxGift = Integer.parseInt(giftCountStr); Member member = memberMapper.selectById(memberId); if (member == null) return; int gifted = member.getOrderCouponGiftCount() != null ? member.getOrderCouponGiftCount() : 0; if (gifted >= maxGift) return; long completedCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda() .eq(Orders::getMemberId, memberId) .eq(Orders::getDeleted, Constants.ZERO) .notIn(Orders::getStatus, Constants.OrderStatus.waitPay.getKey(), Constants.OrderStatus.waitDeposit.getKey())); if (completedCount < orderCount || completedCount % orderCount != 0) return; String[] idArr = couponIdsStr.split(","); Date now = new Date(); for (String idStr : idArr) { String trimmed = idStr.trim(); if (StringUtils.isBlank(trimmed)) continue; Coupon coupon = couponMapper.selectById(Integer.valueOf(trimmed)); if (coupon == null || Constants.equalsInteger(coupon.getIsdeleted(), Constants.ONE)) continue; MemberCoupon mc = new MemberCoupon(); mc.setCouponId(coupon.getId()); mc.setMemberId(memberId); mc.setStatus(Constants.CouponStatus.waitClaim.getKey()); Calendar validCal = Calendar.getInstance(); validCal.add(Calendar.DAY_OF_MONTH, coupon.getPushDays() != null ? coupon.getPushDays() : 7); mc.setValidDate(validCal.getTime()); mc.setName(coupon.getName()); mc.setInfo(coupon.getInfo()); mc.setType(coupon.getType()); mc.setLimitPrice(coupon.getLimitPrice()); mc.setPrice(coupon.getPrice()); mc.setGetMethod(coupon.getGetMethod()); mc.setCouponType(coupon.getCouponType()); mc.setPushDays(coupon.getPushDays()); mc.setValidDays(coupon.getValidDays()); mc.setIsdeleted(Constants.ZERO); mc.setCreateDate(now); mc.setEditDate(now); memberCouponMapper.insert(mc); } member.setOrderCouponGiftCount(gifted + 1); memberMapper.updateById(member); } private void saveCancelLog(Orders order, Constants.OrderLogType logType, String reason, Integer memberId) { @@ -2406,7 +2490,7 @@ refund.setRefundTime(now); ordersRefundMapper.updateById(refund); // 退款成功,执行扣款 processManualRefundDeduction(order, depositShopDeduct, takeShopDeduct, driverDeduct); //processManualRefundDeduction(order, depositShopDeduct, takeShopDeduct, driverDeduct); } else if (com.wechat.pay.java.service.refund.model.Status.PROCESSING.equals(refundStatus)) { refund.setStatus(Constants.ZERO); // 退款中,等回调 ordersRefundMapper.updateById(refund); @@ -2428,7 +2512,6 @@ // 7. 更新订单:标记已手动退款,累加退款金额 ordersMapper.update(new UpdateWrapper<Orders>().lambda() .set(Orders::getManualRefund, Constants.ONE) .setSql(" REFUND_AMOUNT = IFNULL(REFUND_AMOUNT, 0) + " + dto.getRefundAmount()) .set(Orders::getUpdateTime, now) .eq(Orders::getId, order.getId())); } @@ -2511,10 +2594,11 @@ Long driverDeduct = deductJson.getLong("driverDeduct"); processManualRefundDeduction(order, depositShopDeduct, takeShopDeduct, driverDeduct); // 标记订单已手动退款,累加退款金额 // 标记订单已手动退款,累加退款金额,同步更新totalAmount ordersMapper.update(new UpdateWrapper<Orders>().lambda() .set(Orders::getManualRefund, Constants.ONE) .setSql(" REFUND_AMOUNT = IFNULL(REFUND_AMOUNT, 0) + " + refundRecord.getRefundAmount()) .setSql(" TOTAL_AMOUNT = IFNULL(TOTAL_AMOUNT, 0) - " + refundRecord.getRefundAmount()) .set(Orders::getUpdateTime, new Date()) .eq(Orders::getId, order.getId())); } @@ -3262,6 +3346,8 @@ sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_PICKUP_REMIND, order.getId(), "orderNo", order.getCode(), "shopName", shopName); } // 寄存成功赠送优惠券 giftOrderCoupon(order.getMemberId()); } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) { // 异地寄存 + 无取件门店 → 无法核销(客户自取,无门店操作) if (Constants.equalsInteger(order.getType(), Constants.ONE) && order.getTakeShopId() == null) { @@ -3881,14 +3967,16 @@ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "核销码无效"); } // 仅异地寄存 + 有取件门店 + 派送中(4) 可核销 if (!Constants.equalsInteger(order.getType(), Constants.ONE)) { // 仅异地寄存 + 有取件门店 + 派送中(4) 可核销(异常订单允许) if (!Constants.equalsInteger(order.getType(), Constants.ONE) && !Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅异地寄存订单支持司机核销"); } if (order.getTakeShopId() == null) { if (order.getTakeShopId() == null && !Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单无取件门店,无需司机核销"); } if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getStatus())) { if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getStatus()) && !Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.deposited.getStatus())) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不允许核销"); } @@ -4601,7 +4689,7 @@ sendSmsNotify(member != null ? member.getTelephone() : null, Constants.SmsNotify.MEMBER_CANCELLED, "orderNo", order.getCode()); } restoreCoupon(order); count++; } catch (Exception e) { log.error("取消超时订单异常, orderId={}, error={}", order.getId(), e.getMessage()); @@ -5019,29 +5107,26 @@ } targetLat = order.getDepositLat(); targetLgt = order.getDepositLgt(); } else if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) { // status=5 门店完成核销 if (Constants.equalsInteger(order.getType(), Constants.ZERO)) { // 就地存取 → 对比存件门店 if (!Constants.equalsInteger(order.getDepositShopId(), userId)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单"); } targetLat = order.getDepositLat(); targetLgt = order.getDepositLgt(); } else { // 异地存取 → 对比取件门店 if (!Constants.equalsInteger(order.getTakeShopId(), userId)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单"); } targetLat = order.getTakeLat(); targetLgt = order.getTakeLgt(); } else if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())&&Constants.equalsInteger(order.getType(), Constants.ZERO)) { // 就地存取 → 对比存件门店 if (!Constants.equalsInteger(order.getDepositShopId(), userId)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单"); } targetLat = order.getDepositLat(); targetLgt = order.getDepositLgt(); } else if ((Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getStatus())||Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus()))&&Constants.equalsInteger(order.getType(), Constants.ONE)) { // 异地存取 → 对比取件门店 if (!Constants.equalsInteger(order.getTakeShopId(), userId)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单"); } targetLat = order.getTakeLat(); targetLgt = order.getTakeLgt(); } else { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "订单状态不允许此操作"); } } else if (Constants.equalsInteger(userType, Constants.ONE)) { // 司机操作 if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.deposited.getStatus())) { if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.accepted.getStatus())) { // status=2 司机取件 if (!Constants.equalsInteger(order.getAcceptDriver(), userId)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单"); @@ -5082,8 +5167,8 @@ if (original.getTakeShopId() != null) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单已关联取件门店,不支持异常处理"); } if (!Constants.equalsInteger(original.getStatus(), Constants.FIVE)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅支持已送达状态的订单"); if (!Constants.equalsInteger(original.getStatus(), Constants.OrderStatus.delivering.getKey())) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅支持配送中的订单进行异常处理"); } if (Constants.equalsInteger(original.getExceptionStatus(), Constants.ONE)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单已处理过异常,请勿重复操作"); @@ -5299,4 +5384,27 @@ } } @Override public ManualRefundDetailVO getManualRefundDetail(Integer orderId) { OrdersRefund refundRecord = ordersRefundMapper.selectOne(new QueryWrapper<OrdersRefund>().lambda() .eq(OrdersRefund::getOrderId, orderId) .eq(OrdersRefund::getType, Constants.FOUR) .eq(OrdersRefund::getDeleted, Constants.ZERO) .orderByDesc(OrdersRefund::getCreateTime) .last("limit 1")); if (refundRecord == null) { throw new BusinessException(ResponseStatus.DATA_EMPTY); } ManualRefundDetailVO vo = new ManualRefundDetailVO(); vo.setRefundAmount(refundRecord.getRefundAmount()); if (StringUtils.isNotBlank(refundRecord.getDeductInfo())) { JSONObject json = JSONObject.parseObject(refundRecord.getDeductInfo()); vo.setDepositShopDeduct(json.getLong("depositShopDeduct")); vo.setTakeShopDeduct(json.getLong("takeShopDeduct")); vo.setDriverDeduct(json.getLong("driverDeduct")); } vo.setRefundRemark(refundRecord.getRefundRemark()); return vo; } } server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java
@@ -19,6 +19,7 @@ import com.doumee.dao.business.MemberMapper; import com.doumee.dao.business.MultifileMapper; import com.doumee.dao.business.OrdersMapper; import com.doumee.dao.business.OtherOrdersMapper; import com.doumee.dao.business.PricingRuleMapper; import com.doumee.dao.business.RevenueMapper; import com.doumee.dao.business.ShopInfoMapper; @@ -27,6 +28,7 @@ import com.doumee.dao.business.model.Member; import com.doumee.dao.business.model.Multifile; import com.doumee.dao.business.model.Orders; import com.doumee.dao.business.model.OtherOrders; import com.doumee.dao.business.model.PricingRule; import com.doumee.dao.business.model.Revenue; import com.doumee.dao.business.model.ShopInfo; @@ -122,6 +124,9 @@ @Autowired private CategoryMapper categoryMapper; @Autowired private OtherOrdersMapper otherOrdersMapper; @Override public Integer create(ShopInfo shopInfo) { @@ -342,6 +347,8 @@ newChange.setCreateTime(now); newChange.setUpdateTime(now); newChange.setRegionMemberId(member.getId()); newChange.setRevenueShareConfig(changeVersion.getRevenueShareConfig()); newChange.setDeliveryArea(changeVersion.getDeliveryArea()); setDepositAmountFromPricingRule(newChange); shopInfoMapper.insert(newChange); @@ -564,6 +571,7 @@ changeVersion.setAuditUserId(auditDTO.getAuditUser()); changeVersion.setRevenueShareConfig(revenueShareConfig); changeVersion.setUpdateTime(now); setDefaultDeliveryRange(changeVersion); shopInfoMapper.updateById(changeVersion); // 标记历史的变更版本为删除 @@ -1213,8 +1221,20 @@ vo.setTagId(tag.getId()); vo.setTagName(tag.getName()); vo.setShopCount(count != null ? count.intValue() : 0); vo.setSortnum(tag.getSortnum()); result.add(vo); } // 按门店数量倒序,相同时按排序码降序 result.sort((a, b) -> { int cmp = Integer.compare( b.getShopCount() != null ? b.getShopCount() : 0, a.getShopCount() != null ? a.getShopCount() : 0); if (cmp != 0) return cmp; return Integer.compare( b.getSortnum() != null ? b.getSortnum() : 0, a.getSortnum() != null ? a.getSortnum() : 0); }); // 总数放在列表第一个 Long totalCount = shopInfoMapper.selectCount(baseQw); @@ -1396,6 +1416,7 @@ throw new BusinessException(ResponseStatus.DATA_EMPTY); } ShopCenterVO vo = new ShopCenterVO(); vo.setId(shop.getId()); vo.setShopName(shop.getName()); vo.setLinkName(shop.getLinkName()); vo.setCompanyType(shop.getCompanyType()); @@ -1597,14 +1618,34 @@ .count(); vo.setFinishedOrderCount((int) finishedCount); // 总营收金额 = sum(totalAmount - refundAmount) long totalRevenue = orders.stream() // 总营收金额 = sum(支付金额 - 退款金额) + 逾期金额 long baseRevenue = orders.stream() .mapToLong(o -> { long total = o.getTotalAmount() != null ? o.getTotalAmount() : 0L; long pay = o.getPayAmount() != null ? o.getPayAmount() : 0L; long refund = o.getRefundAmount() != null ? o.getRefundAmount() : 0L; return total - refund; return pay - refund; }).sum(); vo.setTotalRevenue(totalRevenue); // 查询该门店参与的逾期费用(从other_orders表获取,type=2已支付) QueryWrapper<OtherOrders> overdueQw = new QueryWrapper<>(); overdueQw.lambda() .eq(OtherOrders::getDeleted, Constants.ZERO) .eq(OtherOrders::getType, Constants.TWO) .eq(OtherOrders::getPayStatus, Constants.ONE) .inSql(OtherOrders::getOrderId, "SELECT id FROM orders WHERE DELETED = 0 AND (DEPOSIT_SHOP_ID = " + shopId + " OR TAKE_SHOP_ID = " + shopId + ")"); if (query.getStartDate() != null) { overdueQw.lambda().ge(OtherOrders::getCreateTime, query.getStartDate()); } if (query.getEndDate() != null) { overdueQw.lambda().le(OtherOrders::getCreateTime, Utils.Date.getEnd(query.getEndDate())); } List<OtherOrders> overdueOrders = otherOrdersMapper.selectList(overdueQw); long overdueTotal = overdueOrders.stream() .mapToLong(o -> o.getPayAccount() != null ? o.getPayAccount() : 0L) .sum(); vo.setTotalRevenue(baseRevenue + overdueTotal); // 门店分成金额 long shopFee = 0L; server/web/src/main/java/com/doumee/api/web/MemberCouponApi.java
@@ -14,6 +14,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Map; @Slf4j @Api(tags = "会员优惠券") @RestController @@ -35,6 +37,16 @@ } @LoginRequired @ApiOperation(value = "待领取/待使用数量", notes = "角标用") @GetMapping("/pendingCount") @ApiImplicitParams({ @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true), }) public ApiResponse<Map<String, Integer>> pendingCount() { return ApiResponse.success("操作成功", memberCouponService.findPendingCount(getMemberId())); } @LoginRequired @ApiOperation(value = "领取优惠券", notes = "小程序端") @GetMapping("/claim") @ApiImplicitParams({ server/web/src/main/java/com/doumee/api/web/PaymentCallback.java
@@ -202,7 +202,8 @@ log.info("退款记录状态已更新, refundRecordId={}, status={}", refundRecord.getId(), refundRecord.getStatus()); // 手动退款(type=4)退款成功 → 执行扣款 if (Status.SUCCESS.equals(refundStatus) && Constants.equalsInteger(refundRecord.getType(), Constants.FOUR)) { if (Status.SUCCESS.equals(refundStatus) && Constants.equalsInteger(refundRecord.getType(), Constants.FOUR)) { try { ordersService.processManualRefundCallback(refundRecord); } catch (Exception ex) {