feat(order): 添加送水订单配送时间和完整下单流程

- 在GltTicketOrder实体中新增sendTime字段用于记录配送时间
- 移除送水订单查询接口的权限验证要求,开放查询功能
- 实现完整的下单流程:验证登录用户、扣减水票、写入核销记录、创建订单
- 新增createWithWriteOff方法处理事务性下单操作,确保数据一致性
- 添加数据库行锁机制防止并发扣减问题
- 优化水票相关接口描述,明确为可用水票总数
- 移除水票日志添加接口的权限验证和操作日志注解
This commit is contained in:
2026-02-06 00:15:20 +08:00
parent 88afd149c3
commit 48cd2e1f7b
7 changed files with 169 additions and 21 deletions

View File

@@ -1,14 +1,24 @@
package com.gxwebsoft.glt.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.glt.entity.GltTicketOrder;
import com.gxwebsoft.glt.entity.GltUserTicket;
import com.gxwebsoft.glt.entity.GltUserTicketLog;
import com.gxwebsoft.glt.mapper.GltTicketOrderMapper;
import com.gxwebsoft.glt.mapper.GltUserTicketMapper;
import com.gxwebsoft.glt.param.GltTicketOrderParam;
import com.gxwebsoft.glt.service.GltTicketOrderService;
import com.gxwebsoft.glt.service.GltUserTicketLogService;
import com.gxwebsoft.glt.service.GltUserTicketService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -20,6 +30,17 @@ import java.util.List;
@Service
public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper, GltTicketOrder> implements GltTicketOrderService {
public static final int CHANGE_TYPE_WRITE_OFF = 20;
@Resource
private GltUserTicketMapper gltUserTicketMapper;
@Resource
private GltUserTicketService gltUserTicketService;
@Resource
private GltUserTicketLogService gltUserTicketLogService;
@Override
public PageResult<GltTicketOrder> pageRel(GltTicketOrderParam param) {
PageParam<GltTicketOrder, GltTicketOrderParam> page = new PageParam<>(param);
@@ -44,4 +65,98 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
return param.getOne(baseMapper.selectListRel(param));
}
@Override
@Transactional(rollbackFor = Exception.class)
public GltTicketOrder createWithWriteOff(GltTicketOrder gltTicketOrder, Integer userId, Integer tenantId) {
if (gltTicketOrder == null) {
throw new BusinessException("订单参数不能为空");
}
if (userId == null) {
throw new BusinessException("请先登录");
}
Integer userTicketId = gltTicketOrder.getUserTicketId();
if (userTicketId == null) {
throw new BusinessException("userTicketId不能为空");
}
int totalNum = gltTicketOrder.getTotalNum() == null ? 0 : gltTicketOrder.getTotalNum();
if (totalNum <= 0) {
throw new BusinessException("totalNum必须大于0");
}
if (tenantId == null) {
throw new BusinessException("租户信息缺失");
}
// 1) 校验水票归属当前用户 + 正常状态,并锁定记录,避免并发扣减导致日志不准确
GltUserTicket userTicket = gltUserTicketMapper.selectByIdForUpdate(userTicketId, userId, tenantId);
if (userTicket == null) {
throw new BusinessException("水票不存在、已失效或无权限");
}
int availableQty = userTicket.getAvailableQty() == null ? 0 : userTicket.getAvailableQty();
int usedQty = userTicket.getUsedQty() == null ? 0 : userTicket.getUsedQty();
if (availableQty < totalNum) {
throw new BusinessException("水票数量不足");
}
// 2) 更新 glt_user_ticket: availableQty -= totalNum, usedQty += totalNum
LocalDateTime now = LocalDateTime.now();
int availableAfter = availableQty - totalNum;
int usedAfter = usedQty + totalNum;
userTicket.setAvailableQty(availableAfter);
userTicket.setUsedQty(usedAfter);
userTicket.setUpdateTime(now);
if (!gltUserTicketService.updateById(userTicket)) {
throw new BusinessException("扣减水票失败");
}
// 4) 插入 glt_ticket_orderstoreId/addressId/totalNum/buyerRemarks…
gltTicketOrder.setUserId(userId);
// 订单基础字段由后端兜底,避免前端误传/恶意传参
gltTicketOrder.setStatus(0);
gltTicketOrder.setDeleted(0);
gltTicketOrder.setTenantId(tenantId);
if (gltTicketOrder.getSortNumber() == null) {
gltTicketOrder.setSortNumber(0);
}
if (gltTicketOrder.getCreateTime() == null) {
gltTicketOrder.setCreateTime(now);
}
gltTicketOrder.setUpdateTime(now);
if (!this.save(gltTicketOrder)) {
throw new BusinessException("创建订单失败");
}
// 3) 插入 glt_user_ticket_log核销记录
GltUserTicketLog log = new GltUserTicketLog();
log.setUserTicketId(userTicketId);
log.setChangeType(CHANGE_TYPE_WRITE_OFF);
log.setChangeAvailable(-totalNum);
log.setChangeFrozen(0);
log.setChangeUsed(totalNum);
log.setAvailableAfter(availableAfter);
log.setFrozenAfter(userTicket.getFrozenQty() == null ? 0 : userTicket.getFrozenQty());
log.setUsedAfter(usedAfter);
log.setOrderId(gltTicketOrder.getId());
log.setOrderNo(gltTicketOrder.getId() == null ? null : String.valueOf(gltTicketOrder.getId()));
log.setUserId(userId);
log.setSortNumber(0);
String comments = gltTicketOrder.getComments();
if (StrUtil.isBlank(comments)) {
comments = gltTicketOrder.getBuyerRemarks();
}
if (StrUtil.isBlank(comments)) {
comments = "水票下单核销";
}
log.setComments(comments);
log.setStatus(0);
log.setDeleted(0);
log.setTenantId(tenantId);
log.setCreateTime(now);
log.setUpdateTime(now);
if (!gltUserTicketLogService.save(log)) {
throw new BusinessException("写入核销记录失败");
}
return gltTicketOrder;
}
}