feat: 积分商城-对账-发票-优化3

This commit is contained in:
2026-09-08 15:09:42 +08:00
parent 7b877255fb
commit ecb5c8f40a
15 changed files with 627 additions and 596 deletions
@@ -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);
@@ -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;
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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());
}
@@ -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,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);
}
}
@@ -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();
}
}
@@ -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());
}
@@ -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) {
@@ -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();
}
}
@@ -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());
}