feat: 积分商城-对账-发票-优化3
This commit is contained in:
+8
@@ -3,6 +3,8 @@ package com.budwk.app.zhgh.pointsmall.invoice.service;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
|
||||
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo;
|
||||
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceApplyParam;
|
||||
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceInfoPageParam;
|
||||
@@ -16,6 +18,12 @@ import java.util.List;
|
||||
|
||||
public interface PointsMallInvoiceService extends BaseService<PointsMallInvoiceMain> {
|
||||
|
||||
/** 同步供应商发票信息及本地文件。 */
|
||||
MallInboundResult invoiceInfo(MallOrderEventDTO dto);
|
||||
|
||||
/** 同步发票,可指定是否重新生成失败发票。 */
|
||||
MallInboundResult invoiceInfo(MallOrderEventDTO dto, boolean retry);
|
||||
|
||||
Pagination<PointsMallInvoiceMain> mainPage(PointsMallInvoiceMainPageParam param);
|
||||
|
||||
Pagination<Record> infoPage(PointsMallInvoiceInfoPageParam param);
|
||||
|
||||
+152
-4
@@ -36,17 +36,24 @@ import java.math.BigDecimal;
|
||||
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
|
||||
import com.budwk.app.sys.enums.SysFileEngineTypeEnum;
|
||||
import com.budwk.app.sys.services.SysFileService;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallPictureOutboundResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class PointsMallInvoiceServiceImpl extends BaseServiceImpl<PointsMallInvoiceMain> implements PointsMallInvoiceService {
|
||||
|
||||
@Inject
|
||||
private MallInvoiceApplyOutboundService mallInvoiceApplyOutboundService;
|
||||
@Inject
|
||||
private MallInboundEventService mallInboundEventService;
|
||||
private SysFileService sysFileService;
|
||||
@Inject
|
||||
private MallInvoiceInfoQueryService mallInvoiceInfoQueryService;
|
||||
|
||||
@@ -54,6 +61,147 @@ public class PointsMallInvoiceServiceImpl extends BaseServiceImpl<PointsMallInvo
|
||||
super(dao);
|
||||
}
|
||||
|
||||
/** 处理供应商发票信息通知,查询发票并同步文件和开票结果。 */
|
||||
@Override
|
||||
public MallInboundResult invoiceInfo(MallOrderEventDTO dto) {
|
||||
return invoiceInfo(dto, false);
|
||||
}
|
||||
|
||||
/** retry=true 时允许供应商重新生成失败发票,后续复用相同的同步流程。 */
|
||||
@Override
|
||||
public MallInboundResult invoiceInfo(MallOrderEventDTO dto, boolean retry) {
|
||||
String settlementId = dto.getOrderId();
|
||||
if (StrUtil.hasBlank(dto.getSupplierId(), settlementId)) {
|
||||
return MallInboundResult.fail(400, "供应商和结算单号不能为空");
|
||||
}
|
||||
QueryInvoiceInfoDTO query = new QueryInvoiceInfoDTO();
|
||||
query.setSupplierId(dto.getSupplierId());
|
||||
query.setSettlementId(settlementId);
|
||||
query.setRetry(retry);
|
||||
GetInvoiceInfoDTO result;
|
||||
try {
|
||||
result = mallInvoiceInfoQueryService.queryInvoicesInfo(query);
|
||||
} catch (Exception e) {
|
||||
log.error("调用供应商获取发票信息失败,supplierId={},settlementId={}", dto.getSupplierId(), settlementId, e);
|
||||
return MallInboundResult.fail(-1, "获取供应商发票信息失败");
|
||||
}
|
||||
|
||||
PointsMallInvoiceMain main = dao().fetch(PointsMallInvoiceMain.class,
|
||||
Cnd.where("settlementId", "=", settlementId).and("delFlag", "=", false));
|
||||
if (main == null) {
|
||||
return MallInboundResult.fail(404, "开票申请记录不存在");
|
||||
}
|
||||
if (result == null || !settlementId.equals(result.getSettlementId())) {
|
||||
return MallInboundResult.fail(502, "供应商返回的发票结算单不匹配");
|
||||
}
|
||||
Integer success = result.getBSuccess() == null ? 0 : result.getBSuccess();
|
||||
if (success == 0) {
|
||||
if (result.getInvoiceInfos() == null || result.getInvoiceInfos().isEmpty() || StrUtil.isBlank(result.getSucOrderIds())) {
|
||||
return MallInboundResult.fail(502, "供应商开票结果缺少发票明细或成功订单");
|
||||
}
|
||||
try {
|
||||
List<String> fileIds = result.getInvoiceInfos().stream().map(InvoiceInfoDTO::getInvoiceId).collect(Collectors.toList());
|
||||
if (fileIds.stream().anyMatch(StrUtil::isBlank)) {
|
||||
throw new IllegalArgumentException("供应商发票文件标识为空");
|
||||
}
|
||||
MallPictureOutboundResult files = mallInvoiceInfoQueryService.queryInvoiceFiles(dto.getSupplierId(), settlementId, fileIds);
|
||||
for (InvoiceInfoDTO item : result.getInvoiceInfos()) {
|
||||
if (item.getStatus() != null && item.getStatus() != 1) {
|
||||
throw new IllegalArgumentException("供应商发票尚未生成成功");
|
||||
}
|
||||
String base64 = files.getImageEncodeMap() == null ? null : files.getImageEncodeMap().get(item.getInvoiceId());
|
||||
if (StrUtil.isBlank(base64)) {
|
||||
throw new IllegalArgumentException("未获取到发票文件Base64:" + item.getInvoiceId());
|
||||
}
|
||||
if (base64.startsWith("data:")) {
|
||||
base64 = base64.substring(base64.indexOf(',') + 1);
|
||||
}
|
||||
byte[] bytes = Base64.getDecoder().decode(base64.replaceAll("\\s", ""));
|
||||
if (bytes.length == 0) {
|
||||
throw new IllegalArgumentException("发票文件内容为空");
|
||||
}
|
||||
String fileType = StrUtil.blankToDefault(files.getFileType(), StrUtil.blankToDefault(item.getFileType(), "PDF"));
|
||||
if (!fileType.matches("(?i)pdf|jpg|jpeg|png")) {
|
||||
throw new IllegalArgumentException("不支持的发票文件类型:" + fileType);
|
||||
}
|
||||
String localUrl = sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(),
|
||||
"invoice-" + java.util.UUID.randomUUID() + "." + fileType.toLowerCase(java.util.Locale.ROOT), bytes);
|
||||
item.setUrl(localUrl);
|
||||
item.setImageEncode(localUrl);
|
||||
item.setFileType(fileType);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发票文件同步失败,settlementId={}", settlementId, e);
|
||||
return MallInboundResult.fail(502, "发票文件同步失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
Trans.exec((Atom) () -> {
|
||||
main.setBSuccess(success);
|
||||
main.setSucOrderIds(result.getSucOrderIds());
|
||||
main.setFailOrderIds(result.getFailOrderIds());
|
||||
main.setFailMsg(result.getFailMsg());
|
||||
if (result.getInvoiceInfos() != null) {
|
||||
for (InvoiceInfoDTO item : result.getInvoiceInfos()) {
|
||||
PointsMallInvoiceInfo info = dao().fetch(PointsMallInvoiceInfo.class,
|
||||
Cnd.where("mainId", "=", main.getId()).and("invoiceId", "=", item.getInvoiceId()).and("delFlag", "=", false));
|
||||
if (info == null) {
|
||||
info = new PointsMallInvoiceInfo();
|
||||
info.setMainId(main.getId());
|
||||
info.setInvoiceId(item.getInvoiceId());
|
||||
}
|
||||
info.setInvoiceCode(item.getInvoiceCode());
|
||||
info.setInvoiceDate(item.getInvoiceDate() == null ? null : DateUtil.parse(item.getInvoiceDate().toString()));
|
||||
info.setInvoiceNakeAmount(item.getInvoiceNakeAmount());
|
||||
info.setInvoiceTaxRate(item.getInvoiceTaxRate());
|
||||
info.setInvoiceTaxAmount(item.getInvoiceTaxAmount());
|
||||
info.setInvoiceAmount(item.getInvoiceAmount());
|
||||
info.setInvoiceType(item.getInvoiceType());
|
||||
info.setUrl(item.getUrl());
|
||||
info.setImageEncode(item.getImageEncode());
|
||||
info.setFileType(item.getFileType());
|
||||
info.setCheckCode(item.getCheckCode());
|
||||
info.setInvoiceAddress(item.getInvoiceAddress());
|
||||
info.setRemark(item.getRemark());
|
||||
if (success != 0) {
|
||||
info.setUrl("");
|
||||
info.setImageEncode("");
|
||||
}
|
||||
if (StrUtil.isBlank(info.getId())) {
|
||||
dao().insert(info);
|
||||
} else {
|
||||
dao().updateIgnoreNull(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> successOrderIds = splitInvoiceOrderIds(result.getSucOrderIds());
|
||||
List<String> failOrderIds = splitInvoiceOrderIds(result.getFailOrderIds());
|
||||
if (!successOrderIds.isEmpty()) {
|
||||
dao().update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 1), Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "in", successOrderIds).and("delFlag", "=", false));
|
||||
}
|
||||
if (!failOrderIds.isEmpty()) {
|
||||
dao().update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 0), Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "in", failOrderIds).and("delFlag", "=", false));
|
||||
}
|
||||
dao().updateIgnoreNull(main);
|
||||
});
|
||||
log.info("供应商发票信息更新完成,supplierId={},settlementId={}", dto.getSupplierId(), settlementId);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
|
||||
/** 拆分开票结果中的订单号,去除空项和重复项,保留原有顺序。 */
|
||||
private List<String> splitInvoiceOrderIds(String value) {
|
||||
if (StrUtil.isBlank(value)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.stream(value.split(","))
|
||||
.map(String::trim)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination<PointsMallInvoiceMain> mainPage(PointsMallInvoiceMainPageParam param) {
|
||||
Cnd cnd = Cnd.where("delFlag", "=", false);
|
||||
@@ -87,7 +235,7 @@ public class PointsMallInvoiceServiceImpl extends BaseServiceImpl<PointsMallInvo
|
||||
MallOrderEventDTO event = new MallOrderEventDTO();
|
||||
event.setSupplierId(order.getSupplierId());
|
||||
event.setOrderId(main.getSettlementId());
|
||||
MallInboundResult result = mallInboundEventService.invoiceInfo(event);
|
||||
MallInboundResult result = invoiceInfo(event);
|
||||
if (result.getResultCode() != 0) {
|
||||
dao().update(PointsMallInvoiceMain.class, Chain.make("bSuccess", 1).add("failMsg", result.getMessage()),
|
||||
Cnd.where("id", "=", main.getId()));
|
||||
@@ -165,7 +313,7 @@ public class PointsMallInvoiceServiceImpl extends BaseServiceImpl<PointsMallInvo
|
||||
|
||||
@Override
|
||||
public void retryInvoice(String mainId) {
|
||||
MallInboundResult result = mallInboundEventService.invoiceInfo(invoiceEvent(mainId), true);
|
||||
MallInboundResult result = invoiceInfo(invoiceEvent(mainId), true);
|
||||
if (result.getResultCode() != 0) {
|
||||
throw new IllegalArgumentException(result.getMessage());
|
||||
}
|
||||
|
||||
@@ -14,5 +14,8 @@ public class MallOrderEventDTO {
|
||||
private String deliveryTime;
|
||||
private String logisticsOrderId;
|
||||
private String crrgBsnNm;
|
||||
// 接口推送时间,Unix 毫秒时间戳。
|
||||
private Long time;
|
||||
// 本次业务操作发生时间,重推时保留原值,格式 yyyy-MM-dd HH:mm:ss。
|
||||
private String operationTime;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/** 订单消息数据,主单、子单和退货消息统一使用。 */
|
||||
@Data
|
||||
public class MallOrderMessageDTO {
|
||||
private String supplierId;
|
||||
private String userId;
|
||||
private String userName;
|
||||
// 退货消息使用退货单号,pointsPrice 使用本次退款积分。
|
||||
private String orderId;
|
||||
private BigDecimal pointsPrice;
|
||||
private BigDecimal totalPrice;
|
||||
private BigDecimal wPayOrAPay;
|
||||
private Date orderCreateTime;
|
||||
private Date orderFinishTime;
|
||||
// 本次操作时间,取供应商事件的 operationTime,不使用接口推送时间或妥投时间。
|
||||
private Date operationTime;
|
||||
private String extJson;
|
||||
}
|
||||
+39
-5
@@ -3,7 +3,16 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
|
||||
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.support.AbstractMallBridgeSupport;
|
||||
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.trans.Atom;
|
||||
import org.nutz.trans.Trans;
|
||||
import java.util.Date;
|
||||
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
|
||||
import java.math.BigDecimal;
|
||||
import com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
@@ -12,19 +21,44 @@ import org.nutz.ioc.loader.annotation.IocBean;
|
||||
* MallAbolishSubOrderEventHandler
|
||||
* orderType=8: 废除子订单事件处理器。
|
||||
*/
|
||||
@IocBean
|
||||
public class MallAbolishSubOrderEventHandler implements MallOrderEventHandler {
|
||||
@Slf4j
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class MallAbolishSubOrderEventHandler extends AbstractMallBridgeSupport implements MallOrderEventHandler {
|
||||
|
||||
@Inject
|
||||
private MallInboundEventService inboundEventService;
|
||||
private PointsMallPointsLockService pointsMallPointsLockService;
|
||||
|
||||
public MallAbolishSubOrderEventHandler(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* 废除指定子订单,在同一事务中更新状态并释放或退回积分。
|
||||
*/
|
||||
@Override
|
||||
public MallInboundResult handle(MallOrderEventDTO dto) {
|
||||
return inboundEventService.updateSubOrderStatus(dto, MallOrderStatusEnum.ABOLISH);
|
||||
Integer state = MallOrderStatusEnum.ABOLISH.getCode();
|
||||
PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false));
|
||||
if (sub == null) {
|
||||
return MallInboundResult.fail(404, "订单不存在");
|
||||
}
|
||||
// 已废除的重复通知不再处理,避免重复执行积分释放或退回。
|
||||
if (state.equals(sub.getOrderState())) {
|
||||
log.info("积分商城子单状态已是目标状态,跳过重复处理,supplierId={},orderId={},state={}", dto.getSupplierId(), dto.getOrderId(), state);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
Trans.exec((Atom) () -> {
|
||||
sub.setOrderState(state);
|
||||
sub.setOrderUpdateTime(new Date());
|
||||
dao.updateIgnoreNull(sub);
|
||||
});
|
||||
log.info("积分商城子单状态更新完成,supplierId={},orderId={},state={}", dto.getSupplierId(), dto.getOrderId(), state);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
+47
-5
@@ -3,7 +3,18 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
|
||||
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 cn.hutool.core.util.StrUtil;
|
||||
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.PointsMallOrderSub;
|
||||
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.trans.Atom;
|
||||
import org.nutz.trans.Trans;
|
||||
import java.util.Date;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
@@ -11,19 +22,50 @@ import org.nutz.ioc.loader.annotation.IocBean;
|
||||
* MallCancelPayedMainOrderEventHandler
|
||||
* orderType=7: 已支付未拆单的整个父单取消。
|
||||
*/
|
||||
@IocBean
|
||||
public class MallCancelPayedMainOrderEventHandler implements MallOrderEventHandler {
|
||||
@Slf4j
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class MallCancelPayedMainOrderEventHandler extends AbstractMallBridgeSupport implements MallOrderEventHandler {
|
||||
|
||||
@Inject
|
||||
private MallInboundEventService inboundEventService;
|
||||
private PointsMallPointsLockService pointsMallPointsLockService;
|
||||
|
||||
public MallCancelPayedMainOrderEventHandler(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理已支付未拆单主单取消通知,沿用原 paidNotSplit=true 分支的校验与积分处理。
|
||||
*/
|
||||
@Override
|
||||
public MallInboundResult handle(MallOrderEventDTO dto) {
|
||||
return inboundEventService.cancelMainOrder(dto, true);
|
||||
// 优先使用 mainOrderId,未提供时使用事件中的 orderId 查询主订单。
|
||||
String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId());
|
||||
if (StrUtil.isBlank(mainOrderId)) {
|
||||
return MallInboundResult.fail(400, "主订单信息不能为空");
|
||||
}
|
||||
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "=", mainOrderId).and("delFlag", "=", false));
|
||||
if (main == null) {
|
||||
return MallInboundResult.fail(404, "主订单不存在");
|
||||
}
|
||||
Date now = new Date();
|
||||
// 主子订单状态更新与主单积分锁释放在同一事务内完成。
|
||||
Trans.exec((Atom) () -> {
|
||||
main.setOrderState(ORDER_CANCELLED);
|
||||
main.setOrderUpdateTime(now);
|
||||
dao.updateIgnoreNull(main);
|
||||
// 同步取消该供应商、该主订单下尚未删除的子订单,保持原处理范围。
|
||||
dao.update(PointsMallOrderSub.class, Chain.make("orderState", ORDER_CANCELLED).add("orderUpdateTime", now),
|
||||
Cnd.where("supplierId", "=", dto.getSupplierId()).and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false));
|
||||
// 使用主订单归属信息释放主单积分锁。
|
||||
pointsMallPointsLockService.releaseMain(main.getUserId(), main.getSupplierId(), main.getOrderId());
|
||||
});
|
||||
log.info("积分商城主单取消完成,supplierId={},mainOrderId={},paidNotSplit={}", dto.getSupplierId(), mainOrderId, true);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
+51
-5
@@ -3,7 +3,18 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
|
||||
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 cn.hutool.core.util.StrUtil;
|
||||
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.PointsMallOrderSub;
|
||||
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.trans.Atom;
|
||||
import org.nutz.trans.Trans;
|
||||
import java.util.Date;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
@@ -11,19 +22,54 @@ import org.nutz.ioc.loader.annotation.IocBean;
|
||||
* MallCancelPendingOrderEventHandler
|
||||
* orderType=1: 取消待支付主单事件处理器。
|
||||
*/
|
||||
@IocBean
|
||||
public class MallCancelPendingOrderEventHandler implements MallOrderEventHandler {
|
||||
@Slf4j
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class MallCancelPendingOrderEventHandler extends AbstractMallBridgeSupport implements MallOrderEventHandler {
|
||||
|
||||
@Inject
|
||||
private MallInboundEventService inboundEventService;
|
||||
private PointsMallPointsLockService pointsMallPointsLockService;
|
||||
|
||||
public MallCancelPendingOrderEventHandler(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理待支付主单取消通知,校验待支付状态后取消订单并释放主单积分锁。
|
||||
*/
|
||||
@Override
|
||||
public MallInboundResult handle(MallOrderEventDTO dto) {
|
||||
return inboundEventService.cancelMainOrder(dto, false);
|
||||
// 优先使用 mainOrderId,未提供时使用事件中的 orderId 查询主订单。
|
||||
String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId());
|
||||
if (StrUtil.isBlank(mainOrderId)) {
|
||||
return MallInboundResult.fail(400, "主订单信息不能为空");
|
||||
}
|
||||
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "=", mainOrderId).and("delFlag", "=", false));
|
||||
if (main == null) {
|
||||
return MallInboundResult.fail(404, "主订单不存在");
|
||||
}
|
||||
// 待支付取消事件仅允许取消尚未支付的主订单。
|
||||
if (!Integer.valueOf(ORDER_PENDING_PAY).equals(main.getOrderState())) {
|
||||
return MallInboundResult.fail(400, "只能取消未支付主单");
|
||||
}
|
||||
Date now = new Date();
|
||||
// 主子订单状态更新与主单积分锁释放在同一事务内完成。
|
||||
Trans.exec((Atom) () -> {
|
||||
main.setOrderState(ORDER_CANCELLED);
|
||||
main.setOrderUpdateTime(now);
|
||||
dao.updateIgnoreNull(main);
|
||||
// 同步取消该供应商、该主订单下尚未删除的子订单,保持原处理范围。
|
||||
dao.update(PointsMallOrderSub.class, Chain.make("orderState", ORDER_CANCELLED).add("orderUpdateTime", now),
|
||||
Cnd.where("supplierId", "=", dto.getSupplierId()).and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false));
|
||||
// 使用主订单归属信息释放主单积分锁。
|
||||
pointsMallPointsLockService.releaseMain(main.getUserId(), main.getSupplierId(), main.getOrderId());
|
||||
});
|
||||
log.info("积分商城主单取消完成,supplierId={},mainOrderId={},paidNotSplit={}", dto.getSupplierId(), mainOrderId, false);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,6 +1,8 @@
|
||||
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderMessageDTO;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.sys.models.Sys_user;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
|
||||
@@ -102,7 +104,10 @@ public class MallCancelSubOrderEventHandler implements MallOrderEventHandler {
|
||||
pointsMallOrderService.updateMainOrderStatus(sub.getMainOrderId(), sub.getSupplierId());
|
||||
});
|
||||
try {
|
||||
mallCallPMessagePushService.pushMessageToGlobal(StrUtil.blankToDefault(user.getUsername(), user.getLoginname()), sub);
|
||||
MallOrderMessageDTO message = BeanUtil.copyProperties(sub, MallOrderMessageDTO.class);
|
||||
message.setOperationTime(StrUtil.isBlank(dto.getOperationTime()) ? null : DateUtil.parse(dto.getOperationTime().replace("T", " ")));
|
||||
message.setUserName(StrUtil.blankToDefault(user.getUsername(), user.getLoginname()));
|
||||
mallCallPMessagePushService.pushMessageToGlobal("66666666", message);
|
||||
} catch (Exception e) {
|
||||
log.error("9.2.1 子单取消,callP消息推送:{},{}", dto, e.getMessage());
|
||||
}
|
||||
|
||||
+106
-5
@@ -1,9 +1,26 @@
|
||||
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderMessageDTO;
|
||||
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.support.AbstractMallBridgeSupport;
|
||||
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.trans.Atom;
|
||||
import org.nutz.trans.Trans;
|
||||
import java.util.Date;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
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.model.MallOutboundResult;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallOrderQueryOutboundService;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallCallPMessagePushService;
|
||||
import com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
@@ -16,19 +33,103 @@ import org.nutz.ioc.loader.annotation.IocBean;
|
||||
* <p>注意:供应商侧“确认收货”对应 zhgh/callP 侧“已妥投(4)”,
|
||||
* 不等同于 zhgh/callP 用户确认收货后的“已完成(5)”。</p>
|
||||
*/
|
||||
@IocBean
|
||||
public class MallConfirmReceiptEventHandler implements MallOrderEventHandler {
|
||||
@Slf4j
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class MallConfirmReceiptEventHandler extends AbstractMallBridgeSupport implements MallOrderEventHandler {
|
||||
|
||||
@Inject
|
||||
private MallInboundEventService inboundEventService;
|
||||
private MallOrderQueryOutboundService mallOrderQueryOutboundService;
|
||||
@Inject
|
||||
private MallCallPMessagePushService mallCallPMessagePushService;
|
||||
|
||||
public MallConfirmReceiptEventHandler(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理供应商确认收货通知,仅更新为已妥投,不执行用户确认完成后的积分扣减。
|
||||
*/
|
||||
@Override
|
||||
public MallInboundResult handle(MallOrderEventDTO dto) {
|
||||
return inboundEventService.updateSubOrderStatus(dto, MallOrderStatusEnum.RECEIVED);
|
||||
Integer state = MallOrderStatusEnum.RECEIVED.getCode();
|
||||
PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false));
|
||||
if (sub == null) {
|
||||
return MallInboundResult.fail(404, "订单不存在");
|
||||
}
|
||||
// 已妥投的重复通知直接返回,保留原处理的幂等行为。
|
||||
if (state.equals(sub.getOrderState())) {
|
||||
log.info("积分商城子单状态已是目标状态,跳过重复处理,supplierId={},orderId={},state={}", dto.getSupplierId(), dto.getOrderId(), state);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
Trans.exec((Atom) () -> {
|
||||
// 补查供应商订单详情;查询失败时沿用原逻辑,仍更新本地妥投状态。
|
||||
fillReceivedSubOrder(dto, sub);
|
||||
sub.setOrderState(state);
|
||||
// 优先保留订单详情中的时间,缺失时使用当前时间。
|
||||
if (sub.getOrderUpdateTime() == null) {
|
||||
sub.setOrderUpdateTime(new Date());
|
||||
}
|
||||
if (sub.getOrderFinishTime() == null) {
|
||||
sub.setOrderFinishTime(new Date());
|
||||
}
|
||||
dao.updateIgnoreNull(sub);
|
||||
});
|
||||
// 妥投状态保存成功后发送提醒,消息异常不回滚订单处理。
|
||||
try {
|
||||
// 按子订单所属用户读取姓名,显式传入妥投提醒的 userName 模板变量。
|
||||
Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", sub.getUserId()).or("loginname", "=", sub.getUserId()));
|
||||
if (user == null) {
|
||||
throw new IllegalArgumentException("订单所属用户不存在,无法生成妥投提醒");
|
||||
}
|
||||
MallOrderMessageDTO message = BeanUtil.copyProperties(sub, MallOrderMessageDTO.class);
|
||||
message.setOperationTime(parseDate(dto.getOperationTime()));
|
||||
message.setUserName(StrUtil.blankToDefault(user.getUsername(), user.getLoginname()));
|
||||
mallCallPMessagePushService.pushMessageToGlobal("22222222", message);
|
||||
} catch (Exception e) {
|
||||
log.error("订单类型为:6(确认收货)时,全局消息推送失败:{}", dto, e);
|
||||
}
|
||||
log.info("积分商城子单状态更新完成,supplierId={},orderId={},state={}", dto.getSupplierId(), dto.getOrderId(), state);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单类型为:6(确认收货)时,调用 9.2.2 获取订单详情接口,补充子单妥投相关信息。
|
||||
*
|
||||
* <p>供应商侧确认收货只代表电商处已收货,对 zhgh/callP 侧应更新为已妥投(4),
|
||||
* 等待 callP 用户侧确认收货后才进入已完成(5)。</p>
|
||||
*/
|
||||
private void fillReceivedSubOrder(MallOrderEventDTO dto, PointsMallOrderSub sub) {
|
||||
if (mallOrderQueryOutboundService == null) {
|
||||
return;
|
||||
}
|
||||
MallOrderQueryDTO queryDTO = new MallOrderQueryDTO();
|
||||
queryDTO.setSupplierId(dto.getSupplierId());
|
||||
queryDTO.setOrderId(dto.getOrderId());
|
||||
queryDTO.setOrderType(2);
|
||||
log.info("订单类型为:6(确认收货)时,调用9.2.2获取订单详情接口:{}", queryDTO);
|
||||
try {
|
||||
MallOutboundResult result = mallOrderQueryOutboundService.queryOrderInfo(queryDTO);
|
||||
if (result == null || result.getResultCode() != 0 || result.getOrderInfo() == null || result.getOrderInfo().isEmpty()) {
|
||||
log.error("订单类型为:6(确认收货)时,调用9.2.2 获取订单详情失败:{},{}", queryDTO, result == null ? null : result.getMessage());
|
||||
return;
|
||||
}
|
||||
OrderInfoDTO orderInfo = result.getOrderInfo().get(0);
|
||||
sub.setOrderCreateTime(parseDate(orderInfo.getCreateTime()));
|
||||
sub.setOrderUpdateTime(parseDate(orderInfo.getUpdateTime()));
|
||||
sub.setOrderFinishTime(parseDate(orderInfo.getOrderFinishTime()));
|
||||
sub.setDeliveryTime(parseDate(orderInfo.getDeliveryTime()));
|
||||
sub.setDeliveryStatus(parseInteger(orderInfo.getDeliveryStatus()));
|
||||
if (orderInfo.getOrderProInfos() != null) {
|
||||
sub.setExtJson(JSONUtil.toJsonStr(orderInfo.getOrderProInfos()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("订单类型为:6(确认收货)时,调用9.2.2 获取订单详情异常:{},{}", queryDTO, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
|
||||
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.invoice.service.PointsMallInvoiceService;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.nutz.ioc.loader.annotation.IocBean;
|
||||
public class MallInvoiceInfoEventHandler implements MallOrderEventHandler {
|
||||
|
||||
@Inject
|
||||
private MallInboundEventService inboundEventService;
|
||||
private PointsMallInvoiceService pointsMallInvoiceService;
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
@@ -24,6 +24,6 @@ public class MallInvoiceInfoEventHandler implements MallOrderEventHandler {
|
||||
|
||||
@Override
|
||||
public MallInboundResult handle(MallOrderEventDTO dto) {
|
||||
return inboundEventService.invoiceInfo(dto);
|
||||
return pointsMallInvoiceService.invoiceInfo(dto);
|
||||
}
|
||||
}
|
||||
|
||||
+96
-7
@@ -3,27 +3,116 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
|
||||
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 org.nutz.ioc.loader.annotation.Inject;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.sys.models.Sys_user;
|
||||
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.PointsMallOrderSub;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.trans.Atom;
|
||||
import org.nutz.trans.Trans;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
/**
|
||||
* MallShipOrderEventHandler
|
||||
* orderType=99: 供应商已发货 / 更新发货状态。
|
||||
* 在处理器内完成订单校验及主子订单物流信息更新,不再回调事件分发服务。
|
||||
* 本事件仅更新发货状态和物流信息,不修改订单业务状态 orderState。
|
||||
*/
|
||||
@IocBean
|
||||
public class MallShipOrderEventHandler implements MallOrderEventHandler {
|
||||
@Slf4j
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class MallShipOrderEventHandler extends AbstractMallBridgeSupport implements MallOrderEventHandler {
|
||||
|
||||
@Inject
|
||||
private MallInboundEventService inboundEventService;
|
||||
public MallShipOrderEventHandler(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return 99;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理供应商发货通知,在同一事务中更新匹配的子订单及一个主订单。
|
||||
*
|
||||
* @param dto 发货事件,orderId 为英文逗号分隔的子订单号,userId 为可选的用户虚拟 ID
|
||||
* @return 参数或用户映射无效时返回 400,无匹配子订单时返回 404,更新完成返回成功
|
||||
*/
|
||||
@Override
|
||||
public MallInboundResult handle(MallOrderEventDTO dto) {
|
||||
return inboundEventService.updateDeliveryStatus(dto);
|
||||
if (StrUtil.isBlank(dto.getSupplierId()) || StrUtil.isBlank(dto.getOrderId())) {
|
||||
return MallInboundResult.fail(400, "供应商或订单不能为空");
|
||||
}
|
||||
|
||||
// 按供应商和明确的子订单号限定更新范围,支持一次通知多个子订单。
|
||||
List<String> orderIds = splitIds(dto.getOrderId());
|
||||
if (orderIds.isEmpty()) {
|
||||
return MallInboundResult.fail(400, "订单信息不能为空");
|
||||
}
|
||||
|
||||
// 未提供有效发货时间时,使用当前处理时间。
|
||||
Date deliveryTime = parseDate(dto.getDeliveryTime());
|
||||
if (deliveryTime == null) {
|
||||
deliveryTime = new Date();
|
||||
}
|
||||
|
||||
Cnd subCnd = Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "in", orderIds)
|
||||
.and("delFlag", "=", false);
|
||||
if (StrUtil.isNotBlank(dto.getUserId())) {
|
||||
// 供应商传入虚拟 ID,转换为本地用户 ID 后参与订单归属查询。
|
||||
Sys_user user = fetchUser(dto.getUserId());
|
||||
if (user == null) {
|
||||
return MallInboundResult.fail(400, "用户ID未配置或不存在");
|
||||
}
|
||||
subCnd.and("userId", "=", user.getId());
|
||||
}
|
||||
|
||||
List<PointsMallOrderSub> subs = dao.query(PointsMallOrderSub.class, subCnd);
|
||||
if (subs.isEmpty()) {
|
||||
return MallInboundResult.fail(404, "订单不存在");
|
||||
}
|
||||
|
||||
Date finalDeliveryTime = deliveryTime;
|
||||
// 已发货事件固定写入 deliveryStatus=1,不使用通知中的状态值覆盖该语义。
|
||||
Integer deliveredStatus = 1;
|
||||
Trans.exec((Atom) () -> {
|
||||
// 物流单号和承运商仅在非空时更新,避免空字段覆盖已有物流信息。
|
||||
Chain subChain = Chain.make("deliveryStatus", deliveredStatus)
|
||||
.add("deliveryTime", finalDeliveryTime);
|
||||
if (StrUtil.isNotBlank(dto.getLogisticsOrderId())) {
|
||||
subChain.add("logisticsOrderId", dto.getLogisticsOrderId());
|
||||
}
|
||||
if (StrUtil.isNotBlank(dto.getCrrgBsnNm())) {
|
||||
subChain.add("crrgBsnNm", dto.getCrrgBsnNm());
|
||||
}
|
||||
dao.update(PointsMallOrderSub.class, subChain, subCnd);
|
||||
|
||||
// 优先使用通知中的主订单号,未提供时取首个匹配子订单的主订单号。
|
||||
// 此处只同步该主订单的物流信息,不检查其余子订单是否全部发货。
|
||||
String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), subs.get(0).getMainOrderId());
|
||||
if (StrUtil.isNotBlank(mainOrderId)) {
|
||||
// 主订单的发货状态字段为字符串,子订单字段为整数。
|
||||
Chain mainChain = Chain.make("deliveryStatus", String.valueOf(deliveredStatus))
|
||||
.add("deliveryTime", finalDeliveryTime);
|
||||
if (StrUtil.isNotBlank(dto.getLogisticsOrderId())) {
|
||||
mainChain.add("logisticsOrderId", dto.getLogisticsOrderId());
|
||||
}
|
||||
if (StrUtil.isNotBlank(dto.getCrrgBsnNm())) {
|
||||
mainChain.add("crrgBsnNm", dto.getCrrgBsnNm());
|
||||
}
|
||||
dao.update(PointsMallOrderMain.class, mainChain, Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "=", mainOrderId).and("delFlag", "=", false));
|
||||
}
|
||||
});
|
||||
|
||||
log.info("供应商已发货事件处理完成,supplierId={},mainOrderId={},orderIds={},deliveryStatus={}",
|
||||
dto.getSupplierId(), dto.getMainOrderId(), orderIds, deliveredStatus);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,4 +1,5 @@
|
||||
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderMessageDTO;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -148,13 +149,15 @@ public class MallSubOrderReturnEventHandler extends AbstractMallBridgeSupport im
|
||||
});
|
||||
try {
|
||||
// callP消息推送
|
||||
PointsMallOrderSub notifySub = BeanUtil.copyProperties(sub, PointsMallOrderSub.class);
|
||||
MallOrderMessageDTO notifySub = BeanUtil.copyProperties(sub, MallOrderMessageDTO.class);
|
||||
notifySub.setOperationTime(parseDate(dto.getOperationTime()));
|
||||
notifySub.setOrderId(dto.getOrderId());
|
||||
notifySub.setPointsPrice(refund);
|
||||
notifySub.setTotalPrice(orderInfo.getTotalPrice());
|
||||
notifySub.setWPayOrAPay(orderInfo.getWPayOrAPay());
|
||||
notifySub.setExtJson(orderExchange.getExtJson());
|
||||
mallCallPMessagePushService.pushMessageToGlobal(StrUtil.blankToDefault(user.getUsername(), user.getLoginname()), notifySub);
|
||||
notifySub.setUserName(StrUtil.blankToDefault(user.getUsername(), user.getLoginname()));
|
||||
mallCallPMessagePushService.pushMessageToGlobal("66666666", notifySub);
|
||||
} catch (Exception e) {
|
||||
log.error("9.2.1 子单取消 或 退货,callP消息推送: {}, {}", dto, e.getMessage());
|
||||
}
|
||||
|
||||
+85
-107
@@ -8,8 +8,7 @@ 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.mallbridge.dto.MallOrderMessageDTO;
|
||||
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
@@ -39,123 +38,102 @@ public class MallCallPMessagePushService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付成功callP消息推送。
|
||||
*
|
||||
* @param mainOrder 订单信息
|
||||
* 统一订单消息入口:11111111 支付成功,22222222 妥投提醒,66666666 积分退回。
|
||||
* 主单、子单均转换为 MallOrderMessageDTO,退货传入退货单号及本次退款积分。
|
||||
* 已成功消息不重复发送;失败记录可在再次调用时重用。
|
||||
* synchronized 防止当前服务实例内的并发通知重复创建消息。
|
||||
*/
|
||||
public void pushMessageToGlobal(PointsMallOrderMain mainOrder) {
|
||||
PointsMallMessageTemplate template = dao.fetch(PointsMallMessageTemplate.class, Cnd.where("id", "=", "11111111").and("delFlag", "=", false));
|
||||
if (template == null) {
|
||||
log.info("支付成功推送消息失败,未配置消息模板");
|
||||
public synchronized void pushMessageToGlobal(String templateId, MallOrderMessageDTO order) {
|
||||
if (order == null || StrUtil.hasBlank(templateId, order.getSupplierId(), order.getUserId(), order.getOrderId())) {
|
||||
throw new IllegalArgumentException("消息模板、供应商、用户和订单不能为空");
|
||||
}
|
||||
if ("66666666".equals(templateId) && nvl(order.getPointsPrice()).signum() <= 0) {
|
||||
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();
|
||||
|
||||
String userId = order.getUserId();
|
||||
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());
|
||||
|
||||
Date finishTime = "11111111".equals(templateId) ? order.getOrderCreateTime()
|
||||
: order.getOrderFinishTime() == null ? now : order.getOrderFinishTime();
|
||||
Date planPushTime = now;
|
||||
String uniqueNo = templateId + "-" + order.getSupplierId() + "-" + order.getOrderId();
|
||||
if ("22222222".equals(templateId)) {
|
||||
// 妥投提醒按自然日去重;取消、退货按业务订单号去重。
|
||||
if (DateUtil.beginOfDay(finishTime).after(DateUtil.beginOfDay(now))) {
|
||||
return;
|
||||
}
|
||||
long day = DateUtil.betweenDay(DateUtil.beginOfDay(finishTime), DateUtil.beginOfDay(now), false);
|
||||
uniqueNo += "-" + day;
|
||||
planPushTime = DateUtil.offsetDay(finishTime, (int) day);
|
||||
}
|
||||
PointsMallMessageTemplate template = dao.fetch(PointsMallMessageTemplate.class,
|
||||
Cnd.where("id", "=", templateId).and("delFlag", "=", false));
|
||||
if (template == null || StrUtil.isBlank(template.getContent())) {
|
||||
log.info("订单消息推送失败,未配置消息模板或模板内容为空,templateId={}", templateId);
|
||||
return;
|
||||
}
|
||||
PointsMallMessage message = dao.fetch(PointsMallMessage.class,
|
||||
Cnd.where("uniqueNo", "=", uniqueNo).and("delFlag", "=", false));
|
||||
if (message != null && Integer.valueOf(1).equals(message.getStatus())) {
|
||||
log.info("订单消息已推送,跳过重复通知:{}", uniqueNo);
|
||||
return;
|
||||
}
|
||||
Map<String, String> params = orderParams(order.getSupplierId(), userId, order.getExtJson());
|
||||
if (StrUtil.isNotBlank(order.getUserName())) {
|
||||
params.put("#{userName}", order.getUserName());
|
||||
}
|
||||
params.put("#{orderId}", order.getOrderId());
|
||||
params.put("#{orderFinishTime}", finishTime == null ? "" : DateUtil.formatDateTime(finishTime));
|
||||
params.put("#{operationTime}", order.getOperationTime() == null ? "" : DateUtil.formatDateTime(order.getOperationTime()));
|
||||
params.put("#{pointPrice}", nvl(order.getPointsPrice()).toPlainString());
|
||||
params.put("#{totalPrice}", nvl(order.getTotalPrice()).toPlainString());
|
||||
params.put("#{payPrice}", nvl(order.getWPayOrAPay()).toPlainString());
|
||||
String content = template.getContent();
|
||||
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
|
||||
content = content.replace(entry.getKey(), entry.getValue());
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
content = content.replace(entry.getKey(), StrUtil.blankToDefault(entry.getValue(), ""));
|
||||
}
|
||||
boolean create = message == null;
|
||||
if (create) {
|
||||
message = new PointsMallMessage();
|
||||
message.setUniqueNo(uniqueNo);
|
||||
}
|
||||
|
||||
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.setTemplateId(templateId);
|
||||
message.setTouser(userId);
|
||||
message.setContent(content);
|
||||
message.setContentLink(template.getContentLink());
|
||||
message.setPlanPushTime(planPushTime);
|
||||
message.setStatus(0);
|
||||
dao.insert(message);
|
||||
if (create) {
|
||||
dao.insert(message);
|
||||
} else {
|
||||
dao.updateIgnoreNull(message);
|
||||
}
|
||||
try {
|
||||
globalMessageSendService.sendLocalSystemMessage(template.getName(), content, 1, Collections.singletonList(userId), null);
|
||||
message.setStatus(1);
|
||||
dao.updateIgnoreNull(message);
|
||||
} catch (Exception e) {
|
||||
message.setStatus(2);
|
||||
dao.updateIgnoreNull(message);
|
||||
throw new IllegalStateException("订单消息推送失败:" + uniqueNo, e);
|
||||
}
|
||||
}
|
||||
|
||||
globalMessageSendService.sendLocalSystemMessage(template.getName(), content, 1, Collections.singletonList(subOrder.getUserId()), null);
|
||||
/** 公共模板变量:用户姓名、供应商名称和订单商品列表。 */
|
||||
private Map<String, String> orderParams(String supplierId, String userId, String extJson) {
|
||||
Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", userId).or("loginname", "=", userId));
|
||||
PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", supplierId).and("delFlag", "=", false));
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("#{userName}", user == null ? userId : StrUtil.blankToDefault(user.getUsername(), userId));
|
||||
params.put("#{supplierName}", supplier == null ? supplierId : StrUtil.blankToDefault(supplier.getSupplierName(), supplierId));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("商品列表: ").append(System.lineSeparator());
|
||||
List<OrderProInfoDTO> products = StrUtil.isBlank(extJson) || "null".equalsIgnoreCase(extJson.trim())
|
||||
? Collections.emptyList() : JSONUtil.toList(extJson, OrderProInfoDTO.class);
|
||||
products.forEach(product -> sb.append(" 商品名称: ").append(product.getName())
|
||||
.append(",商品价格:").append(formatMoney(product.getPrice())).append("元").append(System.lineSeparator()));
|
||||
params.put("#{productList}", sb.toString());
|
||||
return params;
|
||||
}
|
||||
|
||||
private String formatMoney(BigDecimal value) {
|
||||
|
||||
-451
@@ -1,18 +1,6 @@
|
||||
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.budwk.app.sys.models.Sys_user;
|
||||
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo;
|
||||
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.GetInvoiceInfoDTO;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.InvoiceInfoDTO;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO;
|
||||
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.mallbridge.dto.QueryInvoiceInfoDTO;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallAbolishSubOrderEventHandler;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallCancelPayedMainOrderEventHandler;
|
||||
@@ -25,33 +13,15 @@ import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallShipOrderEven
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallSplitSubOrderEventHandler;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallSubOrderReturnEventHandler;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
|
||||
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.PointsMallOrderSub;
|
||||
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Chain;
|
||||
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.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Base64;
|
||||
import java.util.stream.Collectors;
|
||||
import com.budwk.app.sys.services.SysFileService;
|
||||
import com.budwk.app.sys.enums.SysFileEngineTypeEnum;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallPictureOutboundResult;
|
||||
|
||||
/**
|
||||
* 商城桥接订单事件入站服务。
|
||||
@@ -133,425 +103,4 @@ public class MallInboundEventService extends AbstractMallBridgeSupport {
|
||||
handlerRegistry.put(handler.getType(), handler);
|
||||
}
|
||||
}
|
||||
|
||||
public MallInboundResult cancelMainOrder(MallOrderEventDTO dto, boolean paidNotSplit) {
|
||||
String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId());
|
||||
if (StrUtil.isBlank(mainOrderId)) {
|
||||
return MallInboundResult.fail(400, "主订单信息不能为空");
|
||||
}
|
||||
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "=", mainOrderId).and("delFlag", "=", false));
|
||||
if (main == null) {
|
||||
return MallInboundResult.fail(404, "主订单不存在");
|
||||
}
|
||||
if (!paidNotSplit && !Integer.valueOf(ORDER_PENDING_PAY).equals(main.getOrderState())) {
|
||||
return MallInboundResult.fail(400, "只能取消未支付主单");
|
||||
}
|
||||
Date now = new Date();
|
||||
Trans.exec((Atom) () -> {
|
||||
main.setOrderState(ORDER_CANCELLED);
|
||||
main.setOrderUpdateTime(now);
|
||||
dao.updateIgnoreNull(main);
|
||||
dao.update(PointsMallOrderSub.class, Chain.make("orderState", ORDER_CANCELLED).add("orderUpdateTime", now),
|
||||
Cnd.where("supplierId", "=", dto.getSupplierId()).and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false));
|
||||
pointsMallPointsLockService.releaseMain(main.getUserId(), main.getSupplierId(), main.getOrderId());
|
||||
});
|
||||
log.info("积分商城主单取消完成,supplierId={},mainOrderId={},paidNotSplit={}", dto.getSupplierId(), mainOrderId, paidNotSplit);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
|
||||
public MallInboundResult saveOrderSub(MallOrderEventDTO dto) {
|
||||
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, "主订单不存在");
|
||||
}
|
||||
List<String> subOrderIds = splitIds(dto.getOrderId());
|
||||
if (mallOrderQueryOutboundService != null) {
|
||||
try {
|
||||
return splitSubOrderByQuery(dto, main, subOrderIds);
|
||||
} catch (Exception e) {
|
||||
log.error("split sub order by query failed, fallback to local split logic, supplierId={}, mainOrderId={}, orderId={}",
|
||||
dto.getSupplierId(), dto.getMainOrderId(), dto.getOrderId(), e);
|
||||
}
|
||||
}
|
||||
if (subOrderIds.isEmpty()) {
|
||||
return MallInboundResult.fail(400, "子订单信息不能为空");
|
||||
}
|
||||
List<OrderProInfoDTO> products = StrUtil.isBlank(main.getExtJson()) || "null".equalsIgnoreCase(main.getExtJson())
|
||||
? Collections.emptyList()
|
||||
: JSONUtil.toList(main.getExtJson(), OrderProInfoDTO.class);
|
||||
List<PointsMallOrderSub> subLocks = new ArrayList<>();
|
||||
Trans.exec((Atom) () -> {
|
||||
for (String subOrderId : subOrderIds) {
|
||||
PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", subOrderId)
|
||||
.and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false));
|
||||
if (sub == null) {
|
||||
sub = toSub(main, subOrderId);
|
||||
sub.setOrderState(ORDER_IN_PROGRESS);
|
||||
dao.insert(sub);
|
||||
insertProducts(subOrderId, products);
|
||||
} else {
|
||||
sub.setOrderState(ORDER_IN_PROGRESS);
|
||||
sub.setOrderUpdateTime(new Date());
|
||||
dao.updateIgnoreNull(sub);
|
||||
}
|
||||
subLocks.add(sub);
|
||||
}
|
||||
pointsMallPointsLockService.createSubLocks(main, subLocks);
|
||||
});
|
||||
log.info("积分商城拆单完成,supplierId={},mainOrderId={},subOrderCount={}", dto.getSupplierId(), dto.getMainOrderId(), subOrderIds.size());
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
|
||||
private MallInboundResult splitSubOrderByQuery(MallOrderEventDTO dto, PointsMallOrderMain main, List<String> subOrderIds) {
|
||||
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)开始执行:{}", dto);
|
||||
if (subOrderIds.isEmpty()) {
|
||||
return MallInboundResult.fail(400, "缺少拆单信息");
|
||||
}
|
||||
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)调用外部服务获取子单信息:{}", dto);
|
||||
List<OrderInfoDTO> orderInfoList;
|
||||
try {
|
||||
orderInfoList = getOrderInfoList(dto);
|
||||
} catch (Exception e) {
|
||||
log.error("9.2.1 生成订单信息推送-订单状态为:2(拆单子单),调用9.2.2获取订单详情接口异常:{}", e.getMessage(), e);
|
||||
return MallInboundResult.fail(-1, "调用9.2.2获取订单详情接口异常,异常订单:" + subOrderIds);
|
||||
}
|
||||
if (orderInfoList == null || orderInfoList.isEmpty()) {
|
||||
return MallInboundResult.fail(404, "子单不存在");
|
||||
}
|
||||
|
||||
List<PointsMallOrderSub> subLocks = new ArrayList<>();
|
||||
Trans.exec((Atom) () -> {
|
||||
for (OrderInfoDTO info : orderInfoList) {
|
||||
PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", info.getOrderId())
|
||||
.and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false));
|
||||
if (sub == null) {
|
||||
sub = toSub(main, info);
|
||||
sub.setOrderState(ORDER_IN_PROGRESS);
|
||||
dao.insert(sub);
|
||||
insertProducts(info.getOrderId(), info.getOrderProInfos() == null ? Collections.emptyList() : info.getOrderProInfos());
|
||||
} else {
|
||||
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)子单已存在:{}", info);
|
||||
sub.setOrderState(ORDER_IN_PROGRESS);
|
||||
sub.setOrderUpdateTime(new Date());
|
||||
dao.updateIgnoreNull(sub);
|
||||
}
|
||||
subLocks.add(sub);
|
||||
}
|
||||
pointsMallPointsLockService.createSubLocks(main, subLocks);
|
||||
});
|
||||
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)结束,supplierId={},mainOrderId={},subOrderCount={}",
|
||||
dto.getSupplierId(), dto.getMainOrderId(), orderInfoList.size());
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
|
||||
private List<OrderInfoDTO> getOrderInfoList(MallOrderEventDTO dto) {
|
||||
MallOrderQueryDTO queryDTO = new MallOrderQueryDTO();
|
||||
queryDTO.setSupplierId(dto.getSupplierId());
|
||||
queryDTO.setOrderId(dto.getOrderId());
|
||||
queryDTO.setOrderType(dto.getOrderType());
|
||||
MallOutboundResult result = mallOrderQueryOutboundService.orderQuery(queryDTO);
|
||||
if (result == null || result.getResultCode() != 0) {
|
||||
throw new RuntimeException(result == null ? "获取订单详情返回为空" : result.getMessage());
|
||||
}
|
||||
return result.getOrderInfo();
|
||||
}
|
||||
|
||||
private PointsMallOrderSub toSub(PointsMallOrderMain main, OrderInfoDTO info) {
|
||||
PointsMallOrderSub sub = toSub(main, info.getOrderId());
|
||||
sub.setOrderNumberId(info.getOrderNumberId());
|
||||
sub.setAplName(StrUtil.blankToDefault(info.getAplName(), main.getAplName()));
|
||||
sub.setOrderCreateTime(parseDate(info.getCreateTime()));
|
||||
sub.setOrderUpdateTime(parseDate(info.getUpdateTime()));
|
||||
sub.setDeliveryTime(parseDate(info.getDeliveryTime()));
|
||||
sub.setOrderFinishTime(parseDate(info.getOrderFinishTime()));
|
||||
sub.setOrderCompleteTime(parseDate(info.getCompleteTime()));
|
||||
sub.setDeliveryStatus(parseInteger(info.getDeliveryStatus()));
|
||||
sub.setFreightCost(nvl(info.getFreightCost()));
|
||||
sub.setWPayOrAPayFreightCost(nvl(info.getWPayOrAPayFreightCost()));
|
||||
sub.setRefund(nvl(info.getRefund()));
|
||||
sub.setWPayOrAPayRefund(nvl(info.getWPayOrAPayRefund()));
|
||||
sub.setLogisticsOrderId(info.getLogisticsOrderId());
|
||||
sub.setCrrgBsnNm(info.getCrrgBsnNm());
|
||||
sub.setTotalPrice(nvl(info.getTotalPrice()));
|
||||
sub.setWPayOrAPay(nvl(info.getWPayOrAPay()));
|
||||
sub.setPointsPrice(nvl(info.getPointsPrice()));
|
||||
sub.setInvoiceCode(info.getInvoiceCode());
|
||||
sub.setExtJson(JSONUtil.toJsonStr(info.getOrderProInfos() == null ? Collections.emptyList() : info.getOrderProInfos()));
|
||||
sub.setReconciliationStatus(0);
|
||||
sub.setInvoiceStatus(0);
|
||||
return sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新子单状态。
|
||||
*
|
||||
* <p>订单类型为:6(确认收货)时,供应商侧“确认收货”对应 zhgh/callP 侧“已妥投(4)”,
|
||||
* 需重新调用 9.2.2 获取订单详情接口,补充确认收货时间、发货时间、商品信息等字段。</p>
|
||||
*
|
||||
* @param dto 请求参数
|
||||
* @param statusEnum zhgh/callP 侧订单状态枚举
|
||||
* @return 操作结果
|
||||
*/
|
||||
public MallInboundResult updateSubOrderStatus(MallOrderEventDTO dto, MallOrderStatusEnum statusEnum) {
|
||||
Integer state = statusEnum.getCode();
|
||||
PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false));
|
||||
if (sub == null) {
|
||||
return MallInboundResult.fail(404, "订单不存在");
|
||||
}
|
||||
if (Integer.valueOf(state).equals(sub.getOrderState())) {
|
||||
log.info("积分商城子单状态已是目标状态,跳过重复处理,supplierId={},orderId={},state={}", dto.getSupplierId(), dto.getOrderId(), state);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
Trans.exec((Atom) () -> {
|
||||
if (statusEnum == MallOrderStatusEnum.RECEIVED) {
|
||||
fillReceivedSubOrder(dto, sub);
|
||||
}
|
||||
sub.setOrderState(state);
|
||||
if (statusEnum != MallOrderStatusEnum.RECEIVED || sub.getOrderUpdateTime() == null) {
|
||||
sub.setOrderUpdateTime(new Date());
|
||||
}
|
||||
if (statusEnum == MallOrderStatusEnum.RECEIVED && sub.getOrderFinishTime() == null) {
|
||||
sub.setOrderFinishTime(new Date());
|
||||
}
|
||||
if (statusEnum == MallOrderStatusEnum.COMPLETED) {
|
||||
sub.setOrderCompleteTime(new Date());
|
||||
}
|
||||
dao.updateIgnoreNull(sub);
|
||||
if (statusEnum == MallOrderStatusEnum.CANCELLED || statusEnum == MallOrderStatusEnum.REFUNDED || statusEnum == MallOrderStatusEnum.ABOLISH) {
|
||||
pointsMallPointsLockService.releaseOrReturnPoints(sub, state, nvl(sub.getRefund(), BigDecimal.ZERO));
|
||||
}
|
||||
if (statusEnum == MallOrderStatusEnum.COMPLETED) {
|
||||
pointsMallPointsLockService.deductCompletedSub(sub);
|
||||
}
|
||||
});
|
||||
log.info("积分商城子单状态更新完成,supplierId={},orderId={},state={}", dto.getSupplierId(), dto.getOrderId(), state);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单类型为:6(确认收货)时,调用 9.2.2 获取订单详情接口,补充子单妥投相关信息。
|
||||
*
|
||||
* <p>供应商侧确认收货只代表电商处已收货,对 zhgh/callP 侧应更新为已妥投(4),
|
||||
* 等待 callP 用户侧确认收货后才进入已完成(5)。</p>
|
||||
*/
|
||||
private void fillReceivedSubOrder(MallOrderEventDTO dto, PointsMallOrderSub sub) {
|
||||
if (mallOrderQueryOutboundService == null) {
|
||||
return;
|
||||
}
|
||||
MallOrderQueryDTO queryDTO = new MallOrderQueryDTO();
|
||||
queryDTO.setSupplierId(dto.getSupplierId());
|
||||
queryDTO.setOrderId(dto.getOrderId());
|
||||
queryDTO.setOrderType(2);
|
||||
log.info("订单类型为:6(确认收货)时,调用9.2.2获取订单详情接口:{}", queryDTO);
|
||||
try {
|
||||
MallOutboundResult result = mallOrderQueryOutboundService.queryOrderInfo(queryDTO);
|
||||
if (result == null || result.getResultCode() != 0 || result.getOrderInfo() == null || result.getOrderInfo().isEmpty()) {
|
||||
log.error("订单类型为:6(确认收货)时,调用9.2.2 获取订单详情失败:{},{}", queryDTO, result == null ? null : result.getMessage());
|
||||
return;
|
||||
}
|
||||
OrderInfoDTO orderInfo = result.getOrderInfo().get(0);
|
||||
sub.setOrderCreateTime(parseDate(orderInfo.getCreateTime()));
|
||||
sub.setOrderUpdateTime(parseDate(orderInfo.getUpdateTime()));
|
||||
sub.setOrderFinishTime(parseDate(orderInfo.getOrderFinishTime()));
|
||||
sub.setDeliveryTime(parseDate(orderInfo.getDeliveryTime()));
|
||||
sub.setDeliveryStatus(parseInteger(orderInfo.getDeliveryStatus()));
|
||||
if (orderInfo.getOrderProInfos() != null) {
|
||||
sub.setExtJson(JSONUtil.toJsonStr(orderInfo.getOrderProInfos()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("订单类型为:6(确认收货)时,调用9.2.2 获取订单详情异常:{},{}", queryDTO, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public MallInboundResult updateDeliveryStatus(MallOrderEventDTO dto) {
|
||||
if (StrUtil.isBlank(dto.getSupplierId()) || StrUtil.isBlank(dto.getOrderId())) {
|
||||
return MallInboundResult.fail(400, "供应商或订单不能为空");
|
||||
}
|
||||
|
||||
List<String> orderIds = splitIds(dto.getOrderId());
|
||||
if (orderIds.isEmpty()) {
|
||||
return MallInboundResult.fail(400, "订单信息不能为空");
|
||||
}
|
||||
|
||||
Date deliveryTime = parseDate(dto.getDeliveryTime());
|
||||
if (deliveryTime == null) {
|
||||
deliveryTime = new Date();
|
||||
}
|
||||
|
||||
Cnd subCnd = Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "in", orderIds)
|
||||
.and("delFlag", "=", false);
|
||||
if (StrUtil.isNotBlank(dto.getUserId())) {
|
||||
Sys_user user = fetchUser(dto.getUserId());
|
||||
if (user == null) {
|
||||
return MallInboundResult.fail(400, "用户ID未配置或不存在");
|
||||
}
|
||||
subCnd.and("userId", "=", user.getId());
|
||||
}
|
||||
|
||||
List<PointsMallOrderSub> subs = dao.query(PointsMallOrderSub.class, subCnd);
|
||||
if (subs.isEmpty()) {
|
||||
return MallInboundResult.fail(404, "订单不存在");
|
||||
}
|
||||
|
||||
Date finalDeliveryTime = deliveryTime;
|
||||
Integer deliveredStatus = 1;
|
||||
Trans.exec((Atom) () -> {
|
||||
Chain subChain = Chain.make("deliveryStatus", deliveredStatus)
|
||||
.add("deliveryTime", finalDeliveryTime);
|
||||
if (StrUtil.isNotBlank(dto.getLogisticsOrderId())) {
|
||||
subChain.add("logisticsOrderId", dto.getLogisticsOrderId());
|
||||
}
|
||||
if (StrUtil.isNotBlank(dto.getCrrgBsnNm())) {
|
||||
subChain.add("crrgBsnNm", dto.getCrrgBsnNm());
|
||||
}
|
||||
dao.update(PointsMallOrderSub.class, subChain, subCnd);
|
||||
|
||||
String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), subs.get(0).getMainOrderId());
|
||||
if (StrUtil.isNotBlank(mainOrderId)) {
|
||||
Chain mainChain = Chain.make("deliveryStatus", String.valueOf(deliveredStatus))
|
||||
.add("deliveryTime", finalDeliveryTime);
|
||||
if (StrUtil.isNotBlank(dto.getLogisticsOrderId())) {
|
||||
mainChain.add("logisticsOrderId", dto.getLogisticsOrderId());
|
||||
}
|
||||
if (StrUtil.isNotBlank(dto.getCrrgBsnNm())) {
|
||||
mainChain.add("crrgBsnNm", dto.getCrrgBsnNm());
|
||||
}
|
||||
dao.update(PointsMallOrderMain.class, mainChain, Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "=", mainOrderId).and("delFlag", "=", false));
|
||||
}
|
||||
});
|
||||
|
||||
log.info("供应商已发货事件处理完成,supplierId={},mainOrderId={},orderIds={},deliveryStatus={}",
|
||||
dto.getSupplierId(), dto.getMainOrderId(), orderIds, deliveredStatus);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
|
||||
public MallInboundResult invoiceInfo(MallOrderEventDTO dto) {
|
||||
return invoiceInfo(dto, false);
|
||||
}
|
||||
|
||||
public MallInboundResult invoiceInfo(MallOrderEventDTO dto, boolean retry) {
|
||||
String settlementId = dto.getOrderId();
|
||||
if (StrUtil.hasBlank(dto.getSupplierId(), settlementId)) {
|
||||
return MallInboundResult.fail(400, "供应商和结算单号不能为空");
|
||||
}
|
||||
QueryInvoiceInfoDTO query = new QueryInvoiceInfoDTO();
|
||||
query.setSupplierId(dto.getSupplierId());
|
||||
query.setSettlementId(settlementId);
|
||||
query.setRetry(retry);
|
||||
GetInvoiceInfoDTO result;
|
||||
try {
|
||||
result = mallInvoiceInfoQueryService.queryInvoicesInfo(query);
|
||||
} catch (Exception e) {
|
||||
log.error("调用供应商获取发票信息失败,supplierId={},settlementId={}", dto.getSupplierId(), settlementId, e);
|
||||
return MallInboundResult.fail(-1, "获取供应商发票信息失败");
|
||||
}
|
||||
|
||||
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class,
|
||||
Cnd.where("settlementId", "=", settlementId).and("delFlag", "=", false));
|
||||
if (main == null) {
|
||||
return MallInboundResult.fail(404, "开票申请记录不存在");
|
||||
}
|
||||
if (result == null || !settlementId.equals(result.getSettlementId())) {
|
||||
return MallInboundResult.fail(502, "供应商返回的发票结算单不匹配");
|
||||
}
|
||||
Integer success = result.getBSuccess() == null ? 0 : result.getBSuccess();
|
||||
if (success == 0) {
|
||||
if (result.getInvoiceInfos() == null || result.getInvoiceInfos().isEmpty() || StrUtil.isBlank(result.getSucOrderIds())) {
|
||||
return MallInboundResult.fail(502, "供应商开票结果缺少发票明细或成功订单");
|
||||
}
|
||||
try {
|
||||
List<String> fileIds = result.getInvoiceInfos().stream().map(InvoiceInfoDTO::getInvoiceId).collect(Collectors.toList());
|
||||
if (fileIds.stream().anyMatch(StrUtil::isBlank)) {
|
||||
throw new IllegalArgumentException("供应商发票文件标识为空");
|
||||
}
|
||||
MallPictureOutboundResult files = mallInvoiceInfoQueryService.queryInvoiceFiles(dto.getSupplierId(), settlementId, fileIds);
|
||||
for (InvoiceInfoDTO item : result.getInvoiceInfos()) {
|
||||
if (item.getStatus() != null && item.getStatus() != 1) {
|
||||
throw new IllegalArgumentException("供应商发票尚未生成成功");
|
||||
}
|
||||
String base64 = files.getImageEncodeMap() == null ? null : files.getImageEncodeMap().get(item.getInvoiceId());
|
||||
if (StrUtil.isBlank(base64)) {
|
||||
throw new IllegalArgumentException("未获取到发票文件Base64:" + item.getInvoiceId());
|
||||
}
|
||||
if (base64.startsWith("data:")) {
|
||||
base64 = base64.substring(base64.indexOf(',') + 1);
|
||||
}
|
||||
byte[] bytes = Base64.getDecoder().decode(base64.replaceAll("\\s", ""));
|
||||
if (bytes.length == 0) {
|
||||
throw new IllegalArgumentException("发票文件内容为空");
|
||||
}
|
||||
String fileType = StrUtil.blankToDefault(files.getFileType(), StrUtil.blankToDefault(item.getFileType(), "PDF"));
|
||||
if (!fileType.matches("(?i)pdf|jpg|jpeg|png")) {
|
||||
throw new IllegalArgumentException("不支持的发票文件类型:" + fileType);
|
||||
}
|
||||
String localUrl = sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(),
|
||||
"invoice-" + java.util.UUID.randomUUID() + "." + fileType.toLowerCase(java.util.Locale.ROOT), bytes);
|
||||
item.setUrl(localUrl);
|
||||
item.setImageEncode(localUrl);
|
||||
item.setFileType(fileType);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发票文件同步失败,settlementId={}", settlementId, e);
|
||||
return MallInboundResult.fail(502, "发票文件同步失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
Trans.exec((Atom) () -> {
|
||||
main.setBSuccess(success);
|
||||
main.setSucOrderIds(result.getSucOrderIds());
|
||||
main.setFailOrderIds(result.getFailOrderIds());
|
||||
main.setFailMsg(result.getFailMsg());
|
||||
if (result.getInvoiceInfos() != null) {
|
||||
for (InvoiceInfoDTO item : result.getInvoiceInfos()) {
|
||||
PointsMallInvoiceInfo info = dao.fetch(PointsMallInvoiceInfo.class,
|
||||
Cnd.where("mainId", "=", main.getId()).and("invoiceId", "=", item.getInvoiceId()).and("delFlag", "=", false));
|
||||
if (info == null) {
|
||||
info = new PointsMallInvoiceInfo();
|
||||
info.setMainId(main.getId());
|
||||
info.setInvoiceId(item.getInvoiceId());
|
||||
}
|
||||
info.setInvoiceCode(item.getInvoiceCode());
|
||||
info.setInvoiceDate(item.getInvoiceDate() == null ? null : DateUtil.parse(item.getInvoiceDate().toString()));
|
||||
info.setInvoiceNakeAmount(item.getInvoiceNakeAmount());
|
||||
info.setInvoiceTaxRate(item.getInvoiceTaxRate());
|
||||
info.setInvoiceTaxAmount(item.getInvoiceTaxAmount());
|
||||
info.setInvoiceAmount(item.getInvoiceAmount());
|
||||
info.setInvoiceType(item.getInvoiceType());
|
||||
info.setUrl(item.getUrl());
|
||||
info.setImageEncode(item.getImageEncode());
|
||||
info.setFileType(item.getFileType());
|
||||
info.setCheckCode(item.getCheckCode());
|
||||
info.setInvoiceAddress(item.getInvoiceAddress());
|
||||
info.setRemark(item.getRemark());
|
||||
if (success != 0) {
|
||||
info.setUrl("");
|
||||
info.setImageEncode("");
|
||||
}
|
||||
if (StrUtil.isBlank(info.getId())) {
|
||||
dao.insert(info);
|
||||
} else {
|
||||
dao.updateIgnoreNull(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> successOrderIds = splitIds(result.getSucOrderIds());
|
||||
List<String> failOrderIds = splitIds(result.getFailOrderIds());
|
||||
if (!successOrderIds.isEmpty()) {
|
||||
dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 1), Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "in", successOrderIds).and("delFlag", "=", false));
|
||||
}
|
||||
if (!failOrderIds.isEmpty()) {
|
||||
dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 0), Cnd.where("supplierId", "=", dto.getSupplierId())
|
||||
.and("orderId", "in", failOrderIds).and("delFlag", "=", false));
|
||||
}
|
||||
dao.updateIgnoreNull(main);
|
||||
});
|
||||
log.info("供应商发票信息更新完成,supplierId={},settlementId={}", dto.getSupplierId(), settlementId);
|
||||
return MallInboundResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderMessageDTO;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseResultDTO;
|
||||
@@ -58,7 +59,7 @@ public class MallInboundPurchaseResultService {
|
||||
}
|
||||
try {
|
||||
// callP消息推送
|
||||
mallCallPMessagePushService.pushMessageToGlobal(orderMain);
|
||||
mallCallPMessagePushService.pushMessageToGlobal("11111111", BeanUtil.copyProperties(orderMain, MallOrderMessageDTO.class));
|
||||
} catch (Exception e) {
|
||||
log.error("9.2.14 支付结果通知接口,callP消息推送:{},{}", dto, e.getMessage());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user