diff --git a/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketReleaseController.java b/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketReleaseController.java index 44198ba..eae605d 100644 --- a/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketReleaseController.java +++ b/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketReleaseController.java @@ -1,15 +1,15 @@ package com.gxwebsoft.glt.controller; +import com.gxwebsoft.common.core.annotation.OperationLog; +import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.common.core.web.BatchParam; +import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.system.entity.User; -import com.gxwebsoft.glt.service.GltUserTicketReleaseService; import com.gxwebsoft.glt.entity.GltUserTicketRelease; import com.gxwebsoft.glt.param.GltUserTicketReleaseParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.annotation.OperationLog; +import com.gxwebsoft.glt.service.GltUserTicketReleaseService; +import com.gxwebsoft.glt.service.impl.GltUserTicketAutoReleaseServiceImpl; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.security.access.prepost.PreAuthorize; @@ -31,6 +31,9 @@ public class GltUserTicketReleaseController extends BaseController { @Resource private GltUserTicketReleaseService gltUserTicketReleaseService; + @Resource + private GltUserTicketAutoReleaseServiceImpl gltUserTicketAutoReleaseService; + @PreAuthorize("hasAuthority('glt:gltUserTicketRelease:list')") @Operation(summary = "分页查询水票释放") @GetMapping("/page") @@ -126,4 +129,11 @@ public class GltUserTicketReleaseController extends BaseController { return fail("删除失败"); } + @Operation(summary = "水票释放测试") + @PostMapping("/releaseTask") + public ApiResult releaseTask() { + gltUserTicketAutoReleaseService.releaseTask(); + return success(true); + } + } diff --git a/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketRelease.java b/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketRelease.java index 18d6d92..0c3f0b2 100644 --- a/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketRelease.java +++ b/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketRelease.java @@ -26,7 +26,7 @@ public class GltUserTicketRelease implements Serializable { private Long id; @Schema(description = "水票ID") - private Long userTicketId; + private Integer userTicketId; @Schema(description = "用户ID") private Integer userId; @@ -56,6 +56,9 @@ public class GltUserTicketRelease implements Serializable { @Schema(description = "状态") private Integer status; + @Schema(description = "备注") + private String remark; + @Schema(description = "是否删除, 0否, 1是") @TableLogic private Integer deleted; diff --git a/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketReleaseMapper.java b/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketReleaseMapper.java index baf5639..d32ecd8 100644 --- a/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketReleaseMapper.java +++ b/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketReleaseMapper.java @@ -37,6 +37,13 @@ public interface GltUserTicketReleaseMapper extends BaseMapper selectListRel(@Param("param") GltUserTicketReleaseParam param); + /** + * 查询当月待释放水票数据 + * @param limitNum 查询数量 + * @return List + */ + List getThisMonthReleaseList(@Param("limitNum") Integer limitNum); + /** * 查询待释放且到期的记录(加行锁,防止多实例重复处理) * diff --git a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketReleaseMapper.xml b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketReleaseMapper.xml index a8364a9..e9d774c 100644 --- a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketReleaseMapper.xml +++ b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketReleaseMapper.xml @@ -58,5 +58,23 @@ + diff --git a/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java b/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java index 985ae98..10f33ef 100644 --- a/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java +++ b/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java @@ -501,7 +501,7 @@ public class GltTicketIssueService { LocalDateTime releaseTime, LocalDateTime now) { GltUserTicketRelease r = new GltUserTicketRelease(); - r.setUserTicketId(userTicket.getId() != null ? userTicket.getId().longValue() : null); + r.setUserTicketId(userTicket.getId() != null ? userTicket.getId() : null); r.setUserId(userTicket.getUserId()); r.setPeriodNo(periodNo); r.setReleaseQty(releaseQty); diff --git a/src/main/java/com/gxwebsoft/glt/service/GltTicketRevokeService.java b/src/main/java/com/gxwebsoft/glt/service/GltTicketRevokeService.java index 196be88..af77ad2 100644 --- a/src/main/java/com/gxwebsoft/glt/service/GltTicketRevokeService.java +++ b/src/main/java/com/gxwebsoft/glt/service/GltTicketRevokeService.java @@ -133,7 +133,7 @@ public class GltTicketRevokeService { LambdaUpdateWrapper uw = new LambdaUpdateWrapper() .eq(GltUserTicketRelease::getTenantId, tenantId) .eq(GltUserTicketRelease::getDeleted, 0) - .eq(GltUserTicketRelease::getUserTicketId, userTicketId.longValue()) + .eq(GltUserTicketRelease::getUserTicketId, userTicketId) // status 为空时也视为“未完成” .and(w -> w.ne(GltUserTicketRelease::getStatus, RELEASE_STATUS_DONE) .or().isNull(GltUserTicketRelease::getStatus)) diff --git a/src/main/java/com/gxwebsoft/glt/service/GltUserTicketAutoReleaseService.java b/src/main/java/com/gxwebsoft/glt/service/GltUserTicketAutoReleaseService.java index 83a7fb3..188709a 100644 --- a/src/main/java/com/gxwebsoft/glt/service/GltUserTicketAutoReleaseService.java +++ b/src/main/java/com/gxwebsoft/glt/service/GltUserTicketAutoReleaseService.java @@ -17,10 +17,8 @@ public interface GltUserTicketAutoReleaseService { int releaseDue(LocalDateTime now, int batchSize); /** - * 释放到期的冻结水票(使用系统当前时间) + * 释放到期的冻结水票【当次执行1000条】 */ - default int releaseDue(int batchSize) { - return releaseDue(LocalDateTime.now(), batchSize); - } + void releaseTask(); } diff --git a/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketAutoReleaseServiceImpl.java b/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketAutoReleaseServiceImpl.java index fb576e4..9a7d894 100644 --- a/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketAutoReleaseServiceImpl.java +++ b/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketAutoReleaseServiceImpl.java @@ -1,5 +1,6 @@ package com.gxwebsoft.glt.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.gxwebsoft.glt.entity.GltUserTicket; import com.gxwebsoft.glt.entity.GltUserTicketLog; import com.gxwebsoft.glt.entity.GltUserTicketRelease; @@ -7,13 +8,20 @@ import com.gxwebsoft.glt.mapper.GltUserTicketLogMapper; import com.gxwebsoft.glt.mapper.GltUserTicketMapper; import com.gxwebsoft.glt.mapper.GltUserTicketReleaseMapper; import com.gxwebsoft.glt.service.GltUserTicketAutoReleaseService; -import lombok.RequiredArgsConstructor; +import com.gxwebsoft.glt.service.GltUserTicketLogService; +import com.gxwebsoft.glt.service.GltUserTicketReleaseService; +import com.gxwebsoft.glt.service.GltUserTicketService; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; /** * 冻结水票自动释放实现: @@ -23,7 +31,7 @@ import java.util.List; */ @Slf4j @Service -@RequiredArgsConstructor +@AllArgsConstructor public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoReleaseService { /** @@ -44,6 +52,13 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel private final GltUserTicketMapper userTicketMapper; private final GltUserTicketLogMapper userTicketLogMapper; + private GltUserTicketReleaseService gltUserTicketReleaseService; + + private GltUserTicketService gltUserTicketService; + + private GltUserTicketLogService gltUserTicketLogService; + + @Override @Transactional(rollbackFor = Exception.class) public int releaseDue(LocalDateTime now, int batchSize) { @@ -71,12 +86,7 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel continue; } - long userTicketIdLong = rel.getUserTicketId(); - if (userTicketIdLong > Integer.MAX_VALUE || userTicketIdLong < 1) { - markFailed(rel.getId(), now, "userTicketId超范围"); - continue; - } - Integer userTicketId = (int) userTicketIdLong; + Integer userTicketId = rel.getUserTicketId(); // 先释放冻结数量(条件更新,确保 frozen_qty >= qty) int updated = userTicketMapper.releaseFrozenQty( @@ -129,4 +139,96 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel releaseMapper.updateStatus(releaseId, RELEASE_STATUS_FAILED, now); log.warn("冻结水票释放标记失败 - releaseId={}, reason={}", releaseId, reason); } + + @Transactional + public void releaseTask() { + LocalDateTime now = LocalDateTime.now(); + log.info("***执行水票释放任务***-执行时间:{}", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + long start = System.currentTimeMillis(); + List releaseList = releaseMapper.getThisMonthReleaseList(1000); + if(CollectionUtils.isEmpty(releaseList)){ + log.info("***本轮任务无待释放水票数据***"); + return; + } + + //查询用户水票信息 + List userTickIdList = releaseList.stream().map(GltUserTicketRelease::getUserTicketId).distinct().collect(Collectors.toList()); + LambdaQueryWrapper userTicketLambdaQueryWrapper = new LambdaQueryWrapper().select(GltUserTicket::getId, GltUserTicket::getAvailableQty, + GltUserTicket::getFrozenQty, GltUserTicket::getReleasedQty).in(GltUserTicket::getId, userTickIdList); + List userTicketList = userTicketMapper.selectList(userTicketLambdaQueryWrapper); + + //创建修改用户水票集合、水票释放计划记录集合 + List updateUserTicketList = new ArrayList<>(); + List userTicketLogList = new ArrayList<>(); + + //遍历水票释放数据执行释放任务 + releaseList.forEach(release ->{ + //1.缺少水票ID、用户ID、租户ID记录错误记录 + if(release.getUserTicketId() == null || release.getUserId() == null || release.getTenantId() == null){ + release.setRemark("缺少userTicketId/userId/tenantId"); + release.setStatus(2); + release.setUpdateTime(now); + } + + GltUserTicket userTicket = userTicketList.stream().filter(gltUserTicket -> release.getUserTicketId().equals(gltUserTicket.getId())).findFirst().orElse(null); + if(userTicket != null){ + Integer releaseQty = release.getReleaseQty(); + if(userTicket.getFrozenQty() > releaseQty){ + //更改用户水票可用数量、冻结数量、已释放数量 + userTicket.setAvailableQty(userTicket.getAvailableQty() + releaseQty); + userTicket.setFrozenQty(userTicket.getFrozenQty() - releaseQty); + userTicket.setReleasedQty(userTicket.getReleasedQty() + releaseQty); + userTicket.setUpdateTime(now); + updateUserTicketList.add(userTicket); + + //记录水票释放计划为成功释放状态 + release.setStatus(1); + release.setRemark("success"); + release.setUpdateTime(now); + + //生成水票释放记录 + GltUserTicketLog ticketLog = new GltUserTicketLog(); + ticketLog.setUserTicketId(release.getUserTicketId()); + ticketLog.setChangeType(CHANGE_TYPE_RELEASE); + ticketLog.setChangeAvailable(releaseQty); + ticketLog.setChangeFrozen(-releaseQty); + ticketLog.setChangeUsed(0); + ticketLog.setAvailableAfter(userTicket.getAvailableQty()); + ticketLog.setFrozenAfter(userTicket.getFrozenQty()); + ticketLog.setUsedAfter(userTicket.getUsedQty()); + ticketLog.setOrderId(Integer.valueOf(String.valueOf(release.getId()))); + ticketLog.setUserId(release.getUserId()); + ticketLog.setComments("冻结水票到期释放"); + ticketLog.setTenantId(release.getTenantId()); + ticketLog.setCreateTime(now); + ticketLog.setUpdateTime(now); + userTicketLogList.add(ticketLog); + }else { + release.setRemark("释放数量大于冻结数量,释放:" + releaseQty + ",冻结:" + userTicket.getFrozenQty()); + release.setStatus(2); + release.setUpdateTime(LocalDateTime.now()); + } + }else { + release.setRemark("查询不到水票数据"); + release.setStatus(2); + release.setUpdateTime(LocalDateTime.now()); + } + }); + + if(CollectionUtils.isNotEmpty(releaseList)){ + gltUserTicketReleaseService.updateBatchById(releaseList); + } + + if(CollectionUtils.isNotEmpty(updateUserTicketList)){ + gltUserTicketService.updateBatchById(updateUserTicketList); + } + + if(CollectionUtils.isNotEmpty(userTicketLogList)){ + gltUserTicketLogService.saveBatch(userTicketLogList); + } + + long end = System.currentTimeMillis(); + long costMs = end - start; + log.info("***执行水票释放任务完成*** 执行耗时:{} ms", costMs); + } } diff --git a/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java b/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java index 8d0d103..a4f8ce6 100644 --- a/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java +++ b/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java @@ -33,7 +33,7 @@ public class GltTicketIssue10584Task { private final AtomicBoolean running = new AtomicBoolean(false); - @Scheduled(cron = "${glt.ticket.issue10584.cron:0/15 * * * * ?}") +// @Scheduled(cron = "${glt.ticket.issue10584.cron:0/15 * * * * ?}") @IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤") public void run() { if (!running.compareAndSet(false, true)) { diff --git a/src/main/java/com/gxwebsoft/glt/task/GltUserTicketAutoReleaseTask.java b/src/main/java/com/gxwebsoft/glt/task/GltUserTicketAutoReleaseTask.java index 91855db..261a8c6 100644 --- a/src/main/java/com/gxwebsoft/glt/task/GltUserTicketAutoReleaseTask.java +++ b/src/main/java/com/gxwebsoft/glt/task/GltUserTicketAutoReleaseTask.java @@ -25,25 +25,17 @@ public class GltUserTicketAutoReleaseTask { private final GltUserTicketAutoReleaseService autoReleaseService; - @Value("${glt.ticket.auto-release.batch-size:200}") - private int batchSize; - private final AtomicBoolean running = new AtomicBoolean(false); - @Scheduled(cron = "${glt.ticket.auto-release.cron:0 */10 * * * ?}") + @Scheduled(cron = "${glt.ticket.auto-release.cron:0 */2 1-3 1 * ?}") @IgnoreTenant("定时任务无登录态,需忽略租户隔离;释放记录自带 tenantId,更新时会校验 tenantId") public void run() { if (!running.compareAndSet(false, true)) { log.warn("冻结水票自动释放任务仍在执行中,本轮跳过"); return; } - try { - LocalDateTime now = LocalDateTime.now(); - int released = autoReleaseService.releaseDue(now, Math.max(batchSize, 1)); - if (released > 0) { - log.info("冻结水票自动释放完成 - released={}, now={}", released, now); - } + autoReleaseService.releaseTask(); } finally { running.set(false); } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index aeccfea..00df5b5 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -13,11 +13,11 @@ spring: driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource - redis: - database: 0 - host: 1Panel-redis-GmNr - port: 6379 - password: redis_t74P8C + redis: + database: 0 + host: 127.0.0.1 + port: 6379 + password: redis_t74P8C # 日志配置 logging: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index e1e85a2..c22fd88 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -13,11 +13,10 @@ spring: driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource - redis: - database: 0 - host: 47.107.249.41 - port: 16379 - password: redis_t74P8C + redis: + database: 0 + host: localhost + port: 6379 # 日志配置 logging: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f61bd6f..910449c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -5,6 +5,8 @@ server: spring: profiles: active: local +# active: dev +# active: prod application: name: server