feat(ticket): 优化套票发放服务支持多商品并实现起始送水自动核销
- 增加对多个商品/模板的支持,修改issueTodayOrders方法接受商品ID列表 - 实现起始送水自动核销功能,按模板配置自动消耗初始水票数量 - 添加起始送水订单生成功能,用于配送端跟踪和后台管理 - 新增GltTicketOrder相关服务和地址快照构建逻辑 - 修改用户水票统计方法,从统计总数量改为统计可用数量 - 增加根据商品ID查询水票模板的API接口 - 优化订单状态更新逻辑,移除发货状态设置 - 添加数据安全检查,防止空值导致的异常处理
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.gxwebsoft.glt.controller;
|
package com.gxwebsoft.glt.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import com.gxwebsoft.glt.service.GltTicketTemplateService;
|
import com.gxwebsoft.glt.service.GltTicketTemplateService;
|
||||||
@@ -54,7 +55,23 @@ public class GltTicketTemplateController extends BaseController {
|
|||||||
return success(gltTicketTemplateService.getByIdRel(id));
|
return success(gltTicketTemplateService.getByIdRel(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('glt:gltTicketTemplate:save')")
|
@Operation(summary = "根据商品ID查询水票")
|
||||||
|
@GetMapping("/getByGoodsId/{goodsId}")
|
||||||
|
public ApiResult<GltTicketTemplate> getByGoodsId(@PathVariable("goodsId") Integer goodsId) {
|
||||||
|
GltTicketTemplate template = gltTicketTemplateService.getOne(
|
||||||
|
new LambdaQueryWrapper<GltTicketTemplate>()
|
||||||
|
.eq(GltTicketTemplate::getGoodsId, goodsId)
|
||||||
|
.eq(GltTicketTemplate::getDeleted, 0)
|
||||||
|
.orderByAsc(GltTicketTemplate::getSortNumber)
|
||||||
|
.orderByDesc(GltTicketTemplate::getCreateTime)
|
||||||
|
.last("limit 1")
|
||||||
|
);
|
||||||
|
return success(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('glt:gltTicketTemplate:update')")
|
||||||
@OperationLog
|
@OperationLog
|
||||||
@Operation(summary = "添加水票")
|
@Operation(summary = "添加水票")
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
@@ -92,7 +109,7 @@ public class GltTicketTemplateController extends BaseController {
|
|||||||
return fail("删除失败");
|
return fail("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('glt:gltTicketTemplate:save')")
|
@PreAuthorize("hasAuthority('glt:gltTicketTemplate:update')")
|
||||||
@OperationLog
|
@OperationLog
|
||||||
@Operation(summary = "批量添加水票")
|
@Operation(summary = "批量添加水票")
|
||||||
@PostMapping("/batch")
|
@PostMapping("/batch")
|
||||||
|
|||||||
@@ -38,17 +38,23 @@ public class GltUserTicketController extends BaseController {
|
|||||||
return success(gltUserTicketService.pageRel(param));
|
return success(gltUserTicketService.pageRel(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "我的水票总数")
|
@Operation(summary = "可用水票总数")
|
||||||
@GetMapping("/my-total")
|
@GetMapping("/my-total")
|
||||||
public ApiResult<?> myTotal() {
|
public ApiResult<?> myTotal() {
|
||||||
Integer userId = getLoginUserId();
|
Integer userId = getLoginUserId();
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
return fail("未登录");
|
return fail("未登录");
|
||||||
}
|
}
|
||||||
Integer totalQty = gltUserTicketService.sumTotalQtyByUserId(userId);
|
Integer tenantId = getTenantId();
|
||||||
|
if (tenantId == null) {
|
||||||
|
return fail("租户信息缺失");
|
||||||
|
}
|
||||||
|
Integer availableQty = gltUserTicketService.sumAvailableQtyByUserId(userId, tenantId);
|
||||||
Map<String, Object> data = new HashMap<>();
|
Map<String, Object> data = new HashMap<>();
|
||||||
data.put("userId", userId);
|
data.put("userId", userId);
|
||||||
data.put("totalQty", totalQty);
|
// 兼容旧字段:totalQty 表示“可用水票总数”
|
||||||
|
data.put("totalQty", availableQty);
|
||||||
|
data.put("availableQty", availableQty);
|
||||||
return success(data);
|
return success(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,6 @@ public class GltUserTicketLogController extends BaseController {
|
|||||||
return success(gltUserTicketLogService.getByIdRel(id));
|
return success(gltUserTicketLogService.getByIdRel(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('glt:gltUserTicketLog:save')")
|
|
||||||
@OperationLog
|
|
||||||
@Operation(summary = "添加消费日志")
|
@Operation(summary = "添加消费日志")
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
public ApiResult<?> save(@RequestBody GltUserTicketLog gltUserTicketLog) {
|
public ApiResult<?> save(@RequestBody GltUserTicketLog gltUserTicketLog) {
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ public class GltUserTicket implements Serializable {
|
|||||||
@Schema(description = "订单商品ID")
|
@Schema(description = "订单商品ID")
|
||||||
private Integer orderGoodsId;
|
private Integer orderGoodsId;
|
||||||
|
|
||||||
|
@Schema(description = "订单商品数量")
|
||||||
|
private Integer orderGoodsQty;
|
||||||
|
|
||||||
@Schema(description = "总数量")
|
@Schema(description = "总数量")
|
||||||
private Integer totalQty;
|
private Integer totalQty;
|
||||||
|
|
||||||
|
|||||||
@@ -2,25 +2,32 @@ package com.gxwebsoft.glt.service;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
||||||
import com.gxwebsoft.glt.entity.GltTicketTemplate;
|
import com.gxwebsoft.glt.entity.GltTicketTemplate;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicket;
|
import com.gxwebsoft.glt.entity.GltUserTicket;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicketLog;
|
import com.gxwebsoft.glt.entity.GltUserTicketLog;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
|
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrderGoods;
|
import com.gxwebsoft.shop.entity.ShopOrderGoods;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopUserAddress;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderService;
|
import com.gxwebsoft.shop.service.ShopOrderService;
|
||||||
|
import com.gxwebsoft.shop.service.ShopUserAddressService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 套票发放(从订单生成用户套票 + 释放计划)的业务逻辑。
|
* 套票发放(从订单生成用户套票 + 释放计划)的业务逻辑。
|
||||||
@@ -36,6 +43,8 @@ import java.util.Objects;
|
|||||||
public class GltTicketIssueService {
|
public class GltTicketIssueService {
|
||||||
|
|
||||||
public static final int CHANGE_TYPE_ISSUE = 10;
|
public static final int CHANGE_TYPE_ISSUE = 10;
|
||||||
|
/** 变更类型:起始送水自动核销(按模板 startSendQty 在发放时自动消耗) */
|
||||||
|
public static final int CHANGE_TYPE_START_SEND_WRITE_OFF = 12;
|
||||||
|
|
||||||
private enum IssueOutcome {
|
private enum IssueOutcome {
|
||||||
ISSUED,
|
ISSUED,
|
||||||
@@ -51,19 +60,43 @@ public class GltTicketIssueService {
|
|||||||
private final GltUserTicketService gltUserTicketService;
|
private final GltUserTicketService gltUserTicketService;
|
||||||
private final GltUserTicketReleaseService gltUserTicketReleaseService;
|
private final GltUserTicketReleaseService gltUserTicketReleaseService;
|
||||||
private final GltUserTicketLogService gltUserTicketLogService;
|
private final GltUserTicketLogService gltUserTicketLogService;
|
||||||
|
private final GltTicketOrderService gltTicketOrderService;
|
||||||
|
private final ShopUserAddressService shopUserAddressService;
|
||||||
private final TransactionTemplate transactionTemplate;
|
private final TransactionTemplate transactionTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扫描“今日订单”,执行套票发放。
|
* 扫描“今日订单”,执行套票发放。
|
||||||
*/
|
*/
|
||||||
public void issueTodayOrders(Integer tenantId, Integer formId) {
|
public void issueTodayOrders(Integer tenantId, Integer formId) {
|
||||||
|
if (formId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
issueTodayOrders(tenantId, List.of(formId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫描“今日订单”,执行套票发放(支持多个商品/模板)。
|
||||||
|
*/
|
||||||
|
public void issueTodayOrders(Integer tenantId, List<Integer> goodsIds) {
|
||||||
|
if (tenantId == null || goodsIds == null || goodsIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Integer> uniqueGoodsIds = goodsIds.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
if (uniqueGoodsIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Integer> goodsIdSet = new HashSet<>(uniqueGoodsIds);
|
||||||
|
|
||||||
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
|
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
|
||||||
LocalDateTime tomorrowStart = todayStart.plusDays(1);
|
LocalDateTime tomorrowStart = todayStart.plusDays(1);
|
||||||
|
|
||||||
List<ShopOrder> orders = shopOrderService.list(
|
List<ShopOrder> orders = shopOrderService.list(
|
||||||
new LambdaQueryWrapper<ShopOrder>()
|
new LambdaQueryWrapper<ShopOrder>()
|
||||||
.eq(ShopOrder::getTenantId, tenantId)
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
.eq(ShopOrder::getFormId, formId)
|
.in(ShopOrder::getFormId, uniqueGoodsIds)
|
||||||
.eq(ShopOrder::getPayStatus, true)
|
.eq(ShopOrder::getPayStatus, true)
|
||||||
.eq(ShopOrder::getOrderStatus, 0)
|
.eq(ShopOrder::getOrderStatus, 0)
|
||||||
// 今日订单(兼容:以 create_time 或 pay_time 任一落在今日即可)
|
// 今日订单(兼容:以 create_time 或 pay_time 任一落在今日即可)
|
||||||
@@ -77,7 +110,7 @@ public class GltTicketIssueService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (orders.isEmpty()) {
|
if (orders.isEmpty()) {
|
||||||
log.debug("套票发放扫描:今日无符合条件的订单 tenantId={}, formId={}", tenantId, formId);
|
log.debug("套票发放扫描:今日无符合条件的订单 tenantId={}, goodsIds={}", tenantId, uniqueGoodsIds);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +120,7 @@ public class GltTicketIssueService {
|
|||||||
|
|
||||||
for (ShopOrder order : orders) {
|
for (ShopOrder order : orders) {
|
||||||
try {
|
try {
|
||||||
int issuedCount = issueForOrder(tenantId, formId, order);
|
int issuedCount = issueForOrder(tenantId, goodsIdSet, order);
|
||||||
if (issuedCount > 0) {
|
if (issuedCount > 0) {
|
||||||
success += issuedCount;
|
success += issuedCount;
|
||||||
} else {
|
} else {
|
||||||
@@ -100,11 +133,11 @@ public class GltTicketIssueService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("套票发放扫描完成 - tenantId={}, formId={}, 订单数={}, 发放成功={}, 跳过={}, 失败={}",
|
log.info("套票发放扫描完成 - tenantId={}, goodsIds={}, 订单数={}, 发放成功={}, 跳过={}, 失败={}",
|
||||||
tenantId, formId, orders.size(), success, skipped, failed);
|
tenantId, uniqueGoodsIds, orders.size(), success, skipped, failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int issueForOrder(Integer tenantId, Integer formId, ShopOrder order) {
|
private int issueForOrder(Integer tenantId, Set<Integer> goodsIds, ShopOrder order) {
|
||||||
List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
|
List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
|
||||||
if (goodsList == null || goodsList.isEmpty()) {
|
if (goodsList == null || goodsList.isEmpty()) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -114,7 +147,7 @@ public class GltTicketIssueService {
|
|||||||
boolean shouldCompleteOrder = false;
|
boolean shouldCompleteOrder = false;
|
||||||
|
|
||||||
for (ShopOrderGoods og : goodsList) {
|
for (ShopOrderGoods og : goodsList) {
|
||||||
if (!Objects.equals(og.getGoodsId(), formId)) {
|
if (og.getGoodsId() == null || !goodsIds.contains(og.getGoodsId())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,9 +168,6 @@ public class GltTicketIssueService {
|
|||||||
new LambdaUpdateWrapper<ShopOrder>()
|
new LambdaUpdateWrapper<ShopOrder>()
|
||||||
.eq(ShopOrder::getOrderId, order.getOrderId())
|
.eq(ShopOrder::getOrderId, order.getOrderId())
|
||||||
.eq(ShopOrder::getTenantId, tenantId)
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
.set(ShopOrder::getOrderStatus, 1)
|
|
||||||
// 同步更新发货状态为“已发货”
|
|
||||||
.set(ShopOrder::getDeliveryStatus, 20)
|
|
||||||
.set(ShopOrder::getHasTakeGift, true)
|
.set(ShopOrder::getHasTakeGift, true)
|
||||||
.set(ShopOrder::getUpdateTime, now)
|
.set(ShopOrder::getUpdateTime, now)
|
||||||
);
|
);
|
||||||
@@ -231,6 +261,7 @@ public class GltTicketIssueService {
|
|||||||
userTicket.setUsedQty(0);
|
userTicket.setUsedQty(0);
|
||||||
// 初始可用量来自“购买量”,视为已释放
|
// 初始可用量来自“购买量”,视为已释放
|
||||||
userTicket.setReleasedQty(buyQty);
|
userTicket.setReleasedQty(buyQty);
|
||||||
|
userTicket.setOrderGoodsQty(og.getTotalNum());
|
||||||
userTicket.setUserId(order.getUserId());
|
userTicket.setUserId(order.getUserId());
|
||||||
userTicket.setSortNumber(0);
|
userTicket.setSortNumber(0);
|
||||||
userTicket.setComments("订单发放套票");
|
userTicket.setComments("订单发放套票");
|
||||||
@@ -274,6 +305,51 @@ public class GltTicketIssueService {
|
|||||||
issueLog.setUpdateTime(now);
|
issueLog.setUpdateTime(now);
|
||||||
gltUserTicketLogService.save(issueLog);
|
gltUserTicketLogService.save(issueLog);
|
||||||
|
|
||||||
|
// 按模板配置:自动“使用掉第一次水票”(起始送水数量)
|
||||||
|
Integer startSendQtyObj = template.getStartSendQty();
|
||||||
|
int startSendQty = startSendQtyObj != null ? startSendQtyObj : 0;
|
||||||
|
if (startSendQty > 0) {
|
||||||
|
int availableBefore = userTicket.getAvailableQty() != null ? userTicket.getAvailableQty() : 0;
|
||||||
|
int usedBefore = userTicket.getUsedQty() != null ? userTicket.getUsedQty() : 0;
|
||||||
|
int toUse = Math.min(startSendQty, availableBefore);
|
||||||
|
if (toUse > 0) {
|
||||||
|
userTicket.setAvailableQty(availableBefore - toUse);
|
||||||
|
userTicket.setUsedQty(usedBefore + toUse);
|
||||||
|
userTicket.setUpdateTime(now);
|
||||||
|
if (!gltUserTicketService.updateById(userTicket)) {
|
||||||
|
throw new IllegalStateException("起始送水自动核销:更新用户水票失败 userTicketId=" + userTicket.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 起始送水:自动核销成功后,生成一条送水订单(用于配送端/后台跟踪)
|
||||||
|
GltTicketOrder ticketOrder = buildStartSendTicketOrder(tenantId, order, userTicket, toUse, now);
|
||||||
|
if (!gltTicketOrderService.save(ticketOrder)) {
|
||||||
|
throw new IllegalStateException("起始送水自动核销:创建送水订单失败 userTicketId=" + userTicket.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
GltUserTicketLog writeOffLog = new GltUserTicketLog();
|
||||||
|
writeOffLog.setUserTicketId(userTicket.getId());
|
||||||
|
writeOffLog.setChangeType(CHANGE_TYPE_START_SEND_WRITE_OFF);
|
||||||
|
writeOffLog.setChangeAvailable(-toUse);
|
||||||
|
writeOffLog.setChangeFrozen(0);
|
||||||
|
writeOffLog.setChangeUsed(toUse);
|
||||||
|
writeOffLog.setAvailableAfter(userTicket.getAvailableQty());
|
||||||
|
writeOffLog.setFrozenAfter(userTicket.getFrozenQty() != null ? userTicket.getFrozenQty() : 0);
|
||||||
|
writeOffLog.setUsedAfter(userTicket.getUsedQty());
|
||||||
|
// 关联送水订单(保持与“用户下单核销”的日志一致),并在备注里保留来源商城订单号便于追溯
|
||||||
|
writeOffLog.setOrderId(ticketOrder.getId());
|
||||||
|
writeOffLog.setOrderNo(ticketOrder.getId() == null ? null : String.valueOf(ticketOrder.getId()));
|
||||||
|
writeOffLog.setUserId(order.getUserId());
|
||||||
|
writeOffLog.setSortNumber(0);
|
||||||
|
writeOffLog.setComments("起始送水自动核销(来源商城订单:" + safe(order.getOrderNo()) + ")");
|
||||||
|
writeOffLog.setStatus(0);
|
||||||
|
writeOffLog.setDeleted(0);
|
||||||
|
writeOffLog.setTenantId(tenantId);
|
||||||
|
writeOffLog.setCreateTime(now);
|
||||||
|
writeOffLog.setUpdateTime(now);
|
||||||
|
gltUserTicketLogService.save(writeOffLog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.info("套票发放成功 - tenantId={}, orderNo={}, orderGoodsId={}, templateId={}, userTicketId={}, totalQty={}",
|
log.info("套票发放成功 - tenantId={}, orderNo={}, orderGoodsId={}, templateId={}, userTicketId={}, totalQty={}",
|
||||||
tenantId, order.getOrderNo(), og.getId(), template.getId(), userTicket.getId(), totalQty);
|
tenantId, order.getOrderNo(), og.getId(), template.getId(), userTicket.getId(), totalQty);
|
||||||
|
|
||||||
@@ -359,4 +435,78 @@ public class GltTicketIssueService {
|
|||||||
LocalDate adjusted = nextMonth.withDayOfMonth(day);
|
LocalDate adjusted = nextMonth.withDayOfMonth(day);
|
||||||
return LocalDateTime.of(adjusted, time);
|
return LocalDateTime.of(adjusted, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
private GltTicketOrder buildStartSendTicketOrder(Integer tenantId,
|
||||||
|
ShopOrder shopOrder,
|
||||||
|
GltUserTicket userTicket,
|
||||||
|
int totalNum,
|
||||||
|
LocalDateTime now) {
|
||||||
|
GltTicketOrder o = new GltTicketOrder();
|
||||||
|
o.setUserTicketId(userTicket.getId());
|
||||||
|
o.setUserId(shopOrder.getUserId());
|
||||||
|
o.setStoreId(shopOrder.getStoreId());
|
||||||
|
o.setWarehouseId(shopOrder.getWarehouseId());
|
||||||
|
o.setRiderId(shopOrder.getRiderId());
|
||||||
|
o.setTotalNum(totalNum);
|
||||||
|
o.setPrice(BigDecimal.ZERO);
|
||||||
|
o.setBuyerRemarks(shopOrder.getBuyerRemarks());
|
||||||
|
|
||||||
|
// 地址快照:优先使用地址表快照;兜底使用商城订单上的 address 字段
|
||||||
|
Integer addressId = shopOrder.getAddressId();
|
||||||
|
o.setAddressId(addressId);
|
||||||
|
String addressSnapshot = null;
|
||||||
|
if (addressId != null) {
|
||||||
|
ShopUserAddress addr = shopUserAddressService.getOne(new LambdaQueryWrapper<ShopUserAddress>()
|
||||||
|
.eq(ShopUserAddress::getId, addressId)
|
||||||
|
.eq(ShopUserAddress::getUserId, shopOrder.getUserId())
|
||||||
|
.eq(ShopUserAddress::getTenantId, tenantId)
|
||||||
|
.last("limit 1"));
|
||||||
|
addressSnapshot = buildAddressSnapshot(addr);
|
||||||
|
}
|
||||||
|
if (addressSnapshot == null || addressSnapshot.isBlank()) {
|
||||||
|
addressSnapshot = shopOrder.getAddress();
|
||||||
|
}
|
||||||
|
o.setAddress(addressSnapshot);
|
||||||
|
|
||||||
|
String preferredSendTime = shopOrder.getSendStartTime();
|
||||||
|
if (preferredSendTime == null || preferredSendTime.isBlank()) {
|
||||||
|
preferredSendTime = now.format(DATETIME_FMT);
|
||||||
|
}
|
||||||
|
o.setSendTime(preferredSendTime);
|
||||||
|
o.setDeliveryStatus(GltTicketOrderService.DELIVERY_STATUS_WAITING);
|
||||||
|
o.setSortNumber(0);
|
||||||
|
o.setComments("起始送水自动下单(来源商城订单:" + safe(shopOrder.getOrderNo()) + ")");
|
||||||
|
o.setStatus(0);
|
||||||
|
o.setDeleted(0);
|
||||||
|
o.setTenantId(tenantId);
|
||||||
|
o.setCreateTime(now);
|
||||||
|
o.setUpdateTime(now);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildAddressSnapshot(ShopUserAddress addr) {
|
||||||
|
if (addr == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (addr.getFullAddress() != null && !addr.getFullAddress().isBlank()) {
|
||||||
|
return addr.getFullAddress();
|
||||||
|
}
|
||||||
|
// 兼容旧数据:fullAddress 为空时,拼接省市区 + 详细地址
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (addr.getProvince() != null) sb.append(addr.getProvince());
|
||||||
|
if (addr.getCity() != null) sb.append(addr.getCity());
|
||||||
|
if (addr.getRegion() != null) sb.append(addr.getRegion());
|
||||||
|
if (addr.getAddress() != null) sb.append(addr.getAddress());
|
||||||
|
String s = sb.toString();
|
||||||
|
if (!s.isBlank()) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return addr.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String safe(String s) {
|
||||||
|
return s == null ? "" : s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,11 +40,12 @@ public interface GltUserTicketService extends IService<GltUserTicket> {
|
|||||||
GltUserTicket getByIdRel(Integer id);
|
GltUserTicket getByIdRel(Integer id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统计指定用户水票总数量(sum(total_qty))
|
* 统计指定用户可用水票总数(sum(available_qty))
|
||||||
*
|
*
|
||||||
* @param userId 用户ID
|
* @param userId 用户ID
|
||||||
* @return 总数量(无记录返回0)
|
* @param tenantId 租户ID
|
||||||
|
* @return 可用总数(无记录返回0)
|
||||||
*/
|
*/
|
||||||
Integer sumTotalQtyByUserId(Integer userId);
|
Integer sumAvailableQtyByUserId(Integer userId, Integer tenantId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -790,7 +790,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
|
|
||||||
// 幂等:同一送水订单同一配送员只结算一次
|
// 幂等:同一送水订单同一配送员只结算一次
|
||||||
boolean already = shopDealerCapitalService.count(
|
boolean already = shopDealerCapitalService.count(
|
||||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<ShopDealerCapital>()
|
new LambdaQueryWrapper<ShopDealerCapital>()
|
||||||
.eq(ShopDealerCapital::getTenantId, tenantId)
|
.eq(ShopDealerCapital::getTenantId, tenantId)
|
||||||
.eq(ShopDealerCapital::getFlowType, 10)
|
.eq(ShopDealerCapital::getFlowType, 10)
|
||||||
.eq(ShopDealerCapital::getUserId, riderId)
|
.eq(ShopDealerCapital::getUserId, riderId)
|
||||||
@@ -804,7 +804,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
// 送水订单提成:先入冻结金额 freeze_money(与分销订单佣金一致)
|
// 送水订单提成:先入冻结金额 freeze_money(与分销订单佣金一致)
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
boolean updated = shopDealerUserService.update(
|
boolean updated = shopDealerUserService.update(
|
||||||
new com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<ShopDealerUser>()
|
new LambdaUpdateWrapper<ShopDealerUser>()
|
||||||
.eq(ShopDealerUser::getTenantId, tenantId)
|
.eq(ShopDealerUser::getTenantId, tenantId)
|
||||||
.eq(ShopDealerUser::getUserId, riderId)
|
.eq(ShopDealerUser::getUserId, riderId)
|
||||||
.setSql("freeze_money = IFNULL(freeze_money,0) + " + money.toPlainString())
|
.setSql("freeze_money = IFNULL(freeze_money,0) + " + money.toPlainString())
|
||||||
@@ -815,7 +815,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
if (!updated) {
|
if (!updated) {
|
||||||
// 配送员可能未开通分销账户:创建后再尝试入账一次(与分销结算逻辑保持一致)
|
// 配送员可能未开通分销账户:创建后再尝试入账一次(与分销结算逻辑保持一致)
|
||||||
ShopDealerUser existed = shopDealerUserService.getOne(
|
ShopDealerUser existed = shopDealerUserService.getOne(
|
||||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<ShopDealerUser>()
|
new LambdaQueryWrapper<ShopDealerUser>()
|
||||||
.eq(ShopDealerUser::getTenantId, tenantId)
|
.eq(ShopDealerUser::getTenantId, tenantId)
|
||||||
.eq(ShopDealerUser::getUserId, riderId)
|
.eq(ShopDealerUser::getUserId, riderId)
|
||||||
.last("limit 1")
|
.last("limit 1")
|
||||||
@@ -848,7 +848,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
}
|
}
|
||||||
|
|
||||||
updated = shopDealerUserService.update(
|
updated = shopDealerUserService.update(
|
||||||
new com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<ShopDealerUser>()
|
new LambdaUpdateWrapper<ShopDealerUser>()
|
||||||
.eq(ShopDealerUser::getTenantId, tenantId)
|
.eq(ShopDealerUser::getTenantId, tenantId)
|
||||||
.eq(ShopDealerUser::getUserId, riderId)
|
.eq(ShopDealerUser::getUserId, riderId)
|
||||||
.setSql("freeze_money = IFNULL(freeze_money,0) + " + money.toPlainString())
|
.setSql("freeze_money = IFNULL(freeze_money,0) + " + money.toPlainString())
|
||||||
|
|||||||
Reference in New Issue
Block a user