From f6eb0c40d0dc666cccc572129d346b146a601cd6 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 17:44:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=92=E9=98=9F=E7=AE=A1=E7=90=86-=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=8E=92=E9=98=9F=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StoreCombinationController.java | 14 +- .../java/com/yzt/admin/model/ScheduleJob.java | 2 +- .../com/yzt/admin/model/ScheduleJobLog.java | 2 +- .../common/model/combination/StorePink.java | 6 + .../AdminGenerateQueueCodeRequest.java | 32 +++ .../yzt/service/service/StorePinkService.java | 9 + .../service/impl/StorePinkServiceImpl.java | 185 ++++++++++++++++++ 7 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 fightwine-common/src/main/java/com/yzt/common/request/AdminGenerateQueueCodeRequest.java 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 ce8b56a..c5fb26d 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 @@ -1,6 +1,7 @@ package com.yzt.admin.controller; import com.yzt.common.page.CommonPage; +import com.yzt.common.request.AdminGenerateQueueCodeRequest; import com.yzt.common.request.PageParamRequest; import com.yzt.common.request.StoreCombinationRequest; import com.yzt.common.request.StoreCombinationSearchRequest; @@ -166,7 +167,14 @@ public class StoreCombinationController { storePinkService.adminCancelQueueAndRecalculate(pinkId); return CommonResult.success(); } + + /** + * 后台管理员新增排队码 + */ + @ApiOperation(value = "后台管理员新增排队码") + @RequestMapping(value = "/admin/generate/queue", method = RequestMethod.POST) + public CommonResult adminGenerateQueueCodes(@RequestBody @Validated AdminGenerateQueueCodeRequest request) { + storePinkService.adminGenerateQueueCodes(request.getUid(), request.getProductId(), request.getCount(), request.getRemark()); + return CommonResult.success("排队码生成成功"); + } } - - - diff --git a/fightwine-admin/src/main/java/com/yzt/admin/model/ScheduleJob.java b/fightwine-admin/src/main/java/com/yzt/admin/model/ScheduleJob.java index a350ab0..f725d41 100644 --- a/fightwine-admin/src/main/java/com/yzt/admin/model/ScheduleJob.java +++ b/fightwine-admin/src/main/java/com/yzt/admin/model/ScheduleJob.java @@ -21,7 +21,7 @@ import java.util.Date; @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) -@TableName("fw_schedule_job") +@TableName("zp_schedule_job") @ApiModel(value="ScheduleJob对象", description="定时任务") public class ScheduleJob implements Serializable { diff --git a/fightwine-admin/src/main/java/com/yzt/admin/model/ScheduleJobLog.java b/fightwine-admin/src/main/java/com/yzt/admin/model/ScheduleJobLog.java index 2bd0a71..58cc3d5 100644 --- a/fightwine-admin/src/main/java/com/yzt/admin/model/ScheduleJobLog.java +++ b/fightwine-admin/src/main/java/com/yzt/admin/model/ScheduleJobLog.java @@ -21,7 +21,7 @@ import java.util.Date; @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) -@TableName("fw_schedule_job_log") +@TableName("zp_schedule_job_log") @ApiModel(value="ScheduleJobLog对象", description="定时任务日志") public class ScheduleJobLog implements Serializable { diff --git a/fightwine-common/src/main/java/com/yzt/common/model/combination/StorePink.java b/fightwine-common/src/main/java/com/yzt/common/model/combination/StorePink.java index 633a9ec..d1b92f2 100644 --- a/fightwine-common/src/main/java/com/yzt/common/model/combination/StorePink.java +++ b/fightwine-common/src/main/java/com/yzt/common/model/combination/StorePink.java @@ -95,4 +95,10 @@ public class StorePink implements Serializable { @ApiModelProperty(value = "排队码编号(年月日时分秒+六位随机数)") private String queueCode; + @ApiModelProperty(value = "操作的管理员ID") + private Integer adminId; + + @ApiModelProperty(value = "备注") + private String remark; + } diff --git a/fightwine-common/src/main/java/com/yzt/common/request/AdminGenerateQueueCodeRequest.java b/fightwine-common/src/main/java/com/yzt/common/request/AdminGenerateQueueCodeRequest.java new file mode 100644 index 0000000..7ecf0f6 --- /dev/null +++ b/fightwine-common/src/main/java/com/yzt/common/request/AdminGenerateQueueCodeRequest.java @@ -0,0 +1,32 @@ +package com.yzt.common.request; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * 后台管理员新增排队码请求参数 + */ +@Data +@ApiModel(value = "AdminGenerateQueueCodeRequest", description = "后台管理员新增排队码请求参数") +public class AdminGenerateQueueCodeRequest { + + @ApiModelProperty(value = "用户uid", required = true) + @NotNull(message = "用户uid不能为空") + private Integer uid; + + @ApiModelProperty(value = "商品product_id", required = true) + @NotNull(message = "商品product_id不能为空") + private Integer productId; + + @ApiModelProperty(value = "生成码数量", required = true) + @NotNull(message = "生成码数量不能为空") + @Min(value = 1, message = "生成码数量至少为1") + private Integer count; + + @ApiModelProperty(value = "备注") + private String remark; +} 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 c1028b7..b16dd5f 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 @@ -109,4 +109,13 @@ public interface StorePinkService extends IService { * @param pinkId 拼团(排队)记录ID */ void adminCancelQueueAndRecalculate(Integer pinkId); + + /** + * 后台管理员新增排队码 + * @param uid 用户ID + * @param productId 商品ID + * @param count 生成码数量 + * @param remark 备注 + */ + void adminGenerateQueueCodes(Integer uid, Integer productId, Integer count, String remark); } 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 46d3657..303fdc5 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 @@ -16,6 +16,7 @@ import com.yzt.common.exception.YztException; import com.yzt.common.model.combination.StoreCombination; import com.yzt.common.model.combination.StorePink; import com.yzt.common.model.order.StoreOrder; +import com.yzt.common.model.product.StoreProductAttr; import com.yzt.common.model.system.SystemNotification; import com.yzt.common.model.user.User; import com.yzt.common.model.user.UserBrokerageRecord; @@ -88,6 +89,9 @@ public class StorePinkServiceImpl extends ServiceImpl attrWrapper = new LambdaQueryWrapper<>(); + attrWrapper.eq(StoreProductAttr::getProductId, productId); + attrWrapper.eq(StoreProductAttr::getIsDel, false); + List attrList = storeProductAttrService.list(attrWrapper); + + if (CollUtil.isEmpty(attrList)) { + throw new YztException("商品属性不存在"); + } + + // 获取第一个属性的attrValues + String attrValues = attrList.get(0).getAttrValues(); + if (StrUtil.isBlank(attrValues)) { + throw new YztException("商品属性值为空"); + } + + //判断队列类型 + Integer queueType; + String redisKey; + if ("1".equals(attrValues)) { + queueType = 1; + redisKey = "queue:counter:member"; + } else if ("2".equals(attrValues)) { + queueType = 2; + redisKey = "queue:counter:hall"; + } else { + throw new YztException("商品属性值异常:" + attrValues + ",仅支持1(黔行)或2(茅香)"); + } + + List queueList = new ArrayList<>(); + long currentTimeSeconds = System.currentTimeMillis() / 1000; + String orderId = String.valueOf(currentTimeSeconds); + + for (int i = 0; i < count; i++) { + // 使用Redis递增获取排队号 + long currentQueueNo = redisUtil.incr(redisKey, 1); + + StorePink queueItem = new StorePink(); + + // 用户信息 + queueItem.setUid(user.getUid()); + queueItem.setNickname(user.getNickname()); + queueItem.setAvatar(user.getAvatar()); + + queueItem.setOrderId(orderId); + queueItem.setOrderIdKey(0); + + queueItem.setPid(productId); + queueItem.setCid(productId); + queueItem.setTotalNum(count); + queueItem.setPrice(BigDecimal.ZERO); + queueItem.setTotalPrice(BigDecimal.ZERO); + + queueItem.setPeople(1); + queueItem.setKId(0); + queueItem.setStatus(1); + queueItem.setAddTime(currentTimeSeconds); + queueItem.setStopTime(0L); + + queueItem.setIsTpl(false); + queueItem.setIsRefund(false); + queueItem.setIs_virtual(false); + + queueItem.setQueueType(queueType); + queueItem.setQueueNo((int) currentQueueNo); + queueItem.setQueueProgress(BigDecimal.ZERO); // 初始为0,后续重算 + + // 生成排队码编号:年月日时分秒+六位随机数 + String queueCode = generateQueueCode(currentTimeSeconds); + queueItem.setQueueCode(queueCode); + + // 设置备注 + queueItem.setRemark(remark); + + try { + Integer adminId = com.yzt.common.utils.SecurityUtil.getCurrentUserId(); + queueItem.setAdminId(adminId); + } catch (Exception e) { + // 如果获取不到管理员ID,不影响主流程,只记录日志 + logger.warn("无法获取当前管理员ID: {}", e.getMessage()); + } + + queueList.add(queueItem); + } + + if (!queueList.isEmpty()) { + this.saveBatch(queueList); + logger.info("管理员生成排队码成功: uid={}, productId={}, count={}, queueType={}, remark={}", + uid, productId, count, queueType, remark); + } + + //重新计算队列进度(查询该队列中所有is_refund=0且status=1的记录) + LambdaQueryWrapper waitingWrapper = new LambdaQueryWrapper<>(); + waitingWrapper.eq(StorePink::getQueueType, queueType) + .eq(StorePink::getStatus, 1) + .eq(StorePink::getIsRefund, false) + .orderByAsc(StorePink::getQueueNo); + List waitingList = this.list(waitingWrapper); + + if (CollUtil.isEmpty(waitingList)) { + return; + } + + // 使用Redis中的计数器值作为Y(队列当前排到的位置) + long redisVal = redisUtil.get(redisKey) == null ? 0 : Long.parseLong(redisUtil.get(redisKey).toString()); + int currentY = (int) redisVal; + if (currentY <= 0) { + currentY = waitingList.size(); + } + + // 重新计算进度:公式 (Y-X) / (5X) + List updates = new ArrayList<>(); + for (StorePink xUser : waitingList) { + Integer xNo = xUser.getQueueNo(); + if (xNo == null || xNo <= 0) { + 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) { + // (Y-X) >= (5X),即 progress >= 1 + xUser.setQueueProgress(BigDecimal.ONE); + xUser.setStatus(2); + } else { + // 计算进度值,保留6位小数 + BigDecimal progress = numerator.divide(denominator, 6, BigDecimal.ROUND_DOWN); + xUser.setQueueProgress(progress); + } + updates.add(xUser); + } + if (CollUtil.isNotEmpty(updates)) { + this.updateBatchById(updates); + logger.info("重新计算队列进度完成: queueType={}, 更新记录数={}", queueType, updates.size()); + } + } + + /** + * 生成排队码编号:年月日时分秒+六位随机数 + * @param addTime Unix时间戳(秒) + * @return 排队码编号 + */ + private String generateQueueCode(Long addTime) { + // 将Unix时间戳转换为日期时间格式 + cn.hutool.core.date.DateTime dateTime = cn.hutool.core.date.DateUtil.date(addTime * 1000); + String timeStr = cn.hutool.core.date.DateUtil.format(dateTime, "yyMMddHHmmss"); + + // 生成六位随机数 + int randomNum = (int)(Math.random() * 1000000); + String randomStr = String.format("%06d", randomNum); + + return timeStr + randomStr; + } }