1.优化链接配置

2.关闭套票发放任务【支付成功回调取代】
3.优化定时解冻水票业务,每月1号即可释放本月水票
This commit is contained in:
2026-05-09 14:36:40 +08:00
parent 81a9974e64
commit c6cec21d12
13 changed files with 173 additions and 42 deletions

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -37,6 +37,13 @@ public interface GltUserTicketReleaseMapper extends BaseMapper<GltUserTicketRele
*/
List<GltUserTicketRelease> selectListRel(@Param("param") GltUserTicketReleaseParam param);
/**
* 查询当月待释放水票数据
* @param limitNum 查询数量
* @return List<User>
*/
List<GltUserTicketRelease> getThisMonthReleaseList(@Param("limitNum") Integer limitNum);
/**
* 查询待释放且到期的记录(加行锁,防止多实例重复处理)
*

View File

@@ -58,5 +58,23 @@
<select id="selectListRel" resultType="com.gxwebsoft.glt.entity.GltUserTicketRelease">
<include refid="selectSql"></include>
</select>
<select id="getThisMonthReleaseList" resultType="com.gxwebsoft.glt.entity.GltUserTicketRelease">
SELECT
id,
user_ticket_id,
user_id,
release_qty,
tenant_id
FROM
glt_user_ticket_release
WHERE
STATUS = 0
AND deleted = 0
AND release_qty > 0
AND DATE_FORMAT(release_time, '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m')
ORDER BY
release_time ASC
LIMIT #{limitNum}
</select>
</mapper>

View File

@@ -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);

View File

@@ -133,7 +133,7 @@ public class GltTicketRevokeService {
LambdaUpdateWrapper<GltUserTicketRelease> uw = new LambdaUpdateWrapper<GltUserTicketRelease>()
.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))

View File

@@ -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();
}

View File

@@ -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<GltUserTicketRelease> releaseList = releaseMapper.getThisMonthReleaseList(1000);
if(CollectionUtils.isEmpty(releaseList)){
log.info("***本轮任务无待释放水票数据***");
return;
}
//查询用户水票信息
List<Integer> userTickIdList = releaseList.stream().map(GltUserTicketRelease::getUserTicketId).distinct().collect(Collectors.toList());
LambdaQueryWrapper<GltUserTicket> userTicketLambdaQueryWrapper = new LambdaQueryWrapper<GltUserTicket>().select(GltUserTicket::getId, GltUserTicket::getAvailableQty,
GltUserTicket::getFrozenQty, GltUserTicket::getReleasedQty).in(GltUserTicket::getId, userTickIdList);
List<GltUserTicket> userTicketList = userTicketMapper.selectList(userTicketLambdaQueryWrapper);
//创建修改用户水票集合、水票释放计划记录集合
List<GltUserTicket> updateUserTicketList = new ArrayList<>();
List<GltUserTicketLog> 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);
}
}

View File

@@ -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)) {

View File

@@ -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);
}

View File

@@ -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:

View File

@@ -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:

View File

@@ -5,6 +5,8 @@ server:
spring:
profiles:
active: local
# active: dev
# active: prod
application:
name: server