From d2cb60f5cf31f14488cd7b349d5f24cbe132e7a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=8C=E5=85=88=E8=88=9C?= <2499071264@qq.com> Date: Tue, 1 Sep 2026 16:07:41 +0800 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=E7=A7=AF=E5=88=86=E5=95=86?= =?UTF-8?q?=E5=9F=8E-=E5=AF=B9=E8=B4=A6=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/OrderInfoByReconciliationDTO.java | 44 ++ .../dto/QueryReconciliationInfoDTO.java | 28 ++ .../dto/ReconciliationResultNoticeDTO.java | 15 + .../dto/ReconciliationResultOrderDTO.java | 22 + .../dto/UnReconciledMsgNoticeDTO.java | 1 + .../model/MallOutboundPageResult.java | 55 +++ .../MallReconciliationInfoQueryService.java | 161 ++++++ ...MallReconciliationResultNoticeService.java | 70 +++ .../MallUnReconciledMsgNoticeService.java | 70 ++- .../PointsMallReconciliationController.java | 8 +- .../PointsMallReconciliationService.java | 3 +- .../PointsMallReconciliationServiceImpl.java | 462 +++++++++++++++--- 12 files changed, 856 insertions(+), 83 deletions(-) create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderInfoByReconciliationDTO.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryReconciliationInfoDTO.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/ReconciliationResultNoticeDTO.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/ReconciliationResultOrderDTO.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallOutboundPageResult.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationInfoQueryService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationResultNoticeService.java diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderInfoByReconciliationDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderInfoByReconciliationDTO.java new file mode 100644 index 00000000..dd85a188 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderInfoByReconciliationDTO.java @@ -0,0 +1,44 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.dto; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +public class OrderInfoByReconciliationDTO { + /** + * 订单id + */ + private String orderId; + + /** + * 供应商id + */ + private String supplierId; + + /** + * 商品购买总价格(含税价) + */ + private BigDecimal totalPrice; + + /** + * 订单消耗积分数,有退货需要计算 + */ + private BigDecimal pointsPrice; + + /** + * 订单微信支付或支付宝支付数目,有退货需要计算 + */ + private BigDecimal wPayOrAPay; + + /** + * 总价格(不含税) + */ + private BigDecimal invoiceNakedPrice; + + /** + * 下单商品信息 + */ + private List orderProInfos; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryReconciliationInfoDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryReconciliationInfoDTO.java new file mode 100644 index 00000000..bdf89d61 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryReconciliationInfoDTO.java @@ -0,0 +1,28 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.dto; + +import lombok.Data; + +@Data +public class QueryReconciliationInfoDTO { + private String supplierId; + private String token; + private Integer reconciliationType; + + /** + * 订单ID + */ + private String orderId; + + /** + * 格式为:yyyy-MM-dd(包含单日) + */ + private String startDate; + + /** + * 格式为:yyyy-MM-dd(包含单日) + */ + private String endDate; + private Integer page; + private Integer pageSize; + private Long timestamp; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/ReconciliationResultNoticeDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/ReconciliationResultNoticeDTO.java new file mode 100644 index 00000000..0063bff9 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/ReconciliationResultNoticeDTO.java @@ -0,0 +1,15 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class ReconciliationResultNoticeDTO { + private String supplierId; + private String token; + private Long timestamp; + private String reconciliationBatchNo; + private String reconciliationTime; + private List reconciliationResults; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/ReconciliationResultOrderDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/ReconciliationResultOrderDTO.java new file mode 100644 index 00000000..4333ce88 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/ReconciliationResultOrderDTO.java @@ -0,0 +1,22 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ReconciliationResultOrderDTO { + private String orderId; + private String userId; + private Integer reconciliationStatus; + private Integer diffType; + private String diffReason; + private BigDecimal platformTotalPrice; + private BigDecimal platformCashAmount; + private BigDecimal platformPointsPrice; + private BigDecimal platformRefundPrice; + private BigDecimal supplierTotalPrice; + private BigDecimal supplierCashAmount; + private BigDecimal supplierPointsPrice; + private BigDecimal supplierRefundPrice; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/UnReconciledMsgNoticeDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/UnReconciledMsgNoticeDTO.java index d2e26696..5b5b27d8 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/UnReconciledMsgNoticeDTO.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/UnReconciledMsgNoticeDTO.java @@ -7,6 +7,7 @@ public class UnReconciledMsgNoticeDTO { private String uniqueSeqNo; private String supplierId; private String token; + private Long timestamp; private String orderIds; private String unReconciledMsg; private String unReconciledType; diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallOutboundPageResult.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallOutboundPageResult.java new file mode 100644 index 00000000..2d98eaa8 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallOutboundPageResult.java @@ -0,0 +1,55 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.model; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoByReconciliationDTO; +import lombok.Data; + +import java.util.List; + +@Data +public class MallOutboundPageResult { + /** + * 0: 查询成功, 其他: 响应失败错误码 + */ + private int resultCode; + + /** + * 失败时, 返回失败原因 + */ + private String message; + + /** + * 总页 + */ + private Integer totalPage; + + /** + * 当前页 + */ + private Integer currentPage; + + //9.2.2 获取订单详情接口时不能为空 + private List orderInfos; + + public static MallOutboundPageResult success() { + MallOutboundPageResult r = new MallOutboundPageResult(); + r.resultCode = 0; + r.message = "成功"; + r.orderInfos = null; + return r; + } + + public static MallOutboundPageResult success(List o) { + MallOutboundPageResult r = new MallOutboundPageResult(); + r.resultCode = 0; + r.message = "成功"; + r.orderInfos = o; + return r; + } + + public static MallOutboundPageResult fail(int code, String message) { + MallOutboundPageResult r = new MallOutboundPageResult(); + r.resultCode = code; + r.message = message; + return r; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationInfoQueryService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationInfoQueryService.java new file mode 100644 index 00000000..adc283e9 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationInfoQueryService.java @@ -0,0 +1,161 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoByReconciliationDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.QueryReconciliationInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundPageResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.util.MallBridgeCryptoUtil; +import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.impl.PropertiesProxy; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +/** + * 获取对账信息 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallReconciliationInfoQueryService { + + private final Dao dao; + + @Inject + private PropertiesProxy conf; + + public MallReconciliationInfoQueryService(Dao dao) { + this.dao = dao; + } + + public List queryReconciliationInfo(QueryReconciliationInfoDTO dto) { + // 从供应商获取对账订单信息 + return getOrderInfoFromSupplier(dto); + } + + private List getOrderInfoFromSupplier(QueryReconciliationInfoDTO dto) { + log.info("9.2.4 对账接口执行开始:{}", dto); + String clientBAB7ServerUrl = conf.get("spdb.bab7.url", ""); + if (StrUtil.isBlank(clientBAB7ServerUrl)) { + throw new IllegalArgumentException("未配置 BAB7 服务地址"); + } + + PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, + Cnd.where("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)); + if (supplier == null) { + log.error("9.2.4 对账接口 未找到对应供应商信息"); + throw new IllegalArgumentException("未找到对应供应商信息"); + } + long timestamp = System.currentTimeMillis(); + // 计算token + String token = MallBridgeCryptoUtil.generateSign(supplier.getSupplierId(), supplier.getClientId(), timestamp); + dto.setToken(token); + dto.setTimestamp(timestamp); + // 组装请求报文 + HashMap bodyMap = buildReqParam(dto, supplier); + log.info("9.2.4 通过对接接口从供应商获取对账订单数据:{}", dto); + String response = HttpUtil.createPost(clientBAB7ServerUrl) + .contentType("application/json;charset=UTF-8") + .body(JSONUtil.toJsonStr(bodyMap)) + .execute() + .body(); + if (response == null) { + return Collections.emptyList(); + } + + JSONObject returnResult = JSONUtil.parseObj(response); + log.info("通过对账接口获取对账订单数据,响应结果:{}", returnResult); + if ("-1".equals(returnResult.getStr("resultCode"))) { + throw new IllegalArgumentException("查询对账信息异常:" + returnResult); + } + + MallOutboundPageResult mallOutboundPageResult = JSONUtil.toBean(returnResult, MallOutboundPageResult.class); + if (mallOutboundPageResult == null) { + return Collections.emptyList(); + } + + // 错误码为0表示查询成功,不等于0表示查询失败 + Integer resultCode = mallOutboundPageResult.getResultCode(); + if (resultCode != 0) { + log.info("通过对账接口获取对账订单数据,查询失败:{}", dto); + } + if (CollUtil.isEmpty(mallOutboundPageResult.getOrderInfos())) { + return Collections.emptyList(); + } + + List orderInfoList = new ArrayList<>(mallOutboundPageResult.getOrderInfos()); + // 页码 + Integer page = dto.getPage(); + // 总页数 + Integer totalPage = mallOutboundPageResult.getTotalPage(); + // 当返回的总页数没有变化,以传入的page来循环拉取 + while (page < totalPage) { + // 循环拉取page页的数据 + dto.setPage(page + 1); + log.info("9.2.4 对账接口-分页循环拉取,入参:{}", dto); + bodyMap = buildReqParam(dto, supplier); + response = HttpUtil.createPost(clientBAB7ServerUrl) + .contentType("application/json;charset=UTF-8") + .body(JSONUtil.toJsonStr(bodyMap)) + .execute() + .body(); + mallOutboundPageResult = JSONUtil.toBean(response, MallOutboundPageResult.class); + log.info("9.2.4 对账接口-分页循环拉取,返回报文:{}", mallOutboundPageResult); + // 错误码为0表示查询成功,不等于0表示查询失败 + if (mallOutboundPageResult.getResultCode() != 0) { + log.info("通过对账接口获取对账订单数据,查询失败:{}", dto); + } + orderInfoList.addAll(mallOutboundPageResult.getOrderInfos()); + page++; + } + + log.info("9.2.4 通过对接接口从供应商获取对账订单数据调用结束,返回报文:{}", orderInfoList); + return orderInfoList; + } + + /** + * 组装请求报文 + * + * @param dto 请求参数 + * @param supplier 供应商信息 + * @return 对账接口请求入参 + */ + private HashMap buildReqParam(QueryReconciliationInfoDTO dto, PointsMallSupplier supplier) { + // 每个供应商的接口地址不一致,后续判断 + String address4 = supplier.getSupplierApiUrl() + "/supplierOpenApi/pointOrder/queryReconciliationInfo"; + String bussDealMd = "P"; + String srvNo = supplier.getSroNo(); + String clientId = supplier.getClientId(); + // 参数(此处需要加密) + String params = JSONUtil.toJsonStr(dto); + log.info("9.2.4 对账接口-请求参数:{}", params); + String keyHex = conf.get("sm4cbc.keyHex", ""); + String ivHex = conf.get("sm4cbc.ivHex", ""); + String encrypt; + try { + encrypt = MallBridgeCryptoUtil.sm4CbcEncrypt(params, keyHex, ivHex); + } catch (Exception e) { + log.error("9.2.4 对账接口请求参数 SM4CBC 加密出错:{}", params, e); + throw new RuntimeException("9.2.4 对账接口请求参数 SM4CBC 加密出错"); + } + + HashMap bodyMap = new HashMap<>(); + bodyMap.put("Address4", address4); + bodyMap.put("BussDealMd", bussDealMd); + bodyMap.put("SroNo", srvNo); + bodyMap.put("RsrvFld1", clientId); + bodyMap.put("AplParmObjct", encrypt); + + return bodyMap; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationResultNoticeService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationResultNoticeService.java new file mode 100644 index 00000000..c3442eab --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationResultNoticeService.java @@ -0,0 +1,70 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.ReconciliationResultNoticeDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.util.MallBridgeCryptoUtil; +import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.impl.PropertiesProxy; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +import java.util.HashMap; + +/** + * 对账结果通知服务 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallReconciliationResultNoticeService { + + private final Dao dao; + + @Inject + private PropertiesProxy conf; + + public MallReconciliationResultNoticeService(Dao dao) { + this.dao = dao; + } + + public MallInboundResult reconciliationResultNotice(ReconciliationResultNoticeDTO dto) { + String clientBAB7ServerUrl = conf.get("spdb.bab7.url", ""); + String keyHex = conf.get("sm4cbc.keyHex", ""); + String ivHex = conf.get("sm4cbc.ivHex", ""); + if (StrUtil.isBlank(clientBAB7ServerUrl)) { + throw new IllegalArgumentException("未配置BAB7服务地址"); + } + + PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, + Cnd.where("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)); + if (supplier == null || StrUtil.isBlank(supplier.getSupplierApiUrl())) { + throw new IllegalArgumentException("未找到供应商接口地址"); + } + + long timestamp = System.currentTimeMillis(); + dto.setTimestamp(timestamp); + dto.setToken(MallBridgeCryptoUtil.generateSign(supplier.getSupplierId(), supplier.getClientId(), timestamp)); + String params = JSONUtil.toJsonStr(dto); + String encrypt = MallBridgeCryptoUtil.sm4CbcEncrypt(params, keyHex, ivHex); + + HashMap bodyMap = new HashMap<>(); + bodyMap.put("Address4", supplier.getSupplierApiUrl() + "/supplierOpenApi/pointOrder/reconciliationResult"); + bodyMap.put("BussDealMd", "P"); + bodyMap.put("SroNo", supplier.getSroNo()); + bodyMap.put("RsrvFld1", supplier.getClientId()); + bodyMap.put("AplParmObjct", encrypt); + + String response = HttpUtil.createPost(clientBAB7ServerUrl) + .contentType("application/json;charset=UTF-8") + .body(JSONUtil.toJsonStr(bodyMap)) + .execute() + .body(); + log.info("对账结果通知接口返回报文:{}", response); + return JSONUtil.toBean(response, MallInboundResult.class); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java index 7bee8b24..e03c0ede 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java @@ -2,25 +2,36 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.service; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.UnReconciledMsgNoticeDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import com.budwk.app.zhgh.pointsmall.mallbridge.util.MallBridgeCryptoUtil; import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationRecord; +import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Cnd; import org.nutz.dao.Dao; +import org.nutz.ioc.impl.PropertiesProxy; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.trans.Atom; import org.nutz.trans.Trans; +import java.util.HashMap; import java.util.List; /** - * 商城桥接对账异常通知服务。 + * 对账异常消息通知服务 */ @Slf4j @IocBean(args = {"refer:dao"}) public class MallUnReconciledMsgNoticeService extends AbstractMallBridgeSupport { + @Inject + private PropertiesProxy conf; + public MallUnReconciledMsgNoticeService(Dao dao) { super(dao); } @@ -50,4 +61,61 @@ public class MallUnReconciledMsgNoticeService extends AbstractMallBridgeSupport log.info("积分商城对账异常通知完成,supplierId={},orderCount={}", dto.getSupplierId(), orderIds.size()); return MallInboundResult.success(); } + + public MallInboundResult unReconciledMsgNotice(UnReconciledMsgNoticeDTO dto) { + log.info("9.3.1 对账异常通知接口调用开始:{}", dto); + String clientBAB7ServerUrl = conf.get("spdb.bab7.url", ""); + String keyHex = conf.get("sm4cbc.keyHex", ""); + String ivHex = conf.get("sm4cbc.ivHex", ""); + if (StrUtil.isBlank(clientBAB7ServerUrl)) { + throw new IllegalArgumentException("未配置BAB7服务地址"); + } + + // 查询供应商信息 + PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, + Cnd.where("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)); + if (supplier == null) { + log.error("9.3.1 对账异常通知接口 未找到对应供应商信息"); + throw new IllegalArgumentException("未找到对应供应商信息"); + } + if (StrUtil.isBlank(supplier.getSupplierApiUrl())) { + return MallInboundResult.fail(404, "供应商地址未配置"); + } + + // 组装请求地址 + String address4 = supplier.getSupplierApiUrl() + "/supplierOpenApi/pointOrder/unReconciledMsg"; + String bussDealMd = "P"; + String sroNo = supplier.getSroNo(); + String clientId = supplier.getClientId(); + long timestamp = System.currentTimeMillis(); + dto.setTimestamp(timestamp); + dto.setToken(MallBridgeCryptoUtil.generateSign(supplier.getSupplierId(), clientId, timestamp)); + // 参数加密 + String params = JSONUtil.toJsonStr(dto); + log.info("9.3.1 对账异常通知接口-请求参数:{}", params); + String encrypt; + try { + encrypt = MallBridgeCryptoUtil.sm4CbcEncrypt(params, keyHex, ivHex); + } catch (Exception e) { + log.error("9.3.1 对账异常通知接口参数加密出错:{}", params, e); + throw new IllegalArgumentException("参数加密失败"); + } + + // 构建请求体 + HashMap bodyMap = new HashMap<>(); + bodyMap.put("Address4", address4); + bodyMap.put("BussDealMd", bussDealMd); + bodyMap.put("SroNo", sroNo); + bodyMap.put("RsrvFld1", clientId); + bodyMap.put("AplParmObjct", encrypt); + + // 发送请求 + String response = HttpUtil.createPost(clientBAB7ServerUrl) + .contentType("application/json;charset=UTF-8") + .body(JSONUtil.toJsonStr(bodyMap)) + .execute() + .body(); + log.info("9.3.1 对账异常通知接口返回报文:{}", response); + return JSONUtil.toBean(response, MallInboundResult.class); + } } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/controller/PointsMallReconciliationController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/controller/PointsMallReconciliationController.java index 6a1bb191..fde95910 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/controller/PointsMallReconciliationController.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/controller/PointsMallReconciliationController.java @@ -48,15 +48,11 @@ public class PointsMallReconciliationController { } } - @At + @At("/execute") @SLog(tag = "积分商城对账管理", msg = "执行订单对账") @SaCheckPermission("points.mall.reconciliation") public Result execute(PointsMallReconciliationParam param) { - try { - return Result.success(pointsMallReconciliationService.execute(param)); - } catch (IllegalArgumentException e) { - return Result.error(e.getMessage()); - } + return pointsMallReconciliationService.executeReconciliation(param); } @At diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/PointsMallReconciliationService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/PointsMallReconciliationService.java index a1817522..547252e0 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/PointsMallReconciliationService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/PointsMallReconciliationService.java @@ -1,6 +1,7 @@ package com.budwk.app.zhgh.pointsmall.reconciliation.service; import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.result.Result; import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationRecord; import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationOrderPageParam; @@ -18,7 +19,7 @@ public interface PointsMallReconciliationService extends BaseService recordPage(PointsMallReconciliationRecordPageParam param); diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java index 8ba61444..06593a94 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java @@ -2,35 +2,65 @@ package com.budwk.app.zhgh.pointsmall.reconciliation.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.result.Result; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoByReconciliationDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.QueryReconciliationInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.ReconciliationResultNoticeDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.ReconciliationResultOrderDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.UnReconciledMsgNoticeDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallReconciliationInfoQueryService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallReconciliationResultNoticeService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallUnReconciledMsgNoticeService; import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; -import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationOrder; import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationRecord; import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationOrderPageParam; import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationParam; import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationRecordPageParam; import com.budwk.app.zhgh.pointsmall.reconciliation.service.PointsMallReconciliationService; import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; +import lombok.extern.slf4j.Slf4j; import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.entity.Record; import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +/** + * 对账功能 + */ +@Slf4j @IocBean(args = {"refer:dao"}) public class PointsMallReconciliationServiceImpl extends BaseServiceImpl implements PointsMallReconciliationService { + @Inject + private MallReconciliationInfoQueryService mallReconciliationInfoQueryService; + + @Inject + private MallUnReconciledMsgNoticeService unReconciledMsgNoticeService; + + @Inject + private MallReconciliationResultNoticeService reconciliationResultNoticeService; + public PointsMallReconciliationServiceImpl(Dao dao) { super(dao); } @@ -53,37 +83,145 @@ public class PointsMallReconciliationServiceImpl extends BaseServiceImpl orders = dao().query(PointsMallOrderSub.class, reconciliationCnd(param, "")); - if (CollUtil.isEmpty(orders)) { - return "未查询到待对账订单"; - } - - String batchNo = DateUtil.format(new Date(), "yyyyMMddHHmmss") + IdUtil.fastSimpleUUID().substring(0, 6); - int success = 0; - int exception = 0; - List records = new ArrayList<>(); - - for (PointsMallOrderSub order : orders) { - PointsMallReconciliationOrder supplierOrder = dao().fetch(PointsMallReconciliationOrder.class, - Cnd.where("orderId", "=", order.getOrderId()) - .and("supplierId", "=", order.getSupplierId()) - .and("delFlag", "=", false)); - PointsMallReconciliationRecord record = buildRecord(order, supplierOrder, batchNo); - if (record.getDiffType() == null || record.getDiffType() == 0) { - success++; - dao().update(PointsMallOrderSub.class, Chain.make("reconciliationStatus", 1), Cnd.where("id", "=", order.getId())); - } else { - exception++; - dao().update(PointsMallOrderSub.class, Chain.make("reconciliationStatus", 2), Cnd.where("id", "=", order.getId())); + public Result executeReconciliation(PointsMallReconciliationParam reconciliationBO) { + Integer reconciliationType = reconciliationBO.getReconciliationType(); + List orderIdList = null; + // 校验参数 + if (reconciliationType == 1) { + if (StrUtil.isBlank(reconciliationBO.getStartDate())) { + return Result.error("请选择开始日期"); } - records.add(record); + } else if (reconciliationType == 2) { // 历史异常订单对账 + if (StrUtil.isBlank(reconciliationBO.getOrderId())) { + return Result.error("订单号不能为空"); + } + orderIdList = StrUtil.splitTrim(reconciliationBO.getOrderId(), ","); + } else { + return Result.error("对账类型不正确"); } - dao().insert(records); - return "对账完成,批次号:" + batchNo + ",成功:" + success + ",异常:" + exception; + // 从浦发积分商城订单表查询对账订单 + List orderInfoList = dao().query(PointsMallOrderSub.class, reconciliationCnd(reconciliationBO, "")); + if (CollUtil.isEmpty(orderInfoList)) { + return Result.error("浦发积分商城没有查询到对账订单数据"); + } + + QueryReconciliationInfoDTO queryReconciliationInfoDTO = new QueryReconciliationInfoDTO(); + queryReconciliationInfoDTO.setSupplierId(reconciliationBO.getSupplierId()); + queryReconciliationInfoDTO.setReconciliationType(reconciliationType); + queryReconciliationInfoDTO.setOrderId(orderIdList == null ? reconciliationBO.getOrderId() : String.join(",", orderIdList)); + queryReconciliationInfoDTO.setStartDate(reconciliationBO.getStartDate()); + queryReconciliationInfoDTO.setEndDate(reconciliationBO.getEndDate()); + queryReconciliationInfoDTO.setPage(1); + queryReconciliationInfoDTO.setPageSize(100); + // 通过对账接口从供应商拉取对账订单数据 + List result = mallReconciliationInfoQueryService.queryReconciliationInfo(queryReconciliationInfoDTO); + if (CollUtil.isEmpty(result)) { + return Result.error("供应商侧没有拉取到对账订单数据"); + } + String supplierId = reconciliationBO.getSupplierId(); + result.forEach(o -> o.setSupplierId(supplierId)); + // 根据供应商ID + 订单ID进行分组 + Map> supplierOrderList = result.stream() + .collect(Collectors.groupingBy(o -> String.join("-", o.getSupplierId(), o.getOrderId()))); + + // 根据供应商ID + 订单ID进行分组 + Map> pfOrderList = orderInfoList.stream() + .collect(Collectors.groupingBy(o -> String.join("-", o.getSupplierId(), o.getOrderId()))); + + Set pfKeySet = pfOrderList.keySet(); + Set supplierKeySet = supplierOrderList.keySet(); + // 浦发商城和供应商都存在的订单 + Set sameKeySet = pfKeySet.stream().filter(supplierKeySet::contains).collect(Collectors.toSet()); + // 1.浦发商城和供应商两边的《供应商ID + 订单ID》可以匹配上 + List reconciliationList1 = getReconciliationResult(sameKeySet, pfOrderList, supplierOrderList); + if (CollUtil.isNotEmpty(reconciliationList1)) { + List reconciliationList0 = reconciliationList1.stream() + .filter(o -> o.getDiffType() == 3).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(reconciliationList0)) { + UnReconciledMsgNoticeDTO noticeDTO = getUnReconciledMsgNoticeDTO(reconciliationBO, reconciliationList0); + noticeDTO.setOrderIds(reconciliationList0.stream().map(PointsMallReconciliationRecord::getOrderId).collect(Collectors.joining(","))); + // 更新子订单状态为对账异常 + subOrderReconciliationUpdate(reconciliationList0, 2); + // 对账异常结果通知接口 + unReconciledMsgNoticeService.unReconciledMsgNotice(noticeDTO); + } + + List reconciliationSuccessList = reconciliationList1.stream() + .filter(o -> o.getDiffType() == 0).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(reconciliationSuccessList)) { + // 更新子订单状态为对账完成 + subOrderReconciliationUpdate(reconciliationSuccessList, 1); + } + } + + // 2.浦发积分商城已确认收货,对账接口未返回 + List reconciliationList2 = orderNotReturn(pfKeySet, supplierKeySet, pfOrderList); + if (CollUtil.isNotEmpty(reconciliationList2)) { + reconciliationList1.addAll(reconciliationList2); + UnReconciledMsgNoticeDTO noticeDTO = getUnReconciledMsgNoticeDTO(reconciliationBO, reconciliationList2); + noticeDTO.setOrderIds(reconciliationList2.stream().map(PointsMallReconciliationRecord::getOrderId).collect(Collectors.joining(","))); + // 更新子订单状态为对账异常 + subOrderReconciliationUpdate(reconciliationList2, 2); + // 对账异常结果通知接口 + unReconciledMsgNoticeService.unReconciledMsgNotice(noticeDTO); + } + + // 3.浦发积分商城不存在此订单,无法对账 + List reconciliationList3 = orderNotExist(supplierKeySet, pfKeySet, supplierOrderList); + if (CollUtil.isNotEmpty(reconciliationList3)) { + reconciliationList1.addAll(reconciliationList3); + UnReconciledMsgNoticeDTO noticeDTO = getUnReconciledMsgNoticeDTO(reconciliationBO, reconciliationList3); + noticeDTO.setOrderIds(reconciliationList3.stream().map(PointsMallReconciliationRecord::getOrderId).collect(Collectors.joining(","))); + // 更新子订单状态为对账异常 + // subOrderReconciliationUpdate(reconciliationList3, 2); + // 对账异常结果通知接口 + unReconciledMsgNoticeService.unReconciledMsgNotice(noticeDTO); + } + + if (CollUtil.isNotEmpty(reconciliationList1)) { + String startDate = reconciliationBO.getStartDate(); + String endDate = reconciliationBO.getEndDate(); + String startEndDate = StrUtil.blankToDefault(startDate, "history").replace("-", "") + + StrUtil.blankToDefault(endDate, "").replace("-", ""); + String loginName = StrUtil.blankToDefault(SecurityUtil.getUserId(), "console"); + LocalDateTime now = LocalDateTime.now(); + String yyyyMMddHHmm = now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")); + reconciliationList1.forEach(o -> + o.setBatchNo(String.format("%s-%s-%s", startEndDate, loginName, yyyyMMddHHmm))); + // 把对账结果写入数据库 + dao().insert(reconciliationList1); + + ReconciliationResultNoticeDTO resultNoticeDTO = new ReconciliationResultNoticeDTO(); + resultNoticeDTO.setSupplierId(reconciliationBO.getSupplierId()); + resultNoticeDTO.setReconciliationBatchNo(reconciliationList1.get(0).getBatchNo()); + resultNoticeDTO.setReconciliationTime(now.toString()); + resultNoticeDTO.setReconciliationResults(reconciliationList1.stream().map(o -> { + ReconciliationResultOrderDTO resultOrderDTO = new ReconciliationResultOrderDTO(); + resultOrderDTO.setOrderId(o.getOrderId()); + resultOrderDTO.setUserId(o.getUserId()); + resultOrderDTO.setReconciliationStatus(o.getDiffType() == 0 ? 1 : 2); + resultOrderDTO.setDiffType(o.getDiffType()); + resultOrderDTO.setDiffReason(o.getDiffReason()); + resultOrderDTO.setPlatformTotalPrice(o.getPTotalPrice()); + resultOrderDTO.setPlatformCashAmount(o.getPCashAmount()); + resultOrderDTO.setPlatformPointsPrice(o.getPPointsPrice()); + resultOrderDTO.setPlatformRefundPrice(o.getPRefund()); + resultOrderDTO.setSupplierTotalPrice(o.getSTotalPrice()); + resultOrderDTO.setSupplierCashAmount(o.getSCashAmount()); + resultOrderDTO.setSupplierPointsPrice(o.getSPointsPrice()); + resultOrderDTO.setSupplierRefundPrice(o.getSRefund()); + return resultOrderDTO; + }).collect(Collectors.toList())); + reconciliationResultNoticeService.reconciliationResultNotice(resultNoticeDTO); + } + return Result.success(); } @Override @@ -192,54 +330,228 @@ public class PointsMallReconciliationServiceImpl extends BaseServiceImpl reasons = new ArrayList<>(); - if (!amountEquals(record.getPTotalPrice(), record.getSTotalPrice())) { - reasons.add("订单总金额不一致"); - } - if (!amountEquals(record.getPPointsPrice(), record.getSPointsPrice())) { - reasons.add("积分金额不一致"); - } - if (!Objects.equals(order.getUserId(), supplierOrder.getUserId())) { - reasons.add("用户ID不一致"); - } - record.setDiffType(reasons.isEmpty() ? 0 : 1); - record.setDiffReason(reasons.isEmpty() ? "对账一致" : String.join(";", reasons)); - return record; + private void subOrderReconciliationUpdate(List reconciliationList, Integer reconciliationStatus) { + // 更新子订单状态为对账异常 + reconciliationList.forEach(o -> dao().update(PointsMallOrderSub.class, + Chain.make("reconciliationStatus", reconciliationStatus), + Cnd.where("orderId", "=", o.getOrderId()).and("supplierId", "=", o.getSupplierId()))); } - private BigDecimal nullToZero(BigDecimal value) { - return value == null ? BigDecimal.ZERO : value; + private UnReconciledMsgNoticeDTO getUnReconciledMsgNoticeDTO(PointsMallReconciliationParam reconciliationBO, + List reconciliationList2) { + UnReconciledMsgNoticeDTO noticeDTO = new UnReconciledMsgNoticeDTO(); + noticeDTO.setUniqueSeqNo("111"); + noticeDTO.setToken("token"); + noticeDTO.setSupplierId(reconciliationBO.getSupplierId()); + noticeDTO.setStartDate(reconciliationBO.getStartDate()); + noticeDTO.setEndDate(reconciliationBO.getEndDate()); + PointsMallReconciliationRecord reconciliation = reconciliationList2.get(0); + noticeDTO.setUnReconciledType(reconciliation.getDiffType() + ""); + noticeDTO.setUnReconciledMsg(reconciliation.getDiffReason()); + return noticeDTO; } - private boolean amountEquals(BigDecimal left, BigDecimal right) { - return nullToZero(left).compareTo(nullToZero(right)) == 0; + /** + * 浦发积分商城已确认收货,对账接口未返回 + * + * @param pfKeySet 浦发key + * @param supplierKeySet 供应商key + * @param pfOrderList 浦发商城对账订单 + * @return 异常订单 + */ + private List orderNotReturn(Set pfKeySet, Set supplierKeySet, + Map> pfOrderList) { + List reconciliationList = new ArrayList<>(); + pfKeySet.stream().filter(key -> !supplierKeySet.contains(key)).forEach(key -> { + List pfOrders = pfOrderList.get(key); + if (CollUtil.isNotEmpty(pfOrders)) { + reconciliationList.add(buildReconciliationRecord(null, pfOrders.get(0), 1, "浦发积分商城已确认收货,对账接口未返回")); + } + }); + return reconciliationList; + } + + /** + * 浦发积分商城不存在此订单,无法对账 + * + * @param supplierKeySet 供应商key + * @param pfKeySet 浦发key + * @param supplierOrderList 供应商对账订单 + * @return 异常订单 + */ + private List orderNotExist(Set supplierKeySet, Set pfKeySet, + Map> supplierOrderList) { + List reconciliationList = new ArrayList<>(); + supplierKeySet.stream().filter(key -> !pfKeySet.contains(key)).forEach(key -> { + List supplierOrders = supplierOrderList.get(key); + if (CollUtil.isNotEmpty(supplierOrders)) { + reconciliationList.add(buildReconciliationRecord(supplierOrders.get(0), null, 2, "浦发积分商城不存在此订单,无法对账")); + } + }); + return reconciliationList; + } + + /** + * 浦发商城和供应商两边的《供应商ID + 订单ID》可以匹配上 + * + * @param sameKeySet 双方都存在的订单ID + * @param pfOrderList 浦发商城的订单 + * @param supplierOrderList 供应商的订单 + * @return 对账结果 + */ + private List getReconciliationResult(Set sameKeySet, + Map> pfOrderList, + Map> supplierOrderList) { + List reconciliationList = new ArrayList<>(); + sameKeySet.forEach(key -> { + List pOrderList = pfOrderList.get(key); + List sOrderList = supplierOrderList.get(key); + BigDecimal pfPointSum = pOrderList.stream() + .map(o -> (o.getPointsPrice() == null ? BigDecimal.ZERO : o.getPointsPrice()) + .subtract(o.getRefund() == null ? BigDecimal.ZERO : o.getRefund())) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal supplierPointSum = sOrderList.stream() + .map(OrderInfoByReconciliationDTO::getTotalPrice) + .reduce(BigDecimal.ZERO, BigDecimal::add); + // 根据供应商ID和订单ID比对积分总金额是否一致 + if (pfPointSum.compareTo(supplierPointSum) != 0) { + reconciliationList.add(buildReconciliationRecord(sOrderList.get(0), pOrderList.get(0), 3, "订单内容不一致,订单金额不一致")); + return; + } + + if (CollUtil.isNotEmpty(pOrderList)) { + OrderInfoByReconciliationDTO sOrder = sOrderList.get(0); + PointsMallOrderSub pfOrder = pOrderList.get(0); + PointsMallReconciliationRecord reconciliation = buildReconciliationRecord(sOrder, pfOrder, 0, "无差异"); + // 比对商品明细的sku,商品数量,商品价格,商品税率是否一致 + if (CollUtil.isNotEmpty(sOrderList)) { + String productInfo = pfOrder.getExtJson(); + // 解析积分商品下的商品明细 + List pfOrderProList = JSONUtil.toList(productInfo, OrderProInfoDTO.class); + // 处理商品名称、价格、税率的小数位(不参与比较的字段) + handleField(pfOrderProList); + + // 从对账接口获取的订单商品 + OrderInfoByReconciliationDTO supplierOrder = sOrderList.get(0); + List supplierOrderProList = supplierOrder.getOrderProInfos(); + handleField(supplierOrderProList); + + // 比较双方商品明细是否一致 + if (!compareProduct(pfOrderProList, supplierOrderProList)) { + reconciliation.setDiffType(3); + reconciliation.setDiffReason("订单内容不一致,商品明细不一致"); + } + } + reconciliationList.add(reconciliation); + } + }); + return reconciliationList; + } + + /** + * 处理商品价格和税率字段的小数位,保证对比的时候小数位一致 + * + * @param productList 订单下的商品集合 + */ + private void handleField(List productList) { + // 商品名称不参与比较,价格和税率小数点位数处理 + productList.forEach(o -> { + o.setName(null); + if (o.getPrice() != null) { + o.setPrice(o.getPrice().setScale(2, RoundingMode.HALF_UP)); + } + if (o.getTaxRate() != null) { + o.setTaxRate(o.getTaxRate().setScale(6, RoundingMode.HALF_UP)); + } + }); + } + + /** + * 比较两个list是否完全一致,不考虑顺序 + * + * @param list1 集合1 + * @param list2 集合2 + * @return 是否相同 + */ + private boolean compareProduct(List list1, List list2) { + // 1. 如果两个列表是同一个引用,直接返回 true + if (list1 == list2) { + return true; + } + + // 2. 如果其中有一个为 null,或者商品总数不一致,直接返回 false + if (list1 == null || list2 == null || list1.size() != list2.size()) { + return false; + } + + // 3. 遍历第一个订单的商品,检查是否都能在第二个订单中找到匹配的商品 + for (OrderProInfoDTO p1 : list1) { + boolean foundMatch = false; + for (OrderProInfoDTO p2 : list2) { + // 通过sku+number+price+taxRate相同判定为商品信息一致 + if (Objects.equals(p1.getSku(), p2.getSku()) && + Objects.equals(p1.getNumber(), p2.getNumber()) && + compareBigDecimal(p1.getPrice(), p2.getPrice()) && + compareBigDecimal(p1.getTaxRate(), p2.getTaxRate())) { + foundMatch = true; + break; + } + } + if (!foundMatch) { + return false; + } + } + return true; + } + + private boolean compareBigDecimal(BigDecimal a, BigDecimal b) { + // 两者都为空,视为相等 + if (a == null && b == null) { + return true; + } + // 只有一个为空,视为不相等 + if (a == null || b == null) { + return false; + } + // 都不为空,使用compareTo 比较数值 + return a.compareTo(b) == 0; + } + + /** + * 订单积分对账数据赋值 + * + * @param sOrder 对账接口返回的订单对账数据 + * @param pOrder 子订单表的订单数据 + * @param diffType 异常类型 + * @param diffReason 异常原因 + * @return 对账结果 + */ + private PointsMallReconciliationRecord buildReconciliationRecord(OrderInfoByReconciliationDTO sOrder, + PointsMallOrderSub pOrder, + int diffType, String diffReason) { + PointsMallReconciliationRecord reconciliation = new PointsMallReconciliationRecord(); + reconciliation.setDiffType(diffType); + reconciliation.setDiffReason(diffReason); + reconciliation.setNoticeStatus(0); + reconciliation.setPushTime(0); + if (sOrder != null) { + // 对账接口返回数据 + reconciliation.setSupplierId(sOrder.getSupplierId()); + reconciliation.setOrderId(sOrder.getOrderId()); + reconciliation.setSTotalPrice(sOrder.getTotalPrice()); + reconciliation.setSPointsPrice(sOrder.getPointsPrice()); + reconciliation.setSCashAmount(sOrder.getWPayOrAPay()); + } + if (pOrder != null) { + // 订单表里数据 + reconciliation.setSupplierId(pOrder.getSupplierId()); + reconciliation.setOrderId(pOrder.getOrderId()); + reconciliation.setUserId(pOrder.getUserId()); + reconciliation.setPTotalPrice(pOrder.getTotalPrice()); + reconciliation.setPPointsPrice(pOrder.getPointsPrice()); + reconciliation.setPCashAmount(pOrder.getWPayOrAPay()); + reconciliation.setPRefund(pOrder.getRefund()); + } + return reconciliation; } } From 43d0d973c90982d2c9d9b04e0be944f78148e1a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=8C=E5=85=88=E8=88=9C?= <2499071264@qq.com> Date: Tue, 1 Sep 2026 16:25:59 +0800 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=E7=A7=AF=E5=88=86=E5=95=86?= =?UTF-8?q?=E5=9F=8E-=E5=AF=B9=E8=B4=A6=E4=BC=98=E5=8C=962?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/budwk/app/sys/models/Sys_user.java | 9 +++++++++ .../mallbridge/service/MallInboundEventService.java | 7 ++++++- .../service/MallInboundPendingOrderService.java | 7 ++++++- .../service/support/AbstractMallBridgeSupport.java | 10 +++++----- src/main/resources/db/points-mall.sql | 4 ++++ .../views/platform/zhghh5/points-mall/index.html | 6 +++--- 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/budwk/app/sys/models/Sys_user.java b/src/main/java/com/budwk/app/sys/models/Sys_user.java index 20985e24..b0eb211b 100644 --- a/src/main/java/com/budwk/app/sys/models/Sys_user.java +++ b/src/main/java/com/budwk/app/sys/models/Sys_user.java @@ -411,6 +411,15 @@ public class Sys_user extends BaseModel implements Serializable { @ColDefine(type = ColType.VARCHAR, width = 50) private String openId; + @Column("vuser_id") + @Comment("虚拟用户ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String vuserId; + + @Column("vorg_id") + @Comment("虚拟组织ID") + @ColDefine(type = ColType.VARCHAR, width = 255) + private String vorgId; /** * 基金会员 diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java index 71b23123..bd831f2d 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java @@ -2,6 +2,7 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.service; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; +import com.budwk.app.sys.models.Sys_user; 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; @@ -369,7 +370,11 @@ public class MallInboundEventService extends AbstractMallBridgeSupport { .and("orderId", "in", orderIds) .and("delFlag", "=", false); if (StrUtil.isNotBlank(dto.getUserId())) { - subCnd.and("userId", "=", dto.getUserId()); + Sys_user user = fetchUser(dto.getUserId()); + if (user == null) { + return MallInboundResult.fail(400, "用户ID未配置或不存在"); + } + subCnd.and("userId", "=", user.getId()); } List subs = dao.query(PointsMallOrderSub.class, subCnd); diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java index 28b0053d..1e60da15 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java @@ -2,6 +2,7 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.service; 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.MallPendingOrderDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; @@ -32,13 +33,17 @@ public class MallInboundPendingOrderService extends AbstractMallBridgeSupport { if (dto.getOrderInfo() == null || StrUtil.isBlank(dto.getOrderInfo().getOrderId())) { return MallInboundResult.fail(400, "订单信息不能为空"); } + Sys_user user = fetchUser(dto.getUserId()); + if (user == null) { + return MallInboundResult.fail(400, "用户虚拟ID未配置或不存在"); + } log.info("积分商城接收待支付订单开始,supplierId={},orderId={}", dto.getSupplierId(), dto.getOrderInfo().getOrderId()); if (dao.count(PointsMallOrderMain.class, Cnd.where("orderId", "=", dto.getOrderInfo().getOrderId()) .and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)) > 0) { return MallInboundResult.fail(-1, "订单已存在"); } Trans.exec((Atom) () -> { - PointsMallOrderMain main = toMain(dto.getSupplierId(), dto.getUserId(), dto.getOrderInfo(), ORDER_PENDING_PAY); + PointsMallOrderMain main = toMain(dto.getSupplierId(), user.getId(), dto.getOrderInfo(), ORDER_PENDING_PAY); dao.insert(main); // 新增主单历史记录 diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java index c1f928bb..fea70ac6 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java @@ -57,12 +57,12 @@ public abstract class AbstractMallBridgeSupport { return dto != null && !StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderId(), dto.getMainOrderId(), dto.getUserId()); } - protected Sys_user fetchUser(String userId) { - Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", userId).and("disabled", "=", false).and("delFlag", "=", false)); - if (user != null) { - return user; + protected Sys_user fetchUser(String vuserId) { + if (StrUtil.isBlank(vuserId)) { + return null; } - return dao.fetch(Sys_user.class, Cnd.where("loginname", "=", userId).and("disabled", "=", false).and("delFlag", "=", false)); + return dao.fetch(Sys_user.class, Cnd.where("vuser_id", "=", vuserId) + .and("disabled", "=", false).and("delFlag", "=", false)); } protected PointsMallOrderExchange toExchange(MallOrderEventDTO dto, OrderInfoDTO orderInfo, PointsMallOrderSub sub, Integer orderStatus) { diff --git a/src/main/resources/db/points-mall.sql b/src/main/resources/db/points-mall.sql index d1982fff..c9af72f1 100644 --- a/src/main/resources/db/points-mall.sql +++ b/src/main/resources/db/points-mall.sql @@ -510,3 +510,7 @@ CREATE TABLE IF NOT EXISTS `points_mall_schedule_user_points_record` ( KEY `idx_points_mall_schedule_user_points_user` (`userId`), KEY `idx_points_mall_schedule_user_points_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城定时发放积分记录'; + +ALTER TABLE sys_user + ADD COLUMN vuser_id VARCHAR(64) NULL COMMENT '虚拟用户ID,积分商城向供应商传递的用户标识', + ADD COLUMN vorg_id VARCHAR(255) NULL COMMENT '虚拟组织ID'; \ No newline at end of file diff --git a/src/main/resources/views/platform/zhghh5/points-mall/index.html b/src/main/resources/views/platform/zhghh5/points-mall/index.html index b58408fb..118f3076 100644 --- a/src/main/resources/views/platform/zhghh5/points-mall/index.html +++ b/src/main/resources/views/platform/zhghh5/points-mall/index.html @@ -151,13 +151,13 @@ layout("/layouts/platform_h5.html"){ this.$toast("暂无跳转地址") return } - const userId = this.$store && this.$store.state.user ? this.$store.state.user.id : "" - if (!userId) { + const vuserId = this.$store && this.$store.state.user ? this.$store.state.user.vuserId : "" + if (!vuserId) { this.$toast("获取用户信息失败,请重新登录后再试") return } window.location.href = this.withSupplierQuery(item.supplierUrl, { - userId: userId + userId: vuserId }) }, withSupplierQuery(url, params) { From 72dfff50a6f8d9508bbe58ac0ef7bdcbc93bf93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=8C=E5=85=88=E8=88=9C?= <2499071264@qq.com> Date: Tue, 1 Sep 2026 16:58:29 +0800 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20=E7=A7=AF=E5=88=86=E5=95=86?= =?UTF-8?q?=E5=9F=8E-=E5=AF=B9=E8=B4=A6=E4=BC=98=E5=8C=963?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/budwk/app/sys/models/Sys_user.java | 8 +++---- .../MallCancelSubOrderEventHandler.java | 14 ++++--------- .../MallInboundPointsQueryService.java | 21 ++++++++++++++++--- .../support/AbstractMallBridgeSupport.java | 12 +++++++---- .../PointsMallReconciliationServiceImpl.java | 9 +++++++- src/main/resources/db/points-mall.sql | 7 +++++-- .../platform/zhghh5/points-mall/index.html | 6 +++--- 7 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/budwk/app/sys/models/Sys_user.java b/src/main/java/com/budwk/app/sys/models/Sys_user.java index b0eb211b..37753d57 100644 --- a/src/main/java/com/budwk/app/sys/models/Sys_user.java +++ b/src/main/java/com/budwk/app/sys/models/Sys_user.java @@ -411,15 +411,15 @@ public class Sys_user extends BaseModel implements Serializable { @ColDefine(type = ColType.VARCHAR, width = 50) private String openId; - @Column("vuser_id") + @Column("virtual_id") @Comment("虚拟用户ID") @ColDefine(type = ColType.VARCHAR, width = 64) - private String vuserId; + private String virtualId; - @Column("vorg_id") + @Column("virtual_org_id") @Comment("虚拟组织ID") @ColDefine(type = ColType.VARCHAR, width = 255) - private String vorgId; + private String virtualOrgId; /** * 基金会员 diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java index a30bbc09..5e52549d 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java @@ -61,7 +61,7 @@ public class MallCancelSubOrderEventHandler implements MallOrderEventHandler { if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderId(), dto.getUserId())) { return MallInboundResult.fail(400, "供应商、订单、用户不能为空"); } - // userId 兼容系统用户ID和工号,后续消息推送需要使用用户信息。 + // 供应商传入 userId 为虚拟用户ID,转换为系统用户后用于消息推送。 Sys_user user = fetchUser(dto.getUserId()); if (user == null) { return MallInboundResult.fail(-1, "用户不存在"); @@ -110,14 +110,8 @@ public class MallCancelSubOrderEventHandler implements MallOrderEventHandler { return MallInboundResult.success(); } - /** - * 入参 userId 可能是系统用户ID或工号,按当前项目用户表兼容查询。 - */ - private Sys_user fetchUser(String userId) { - Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", userId).and("disabled", "=", false).and("delFlag", "=", false)); - if (user != null) { - return user; - } - return dao.fetch(Sys_user.class, Cnd.where("loginname", "=", userId).and("disabled", "=", false).and("delFlag", "=", false)); + private Sys_user fetchUser(String virtualId) { + return dao.fetch(Sys_user.class, Cnd.where("virtual_id", "=", virtualId) + .and("disabled", "=", false).and("delFlag", "=", false)); } } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java index 822f1eef..125f1dc5 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java @@ -1,9 +1,13 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.service; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.sys.models.Sys_user; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPointQueryDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundPointsResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService; import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Dao; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; @@ -11,17 +15,28 @@ import org.nutz.ioc.loader.annotation.IocBean; * 商城桥接积分查询服务。 */ @Slf4j -@IocBean -public class MallInboundPointsQueryService { +@IocBean(args = {"refer:dao"}) +public class MallInboundPointsQueryService extends AbstractMallBridgeSupport { @Inject private PointsMallUserPointsService pointsMallUserPointsService; + public MallInboundPointsQueryService(Dao dao) { + super(dao); + } + /** * 查询用户可用积分。 */ public MallInboundPointsResult queryPoints(MallPointQueryDTO dto) { log.info("积分商城查询用户积分,supplierId={},userId={}", dto == null ? null : dto.getSupplierId(), dto == null ? null : dto.getUserId()); - return MallInboundPointsResult.success(pointsMallUserPointsService.availablePoints(dto.getUserId())); + if (dto == null || StrUtil.isBlank(dto.getUserId())) { + return MallInboundPointsResult.fail(400, "用户虚拟ID不能为空"); + } + Sys_user user = fetchUser(dto.getUserId()); + if (user == null) { + return MallInboundPointsResult.fail(400, "用户虚拟ID未配置或不存在"); + } + return MallInboundPointsResult.success(pointsMallUserPointsService.availablePoints(user.getId())); } } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java index fea70ac6..0be4563d 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java @@ -57,20 +57,24 @@ public abstract class AbstractMallBridgeSupport { return dto != null && !StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderId(), dto.getMainOrderId(), dto.getUserId()); } - protected Sys_user fetchUser(String vuserId) { - if (StrUtil.isBlank(vuserId)) { + protected Sys_user fetchUser(String virtualId) { + if (StrUtil.isBlank(virtualId)) { return null; } - return dao.fetch(Sys_user.class, Cnd.where("vuser_id", "=", vuserId) + return dao.fetch(Sys_user.class, Cnd.where("virtual_id", "=", virtualId) .and("disabled", "=", false).and("delFlag", "=", false)); } protected PointsMallOrderExchange toExchange(MallOrderEventDTO dto, OrderInfoDTO orderInfo, PointsMallOrderSub sub, Integer orderStatus) { + Sys_user user = fetchUser(dto.getUserId()); + if (user == null) { + throw new IllegalArgumentException("用户虚拟ID未配置或不存在"); + } PointsMallOrderExchange exchange = new PointsMallOrderExchange(); exchange.setOrderMainId(dto.getMainOrderId()); exchange.setOrderId(dto.getOrderId()); exchange.setSupplierId(dto.getSupplierId()); - exchange.setUserId(dto.getUserId()); + exchange.setUserId(user.getId()); exchange.setAplName(orderInfo.getAplName()); exchange.setOrderCreateTime(parseDate(orderInfo.getCreateTime())); exchange.setOrderUpdateTime(parseDate(orderInfo.getUpdateTime())); diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java index 06593a94..5cfd8541 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.json.JSONUtil; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.result.Result; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.sys.models.Sys_user; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoByReconciliationDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO; @@ -205,7 +206,13 @@ public class PointsMallReconciliationServiceImpl extends BaseServiceImpl { ReconciliationResultOrderDTO resultOrderDTO = new ReconciliationResultOrderDTO(); resultOrderDTO.setOrderId(o.getOrderId()); - resultOrderDTO.setUserId(o.getUserId()); + if (StrUtil.isNotBlank(o.getUserId())) { + Sys_user user = dao().fetch(Sys_user.class, o.getUserId()); + if (user == null || StrUtil.isBlank(user.getVirtualId())) { + throw new IllegalArgumentException("用户虚拟ID未配置或不存在"); + } + resultOrderDTO.setUserId(user.getVirtualId()); + } resultOrderDTO.setReconciliationStatus(o.getDiffType() == 0 ? 1 : 2); resultOrderDTO.setDiffType(o.getDiffType()); resultOrderDTO.setDiffReason(o.getDiffReason()); diff --git a/src/main/resources/db/points-mall.sql b/src/main/resources/db/points-mall.sql index c9af72f1..afb83e9b 100644 --- a/src/main/resources/db/points-mall.sql +++ b/src/main/resources/db/points-mall.sql @@ -512,5 +512,8 @@ CREATE TABLE IF NOT EXISTS `points_mall_schedule_user_points_record` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城定时发放积分记录'; ALTER TABLE sys_user - ADD COLUMN vuser_id VARCHAR(64) NULL COMMENT '虚拟用户ID,积分商城向供应商传递的用户标识', - ADD COLUMN vorg_id VARCHAR(255) NULL COMMENT '虚拟组织ID'; \ No newline at end of file + ADD COLUMN virtual_id VARCHAR(64) NULL COMMENT '虚拟用户ID,积分商城向供应商传递的用户标识', + ADD COLUMN virtual_org_id VARCHAR(255) NULL COMMENT '虚拟组织ID'; + +UPDATE sys_user SET virtual_id = MD5(CONCAT(id, '_', loginName, '_', salt)) +where id is not null and loginName is not null and salt is not null; \ No newline at end of file diff --git a/src/main/resources/views/platform/zhghh5/points-mall/index.html b/src/main/resources/views/platform/zhghh5/points-mall/index.html index 118f3076..939e8814 100644 --- a/src/main/resources/views/platform/zhghh5/points-mall/index.html +++ b/src/main/resources/views/platform/zhghh5/points-mall/index.html @@ -151,13 +151,13 @@ layout("/layouts/platform_h5.html"){ this.$toast("暂无跳转地址") return } - const vuserId = this.$store && this.$store.state.user ? this.$store.state.user.vuserId : "" - if (!vuserId) { + const virtualId = this.$store && this.$store.state.user ? this.$store.state.user.virtualId : "" + if (!virtualId) { this.$toast("获取用户信息失败,请重新登录后再试") return } window.location.href = this.withSupplierQuery(item.supplierUrl, { - userId: vuserId + userId: virtualId }) }, withSupplierQuery(url, params) { From 9159f8912dddbad1ca3ac88013b58976ebb16325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=8C=E5=85=88=E8=88=9C?= <2499071264@qq.com> Date: Thu, 3 Sep 2026 18:19:03 +0800 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=E7=A7=AF=E5=88=86=E5=95=86?= =?UTF-8?q?=E5=9F=8E-=E5=AF=B9=E8=B4=A6-=E5=8F=91=E7=A5=A8-=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../param/PointsMallInvoiceApplyParam.java | 2 +- .../impl/PointsMallInvoiceServiceImpl.java | 69 ++-- .../PointsMallBridgeController.java | 22 ++ .../mallbridge/dto/MallInvoiceApplyDTO.java | 2 +- .../dto/MallSupplierSessionDTO.java | 8 + .../service/MallInboundEventService.java | 80 ++++- .../MallInvoiceApplyOutboundService.java | 107 +++--- .../service/MallInvoiceInfoQueryService.java | 82 +++-- .../PointsMallReconciliationServiceImpl.java | 14 +- ...tsMallReconciliationRecordServiceImpl.java | 8 +- .../impl/PointsMallSupplierServiceImpl.java | 40 ++- .../zhgh/points-mall/invoice/index.html | 4 +- .../points-mall/reconciliation/index.html | 326 +++++++++++++----- .../platform/zhghh5/points-mall/index.html | 28 +- 14 files changed, 558 insertions(+), 234 deletions(-) create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallSupplierSessionDTO.java diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceApplyParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceApplyParam.java index 62c63f96..5240d5b1 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceApplyParam.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceApplyParam.java @@ -11,7 +11,7 @@ public class PointsMallInvoiceApplyParam { private String endDate; private String invoiceDate; private String invoiceTitle; - private String invoiceCode; + private String taxIdNumber; private String invoiceAddress; private String invoiceContact; private Integer invoiceType; diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java index 2ea00da8..808122d9 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java @@ -6,13 +6,15 @@ import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.service.impl.BaseServiceImpl; -import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo; import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain; import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceApplyParam; import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceInfoPageParam; import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceMainPageParam; import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceOrderPageParam; import com.budwk.app.zhgh.pointsmall.invoice.service.PointsMallInvoiceService; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallInvoiceApplyDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInvoiceApplyOutboundService; import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; import org.nutz.dao.Chain; @@ -21,6 +23,7 @@ import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.entity.Record; import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import java.math.BigDecimal; @@ -30,6 +33,9 @@ import java.util.stream.Collectors; @IocBean(args = {"refer:dao"}) public class PointsMallInvoiceServiceImpl extends BaseServiceImpl implements PointsMallInvoiceService { + @Inject + private MallInvoiceApplyOutboundService mallInvoiceApplyOutboundService; + public PointsMallInvoiceServiceImpl(Dao dao) { super(dao); } @@ -38,7 +44,7 @@ public class PointsMallInvoiceServiceImpl extends BaseServiceImpl mainPage(PointsMallInvoiceMainPageParam param) { Cnd cnd = Cnd.where("delFlag", "=", false); if (StrUtil.isNotBlank(param.getSettlementId())) { - cnd.and("settlementId", "like", "%" + param.getSettlementId() + "%"); + cnd.and("settlementId", "=", param.getSettlementId()); } if (param.getBSuccess() != null) { cnd.and("bSuccess", "=", param.getBSuccess()); @@ -63,10 +69,10 @@ public class PointsMallInvoiceServiceImpl extends BaseServiceImpl orderIds = splitIds(dto.getOrderIds()); - log.info("积分商城开票申请开始,supplierId={},settlementId={},orderCount={}", dto.getSupplierId(), dto.getSettlementId(), orderIds.size()); - List orders = dao.query(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId()) - .and("orderId", "in", orderIds).and("delFlag", "=", false)); - if (orders.isEmpty()) { - return MallOutboundResult.fail(404, "未找到可开票订单"); + String clientBAB7ServerUrl = conf.get("spdb.bab7.url", ""); + if (StrUtil.isBlank(clientBAB7ServerUrl)) { + return MallOutboundResult.fail(500, "未配置 BAB7 服务地址"); } - if (dto.getOrderNum() != null && dto.getOrderNum() != orders.size()) { - return MallOutboundResult.fail(400, "订单数量与开票申请不一致"); + PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, + Cnd.where("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)); + if (supplier == null) { + return MallOutboundResult.fail(404, "未找到对应供应商信息"); } - BigDecimal total = orders.stream().map(PointsMallOrderSub::getTotalPrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); - if (dto.getOrderTotalPrice() != null && total.compareTo(dto.getOrderTotalPrice()) != 0) { - return MallOutboundResult.fail(400, "订单金额与开票申请不一致"); + if (StrUtil.isBlank(supplier.getSupplierApiUrl())) { + return MallOutboundResult.fail(404, "供应商地址未配置"); } - Trans.exec((Atom) () -> saveInvoiceApply(dto, orderIds, total)); - log.info("积分商城开票申请完成,supplierId={},settlementId={}", dto.getSupplierId(), dto.getSettlementId()); - return MallOutboundResult.success(); - } + long timestamp = System.currentTimeMillis(); + dto.setToken(MallBridgeCryptoUtil.generateSign(supplier.getSupplierId(), supplier.getClientId(), timestamp)); + dto.setTimestamp(timestamp); - private void saveInvoiceApply(MallInvoiceApplyDTO dto, List orderIds, BigDecimal total) { - PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getSettlementId()).and("delFlag", "=", false)); - if (main == null) { - main = new PointsMallInvoiceMain(); - main.setSettlementId(dto.getSettlementId()); - main.setBSuccess(0); - main.setSucOrderIds(String.join(",", orderIds)); - main.setFailOrderIds(""); - main.setFailMsg(""); - dao.insert(main); - } else { - main.setBSuccess(0); - main.setSucOrderIds(String.join(",", orderIds)); - main.setFailOrderIds(""); - main.setFailMsg(""); - dao.updateIgnoreNull(main); + String keyHex = conf.get("sm4cbc.keyHex", ""); + String ivHex = conf.get("sm4cbc.ivHex", ""); + String encrypt; + try { + encrypt = MallBridgeCryptoUtil.sm4CbcEncrypt(JSONUtil.toJsonStr(dto), keyHex, ivHex); + } catch (Exception e) { + log.error("9.2.5 发票开具申请接口请求参数 SM4CBC 加密出错", e); + return MallOutboundResult.fail(500, "发票开具申请参数加密失败"); } - PointsMallInvoiceInfo info = new PointsMallInvoiceInfo(); - info.setMainId(main.getId()); - info.setInvoiceId(dto.getSettlementId()); - info.setInvoiceCode(dto.getInvoiceCode()); - info.setInvoiceDate(parseDate(dto.getInvoiceDate())); - info.setInvoiceNakeAmount(nvl(dto.getInvoiceNakedPrice(), total)); - info.setInvoiceTaxAmount(nvl(dto.getInvoiceTaxPrice(), BigDecimal.ZERO)); - info.setInvoiceAmount(total); - info.setInvoiceType(Convert.toInt(dto.getInvoiceType(), null)); - info.setInvoiceAddress(dto.getInvoiceAddress()); - info.setInvoiceContact(dto.getInvoiceContact()); - info.setInvoiceContent(dto.getInvoiceContent()); - info.setInvoiceBank(dto.getBankName()); - info.setInvoiceAccount(dto.getBankAccount()); - info.setTaxIdNumber(dto.getInvoiceCode()); - info.setRemark(dto.getRemark()); - dao.insert(info); + HashMap bodyMap = new HashMap<>(); + bodyMap.put("Address4", supplier.getSupplierApiUrl() + "/supplierOpenApi/pointOrder/applyForInvoices"); + bodyMap.put("BussDealMd", "P"); + bodyMap.put("SroNo", supplier.getSroNo()); + bodyMap.put("RsrvFld1", supplier.getClientId()); + bodyMap.put("AplParmObjct", encrypt); - dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 2).add("invoiceCode", dto.getSettlementId()), - Cnd.where("supplierId", "=", dto.getSupplierId()).and("orderId", "in", orderIds)); + String response = HttpUtil.createPost(clientBAB7ServerUrl) + .contentType("application/json;charset=UTF-8") + .body(JSONUtil.toJsonStr(bodyMap)) + .execute() + .body(); + if (StrUtil.isBlank(response)) { + return MallOutboundResult.fail(502, "供应商开票申请无响应"); + } + return JSONUtil.toBean(response, MallOutboundResult.class); } } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java index 656571f0..50aabaad 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java @@ -1,19 +1,22 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.service; import cn.hutool.core.util.StrUtil; -import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo; -import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; 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.QueryInvoiceInfoDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import com.budwk.app.zhgh.pointsmall.mallbridge.util.MallBridgeCryptoUtil; +import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; import lombok.extern.slf4j.Slf4j; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; +import org.nutz.ioc.impl.PropertiesProxy; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; -import java.util.List; -import java.util.stream.Collectors; +import java.util.HashMap; /** * 商城桥接发票信息查询服务。 @@ -22,6 +25,9 @@ import java.util.stream.Collectors; @IocBean(args = {"refer:dao"}) public class MallInvoiceInfoQueryService extends AbstractMallBridgeSupport { + @Inject + private PropertiesProxy conf; + public MallInvoiceInfoQueryService(Dao dao) { super(dao); } @@ -33,21 +39,57 @@ public class MallInvoiceInfoQueryService extends AbstractMallBridgeSupport { if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getSettlementId())) { throw new IllegalArgumentException("发票查询参数不能为空"); } - log.info("积分商城查询发票信息,supplierId={},settlementId={}", dto.getSupplierId(), dto.getSettlementId()); - PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getSettlementId()) - .and("delFlag", "=", false)); - if (main == null) { - throw new IllegalArgumentException("发票信息不存在"); + String clientBAB7ServerUrl = conf.get("spdb.bab7.url", ""); + if (StrUtil.isBlank(clientBAB7ServerUrl)) { + throw new IllegalArgumentException("未配置 BAB7 服务地址"); } - List invoiceInfos = dao.query(PointsMallInvoiceInfo.class, Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false)) - .stream().map(this::toInvoiceInfoDTO).collect(Collectors.toList()); - GetInvoiceInfoDTO result = new GetInvoiceInfoDTO(); - result.setBSuccess(main.getBSuccess()); - result.setSettlementId(main.getSettlementId()); - result.setSucOrderIds(main.getSucOrderIds()); - result.setFailOrderIds(main.getFailOrderIds()); - result.setFailMsg(main.getFailMsg()); - result.setInvoiceInfos(invoiceInfos); - return result; + PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, + Cnd.where("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)); + if (supplier == null) { + throw new IllegalArgumentException("未找到对应供应商信息"); + } + if (StrUtil.isBlank(supplier.getSupplierApiUrl())) { + throw new IllegalArgumentException("供应商地址未配置"); + } + + long timestamp = System.currentTimeMillis(); + dto.setToken(MallBridgeCryptoUtil.generateSign(supplier.getSupplierId(), supplier.getClientId(), timestamp)); + dto.setTimestamp(timestamp); + String encrypt; + try { + encrypt = MallBridgeCryptoUtil.sm4CbcEncrypt( + JSONUtil.toJsonStr(dto), + conf.get("sm4cbc.keyHex", ""), + conf.get("sm4cbc.ivHex", "") + ); + } catch (Exception e) { + log.error("9.2.10 发票获取接口请求参数 SM4CBC 加密出错", e); + throw new IllegalArgumentException("发票获取请求参数加密失败"); + } + + HashMap bodyMap = new HashMap<>(); + bodyMap.put("Address4", supplier.getSupplierApiUrl() + "/supplierOpenApi/pointOrder/queryInvoicesInfo"); + bodyMap.put("BussDealMd", "P"); + bodyMap.put("SroNo", supplier.getSroNo()); + bodyMap.put("RsrvFld1", supplier.getClientId()); + bodyMap.put("AplParmObjct", encrypt); + + String response = HttpUtil.createPost(clientBAB7ServerUrl) + .contentType("application/json;charset=UTF-8") + .body(JSONUtil.toJsonStr(bodyMap)) + .execute() + .body(); + if (StrUtil.isBlank(response)) { + throw new IllegalArgumentException("没有查询到发票信息"); + } + JSONObject responseJson = JSONUtil.parseObj(response); + if (responseJson.getInt("resultCode", -1) != 0) { + throw new IllegalArgumentException(StrUtil.blankToDefault(responseJson.getStr("message"), "获取发票信息失败")); + } + JSONObject invoiceInfo = responseJson.getJSONObject("getInvoiceInfo"); + if (invoiceInfo == null) { + throw new IllegalArgumentException("没有查询到发票信息"); + } + return JSONUtil.toBean(invoiceInfo, GetInvoiceInfoDTO.class); } } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java index 5cfd8541..ca3edacc 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java @@ -238,21 +238,21 @@ public class PointsMallReconciliationServiceImpl extends BaseServiceImpl reconciliationList, Integer reconciliationStatus) { diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/service/impl/PointsMallReconciliationRecordServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/service/impl/PointsMallReconciliationRecordServiceImpl.java index 405b3a43..f8aabb03 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/service/impl/PointsMallReconciliationRecordServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/service/impl/PointsMallReconciliationRecordServiceImpl.java @@ -30,21 +30,21 @@ public class PointsMallReconciliationRecordServiceImpl extends BaseServiceImpl

implements PointsMallSupplierService { + @Inject + private RedisService redisService; + public PointsMallSupplierServiceImpl(Dao dao) { super(dao); } @@ -52,7 +65,32 @@ public class PointsMallSupplierServiceImpl extends BaseServiceImpl list = dao().query(PointsMallSupplier.class, cnd); + if (Objects.equals(1, mallType)) { + Sys_user user = dao().fetch(Sys_user.class, SecurityUtil.getUserId()); + if (user == null || StrUtil.isBlank(user.getVirtualId())) { + throw new BaseException("用户不存在"); + } + for (PointsMallSupplier supplier : list) { + supplier.setSupplierUrl(getSupplierIndexUrl(user, supplier)); + } + } + return list; + } + + private String getSupplierIndexUrl(Sys_user user, PointsMallSupplier supplier) { + String supplierUrl = supplier.getSupplierUrl(); + if (StrUtil.isBlank(supplierUrl) || !Objects.equals(1, supplier.getMallType())) { + return supplierUrl; + } + Map params = new HashMap<>(); + params.put("userId", user.getVirtualId()); + if ("10101013".equals(supplier.getSupplierId())) { + params.put("orgId", user.getVirtualOrgId()); + } + String keyword = IdUtil.fastSimpleUUID(); + redisService.setex("supplier-session:" + keyword, 300, JSONUtil.toJsonStr(params)); + return supplierUrl.concat(keyword); } @Override diff --git a/src/main/resources/views/platform/zhgh/points-mall/invoice/index.html b/src/main/resources/views/platform/zhgh/points-mall/invoice/index.html index 63be5be5..8288e294 100644 --- a/src/main/resources/views/platform/zhgh/points-mall/invoice/index.html +++ b/src/main/resources/views/platform/zhgh/points-mall/invoice/index.html @@ -86,7 +86,7 @@ layout("/layouts/platform.html"){ - + @@ -275,7 +275,7 @@ layout("/layouts/platform.html"){ endDate: null, invoiceDate: null, invoiceTitle: null, - invoiceCode: null, + taxIdNumber: null, invoiceAddress: null, invoiceContact: null, invoiceType: 10, diff --git a/src/main/resources/views/platform/zhgh/points-mall/reconciliation/index.html b/src/main/resources/views/platform/zhgh/points-mall/reconciliation/index.html index e0ee1f7d..e425c305 100644 --- a/src/main/resources/views/platform/zhgh/points-mall/reconciliation/index.html +++ b/src/main/resources/views/platform/zhgh/points-mall/reconciliation/index.html @@ -3,44 +3,74 @@ layout("/layouts/platform.html"){ #-->

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 查询 + 重置 + 对账 + 开票申请 + + + + - - 对账 - 对账记录 - - + + + @@ -87,39 +117,43 @@ layout("/layouts/platform.html"){ - - - - - - - - - - - - - - - - - - - - - - - {{ reconcilePointsTotal }} - - - + + {{ reconcilePointsTotal }} + + 取消 - 计算金额 确认对账 + + + + + + + + + + + + + + + + + + + + + + + 取消 + 确认开票 + + + @@ -209,6 +243,8 @@ layout("/layouts/platform.html"){ supplierList: [], tableData: [], tableLoading: false, + selections: [], + hasSearched: false, pageForm: { pageNumber: 1, pageSize: 10, @@ -218,6 +254,7 @@ layout("/layouts/platform.html"){ userId: null, startDate: null, endDate: null, + reconciliationType: null, reconciliationStatus: null, invoiceStatus: null }, @@ -232,6 +269,36 @@ layout("/layouts/platform.html"){ startDate: null, endDate: null }, + applyDialogVisible: false, + applyLoading: false, + applyForm: { + invoiceDate: null, + invoiceTitle: null, + taxIdNumber: null, + invoiceAddress: null, + invoiceContact: null, + invoiceType: null + }, + applyRules: { + invoiceDate: [{ required: true, message: "请选择开票日期", trigger: "change" }], + invoiceTitle: [{ required: true, message: "请输入发票抬头", trigger: "blur" }], + taxIdNumber: [{ required: true, message: "请输入纳税人识别号", trigger: "blur" }], + invoiceAddress: [{ required: true, message: "请输入开户地址", trigger: "blur" }], + invoiceContact: [ + { required: true, message: "请输入联系电话", trigger: "blur" }, + { + validator(rule, value, callback) { + if (value && !/^1[3-9]\d{9}$/.test(value)) { + callback(new Error("请输入正确的手机号码")) + } else { + callback() + } + }, + trigger: "blur" + } + ], + invoiceType: [{ required: true, message: "请选择发票类型", trigger: "change" }] + }, recordDialogVisible: false, recordLoading: false, recordList: [], @@ -279,9 +346,32 @@ layout("/layouts/platform.html"){ return index + 1 + (this.recordForm.pageNumber - 1) * this.recordForm.pageSize }, doSearch() { + this.hasSearched = true + this.selections = [] this.pageForm.pageNumber = 1 this.pageData() }, + resetSearch() { + this.pageForm = { + pageNumber: 1, + pageSize: 10, + totalCount: 0, + supplierId: null, + orderId: null, + userId: null, + startDate: null, + endDate: null, + reconciliationType: null, + reconciliationStatus: null, + invoiceStatus: null + } + this.hasSearched = false + this.selections = [] + this.pageData() + }, + selectionChange(rows) { + this.selections = rows || [] + }, async pageData() { this.tableLoading = true const resp = await this.$axios.post(loc() + "/pageData", this.pageForm) @@ -308,48 +398,114 @@ layout("/layouts/platform.html"){ this.supplierList = resp.data || [] } }, - openReconcileDialog() { + async openReconcileDialog() { this.reconcileForm = { supplierId: this.pageForm.supplierId, - reconciliationType: this.pageForm.reconciliationStatus === 2 ? 2 : 1, + reconciliationType: this.pageForm.reconciliationType, orderId: this.pageForm.orderId, startDate: this.pageForm.startDate, endDate: this.pageForm.endDate } this.reconcilePointsTotal = 0 - this.reconcileDialogVisible = true - this.loadPointsTotal() - }, - async loadPointsTotal() { - if (!this.reconcileForm.supplierId || !this.reconcileForm.startDate || !this.reconcileForm.endDate || !this.reconcileForm.reconciliationType) { - this.notifyWarning("请先选择供应商、对账类型和对账日期") + if (!this.reconciliationParamsComplete()) { + this.notifyWarning(this.reconcileForm.reconciliationType === 2 ? "请选择供应商、对账类型、对账日期并填写订单ID" : "请选择供应商、对账类型和对账日期") return } + if (await this.loadPointsTotal()) { + this.reconcileDialogVisible = true + } + }, + reconciliationParamsComplete() { + if (!this.reconcileForm.supplierId || !this.reconcileForm.reconciliationType) { + return false + } + if (!this.reconcileForm.startDate || !this.reconcileForm.endDate) { + return false + } + return this.reconcileForm.reconciliationType !== 2 || !!this.reconcileForm.orderId + }, + async loadPointsTotal() { + if (!this.reconciliationParamsComplete()) { + return false + } this.pointsTotalLoading = true - const resp = await this.$axios.post(loc() + "/pointsTotal", this.reconcileForm) - this.pointsTotalLoading = false - if (resp.code === 0) { - this.reconcilePointsTotal = resp.data || 0 - } else { + try { + const resp = await this.$axios.post(loc() + "/pointsTotal", this.reconcileForm) + if (resp.code === 0) { + this.reconcilePointsTotal = resp.data || 0 + return true + } this.notifyWarning(resp.msg) + return false + } finally { + this.pointsTotalLoading = false } }, async executeReconciliation() { - if (!this.reconcileForm.supplierId || !this.reconcileForm.startDate || !this.reconcileForm.endDate || !this.reconcileForm.reconciliationType) { - this.notifyWarning("请先选择供应商、对账类型和对账日期") + if (!this.reconciliationParamsComplete()) { + this.notifyWarning(this.reconcileForm.reconciliationType === 2 ? "请先选择供应商并填写订单ID" : "请先选择供应商、对账类型和对账日期") return } this.executeLoading = true - const resp = await this.$axios.post(loc() + "/execute", this.reconcileForm) - this.executeLoading = false - if (resp.code === 0) { - this.notifySuccess(resp.data || "对账完成") - this.reconcileDialogVisible = false - this.pageData() - } else { - this.notifyWarning(resp.msg) + try { + const resp = await this.$axios.post(loc() + "/execute", this.reconcileForm) + if (resp.code === 0) { + this.notifySuccess(resp.data || "对账完成") + this.reconcileDialogVisible = false + this.pageData() + } else { + this.notifyWarning(resp.msg) + } + } finally { + this.executeLoading = false } }, + openApplyDialog() { + if (!this.pageForm.supplierId || !this.pageForm.startDate || !this.pageForm.endDate) { + return + } + if (!this.selections.length) { + this.notifyWarning("请先勾选需要开票的订单") + return + } + this.applyForm = { + invoiceDate: null, + invoiceTitle: null, + taxIdNumber: null, + invoiceAddress: null, + invoiceContact: null, + invoiceType: null + } + this.applyDialogVisible = true + }, + submitApply() { + this.$refs.applyFormRef.validate(async (valid) => { + if (!valid) { + return + } + this.applyLoading = true + try { + const params = Object.assign({}, this.applyForm, { + supplierId: this.pageForm.supplierId, + startDate: this.pageForm.startDate, + endDate: this.pageForm.endDate, + orderIds: this.selections.map((item) => item.orderId).join(",") + }) + const resp = await this.$axios.post("/platform/zhgh/points-mall/invoice/applyInvoice", params) + if (resp.code === 0) { + this.notifySuccess(resp.data || "开票申请成功") + this.applyDialogVisible = false + this.selections = [] + this.$refs.tableRef.clearSelection() + this.pageData() + } else { + this.notifyWarning(resp.msg) + } + } finally { + this.applyLoading = false + } + }) + }, openRecordDialog() { this.recordDialogVisible = true this.recordForm.supplierId = this.pageForm.supplierId diff --git a/src/main/resources/views/platform/zhghh5/points-mall/index.html b/src/main/resources/views/platform/zhghh5/points-mall/index.html index 939e8814..77bcb11f 100644 --- a/src/main/resources/views/platform/zhghh5/points-mall/index.html +++ b/src/main/resources/views/platform/zhghh5/points-mall/index.html @@ -146,36 +146,12 @@ layout("/layouts/platform_h5.html"){ this.userPoints = resp.data || 0 } }, - async openSupplier(item) { + openSupplier(item) { if (!item.supplierUrl) { this.$toast("暂无跳转地址") return } - const virtualId = this.$store && this.$store.state.user ? this.$store.state.user.virtualId : "" - if (!virtualId) { - this.$toast("获取用户信息失败,请重新登录后再试") - return - } - window.location.href = this.withSupplierQuery(item.supplierUrl, { - userId: virtualId - }) - }, - withSupplierQuery(url, params) { - const query = Object.keys(params) - .filter((key) => params[key] !== undefined && params[key] !== null && params[key] !== "") - .map((key) => encodeURIComponent(key) + "=" + encodeURIComponent(params[key])) - .join("&") - if (!query) { - return url - } - const hashIndex = url.indexOf("#") - if (hashIndex > -1) { - const base = url.slice(0, hashIndex) - const hash = url.slice(hashIndex) - const joiner = hash.indexOf("?") > -1 ? "&" : "?" - return base + hash + joiner + query - } - return url + (url.indexOf("?") > -1 ? "&" : "?") + query + window.location.href = item.supplierUrl }, onImgError(e) { e.target.src = this.defaultLogo From 7b877255fbebf899fc6c6eda7a1c04af93ccfcfc Mon Sep 17 00:00:00 2001 From: c-chenxs1 <2499071264@qq.com> Date: Tue, 8 Sep 2026 13:40:12 +0800 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=E7=A7=AF=E5=88=86=E5=95=86?= =?UTF-8?q?=E5=9F=8E-=E5=AF=B9=E8=B4=A6-=E5=8F=91=E7=A5=A8-=E4=BC=98?= =?UTF-8?q?=E5=8C=962?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/sys/services/SysFileService.java | 2 + .../sys/services/impl/SysFileServiceImpl.java | 5 + .../PointsMallInvoiceController.java | 25 +- .../invoice/models/PointsMallInvoiceInfo.java | 3 + .../service/PointsMallInvoiceService.java | 5 +- .../impl/PointsMallInvoiceServiceImpl.java | 129 +++++- .../mallbridge/dto/InvoiceInfoDTO.java | 2 + .../mallbridge/dto/QueryInvoiceInfoDTO.java | 1 + .../MallSplitSubOrderEventHandler.java | 2 +- .../service/MallInboundEventService.java | 67 ++- .../MallInvoiceApplyOutboundService.java | 3 +- .../service/MallInvoiceInfoQueryService.java | 40 +- .../MallOrderQueryOutboundService.java | 28 +- .../MallReconciliationInfoQueryService.java | 72 ++- .../support/AbstractMallBridgeSupport.java | 112 +++++ .../util/MallBridgeResponseUtil.java | 37 ++ .../param/PointsMallReconciliationParam.java | 4 + .../PointsMallReconciliationServiceImpl.java | 50 +-- .../zhgh/points-mall/invoice/index.html | 409 +++++++----------- .../points-mall/reconciliation/index.html | 61 ++- .../zhghh5/points-mall/order/index.html | 1 + 21 files changed, 707 insertions(+), 351 deletions(-) create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeResponseUtil.java diff --git a/src/main/java/com/budwk/app/sys/services/SysFileService.java b/src/main/java/com/budwk/app/sys/services/SysFileService.java index 9976d46b..88f07f0f 100644 --- a/src/main/java/com/budwk/app/sys/services/SysFileService.java +++ b/src/main/java/com/budwk/app/sys/services/SysFileService.java @@ -25,6 +25,8 @@ public interface SysFileService extends BaseService { */ String uploadReturnUrl(String engine, TempFile file); + String uploadReturnUrl(String engine, String fileName, byte[] bytes); + /** * 分页 */ diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java index df36d760..4f730181 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysFileServiceImpl.java @@ -69,6 +69,11 @@ public class SysFileServiceImpl extends BaseServiceImpl implements Sys return this.storageFile(engine, file, false); } + @Override + public String uploadReturnUrl(String engine, String fileName, byte[] bytes) { + return this.storageFile(engine, fileName, bytes, false); + } + @Override public Pagination page(Sys_file file) { return null; diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/controller/PointsMallInvoiceController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/controller/PointsMallInvoiceController.java index f32cee59..b1962558 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/controller/PointsMallInvoiceController.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/controller/PointsMallInvoiceController.java @@ -37,13 +37,34 @@ public class PointsMallInvoiceController { @At @SaCheckPermission("points.mall.invoice") public Result infoPage(PointsMallInvoiceInfoPageParam param) { - return Result.success(pointsMallInvoiceService.infoPage(param)); + try { + return Result.success(pointsMallInvoiceService.infoPage(param)); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } } @At @SaCheckPermission("points.mall.invoice") public Result infoDetail(@Param("id") String id) { - return Result.success(pointsMallInvoiceService.infoDetail(id)); + try { + return Result.success(pointsMallInvoiceService.infoDetail(id)); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + } + + @At + @org.nutz.mvc.annotation.POST + @SLog(tag = "积分商城发票管理", msg = "重新获取发票") + @SaCheckPermission("points.mall.invoice") + public Result retryInvoice(@Param("mainId") String mainId) { + try { + pointsMallInvoiceService.retryInvoice(mainId); + return Result.success(); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } } @At diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/models/PointsMallInvoiceInfo.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/models/PointsMallInvoiceInfo.java index 45922f11..45fee015 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/models/PointsMallInvoiceInfo.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/models/PointsMallInvoiceInfo.java @@ -16,6 +16,9 @@ import java.util.Date; @Comment("积分商城发票信息") public class PointsMallInvoiceInfo extends BaseModel implements Serializable { + // 详情实时读取供应商 PDF 生成结果:1 成功,2 失败,不以本地文件地址推断状态。 + private Integer status; + @Name @Column @Comment("ID") diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/PointsMallInvoiceService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/PointsMallInvoiceService.java index 49d8e144..6a293735 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/PointsMallInvoiceService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/PointsMallInvoiceService.java @@ -3,6 +3,7 @@ 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.invoice.models.PointsMallInvoiceInfo; import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceApplyParam; import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceInfoPageParam; import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceMainPageParam; @@ -19,7 +20,9 @@ public interface PointsMallInvoiceService extends BaseService infoPage(PointsMallInvoiceInfoPageParam param); - Record infoDetail(String id); + PointsMallInvoiceInfo infoDetail(String id); + + void retryInvoice(String mainId); Pagination orderPage(PointsMallInvoiceOrderPageParam param); diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java index 808122d9..588ecc59 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java @@ -13,11 +13,16 @@ import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceMainPagePara import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceOrderPageParam; import com.budwk.app.zhgh.pointsmall.invoice.service.PointsMallInvoiceService; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallInvoiceApplyDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.InvoiceInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.QueryInvoiceInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.GetInvoiceInfoDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult; import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInvoiceApplyOutboundService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInvoiceInfoQueryService; import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; import org.nutz.dao.Chain; +import org.nutz.trans.Atom; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; @@ -25,8 +30,13 @@ import org.nutz.dao.entity.Record; import org.nutz.dao.sql.Sql; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.trans.Trans; 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 java.util.List; import java.util.stream.Collectors; @@ -35,6 +45,10 @@ public class PointsMallInvoiceServiceImpl extends BaseServiceImpl infoPage(PointsMallInvoiceInfoPageParam param) { + if (StrUtil.isNotBlank(param.getMainId()) && dao().count(PointsMallInvoiceInfo.class, + Cnd.where("mainId", "=", param.getMainId()).and("delFlag", "=", false)) == 0) { + PointsMallInvoiceMain main = fetch(param.getMainId()); + PointsMallOrderSub order = main == null ? null : dao().fetch(PointsMallOrderSub.class, + Cnd.where("invoiceCode", "=", main.getSettlementId()).and("delFlag", "=", false)); + if (order != null) { + MallOrderEventDTO event = new MallOrderEventDTO(); + event.setSupplierId(order.getSupplierId()); + event.setOrderId(main.getSettlementId()); + MallInboundResult result = mallInboundEventService.invoiceInfo(event); + if (result.getResultCode() != 0) { + dao().update(PointsMallInvoiceMain.class, Chain.make("bSuccess", 1).add("failMsg", result.getMessage()), + Cnd.where("id", "=", main.getId())); + throw new IllegalArgumentException(result.getMessage()); + } + } + } Cnd cnd = Cnd.where("delFlag", "=", false); if (StrUtil.isNotBlank(param.getMainId())) { cnd.and("mainId", "=", param.getMainId()); @@ -87,13 +118,61 @@ public class PointsMallInvoiceServiceImpl extends BaseServiceImpl info.getInvoiceId().equals(item.getInvoiceId())).findFirst() + .orElseThrow(() -> new IllegalArgumentException("供应商未返回对应发票详情")); + info.setStatus(remote.getStatus()); + info.setRemark(remote.getRemark()); + if (!Integer.valueOf(1).equals(remote.getStatus())) { + info.setUrl(""); + info.setImageEncode(""); + } + return info; + } + + private MallOrderEventDTO invoiceEvent(String mainId) { + PointsMallInvoiceMain main = StrUtil.isBlank(mainId) ? null : dao().fetch(PointsMallInvoiceMain.class, + Cnd.where("id", "=", mainId).and("delFlag", "=", false)); + if (main == null) { + throw new IllegalArgumentException("开票申请记录不存在"); + } + PointsMallOrderSub order = dao().fetch(PointsMallOrderSub.class, + Cnd.where("invoiceCode", "=", main.getSettlementId()).and("delFlag", "=", false)); + if (order == null) { + throw new IllegalArgumentException("未找到结算单对应的供应商订单"); + } + MallOrderEventDTO event = new MallOrderEventDTO(); + event.setSupplierId(order.getSupplierId()); + event.setOrderId(main.getSettlementId()); + return event; + } + + @Override + public void retryInvoice(String mainId) { + MallInboundResult result = mallInboundEventService.invoiceInfo(invoiceEvent(mainId), true); + if (result.getResultCode() != 0) { + throw new IllegalArgumentException(result.getMessage()); + } + PointsMallInvoiceMain main = fetch(mainId); + if (!Integer.valueOf(0).equals(main.getBSuccess())) { + throw new IllegalArgumentException(StrUtil.blankToDefault(main.getFailMsg(), "供应商发票生成失败")); + } } @Override @@ -132,8 +211,8 @@ public class PointsMallInvoiceServiceImpl extends BaseServiceImpl { + dao().update(PointsMallInvoiceInfo.class, Chain.make("delFlag", true), + Cnd.where("mainId", "=", invoice.getId())); + dao().update(PointsMallInvoiceMain.class, Chain.make("delFlag", true), + Cnd.where("id", "=", invoice.getId())); + dao().update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 0).add("invoiceCode", ""), + Cnd.where("supplierId", "=", param.getSupplierId()).and("orderId", "in", orderIds) + .and("invoiceCode", "=", settlementId).and("invoiceStatus", "in", new Integer[]{0, 2})); + }); + throw new IllegalArgumentException(result.getMessage()); + } invoice.setBSuccess(1); invoice.setSucOrderIds(""); invoice.setFailOrderIds(String.join(",", orderIds)); invoice.setFailMsg(result == null ? "供应商开票申请无响应" : result.getMessage()); updateIgnoreNull(invoice); + dao().update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 0), + Cnd.where("supplierId", "=", param.getSupplierId()).and("orderId", "in", orderIds) + .and("invoiceCode", "=", settlementId).and("invoiceStatus", "=", 2)); throw new IllegalArgumentException(invoice.getFailMsg()); } - dao().update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 2).add("invoiceCode", settlementId), - Cnd.where("supplierId", "=", param.getSupplierId()).and("orderId", "in", orderIds)); return "开票申请已生成,结算单号:" + settlementId; } @@ -189,11 +294,7 @@ public class PointsMallInvoiceServiceImpl extends BaseServiceImpl orderIds = StrUtil.splitTrim(param.getOrderId(), ","); - if (orderIds.size() > 1) { - cnd.and(field(alias, "orderId"), "in", orderIds); - } else { - cnd.and(field(alias, "orderId"), "like", "%" + param.getOrderId() + "%"); - } + cnd.and(field(alias, "orderId"), "in", orderIds); } if (StrUtil.isNotBlank(param.getStartDate())) { cnd.and(field(alias, "orderCompleteTime"), ">=", DateUtil.beginOfDay(DateUtil.parse(param.getStartDate()))); diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/InvoiceInfoDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/InvoiceInfoDTO.java index 657ea471..ae05e848 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/InvoiceInfoDTO.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/InvoiceInfoDTO.java @@ -7,6 +7,8 @@ import java.time.LocalDate; @Data public class InvoiceInfoDTO { + private Integer status; + private String remark; private String invoiceId; private String invoiceCode; private LocalDate invoiceDate; diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryInvoiceInfoDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryInvoiceInfoDTO.java index 293d8696..d77a32e0 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryInvoiceInfoDTO.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryInvoiceInfoDTO.java @@ -4,6 +4,7 @@ import lombok.Data; @Data public class QueryInvoiceInfoDTO { + private Boolean retry; private String supplierId; private String token; private String settlementId; diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java index 159a30df..01ef8cde 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java @@ -94,7 +94,7 @@ public class MallSplitSubOrderEventHandler implements MallOrderEventHandler { // 调用外部服务获取子订单信息 MallOutboundResult result = mallOrderQueryOutboundService.orderQuery(queryDTO); if (result == null || result.getResultCode() != 0) { - throw new RuntimeException(result == null ? "获取订单详情返回为空" : result.getMessage()); + return MallInboundResult.fail(result == null ? 502 : result.getResultCode(), result == null ? "获取订单详情返回为空" : result.getMessage()); } orderInfoList = result.getOrderInfo(); } catch (Exception e) { diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java index a212f205..84e4d5af 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java @@ -47,6 +47,11 @@ 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; /** * 商城桥接订单事件入站服务。 @@ -62,6 +67,8 @@ public class MallInboundEventService extends AbstractMallBridgeSupport { @Inject private MallInvoiceInfoQueryService mallInvoiceInfoQueryService; @Inject + private SysFileService sysFileService; + @Inject private MallCancelPendingOrderEventHandler mallCancelPendingOrderEventHandler; @Inject private MallSplitSubOrderEventHandler mallSplitSubOrderEventHandler; @@ -424,6 +431,10 @@ public class MallInboundEventService extends AbstractMallBridgeSupport { } 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, "供应商和结算单号不能为空"); @@ -431,6 +442,7 @@ public class MallInboundEventService extends AbstractMallBridgeSupport { QueryInvoiceInfoDTO query = new QueryInvoiceInfoDTO(); query.setSupplierId(dto.getSupplierId()); query.setSettlementId(settlementId); + query.setRetry(retry); GetInvoiceInfoDTO result; try { result = mallInvoiceInfoQueryService.queryInvoicesInfo(query); @@ -444,14 +456,56 @@ public class MallInboundEventService extends AbstractMallBridgeSupport { 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 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()); - dao.updateIgnoreNull(main); - - if (success == 0 && result.getInvoiceInfos() != null) { + 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)); @@ -472,6 +526,11 @@ public class MallInboundEventService extends AbstractMallBridgeSupport { 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 { @@ -490,6 +549,8 @@ public class MallInboundEventService extends AbstractMallBridgeSupport { 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(); } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java index 392b2875..1ac134dd 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java @@ -3,6 +3,7 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.service; import cn.hutool.http.HttpUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; +import com.budwk.app.zhgh.pointsmall.mallbridge.util.MallBridgeResponseUtil; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallInvoiceApplyDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult; import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; @@ -79,6 +80,6 @@ public class MallInvoiceApplyOutboundService extends AbstractMallBridgeSupport { if (StrUtil.isBlank(response)) { return MallOutboundResult.fail(502, "供应商开票申请无响应"); } - return JSONUtil.toBean(response, MallOutboundResult.class); + return JSONUtil.toBean(MallBridgeResponseUtil.parse(response), MallOutboundResult.class); } } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java index 50aabaad..96536683 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java @@ -4,6 +4,7 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; +import com.budwk.app.zhgh.pointsmall.mallbridge.util.MallBridgeResponseUtil; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.GetInvoiceInfoDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.QueryInvoiceInfoDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; @@ -17,6 +18,9 @@ import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import java.util.HashMap; +import java.util.List; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.PictureQueryDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallPictureOutboundResult; /** * 商城桥接发票信息查询服务。 @@ -32,6 +36,40 @@ public class MallInvoiceInfoQueryService extends AbstractMallBridgeSupport { super(dao); } + public MallPictureOutboundResult queryInvoiceFiles(String supplierId, String settlementId, List fileIds) { + PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, + Cnd.where("supplierId", "=", supplierId).and("delFlag", "=", false)); + if (supplier == null || StrUtil.isBlank(supplier.getSupplierApiUrl())) { + throw new IllegalArgumentException("供应商或供应商接口地址未配置"); + } + String apiUrl = conf.get("spdb.bab7.url", ""); + if (StrUtil.isBlank(apiUrl)) { + throw new IllegalArgumentException("未配置 BAB7 服务地址"); + } + PictureQueryDTO dto = new PictureQueryDTO(); + long timestamp = System.currentTimeMillis(); + dto.setSupplierId(supplierId); + dto.setToken(MallBridgeCryptoUtil.generateSign(supplierId, supplier.getClientId(), timestamp)); + dto.setTimestamp(timestamp); + dto.setQueryType("2"); + dto.setIdNumber(settlementId); + dto.setFileIdList(fileIds); + HashMap body = new HashMap<>(); + body.put("Address4", supplier.getSupplierApiUrl() + "/supplierOpenApi/pointOrder/queryFileBase64Info"); + body.put("BussDealMd", "P"); + body.put("SroNo", supplier.getSroNo()); + body.put("RsrvFld1", supplier.getClientId()); + body.put("AplParmObjct", MallBridgeCryptoUtil.sm4CbcEncrypt(JSONUtil.toJsonStr(dto), + conf.get("sm4cbc.keyHex", ""), conf.get("sm4cbc.ivHex", ""))); + String response = HttpUtil.createPost(apiUrl).contentType("application/json;charset=UTF-8") + .body(JSONUtil.toJsonStr(body)).execute().body(); + JSONObject payload = MallBridgeResponseUtil.parse(response); + if (payload.getInt("resultCode") != 0) { + throw new IllegalArgumentException(StrUtil.blankToDefault(payload.getStr("message"), "获取发票文件失败")); + } + return JSONUtil.toBean(payload, MallPictureOutboundResult.class); + } + /** * 查询指定结算单的发票信息。 */ @@ -82,7 +120,7 @@ public class MallInvoiceInfoQueryService extends AbstractMallBridgeSupport { if (StrUtil.isBlank(response)) { throw new IllegalArgumentException("没有查询到发票信息"); } - JSONObject responseJson = JSONUtil.parseObj(response); + JSONObject responseJson = MallBridgeResponseUtil.parse(response); if (responseJson.getInt("resultCode", -1) != 0) { throw new IllegalArgumentException(StrUtil.blankToDefault(responseJson.getStr("message"), "获取发票信息失败")); } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java index d6a079a2..f437aeff 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java @@ -2,6 +2,7 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.service; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult; @@ -85,6 +86,31 @@ public class MallOrderQueryOutboundService { .execute() .body(); log.info("9.2.2 获取订单详情调用结束,返回报文:{}", response); - return JSONUtil.toBean(response, MallOutboundResult.class); + JSONObject payload = JSONUtil.parseObj(response); + if (!payload.containsKey("resultCode")) { + JSONObject data = payload.getJSONObject("data"); + JSONObject body = data == null ? null : data.getJSONObject("Body"); + if (body == null) { + return MallOutboundResult.fail(502, "获取订单详情返回报文格式异常"); + } + JSONObject header = body.getJSONObject("RspSvcHeader"); + if (header == null || !"000000000000".equals(header.getStr("ReturnCode"))) { + return MallOutboundResult.fail(502, header == null ? "BAB7返回服务头缺失" : + StrUtil.blankToDefault(header.getStr("ReturnMsg"), "BAB7获取订单详情失败")); + } + JSONObject serviceBody = body.getJSONObject("RspSvcBody"); + if (serviceBody == null || !"200".equals(serviceBody.getStr("ReturnStCd"))) { + return MallOutboundResult.fail(502, "BAB7调用供应商订单详情接口失败"); + } + String content = serviceBody.getStr("ReturnCntnt"); + if (StrUtil.isBlank(content)) { + return MallOutboundResult.fail(502, "供应商订单详情返回内容为空"); + } + payload = JSONUtil.parseObj(content); + } + if (payload.getInt("resultCode") == null) { + return MallOutboundResult.fail(502, "供应商订单详情返回状态缺失"); + } + return JSONUtil.toBean(payload, MallOutboundResult.class); } } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationInfoQueryService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationInfoQueryService.java index adc283e9..70891998 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationInfoQueryService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallReconciliationInfoQueryService.java @@ -69,26 +69,8 @@ public class MallReconciliationInfoQueryService { .body(JSONUtil.toJsonStr(bodyMap)) .execute() .body(); - if (response == null) { - return Collections.emptyList(); - } - - JSONObject returnResult = JSONUtil.parseObj(response); - log.info("通过对账接口获取对账订单数据,响应结果:{}", returnResult); - if ("-1".equals(returnResult.getStr("resultCode"))) { - throw new IllegalArgumentException("查询对账信息异常:" + returnResult); - } - - MallOutboundPageResult mallOutboundPageResult = JSONUtil.toBean(returnResult, MallOutboundPageResult.class); - if (mallOutboundPageResult == null) { - return Collections.emptyList(); - } - - // 错误码为0表示查询成功,不等于0表示查询失败 - Integer resultCode = mallOutboundPageResult.getResultCode(); - if (resultCode != 0) { - log.info("通过对账接口获取对账订单数据,查询失败:{}", dto); - } + log.info("通过对账接口获取对账订单数据,响应结果:{}", response); + MallOutboundPageResult mallOutboundPageResult = parseReconciliationResponse(response); if (CollUtil.isEmpty(mallOutboundPageResult.getOrderInfos())) { return Collections.emptyList(); } @@ -109,11 +91,10 @@ public class MallReconciliationInfoQueryService { .body(JSONUtil.toJsonStr(bodyMap)) .execute() .body(); - mallOutboundPageResult = JSONUtil.toBean(response, MallOutboundPageResult.class); + mallOutboundPageResult = parseReconciliationResponse(response); log.info("9.2.4 对账接口-分页循环拉取,返回报文:{}", mallOutboundPageResult); - // 错误码为0表示查询成功,不等于0表示查询失败 - if (mallOutboundPageResult.getResultCode() != 0) { - log.info("通过对账接口获取对账订单数据,查询失败:{}", dto); + if (CollUtil.isEmpty(mallOutboundPageResult.getOrderInfos())) { + throw new IllegalArgumentException("供应商对账分页数据为空,页码:" + dto.getPage()); } orderInfoList.addAll(mallOutboundPageResult.getOrderInfos()); page++; @@ -123,6 +104,49 @@ public class MallReconciliationInfoQueryService { return orderInfoList; } + /** + * 解析 BAB7 包装或供应商直接返回的对账报文,并校验查询结果。 + */ + private MallOutboundPageResult parseReconciliationResponse(String response) { + if (StrUtil.isBlank(response)) { + throw new IllegalArgumentException("获取对账信息返回报文为空"); + } + JSONObject payload = JSONUtil.parseObj(response); + if (!payload.containsKey("resultCode")) { + JSONObject data = payload.getJSONObject("data"); + JSONObject body = data == null ? null : data.getJSONObject("Body"); + if (body == null) { + throw new IllegalArgumentException("获取对账信息返回报文格式异常"); + } + JSONObject header = body.getJSONObject("RspSvcHeader"); + if (header == null || !"000000000000".equals(header.getStr("ReturnCode"))) { + throw new IllegalArgumentException(header == null ? "BAB7返回服务头缺失" : + StrUtil.blankToDefault(header.getStr("ReturnMsg"), "BAB7获取对账信息失败")); + } + JSONObject serviceBody = body.getJSONObject("RspSvcBody"); + if (serviceBody == null || !"200".equals(serviceBody.getStr("ReturnStCd"))) { + throw new IllegalArgumentException("BAB7调用供应商对账接口失败"); + } + String content = serviceBody.getStr("ReturnCntnt"); + if (StrUtil.isBlank(content)) { + throw new IllegalArgumentException("供应商对账接口返回内容为空"); + } + payload = JSONUtil.parseObj(content); + } + Integer resultCode = payload.getInt("resultCode"); + if (resultCode == null) { + throw new IllegalArgumentException("供应商对账接口返回状态缺失"); + } + if (resultCode != 0) { + throw new IllegalArgumentException(StrUtil.blankToDefault(payload.getStr("message"), "供应商对账查询失败")); + } + MallOutboundPageResult result = JSONUtil.toBean(payload, MallOutboundPageResult.class); + if (CollUtil.isNotEmpty(result.getOrderInfos()) && (result.getTotalPage() == null || result.getTotalPage() < 1)) { + throw new IllegalArgumentException("供应商对账接口返回总页数无效"); + } + return result; + } + /** * 组装请求报文 * diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java index 0be4563d..b9c2b7ce 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java @@ -34,29 +34,77 @@ import java.util.stream.Collectors; *

只放通用转换和常量,具体接口业务放在各自服务中。

*/ public abstract class AbstractMallBridgeSupport { + /** + * 访问令牌有效期,单位为秒(1 小时)。 + */ protected static final long ACCESS_EXPIRE_SECONDS = 3600; + /** + * 刷新令牌有效期,单位为秒(7 天)。 + */ protected static final long REFRESH_EXPIRE_SECONDS = 86400L * 7; + /** + * 订单状态:待支付。 + */ protected static final int ORDER_PENDING_PAY = 0; + /** + * 订单状态:已支付。 + */ protected static final int ORDER_FINISH_PAY = 1; + /** + * 订单状态:处理中。 + */ protected static final int ORDER_IN_PROGRESS = 2; + /** + * 订单状态:已取消。 + */ protected static final int ORDER_CANCELLED = 3; + /** + * 订单状态:已收货。 + */ protected static final int ORDER_RECEIVED = 4; + /** + * 订单状态:已完成。 + */ protected static final int ORDER_COMPLETED = 5; + /** + * 订单状态:退换货。 + */ protected static final int ORDER_EXCHANGE = 6; + /** + * 订单状态:已退款。 + */ protected static final int ORDER_REFUNDED = 7; + /** + * 订单状态:已作废。 + */ protected static final int ORDER_ABOLISH = 8; + /** + * 供桥接服务查询和保存用户、订单、商品等数据的 DAO。 + */ protected final Dao dao; + /** + * 初始化桥接公共支撑类,复用业务服务传入的 DAO。 + */ protected AbstractMallBridgeSupport(Dao dao) { this.dao = dao; } + /** + * 检查订单事件及供应商、订单、主订单、用户标识是否齐全;不校验对应数据是否存在。 + */ protected boolean hasRequiredOrderEventFields(MallOrderEventDTO dto) { return dto != null && !StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderId(), dto.getMainOrderId(), dto.getUserId()); } + /** + * 根据对外用户虚拟 ID 查询未禁用、未删除的系统用户。 + * + * @param virtualId 供应商接口传入的用户虚拟 ID,不是系统用户主键 + * @return 符合条件的用户;虚拟 ID 为空或用户不存在时返回 null + */ protected Sys_user fetchUser(String virtualId) { if (StrUtil.isBlank(virtualId)) { return null; @@ -65,6 +113,16 @@ public abstract class AbstractMallBridgeSupport { .and("disabled", "=", false).and("delFlag", "=", false)); } + /** + * 将退换货事件和详情转换为退换货记录,并从原子单补充商品图片及文件信息。 + * + * @param dto 订单事件,其中 userId 为用户虚拟 ID,保存时转换为系统用户主键 + * @param orderInfo 供应商返回的退换货单详情 + * @param sub 原子单,提供商品扩展信息 + * @param orderStatus 待保存的退换货订单状态 + * @return 尚未入库的退换货记录 + * @throws IllegalArgumentException 虚拟 ID 对应的有效用户不存在时抛出 + */ protected PointsMallOrderExchange toExchange(MallOrderEventDTO dto, OrderInfoDTO orderInfo, PointsMallOrderSub sub, Integer orderStatus) { Sys_user user = fetchUser(dto.getUserId()); if (user == null) { @@ -94,6 +152,9 @@ public abstract class AbstractMallBridgeSupport { return exchange; } + /** + * 按 SKU 将原子单的图片及文件信息覆盖到传入商品列表中;原子单重复 SKU 取首项,传入列表为空时返回空列表。 + */ protected List mergeProductInfo(List products, String subExtJson) { if (products == null || products.isEmpty()) { return Collections.emptyList(); @@ -115,6 +176,15 @@ public abstract class AbstractMallBridgeSupport { return products; } + /** + * 将供应商订单详情转换为主订单,金额为空时按零处理,现金支付标记初始化为 0。 + * + * @param supplierId 供应商标识 + * @param userId 已由虚拟 ID 查得的系统用户主键 + * @param orderInfo 供应商返回的订单详情 + * @param state 主订单状态 + * @return 尚未入库的主订单 + */ protected PointsMallOrderMain toMain(String supplierId, String userId, OrderInfoDTO orderInfo, Integer state) { PointsMallOrderMain main = new PointsMallOrderMain(); main.setOrderId(orderInfo.getOrderId()); @@ -142,6 +212,9 @@ public abstract class AbstractMallBridgeSupport { return main; } + /** + * 从主订单复制信息生成子订单;子订单号为空时使用主订单号,更新时间取当前时间,对账和发票状态初始化为 0。 + */ protected PointsMallOrderSub toSub(PointsMallOrderMain main, String subOrderId) { PointsMallOrderSub sub = new PointsMallOrderSub(); sub.setOrderId(StrUtil.blankToDefault(subOrderId, main.getOrderId())); @@ -171,6 +244,9 @@ public abstract class AbstractMallBridgeSupport { return sub; } + /** + * 将商品逐条保存到指定子订单,仅保存图片列表中的首张图片;商品列表为 null 时不处理。 + */ protected void insertProducts(String orderSubId, List products) { if (products == null) { return; @@ -188,6 +264,9 @@ public abstract class AbstractMallBridgeSupport { } } + /** + * 将查询记录转换为对外订单详情,统一日期和金额格式,并读取订单商品信息。 + */ protected OrderInfoDTO toOrderInfo(Record record) { OrderInfoDTO dto = new OrderInfoDTO(); dto.setOrderId(record.getString("orderId")); @@ -215,6 +294,9 @@ public abstract class AbstractMallBridgeSupport { return dto; } + /** + * 优先读取订单下未删除的子单商品;无商品记录时从 extJson 解析,扩展信息为空或为 null 字符串时返回空列表。 + */ protected List readProducts(String orderId, String extJson) { List products = dao.query(PointsMallOrderSubProduct.class, Cnd.where("orderSubId", "=", orderId).and("delFlag", "=", false)); if (!products.isEmpty()) { @@ -226,6 +308,9 @@ public abstract class AbstractMallBridgeSupport { return JSONUtil.toList(extJson, OrderProInfoDTO.class); } + /** + * 将子单商品转换为对外商品信息,单张图片包装为列表,并以 SKU 和图片地址填充文件标识及路径。 + */ protected OrderProInfoDTO toProductDTO(PointsMallOrderSubProduct product) { OrderProInfoDTO dto = new OrderProInfoDTO(); dto.setSku(product.getSku()); @@ -239,6 +324,9 @@ public abstract class AbstractMallBridgeSupport { return dto; } + /** + * 将发票记录转换为对外发票信息,并将开票时间转换为本地日期。 + */ protected InvoiceInfoDTO toInvoiceInfoDTO(PointsMallInvoiceInfo info) { InvoiceInfoDTO dto = new InvoiceInfoDTO(); dto.setInvoiceId(info.getInvoiceId()); @@ -257,6 +345,9 @@ public abstract class AbstractMallBridgeSupport { return dto; } + /** + * 解析日期字符串,将日期时间分隔符 T 替换为空格;空白值返回 null,非法日期由日期工具抛出异常。 + */ protected Date parseDate(String value) { if (StrUtil.isBlank(value)) { return null; @@ -264,6 +355,9 @@ public abstract class AbstractMallBridgeSupport { return DateUtil.parse(value.replace("T", " ")); } + /** + * 将日期值统一格式化为 yyyy-MM-dd HH:mm:ss;null 值返回 null。 + */ protected String formatDate(Object value) { if (value == null) { return null; @@ -271,10 +365,16 @@ public abstract class AbstractMallBridgeSupport { return DateUtil.format(Convert.toDate(value), "yyyy-MM-dd HH:mm:ss"); } + /** + * 使用系统默认时区将时间转换为日期;null 值返回 null。 + */ protected LocalDate toLocalDate(Date date) { return date == null ? null : date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); } + /** + * 拆分逗号分隔的标识,去除首尾空白、空项和重复项,保留首次出现的顺序;空白值返回空列表。 + */ protected List splitIds(String value) { if (StrUtil.isBlank(value)) { return Collections.emptyList(); @@ -286,18 +386,30 @@ public abstract class AbstractMallBridgeSupport { .collect(Collectors.toList()); } + /** + * 金额为 null 时返回零,否则保留原值。 + */ protected BigDecimal nvl(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } + /** + * 金额为 null 时返回指定默认值,否则保留原值。 + */ protected BigDecimal nvl(BigDecimal value, BigDecimal defaultValue) { return value == null ? defaultValue : value; } + /** + * 将对象转换为金额,无法转换或值为 null 时使用零作为默认值。 + */ protected BigDecimal decimal(Object value) { return Convert.toBigDecimal(value, BigDecimal.ZERO); } + /** + * 将字符串转换为整数;空白值或无法转换的值返回 null。 + */ protected Integer parseInteger(String value) { if (StrUtil.isBlank(value)) { return null; diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeResponseUtil.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeResponseUtil.java new file mode 100644 index 00000000..3d1c66c1 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeResponseUtil.java @@ -0,0 +1,37 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.util; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; + +/** 提取供应商业务响应,兼容 BAB7 包装报文。 */ +public class MallBridgeResponseUtil { + public static JSONObject parse(String response) { + if (StrUtil.isBlank(response)) { + throw new IllegalArgumentException("供应商接口返回为空"); + } + JSONObject payload = JSONUtil.parseObj(response); + if (!payload.containsKey("resultCode")) { + JSONObject data = payload.getJSONObject("data"); + JSONObject body = data == null ? null : data.getJSONObject("Body"); + JSONObject header = body == null ? null : body.getJSONObject("RspSvcHeader"); + if (header == null || !"000000000000".equals(header.getStr("ReturnCode"))) { + throw new IllegalArgumentException(header == null ? "BAB7返回报文格式异常" : + StrUtil.blankToDefault(header.getStr("ReturnMsg"), "BAB7调用失败")); + } + JSONObject serviceBody = body.getJSONObject("RspSvcBody"); + if (serviceBody == null || !"200".equals(serviceBody.getStr("ReturnStCd"))) { + throw new IllegalArgumentException("BAB7调用供应商接口失败"); + } + String content = serviceBody.getStr("ReturnCntnt"); + if (StrUtil.isBlank(content)) { + throw new IllegalArgumentException("供应商业务响应为空"); + } + payload = JSONUtil.parseObj(content); + } + if (payload.getInt("resultCode") == null) { + throw new IllegalArgumentException("供应商业务响应缺少结果码"); + } + return payload; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationParam.java index 04c3164a..8a81f2a4 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationParam.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationParam.java @@ -8,6 +8,10 @@ public class PointsMallReconciliationParam { private String supplierId; private Integer reconciliationType; private String orderId; + private String queryOrderId; + private String userId; + private Integer reconciliationStatus; + private Integer invoiceStatus; private String startDate; private String endDate; } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java index ca3edacc..d5563d73 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java @@ -91,32 +91,28 @@ public class PointsMallReconciliationServiceImpl extends BaseServiceImpl orderIdList = null; - // 校验参数 - if (reconciliationType == 1) { - if (StrUtil.isBlank(reconciliationBO.getStartDate())) { - return Result.error("请选择开始日期"); - } - } else if (reconciliationType == 2) { // 历史异常订单对账 - if (StrUtil.isBlank(reconciliationBO.getOrderId())) { - return Result.error("订单号不能为空"); - } - orderIdList = StrUtil.splitTrim(reconciliationBO.getOrderId(), ","); - } else { - return Result.error("对账类型不正确"); + try { + checkReconciliationParam(reconciliationBO); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); } + Integer reconciliationType = reconciliationBO.getReconciliationType(); // 从浦发积分商城订单表查询对账订单 List orderInfoList = dao().query(PointsMallOrderSub.class, reconciliationCnd(reconciliationBO, "")); if (CollUtil.isEmpty(orderInfoList)) { return Result.error("浦发积分商城没有查询到对账订单数据"); } + Set orderIdList = orderInfoList.stream().map(PointsMallOrderSub::getOrderId).collect(Collectors.toSet()); + if (StrUtil.isNotBlank(reconciliationBO.getOrderId()) + && !orderIdList.containsAll(StrUtil.splitTrim(reconciliationBO.getOrderId(), ","))) { + return Result.error("部分勾选订单已不符合对账条件,请重新查询后选择"); + } QueryReconciliationInfoDTO queryReconciliationInfoDTO = new QueryReconciliationInfoDTO(); queryReconciliationInfoDTO.setSupplierId(reconciliationBO.getSupplierId()); queryReconciliationInfoDTO.setReconciliationType(reconciliationType); - queryReconciliationInfoDTO.setOrderId(orderIdList == null ? reconciliationBO.getOrderId() : String.join(",", orderIdList)); + queryReconciliationInfoDTO.setOrderId(String.join(",", orderIdList)); queryReconciliationInfoDTO.setStartDate(reconciliationBO.getStartDate()); queryReconciliationInfoDTO.setEndDate(reconciliationBO.getEndDate()); queryReconciliationInfoDTO.setPage(1); @@ -126,6 +122,7 @@ public class PointsMallReconciliationServiceImpl extends BaseServiceImpl orderIdList.contains(o.getOrderId())).collect(Collectors.toList()); String supplierId = reconciliationBO.getSupplierId(); result.forEach(o -> o.setSupplierId(supplierId)); // 根据供应商ID + 订单ID进行分组 @@ -291,22 +288,23 @@ public class PointsMallReconciliationServiceImpl extends BaseServiceImpl 1) { - cnd.and(field(alias, "orderId"), "in", orderIds); - } else { - cnd.and(field(alias, "orderId"), "like", "%" + param.getOrderId() + "%"); - } + cnd.and(field(alias, "orderId"), "in", StrUtil.splitTrim(param.getOrderId(), ",")); } - addDateRange(cnd, alias, param.getStartDate(), param.getEndDate()); return cnd; } @@ -336,8 +334,8 @@ public class PointsMallReconciliationServiceImpl extends BaseServiceImpl - - - - - -
- 开票申请 - 可开票订单 @@ -36,13 +28,11 @@ layout("/layouts/platform.html"){ {{ invoiceResultName(row.bSuccess) }} - - - - + @@ -58,114 +48,18 @@ layout("/layouts/platform.html"){ > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ orderTotalPoints }} - 计算 - - - - 取消 - 提交申请 - - - - - - - - - - - - - - - - - - - - - - - - - 查询 - 重置 - 用于开票 - - - - + + + - + + + - - - - - - - {{ currentRecord.batchNo || "-" }} {{ currentRecord.orderId || "-" }} {{ currentRecord.supplierName || currentRecord.supplierId || "-" }} - {{ currentRecord.userId || "-" }} + {{ currentRecord.loginname || "-" }} {{ currentRecord.pTotalPrice || "0" }} {{ currentRecord.sTotalPrice || "0" }} {{ currentRecord.pCashAmount || "0" }} @@ -121,11 +90,7 @@ layout("/layouts/platform.html"){ pageSize: 10, totalCount: 0, supplierId: null, - orderId: null, - userId: null, - diffType: null, - batchNo: null, - noticeStatus: null + orderId: null }, detailDialogVisible: false, currentRecord: null