feat: 优化

This commit is contained in:
2026-07-31 10:42:00 +08:00
parent 5de4d6f9f4
commit 9be904bde0
17 changed files with 744 additions and 86 deletions
@@ -1,30 +1,123 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallCallPMessagePushService;
import com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubHistory;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderService;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubService;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.math.BigDecimal;
import java.util.Date;
/**
* MallCancelSubOrderEventHandler
* orderType=3: 取消子订单事件处理器。
* orderType=3:取消子订单事件处理器。
*/
@IocBean
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallCancelSubOrderEventHandler implements MallOrderEventHandler {
private final Dao dao;
@Inject
private MallInboundEventService inboundEventService;
private PointsMallPointsLockService pointsMallPointsLockService;
@Inject
private PointsMallOrderService pointsMallOrderService;
@Inject
private PointsMallOrderSubService pointsMallOrderSubService;
@Inject
private MallCallPMessagePushService mallCallPMessagePushService;
public MallCancelSubOrderEventHandler(Dao dao) {
this.dao = dao;
}
@Override
public int getType() {
return 3;
}
/**
* 取消子订单:校验用户和子单,更新子单状态,写入子单历史,释放积分,联动主单状态并推送本地消息。
*/
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.updateSubOrderStatus(dto, MallOrderStatusEnum.CANCELLED);
// 供应商推送取消子单时必须携带子单号和用户标识。
if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderId(), dto.getUserId())) {
return MallInboundResult.fail(400, "供应商、订单、用户不能为空");
}
// userId 兼容系统用户ID和工号,后续消息推送需要使用用户信息。
Sys_user user = fetchUser(dto.getUserId());
if (user == null) {
return MallInboundResult.fail(-1, "用户不存在");
}
// 按供应商和外部子单号定位当前子订单。
PointsMallOrderSub sub = pointsMallOrderSubService.findBySubOrderExtId(dto.getOrderId(), dto.getSupplierId());
if (sub == null) {
log.info("9.2.1生成订单信息推送,订单不存在:{}", dto);
return MallInboundResult.fail(-1, "订单不存在");
}
Integer orderStatus = MallOrderStatusEnum.CANCELLED.getCode();
log.info("推送的订单类型比较:推送过来的订单状态:{},库中订单状态:{}", orderStatus, sub.getOrderState());
if (orderStatus.equals(sub.getOrderState())) {
return MallInboundResult.fail(-1, "请不要重复推送");
}
Date now = new Date();
Trans.exec((Atom) () -> {
// 只更新子单状态相关字段,避免完整对象回写影响其它订单字段。
PointsMallOrderSub modifySub = new PointsMallOrderSub();
modifySub.setId(sub.getId());
modifySub.setOrderState(orderStatus);
modifySub.setOrderUpdateTime(now);
pointsMallOrderSubService.updateSubOrderStatus(modifySub);
sub.setOrderState(orderStatus);
sub.setOrderUpdateTime(now);
// 记录子单状态变更历史,便于后续追踪供应商推送流水。
PointsMallOrderSubHistory history = BeanUtil.copyProperties(sub, PointsMallOrderSubHistory.class);
history.setId(null);
history.setOrderState(orderStatus);
dao.insert(history);
log.info("9.2.1生成订单信息推送-订单状态为 已取消 释放积分");
// 释放积分/返回用户积分
pointsMallPointsLockService.releaseOrReturnPoints(sub, orderStatus, BigDecimal.ZERO);
// 更新主订单状态,所有子单退货/取消/完成后,修改主单状态为已完成。
pointsMallOrderService.updateMainOrderStatus(sub.getMainOrderId(), sub.getSupplierId());
});
try {
mallCallPMessagePushService.pushMessageToGlobal(StrUtil.blankToDefault(user.getUsername(), user.getLoginname()), sub);
} catch (Exception e) {
log.error("9.2.1 子单取消,callP消息推送:{},{}", dto, e.getMessage());
}
log.info("9.2.1生成订单信息推送,取消子订单处理完成,supplierId={}orderId={}", dto.getSupplierId(), dto.getOrderId());
return MallInboundResult.success();
}
/**
* 入参 userId 可能是系统用户ID或工号,按当前项目用户表兼容查询。
*/
private Sys_user fetchUser(String userId) {
Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", userId).and("disabled", "=", false).and("delFlag", "=", false));
if (user != null) {
return user;
}
return dao.fetch(Sys_user.class, Cnd.where("loginname", "=", userId).and("disabled", "=", false).and("delFlag", "=", false));
}
}
@@ -1,29 +1,144 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.exception.MallBridgeError;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallOrderQueryOutboundService;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubHistory;
import com.budwk.app.zhgh.pointsmall.order.result.SubOrderBuildResult;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubHistoryService;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* MallSplitSubOrderEventHandler
* orderType=2: 拆分子订单事件处理器。
* 行为: 根据主单外部ID与子单外部ID新增子单映射(status=0)。
*/
@IocBean
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallSplitSubOrderEventHandler implements MallOrderEventHandler {
private final Dao dao;
@Inject
private MallInboundEventService inboundEventService;
private PointsMallPointsLockService pointsMallPointsLockService;
@Inject
private MallOrderQueryOutboundService mallOrderQueryOutboundService;
@Inject
private PointsMallOrderSubHistoryService pointsMallOrderSubHistoryService;
public MallSplitSubOrderEventHandler(Dao dao) {
this.dao = dao;
}
@Override
public int getType() {
return 2;
}
/**
* 第一个接口,orderType是2的时候,获取子单信息,存储到数据库
*
* @param dto 入参
* @return 成功
*/
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.saveOrderSub(dto);
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹) 开始执行:{}", dto);
// 校验 orderId 必须存在且有效
if (dto == null || StrUtil.isBlank(dto.getOrderId())) {
return MallInboundResult.fail(400, "缺少主订单信息");
}
// 分割子订单ID列表
List<String> subOrderIds = Arrays.stream(dto.getOrderId().split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.distinct()
.collect(Collectors.toList());
if (subOrderIds.isEmpty()) {
return MallInboundResult.fail(400, "缺少拆单信息");
}
// 查询主单是否存在
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", dto.getMainOrderId()).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(404, "主单不存在");
}
log.info("9.2.1生成订单信息推送-调用外部服务获取子单信息:{}", dto);
List<OrderInfoDTO> orderInfoList;
MallOrderQueryDTO queryDTO = new MallOrderQueryDTO();
queryDTO.setSupplierId(dto.getSupplierId());
queryDTO.setOrderId(dto.getOrderId());
queryDTO.setOrderType(dto.getOrderType());
try {
// 调用外部服务获取子订单信息
MallOutboundResult result = mallOrderQueryOutboundService.orderQuery(queryDTO);
if (result == null || result.getResultCode() != 0) {
throw new RuntimeException(result == null ? "获取订单详情返回为空" : result.getMessage());
}
orderInfoList = result.getOrderInfo();
} catch (Exception e) {
log.error("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹),调用9.2.2获取订单详情接口异常{}", e.getMessage());
return MallInboundResult.fail(-1, "调用9.2.2获取订单详情接口异常,异常订单:" + subOrderIds);
}
if (orderInfoList == null || orderInfoList.isEmpty()) {
return MallInboundResult.fail(MallBridgeError.ORDER_NOT_EXISTS.code(), MallBridgeError.ORDER_NOT_EXISTS.message());
}
// 1. 批量查询已存在的子单
Set<String> existingOrderIds = dao.query(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "in", orderInfoList.stream().map(OrderInfoDTO::getOrderId).collect(Collectors.toList()))
.and("delFlag", "=", false))
.stream().map(PointsMallOrderSub::getOrderId).collect(Collectors.toSet());
// 子订单单表
List<PointsMallOrderSub> mallOrderSubList = new ArrayList<>();
// 子订单历史记录表
List<PointsMallOrderSubHistory> mallOrderSubHistoryList = new ArrayList<>();
log.info("9.2.1 生成订单信息推送-调用外部服务获取子单信息:{}", orderInfoList);
for (OrderInfoDTO info : orderInfoList) {
// 2. 检查子单是否已存在
if (existingOrderIds.contains(info.getOrderId())) {
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹) 子单已存在:{}", info);
return MallInboundResult.fail(MallBridgeError.ORDER_EXISTS.code(), MallBridgeError.ORDER_EXISTS.message());
}
try {
// 构造对象
SubOrderBuildResult result = pointsMallOrderSubHistoryService.buildSubOrder(info, dto, main.getUserId());
mallOrderSubList.add(result.getSub());
mallOrderSubHistoryList.add(result.getHistory());
} catch (IllegalArgumentException e) {
return MallInboundResult.fail(-1, e.getMessage());
}
}
Trans.exec((Atom) () -> {
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹),保存子订单");
dao.insert(mallOrderSubList);
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹),保存子订单流水记录");
dao.insert(mallOrderSubHistoryList);
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹),保存子订单积分锁定记录");
pointsMallPointsLockService.createSubLocks(main, mallOrderSubList);
});
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹) 执行结束");
return MallInboundResult.success();
}
}
@@ -0,0 +1,168 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO;
import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessage;
import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessageTemplate;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallCallPMessagePushService {
private final Dao dao;
@Inject
private GlobalMessageSendService globalMessageSendService;
public MallCallPMessagePushService(Dao dao) {
this.dao = dao;
}
/**
* 支付成功callP消息推送。
*
* @param mainOrder 订单信息
*/
public void pushMessageToGlobal(PointsMallOrderMain mainOrder) {
PointsMallMessageTemplate template = dao.fetch(PointsMallMessageTemplate.class, Cnd.where("id", "=", "11111111").and("delFlag", "=", false));
if (template == null) {
log.info("支付成功推送消息失败,未配置消息模板");
return;
}
Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", mainOrder.getUserId()).or("loginname", "=", mainOrder.getUserId()));
PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", mainOrder.getSupplierId()).and("delFlag", "=", false));
String supplierName = supplier == null || StrUtil.isBlank(supplier.getSupplierName()) ? mainOrder.getSupplierId() : supplier.getSupplierName();
// 消息唯一标识 模板id+供应商+订单号
String uniqueNo = template.getId().concat("-").concat(mainOrder.getSupplierId()).concat("-").concat(mainOrder.getOrderId());
int count = dao.count(PointsMallMessage.class, Cnd.where("uniqueNo", "=", uniqueNo).and("delFlag", "=", false));
if (count > 0) {
log.info("9.2.14支付结果通知接口-支付成功2,消息重复推送:{}", uniqueNo);
return;
}
StringBuilder sb = new StringBuilder();
sb.append(" 商品列表:").append(System.lineSeparator());
List<OrderProInfoDTO> productList = StrUtil.isBlank(mainOrder.getExtJson())
? Collections.emptyList()
: JSONUtil.toList(mainOrder.getExtJson(), OrderProInfoDTO.class);
productList.forEach(product ->
sb.append(" 商品名称:").append(product.getName()).append(",商品价格:").append(formatMoney(product.getPrice())).append("").append(System.lineSeparator())
);
// 模板变量替换数据
Map<String, String> paramMap = new HashMap<>();
paramMap.put("#{userName}", user == null || StrUtil.isBlank(user.getUsername()) ? mainOrder.getUserId() : user.getUsername());
paramMap.put("#{orderFinishTime}", DateUtil.formatDateTime(mainOrder.getOrderCreateTime()));
paramMap.put("#{pointPrice}", nvl(mainOrder.getPointsPrice()).toPlainString());
paramMap.put("#{supplierName}", supplierName);
paramMap.put("#{totalPrice}", nvl(mainOrder.getTotalPrice()).toPlainString());
paramMap.put("#{payPrice}", nvl(mainOrder.getWPayOrAPay()).toPlainString());
paramMap.put("#{productList}", sb.toString());
// 模板转换成具体的消息
String content = template.getContent();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
content = content.replace(entry.getKey(), entry.getValue());
}
// 生成记录
PointsMallMessage message = new PointsMallMessage();
message.setUniqueNo(uniqueNo);
message.setPushType("CALLP");
message.setTemplateId(template.getId());
message.setTouser(mainOrder.getUserId());
message.setContent(content);
message.setContentLink(template.getContentLink());
message.setPlanPushTime(new Date());
// 状态设置为已推送
message.setStatus(1);
dao.insert(message);
globalMessageSendService.sendLocalSystemMessage(template.getName(), content, 1, Collections.singletonList(mainOrder.getUserId()), null);
}
/**
* callP消息推送。
*
* @param userName 用户姓名
* @param subOrder 订单信息
*/
public void pushMessageToGlobal(String userName, PointsMallOrderSub subOrder) {
PointsMallMessageTemplate template = dao.fetch(PointsMallMessageTemplate.class, Cnd.where("id", "=", "22222222").and("delFlag", "=", false));
if (template == null) {
log.info("子单取消后推送消息失败,未配置消息模板");
return;
}
PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", subOrder.getSupplierId()).and("delFlag", "=", false));
String supplierName = supplier == null || StrUtil.isBlank(supplier.getSupplierName()) ? subOrder.getSupplierId() : supplier.getSupplierName();
Date now = new Date();
Date finishTime = subOrder.getOrderFinishTime() == null ? now : subOrder.getOrderFinishTime();
long day = DateUtil.betweenDay(DateUtil.beginOfDay(finishTime), DateUtil.beginOfDay(now), false);
String uniqueNo = template.getId().concat("-").concat(subOrder.getSupplierId()).concat("-")
.concat(subOrder.getOrderId()).concat("-").concat(String.valueOf(day));
StringBuilder sb = new StringBuilder();
sb.append("商品列表: ").append(System.lineSeparator());
List<OrderProInfoDTO> productList = StrUtil.isBlank(subOrder.getExtJson())
? Collections.emptyList()
: JSONUtil.toList(subOrder.getExtJson(), OrderProInfoDTO.class);
productList.forEach(product ->
sb.append(" 商品名称: ").append(product.getName()).append(",商品价格:").append(formatMoney(product.getPrice())).append("").append(System.lineSeparator())
);
Map<String, String> paramMap = new HashMap<>();
paramMap.put("#{userName}", userName);
paramMap.put("#{orderFinishTime}", DateUtil.formatDateTime(finishTime));
paramMap.put("#{pointPrice}", nvl(subOrder.getPointsPrice()).toPlainString());
paramMap.put("#{supplierName}", supplierName);
paramMap.put("#{totalPrice}", nvl(subOrder.getTotalPrice()).toPlainString());
paramMap.put("#{payPrice}", nvl(subOrder.getWPayOrAPay()).toPlainString());
paramMap.put("#{productList}", sb.toString());
String content = template.getContent();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
content = content.replace(entry.getKey(), entry.getValue());
}
Date planPushTime = DateUtil.offsetDay(finishTime, (int) day);
PointsMallMessage message = new PointsMallMessage();
message.setUniqueNo(uniqueNo);
message.setPushType("CALLP");
message.setTemplateId(template.getId());
message.setTouser(subOrder.getUserId());
message.setContent(content);
message.setContentLink(template.getContentLink());
message.setPlanPushTime(planPushTime);
message.setStatus(0);
dao.insert(message);
globalMessageSendService.sendLocalSystemMessage(template.getName(), content, 1, Collections.singletonList(subOrder.getUserId()), null);
}
private String formatMoney(BigDecimal value) {
return nvl(value).setScale(2, RoundingMode.HALF_UP).toPlainString();
}
private BigDecimal nvl(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value;
}
}
@@ -5,7 +5,6 @@ import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
@@ -25,8 +24,6 @@ import java.util.Date;
@IocBean(args = {"refer:dao"})
public class MallInboundConfirmPurchaseService extends AbstractMallBridgeSupport {
@Inject
private PointsMallUserPointsService pointsMallUserPointsService;
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
@@ -38,27 +35,37 @@ public class MallInboundConfirmPurchaseService extends AbstractMallBridgeSupport
* 确认购买:校验积分是否充足,更新主单支付状态,并锁定主单积分。
*/
public MallInboundResult confirmPurchase(MallPurchaseConfirmDTO dto) {
log.info("积分商城确认购买开始,supplierId={}orderId={}", dto.getSupplierId(), dto.getOrderId());
log.info("9.2.9 确认购买调用开始: {}", dto);
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(404, "订单不存在");
return MallInboundResult.fail(-1, "订单不存在");
}
BigDecimal totalPrice = money(nvl(dto.getTotalPrice(), main.getTotalPrice()));
BigDecimal cash = money(nvl(dto.getWPayOrAPay()));
BigDecimal points = money(nvl(dto.getPointsPrice()));
if (dto.getTotalPrice() == null || dto.getPointsPrice() == null || dto.getWPayOrAPay() == null) {
return MallInboundResult.fail(-1, "参数缺少");
}
BigDecimal totalPrice = money(dto.getTotalPrice());
BigDecimal cash = money(dto.getWPayOrAPay());
BigDecimal points = money(dto.getPointsPrice());
if (totalPrice.compareTo(cash.add(points)) != 0 || money(nvl(main.getTotalPrice())).compareTo(totalPrice) != 0) {
return MallInboundResult.fail(400, "订单总金额不一致");
return MallInboundResult.fail(-1, "订单总金额不一致");
}
BigDecimal available = pointsMallUserPointsService.availablePoints(main.getUserId());
if (available.compareTo(points) < 0) {
return MallInboundResult.fail(400, "积分不足,无法确认购买");
try {
Trans.exec((Atom) () -> {
// 支付成功,修改主订单支付状态为已支付
updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), cash.compareTo(BigDecimal.ZERO) == 0 ? 0 : 1, totalPrice, cash, points);
// 锁定积分,重复确认和并发超用由积分锁服务处理
log.info("9.2.9确认购买,锁定积分:supplierId={}orderId={}points={}", dto.getSupplierId(), dto.getOrderId(), points);
pointsMallPointsLockService.lockMain(main, points);
});
} catch (IllegalArgumentException e) {
log.error(">>> 积分锁定失败:", e);
return MallInboundResult.fail(-1, e.getMessage());
} catch (Exception e) {
log.error(">>> 积分锁定失败:", e);
return MallInboundResult.fail(-1, "服务器内部错误");
}
Trans.exec((Atom) () -> {
updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), cash.compareTo(BigDecimal.ZERO) == 0 ? 0 : 1, totalPrice, cash, points);
pointsMallPointsLockService.lockMain(main, points);
});
log.info("积分商城确认购买完成,supplierId={}orderId={}points={}", dto.getSupplierId(), dto.getOrderId(), points);
log.info("9.2.9 确认购买调用结束。");
return MallInboundResult.success();
}
@@ -305,7 +305,7 @@ public class MallInboundEventService extends AbstractMallBridgeSupport {
}
dao.updateIgnoreNull(sub);
if (statusEnum == MallOrderStatusEnum.CANCELLED || statusEnum == MallOrderStatusEnum.REFUNDED || statusEnum == MallOrderStatusEnum.ABOLISH) {
pointsMallPointsLockService.releaseOrReturnSub(sub, state, nvl(sub.getRefund(), BigDecimal.ZERO));
pointsMallPointsLockService.releaseOrReturnPoints(sub, state, nvl(sub.getRefund(), BigDecimal.ZERO));
}
if (statusEnum == MallOrderStatusEnum.COMPLETED) {
pointsMallPointsLockService.deductCompletedSub(sub);
@@ -1,10 +1,12 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPendingOrderDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMainHistory;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
@@ -35,7 +37,15 @@ public class MallInboundPendingOrderService extends AbstractMallBridgeSupport {
.and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)) > 0) {
return MallInboundResult.fail(-1, "订单已存在");
}
Trans.exec((Atom) () -> dao.insert(toMain(dto.getSupplierId(), dto.getUserId(), dto.getOrderInfo(), ORDER_PENDING_PAY)));
Trans.exec((Atom) () -> {
PointsMallOrderMain main = toMain(dto.getSupplierId(), dto.getUserId(), dto.getOrderInfo(), ORDER_PENDING_PAY);
dao.insert(main);
// 新增主单历史记录
PointsMallOrderMainHistory orderMainHistory = BeanUtil.copyProperties(main, PointsMallOrderMainHistory.class);
orderMainHistory.setId(null);
dao.insert(orderMainHistory);
});
log.info("积分商城接收待支付订单完成,supplierId={}orderId={}", dto.getSupplierId(), dto.getOrderInfo().getOrderId());
return MallInboundResult.success();
}
@@ -1,20 +1,13 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseResultDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.exception.MallBridgeError;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMainHistory;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
@@ -25,9 +18,6 @@ import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
@@ -42,7 +32,7 @@ public class MallInboundPurchaseResultService {
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
@Inject
private GlobalMessageSendService globalMessageSendService;
private MallCallPMessagePushService mallCallPMessagePushService;
public MallInboundPurchaseResultService(Dao dao) {
this.dao = dao;
@@ -56,15 +46,19 @@ public class MallInboundPurchaseResultService {
if (null == orderMain) {
return MallInboundResult.fail(MallBridgeError.ORDER_NOT_EXISTS.code(), "订单不存在");
}
if (dto.getTotalPrice() == null || dto.getPointsPrice() == null || dto.getWPayOrAPay() == null || dto.getPurchaseResult() == null) {
return MallInboundResult.fail(MallBridgeError.PARAM_ERROR.code(), "参数缺少");
}
// 支付结果
if (dto.getPurchaseResult()) {
log.info("9.2.14 支付结果通知接口,支付成功");
if (BigDecimal.ZERO.compareTo(orderMain.getWPayOrAPay()) != 0) {
// 已现金支付
dao.update(PointsMallOrderMain.class, Chain.make("cashPaid", 2), Cnd.where("id", "=", orderMain.getId()));
}
try {
// callP消息推送
pushMessageToCallP(orderMain);
mallCallPMessagePushService.pushMessageToGlobal(orderMain);
} catch (Exception e) {
log.error("9.2.14 支付结果通知接口,callP消息推送:{},{}", dto, e.getMessage());
}
@@ -98,46 +92,4 @@ public class MallInboundPurchaseResultService {
log.info("9.2.14 支付结果通知接口 调用结束");
return MallInboundResult.success();
}
/**
* callP消息推送
*
* @param mainOrder 订单信息
*/
public void pushMessageToCallP(PointsMallOrderMain mainOrder) {
Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", mainOrder.getUserId()).or("loginname", "=", mainOrder.getUserId()));
PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", mainOrder.getSupplierId()).and("delFlag", "=", false));
String supplierName = supplier == null || StrUtil.isBlank(supplier.getSupplierName()) ? mainOrder.getSupplierId() : supplier.getSupplierName();
StringBuilder sb = new StringBuilder();
sb.append("商品列表:<ol>");
List<OrderProInfoDTO> productList = StrUtil.isBlank(mainOrder.getExtJson())
? Collections.emptyList()
: JSONUtil.toList(mainOrder.getExtJson(), OrderProInfoDTO.class);
productList.forEach(product ->
sb.append("<li>商品名称:").append(product.getName()).append(",商品价格:").append(formatMoney(product.getPrice())).append("元</li>")
);
sb.append("</ol>");
String userName = user == null || StrUtil.isBlank(user.getUsername()) ? mainOrder.getUserId() : user.getUsername();
String content = "尊敬的" + userName + ",您的积分商城订单已支付成功。"
+ "<br>"
+ "订单时间:" + DateUtil.formatDateTime(mainOrder.getOrderCreateTime())
+ "<br>"
+ "消费积分:" + mainOrder.getPointsPrice()
+ "<br>"
+ "供应商:" + supplierName
+ "<br>"
+ "订单总金额:" + mainOrder.getTotalPrice()
+ "<br>"
+ "现金金额:" + mainOrder.getWPayOrAPay()
+ "<br>"
+ sb;
globalMessageSendService.sendLocalSystemMessage("积分商城支付成功", content, 1, Collections.singletonList(mainOrder.getUserId()), null);
}
private String formatMoney(BigDecimal value) {
return (value == null ? BigDecimal.ZERO : value).setScale(2, RoundingMode.HALF_UP).toPlainString();
}
}
@@ -0,0 +1,151 @@
package com.budwk.app.zhgh.pointsmall.order.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_order_sub_history")
@Comment("积分商城子订单历史记录")
public class PointsMallOrderSubHistory extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("子订单ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderId;
@Column
@Comment("订单编号")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderNumberId;
@Column
@Comment("主订单ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String mainOrderId;
@Column
@Comment("供应商编码")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String supplierId;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String userId;
@Column
@Comment("应用名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String aplName;
@Column
@Comment("订单创建时间")
private Date orderCreateTime;
@Column
@Comment("订单更新时间")
private Date orderUpdateTime;
@Column
@Comment("发货时间")
private Date deliveryTime;
@Column
@Comment("确认收货时间")
private Date orderFinishTime;
@Column
@Comment("完成时间")
private Date orderCompleteTime;
@Column
@Comment("发货状态")
private Integer deliveryStatus;
@Column
@Comment("运费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal freightCost;
@Column
@Comment("运费现金")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPayFreightCost;
@Column
@Comment("退款积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal refund;
@Column
@Comment("退款现金")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPayRefund;
@Column
@Comment("物流单号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String logisticsOrderId;
@Column
@Comment("物流承运商")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String crrgBsnNm;
@Column
@Comment("订单状态")
private Integer orderState;
@Column
@Comment("订单总金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal totalPrice;
@Column
@Comment("现金金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPay;
@Column
@Comment("消费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal pointsPrice;
@Column
@Comment("发票代码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceCode;
@Column
@Comment("扩展信息JSON")
@ColDefine(type = ColType.TEXT)
private String extJson;
@Column
@Comment("对账状态")
private Integer reconciliationStatus;
@Column
@Comment("开票状态")
private Integer invoiceStatus;
}
@@ -0,0 +1,13 @@
package com.budwk.app.zhgh.pointsmall.order.result;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubHistory;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class SubOrderBuildResult {
private PointsMallOrderSub sub;
private PointsMallOrderSubHistory history;
}
@@ -41,5 +41,7 @@ public interface PointsMallOrderService extends BaseService<PointsMallOrderMain>
boolean deleteExchange(String id);
void updateMainOrderStatus(String mainOrderId, String supplierId);
List<PointsMallSupplier> listSuppliers();
}
@@ -0,0 +1,17 @@
package com.budwk.app.zhgh.pointsmall.order.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubHistory;
import com.budwk.app.zhgh.pointsmall.order.result.SubOrderBuildResult;
import java.util.List;
public interface PointsMallOrderSubHistoryService extends BaseService<PointsMallOrderSubHistory> {
List<OrderProInfoDTO> getPictureBase64(String supplierId, String orderId, String queryType, List<OrderProInfoDTO> orderProInfo);
SubOrderBuildResult buildSubOrder(OrderInfoDTO info, MallOrderEventDTO dto, String userId);
}
@@ -0,0 +1,17 @@
package com.budwk.app.zhgh.pointsmall.order.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
public interface PointsMallOrderSubService extends BaseService<PointsMallOrderSub> {
/**
* 根据供应商编码和子订单外部ID查询子订单。
*/
PointsMallOrderSub findBySubOrderExtId(String subOrderId, String supplierId);
/**
* 更新子订单状态和更新时间。
*/
boolean updateSubOrderStatus(PointsMallOrderSub orderSub);
}
@@ -453,7 +453,8 @@ public class PointsMallOrderServiceImpl extends BaseServiceImpl<PointsMallOrderM
/**
* 更新主订单状态,所有子单取消、退货或完成后,修改主单状态为已完成。
*/
private void updateMainOrderStatus(String mainOrderId, String supplierId) {
@Override
public void updateMainOrderStatus(String mainOrderId, String supplierId) {
if (StrUtil.hasBlank(mainOrderId, supplierId)) {
return;
}
@@ -0,0 +1,70 @@
package com.budwk.app.zhgh.pointsmall.order.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO;
import com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubHistory;
import com.budwk.app.zhgh.pointsmall.order.result.SubOrderBuildResult;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubHistoryService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Collections;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class PointsMallOrderSubHistoryServiceImpl extends BaseServiceImpl<PointsMallOrderSubHistory> implements PointsMallOrderSubHistoryService {
public PointsMallOrderSubHistoryServiceImpl(Dao dao) {
super(dao);
}
@Override
public List<OrderProInfoDTO> getPictureBase64(String supplierId, String orderId, String queryType, List<OrderProInfoDTO> orderProInfo) {
return CollUtil.isEmpty(orderProInfo) ? Collections.emptyList() : orderProInfo;
}
@Override
public SubOrderBuildResult buildSubOrder(OrderInfoDTO info, MallOrderEventDTO dto, String userId) {
// 3. 检查商品信息
List<OrderProInfoDTO> orderProInfo = info.getOrderProInfos();
if (orderProInfo == null || orderProInfo.isEmpty()) {
// 必须要存在商品信息,若无商品信息返回错误
throw new IllegalArgumentException("缺少商品信息");
}
// 4. 获取图片base64编码,1:商品图片(正常订单)2:发票 3:退货单图片,4:换货单图片
List<OrderProInfoDTO> pictureBase64 = this.getPictureBase64(dto.getSupplierId(), info.getOrderId(), "1", orderProInfo);
// 5. 构建子单对象
PointsMallOrderSub sub = new PointsMallOrderSub();
BeanUtil.copyProperties(info, sub);
sub.setSupplierId(dto.getSupplierId());
sub.setMainOrderId(dto.getMainOrderId());
sub.setUserId(userId);
sub.setOrderState(MallOrderStatusEnum.IN_PROGRESS.getCode());
sub.setExtJson(JSONUtil.toJsonStr(pictureBase64));
sub.setReconciliationStatus(0);
sub.setInvoiceStatus(0);
sub.setOrderCreateTime(StrUtil.isBlank(info.getCreateTime()) ? null : cn.hutool.core.date.DateUtil.parse(info.getCreateTime().replace("T", " ")));
sub.setOrderUpdateTime(StrUtil.isBlank(info.getUpdateTime()) ? null : cn.hutool.core.date.DateUtil.parse(info.getUpdateTime().replace("T", " ")));
sub.setDeliveryTime(StrUtil.isBlank(info.getDeliveryTime()) ? null : cn.hutool.core.date.DateUtil.parse(info.getDeliveryTime().replace("T", " ")));
sub.setOrderFinishTime(StrUtil.isBlank(info.getOrderFinishTime()) ? null : cn.hutool.core.date.DateUtil.parse(info.getOrderFinishTime().replace("T", " ")));
sub.setOrderCompleteTime(StrUtil.isBlank(info.getCompleteTime()) ? null : cn.hutool.core.date.DateUtil.parse(info.getCompleteTime().replace("T", " ")));
sub.setDeliveryStatus(StrUtil.isBlank(info.getDeliveryStatus()) ? null : Integer.valueOf(info.getDeliveryStatus()));
// 6. 构建子单历史记录
PointsMallOrderSubHistory subHis = new PointsMallOrderSubHistory();
BeanUtil.copyProperties(sub, subHis);
subHis.setId(null);
return new SubOrderBuildResult(sub, subHis);
}
}
@@ -0,0 +1,39 @@
package com.budwk.app.zhgh.pointsmall.order.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubService;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Date;
@IocBean(args = {"refer:dao"})
public class PointsMallOrderSubServiceImpl extends BaseServiceImpl<PointsMallOrderSub> implements PointsMallOrderSubService {
public PointsMallOrderSubServiceImpl(Dao dao) {
super(dao);
}
@Override
public PointsMallOrderSub findBySubOrderExtId(String subOrderId, String supplierId) {
return dao().fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", subOrderId)
.and("supplierId", "=", supplierId).and("delFlag", "=", false));
}
/**
* 只更新状态相关字段,避免完整子单对象回写影响其它字段。
*/
@Override
public boolean updateSubOrderStatus(PointsMallOrderSub orderSub) {
if (orderSub == null || StrUtil.isBlank(orderSub.getId())) {
return false;
}
Chain chain = Chain.make("orderState", orderSub.getOrderState())
.add("orderUpdateTime", orderSub.getOrderUpdateTime() == null ? new Date() : orderSub.getOrderUpdateTime());
return dao().update(PointsMallOrderSub.class, chain, Cnd.where("id", "=", orderSub.getId()).and("delFlag", "=", false)) > 0;
}
}
@@ -16,7 +16,10 @@ public interface PointsMallPointsLockService {
void createSubLocks(PointsMallOrderMain mainOrder, List<PointsMallOrderSub> subOrders);
void releaseOrReturnSub(PointsMallOrderSub subOrder, Integer orderState, BigDecimal refund);
/**
* 子单取消时释放锁定积分,退货时退回用户积分。
*/
void releaseOrReturnPoints(PointsMallOrderSub subOrder, Integer orderState, BigDecimal refund);
void deductCompletedSub(PointsMallOrderSub subOrder);
}
@@ -116,7 +116,7 @@ public class PointsMallPointsLockServiceImpl implements PointsMallPointsLockServ
}
@Override
public void releaseOrReturnSub(PointsMallOrderSub subOrder, Integer orderState, BigDecimal refund) {
public void releaseOrReturnPoints(PointsMallOrderSub subOrder, Integer orderState, BigDecimal refund) {
if (subOrder == null || StrUtil.hasBlank(subOrder.getSupplierId(), subOrder.getOrderId(), subOrder.getMainOrderId())) {
return;
}