From 707b8b0a5b473db295749d417f4dfe527c4bb402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E7=9D=A1=E8=A7=89=E7=9A=84=E6=9C=88=E4=BA=AE?= <97796069+Cookies-OvO@users.noreply.github.com> Date: Thu, 4 Dec 2025 15:17:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=92=E9=98=9F=E7=AE=A1=E7=90=86-=E5=A4=B1?= =?UTF-8?q?=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StoreCombinationController.java | 11 + .../yzt/service/service/StorePinkService.java | 7 + .../service/impl/OrderServiceImpl.java | 2 +- .../impl/StoreOrderTaskServiceImpl.java | 18 +- .../service/impl/StorePinkServiceImpl.java | 302 +++++++----------- 5 files changed, 143 insertions(+), 197 deletions(-) diff --git a/fightwine-admin/src/main/java/com/yzt/admin/controller/StoreCombinationController.java b/fightwine-admin/src/main/java/com/yzt/admin/controller/StoreCombinationController.java index f877cb0..ce8b56a 100644 --- a/fightwine-admin/src/main/java/com/yzt/admin/controller/StoreCombinationController.java +++ b/fightwine-admin/src/main/java/com/yzt/admin/controller/StoreCombinationController.java @@ -155,6 +155,17 @@ public class StoreCombinationController { public CommonResult> getPinkList(@PathVariable(value = "id") Integer id) { return CommonResult.success(storePinkService.getAdminList(id)); } + + /** + * 后台管理员操作:根据拼团(排队)记录ID取消该排队并重新计算队列进度 + * 不做细粒度权限控制,只要是已登录的后台用户即可调用 + */ + @ApiOperation(value = "取消指定排队记录并重算队列进度") + @RequestMapping(value = "/combine/queue/cancel", method = RequestMethod.POST) + public CommonResult adminCancelQueueAndRecalculate(@RequestParam("pinkId") Integer pinkId) { + storePinkService.adminCancelQueueAndRecalculate(pinkId); + return CommonResult.success(); + } } diff --git a/fightwine-service/src/main/java/com/yzt/service/service/StorePinkService.java b/fightwine-service/src/main/java/com/yzt/service/service/StorePinkService.java index 793d4db..c1028b7 100644 --- a/fightwine-service/src/main/java/com/yzt/service/service/StorePinkService.java +++ b/fightwine-service/src/main/java/com/yzt/service/service/StorePinkService.java @@ -102,4 +102,11 @@ public interface StorePinkService extends IService { * @param orderId 订单号 */ void invalidateQueueByOrderId(String orderId); + + /** + * 后台管理员操作:根据拼团(排队)记录ID取消该记录,并重新计算该队列的排队进度 + * 仅修改该记录的 isRefund 标记为已取消,并对同队列剩余未取消记录重新排队和计算进度 + * @param pinkId 拼团(排队)记录ID + */ + void adminCancelQueueAndRecalculate(Integer pinkId); } diff --git a/fightwine-service/src/main/java/com/yzt/service/service/impl/OrderServiceImpl.java b/fightwine-service/src/main/java/com/yzt/service/service/impl/OrderServiceImpl.java index c18ce20..d049cc2 100644 --- a/fightwine-service/src/main/java/com/yzt/service/service/impl/OrderServiceImpl.java +++ b/fightwine-service/src/main/java/com/yzt/service/service/impl/OrderServiceImpl.java @@ -1918,7 +1918,7 @@ public class OrderServiceImpl implements OrderService { if (numerator.compareTo(denominator) >= 0) { xUser.setQueueProgress(BigDecimal.ONE); xUser.setStatus(2); - xUser.setIsRefund(true); + // 这里不再自动将排队记录标记为已取消,仅标记完成状态 // 注释掉订单已完成的特殊处理,统一走待释放流程 // LambdaQueryWrapper orderWrapper = new LambdaQueryWrapper<>(); diff --git a/fightwine-service/src/main/java/com/yzt/service/service/impl/StoreOrderTaskServiceImpl.java b/fightwine-service/src/main/java/com/yzt/service/service/impl/StoreOrderTaskServiceImpl.java index 5b0fd4d..809cd98 100644 --- a/fightwine-service/src/main/java/com/yzt/service/service/impl/StoreOrderTaskServiceImpl.java +++ b/fightwine-service/src/main/java/com/yzt/service/service/impl/StoreOrderTaskServiceImpl.java @@ -375,16 +375,16 @@ public class StoreOrderTaskServiceImpl implements StoreOrderTaskService { tempOrder.setUpdateTime(DateUtil.date()); storeOrderService.updateById(tempOrder); - // 拼团状态处理 - if (storeOrder.getCombinationId() > 0) { - StorePink storePink = storePinkService.getByOrderId(storeOrder.getOrderId()); - storePink.setStatus(3); - storePink.setIsRefund(true); - storePinkService.updateById(storePink); - } + // 拼团状态处理(原先会将拼团记录标记为已退款并触发排队记录失效逻辑,此处逻辑废弃) +// if (storeOrder.getCombinationId() > 0) { +// StorePink storePink = storePinkService.getByOrderId(storeOrder.getOrderId()); +// storePink.setStatus(3); +// storePink.setIsRefund(true); +// storePinkService.updateById(storePink); +// } - // 排队记录失效处理:标记为退款、队列前移、重新计算进度 - storePinkService.invalidateQueueByOrderId(storeOrder.getOrderId()); + // 排队记录失效处理:标记为退款、队列前移、重新计算进度(逻辑废弃) +// storePinkService.invalidateQueueByOrderId(storeOrder.getOrderId()); // 退优惠券 if (storeOrder.getCouponId() > 0 ) { diff --git a/fightwine-service/src/main/java/com/yzt/service/service/impl/StorePinkServiceImpl.java b/fightwine-service/src/main/java/com/yzt/service/service/impl/StorePinkServiceImpl.java index 60440d5..46d3657 100644 --- a/fightwine-service/src/main/java/com/yzt/service/service/impl/StorePinkServiceImpl.java +++ b/fightwine-service/src/main/java/com/yzt/service/service/impl/StorePinkServiceImpl.java @@ -218,202 +218,130 @@ public class StorePinkServiceImpl extends ServiceImpl lqw = new LambdaQueryWrapper<>(); - lqw.eq(StorePink::getOrderId, orderId); - List targetList = mapper.selectList(lqw); - if (CollUtil.isEmpty(targetList)) return; - - List queueTypes = targetList.stream().map(StorePink::getQueueType).distinct().collect(Collectors.toList()); - - for (StorePink p : targetList) { - p.setIsRefund(true); - } - this.updateBatchById(targetList); - - // 对每个受影响队列,重新编号并重新计算进度/处理免单 - for (Integer queueType : queueTypes) { - // 获取被删除记录的 queue_no 列表(用于计算前移量) - List removedRecords = targetList.stream() - .filter(t -> ObjectUtil.isNotNull(t.getQueueNo()) && ObjectUtil.isNotNull(t.getQueueType()) && t.getQueueType().equals(queueType)) - .collect(Collectors.toList()); - if (CollUtil.isEmpty(removedRecords)) continue; - - int minRemovedQueueNo = removedRecords.stream().mapToInt(StorePink::getQueueNo).min().orElse(Integer.MAX_VALUE); - int removedCount = removedRecords.size(); - - // 更新 Redis 全局计数器:减去删除的数量 - String redisKey = queueType != null && queueType == 2 ? "queue:counter:hall" : "queue:counter:member"; - redisUtil.decr(redisKey, removedCount); - - // 查询所有需要前移的记录(queue_no > minRemovedQueueNo 且未退款的记录) - LambdaQueryWrapper shiftWrapper = new LambdaQueryWrapper<>(); - shiftWrapper.eq(StorePink::getQueueType, queueType) - .gt(StorePink::getQueueNo, minRemovedQueueNo) - .eq(StorePink::getIsRefund, false) - .orderByAsc(StorePink::getQueueNo); - List recordsToShift = mapper.selectList(shiftWrapper); - - // 队列前移:减去 removedCount - for (StorePink record : recordsToShift) { - record.setQueueNo(record.getQueueNo() - removedCount); - } - if (!recordsToShift.isEmpty()) { - this.updateBatchById(recordsToShift); - } - - // 查询所有等待中的记录(用于重新计算进度) - LambdaQueryWrapper waitingWrapper = new LambdaQueryWrapper<>(); - waitingWrapper.eq(StorePink::getQueueType, queueType) - .eq(StorePink::getStatus, 1) - .eq(StorePink::getIsRefund, false) - .orderByAsc(StorePink::getQueueNo); - List waitingList = mapper.selectList(waitingWrapper); - if (CollUtil.isEmpty(waitingList)) continue; - - // 读取更新后的 globalY(redis 中的全局计数) - Object globalVal = redisUtil.get(redisKey); - int globalY = 0; - try { - if (globalVal != null) { - globalY = Integer.parseInt(globalVal.toString()); - } - } catch (Exception ignore) {} - if (globalY <= 0) { - // fallback to max queueNo if exists - int maxQueueNo = waitingList.stream().mapToInt(wp -> wp.getQueueNo() == null ? 0 : wp.getQueueNo()).max().orElse(0); - globalY = Math.max(maxQueueNo, waitingList.size()); - } - int newMaxY = globalY; - - // 重新计算进度与处理可能满足免单的用户(注意:若该用户的订单已退款,则**不**生成佣金记录) - List updates = new ArrayList<>(); - for (StorePink xUser : waitingList) { - int x = xUser.getQueueNo(); - if (x >= newMaxY) continue; - - BigDecimal numerator = new BigDecimal(newMaxY - x); - BigDecimal denominator = new BigDecimal(3 * x + 1); - - if (numerator.compareTo(denominator) >= 0) { - xUser.setQueueProgress(BigDecimal.ONE); - xUser.setStatus(2); - xUser.setIsRefund(true); - - // 检查该排队记录关联的订单是否已退款:若已退款则跳过生成佣金/孵化奖 - LambdaQueryWrapper orderWrapper = new LambdaQueryWrapper<>(); - orderWrapper.eq(StoreOrder::getOrderId, xUser.getOrderId()).last("limit 1"); - StoreOrder order = storeOrderService.getOne(orderWrapper); - boolean orderRefunded = (order != null && order.getRefundStatus() != null && order.getRefundStatus() != 0); - - BigDecimal recordPrice = xUser.getPrice(); - if (!orderRefunded) { - if (order != null && order.getStatus() == 3) { - int pointsToAdd = recordPrice.intValue(); - if (pointsToAdd > 0) { - com.yzt.common.request.UserOperateIntegralMoneyRequest integralRequest = new com.yzt.common.request.UserOperateIntegralMoneyRequest(); - integralRequest.setUid(xUser.getUid()); - integralRequest.setIntegralType(1); - integralRequest.setIntegralValue(pointsToAdd); - integralRequest.setMoneyType(1); - integralRequest.setMoneyValue(BigDecimal.ZERO); - userService.updateIntegralMoney(integralRequest); - } - - // 丙的上级获得免单孵化奖 - User user = userService.getById(xUser.getUid()); - if (user != null && user.getSpreadUid() != null && user.getSpreadUid() > 0) { - User parent = userService.getById(user.getSpreadUid()); - // 自身等级为2(拼团会员)时,不获得孵化奖 - if (parent != null && parent.getLevel() != 2) { - BigDecimal incubatorPrize = recordPrice.multiply(new BigDecimal("0.05")).setScale(2, BigDecimal.ROUND_DOWN); - UserBrokerageRecord incubatorRecord = new UserBrokerageRecord(); - incubatorRecord.setUid(parent.getUid()); - incubatorRecord.setLinkId(xUser.getOrderId()); - incubatorRecord.setLinkType("queue_incubator"); - incubatorRecord.setType(1); - incubatorRecord.setTitle("排队免单孵化奖励"); - incubatorRecord.setPrice(incubatorPrize); - incubatorRecord.setBalance(BigDecimal.ZERO); - incubatorRecord.setMark("我是" + user.getNickname() + "的上级,获得其免单金额5%(" + incubatorPrize + ")的免单孵化奖"); - incubatorRecord.setStatus(1); - incubatorRecord.setCreateTime(new Date()); - incubatorRecord.setBrokerageLevel(1); - userBrokerageRecordService.save(incubatorRecord); - } - } - } else { - // 丙获得排队免单收益 - UserBrokerageRecord brokerageRecord = new UserBrokerageRecord(); - brokerageRecord.setUid(xUser.getUid()); - brokerageRecord.setLinkId(xUser.getOrderId()); - brokerageRecord.setLinkType("queue"); - brokerageRecord.setType(1); - brokerageRecord.setTitle("排队免单收益"); - brokerageRecord.setPrice(recordPrice); - brokerageRecord.setBalance(BigDecimal.ZERO); - brokerageRecord.setMark("排队免单成功(订单未完成),排队号:" + x); - brokerageRecord.setStatus(1); - brokerageRecord.setCreateTime(new Date()); - brokerageRecord.setBrokerageLevel(0); - userBrokerageRecordService.save(brokerageRecord); - - // 丙的上级获得孵化奖 - User user = userService.getById(xUser.getUid()); - if (user != null && user.getSpreadUid() != null && user.getSpreadUid() > 0) { - User parent = userService.getById(user.getSpreadUid()); - // 自身等级为2(拼团会员)时,不获得孵化奖 - if (parent != null && parent.getLevel() != 2) { - BigDecimal incubatorPrize = recordPrice.multiply(new BigDecimal("0.05")).setScale(2, BigDecimal.ROUND_DOWN); - UserBrokerageRecord incubatorRecord = new UserBrokerageRecord(); - incubatorRecord.setUid(parent.getUid()); - incubatorRecord.setLinkId(xUser.getOrderId()); - incubatorRecord.setLinkType("queue_incubator"); - incubatorRecord.setType(1); - incubatorRecord.setTitle("排队免单孵化奖励"); - incubatorRecord.setPrice(incubatorPrize); - incubatorRecord.setBalance(BigDecimal.ZERO); - incubatorRecord.setMark("我是" + user.getNickname() + "的上级,获得其免单金额5%(" + incubatorPrize + ")的免单孵化奖"); - incubatorRecord.setStatus(1); - incubatorRecord.setCreateTime(new Date()); - incubatorRecord.setBrokerageLevel(1); - userBrokerageRecordService.save(incubatorRecord); - } - } - } - } - } else { - BigDecimal progress = numerator.divide(denominator, 6, BigDecimal.ROUND_DOWN); - xUser.setQueueProgress(progress); - } - updates.add(xUser); - } - if (!updates.isEmpty()) { - this.updateBatchById(updates); - } - } - } - - /** - * 获取拼团列表uid + * 获取用户拼团列表 * @param uid 用户uid + * @param type 队列类型 * @return */ @Override - public List getListByUid(Integer uid,Integer type) { + public List getListByUid(Integer uid, Integer type) { LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); lqw.eq(StorePink::getUid, uid); - lqw.eq(StorePink::getQueueType,type); + lqw.eq(StorePink::getQueueType, type); lqw.orderByDesc(StorePink::getId); return mapper.selectList(lqw); } + /** + * 使指定订单号的所有排队记录失效,并重新计算剩余排队的 queueNo 与进度 + * @param orderId 订单号 + */ + @Override + public void invalidateQueueByOrderId(String orderId) { + // 旧的按订单号失效排队记录逻辑已废弃,保留空实现以兼容历史调用 + if (true) { + return; + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void adminCancelQueueAndRecalculate(Integer pinkId) { + if (pinkId == null || pinkId <= 0) { + return; + } + StorePink target = mapper.selectById(pinkId); + if (target == null) { + // 查不到记录,直接返回 + return; + } + + if (Boolean.TRUE.equals(target.getIsRefund())) { + return; + } + + Integer queueType = target.getQueueType(); + Integer queueNo = target.getQueueNo(); + if (queueType == null || queueNo == null) { + // 队列信息不完整,无法参与排队计算,直接返回 + return; + } + + target.setIsRefund(true); + mapper.updateById(target); + + LambdaQueryWrapper shiftWrapper = new LambdaQueryWrapper<>(); + shiftWrapper.eq(StorePink::getQueueType, queueType) + .gt(StorePink::getQueueNo, queueNo) + .eq(StorePink::getIsRefund, false) + .orderByAsc(StorePink::getQueueNo); + List recordsToShift = mapper.selectList(shiftWrapper); + if (CollUtil.isNotEmpty(recordsToShift)) { + for (StorePink record : recordsToShift) { + Integer qn = record.getQueueNo(); + if (qn != null && qn > 0) { + // 将后面的每个人往前挪一位 + record.setQueueNo(qn - 1); + } + } + // 批量更新排队号 + this.updateBatchById(recordsToShift); + } + + LambdaQueryWrapper waitingWrapper = new LambdaQueryWrapper<>(); + waitingWrapper.eq(StorePink::getQueueType, queueType) + .eq(StorePink::getStatus, 1) // 1 = 排队中 + .eq(StorePink::getIsRefund, false) // 未取消 + .orderByAsc(StorePink::getQueueNo); + List waitingList = mapper.selectList(waitingWrapper); + if (CollUtil.isEmpty(waitingList)) { + // 队列里已经没人排队了,直接返回 + return; + } + + // 使用 redis 维护“队列当前排到了多少位”(Y): + String redisKey = (queueType == 2) + ? "queue:counter:hall" // 会馆队列计数器 + : "queue:counter:member"; // 会员队列计数器 + long redisVal = redisUtil.decr(redisKey, 1L); + int currentY = (int) redisVal; + if (currentY <= 0) { + currentY = waitingList.size(); + } + + List updates = new ArrayList<>(); + for (StorePink xUser : waitingList) { + Integer xNo = xUser.getQueueNo(); + if (xNo == null) { + continue; + } + int x = xNo; + if (x >= currentY) { + continue; + } + + BigDecimal numerator = new BigDecimal(currentY - x); // 分子 = Y - X + BigDecimal denominator = new BigDecimal(5 * x); // 分母 = 5X + if (denominator.compareTo(BigDecimal.ZERO) <= 0) { + // 分母异常时跳过,避免除零错误 + continue; + } + + if (numerator.compareTo(denominator) >= 0) { + xUser.setQueueProgress(BigDecimal.ONE); + xUser.setStatus(2); + } else { + BigDecimal progress = numerator.divide(denominator, 6, BigDecimal.ROUND_DOWN); + xUser.setQueueProgress(progress); + } + updates.add(xUser); + } + + if (CollUtil.isNotEmpty(updates)) { + this.updateBatchById(updates); + } + } + /** * 拼团实体查询 * @param storePink @@ -437,7 +365,7 @@ public class StorePinkServiceImpl extends ServiceImpl pinkList = mapper.selectList(lqw); - // 将拼团状态提换为订单状态 + // 将拼团状态替换为订单状态 List responseList = pinkList.stream().map(pink -> { StorePinkDetailResponse response = new StorePinkDetailResponse(); BeanUtils.copyProperties(pink, response);