diff --git a/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TmsOrderBillStatusController.java b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TmsOrderBillStatusController.java
new file mode 100644
index 00000000..e2ad6854
--- /dev/null
+++ b/bms-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TmsOrderBillStatusController.java
@@ -0,0 +1,59 @@
+package com.ruoyi.web.controller.api;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.annotation.Anonymous;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.config.properties.TmsApiProperties;
+import com.ruoyi.price.domain.vo.TmsOrderBillStatusResult;
+import com.ruoyi.price.service.IPrcOrderBillService;
+
+/**
+ * TMS 订单客户状态查询接口。
+ *
+ *
该接口不要求登录 Token,但必须携带 X-Api-Code。
+ * identifier 会按订单号、运单号、转单号进行精确匹配。
+ */
+@Anonymous
+@RestController
+@RequestMapping("/api/tms/order-bill")
+public class TmsOrderBillStatusController extends BaseController {
+ private static final String HEADER_API_CODE = "X-Api-Code";
+
+ @Autowired
+ private IPrcOrderBillService prcOrderBillService;
+
+ @Autowired
+ private TmsApiProperties tmsApiProperties;
+
+ /**
+ * 查询订单对应客户是否正常、额度是否欠费。
+ *
+ * 接口只返回查询事实和状态字段,不判断上游是否继续处理。
+ */
+ @GetMapping("/status")
+ public AjaxResult status(
+ @RequestHeader(value = HEADER_API_CODE, required = false) String headerApiCode,
+ @RequestParam(value = "identifier", required = false) String identifier) {
+ assertApiCode(headerApiCode);
+ TmsOrderBillStatusResult result = prcOrderBillService.queryTmsOrderBillStatus(identifier);
+ return success(result);
+ }
+
+ private void assertApiCode(String headerApiCode) {
+ String expected = tmsApiProperties.getApiCode();
+ if (StringUtils.isEmpty(expected)) {
+ throw new ServiceException("API_CODE_NOT_CONFIGURED: bms.tms.api-code is empty");
+ }
+ if (StringUtils.isEmpty(headerApiCode) || !expected.equals(headerApiCode.trim())) {
+ throw new ServiceException("API_CODE_INVALID: missing or invalid X-Api-Code");
+ }
+ }
+}
diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/vo/TmsOrderBillStatusResult.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/vo/TmsOrderBillStatusResult.java
new file mode 100644
index 00000000..93cb0663
--- /dev/null
+++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/domain/vo/TmsOrderBillStatusResult.java
@@ -0,0 +1,47 @@
+package com.ruoyi.price.domain.vo;
+
+import lombok.Data;
+
+/**
+ * TMS 查询订单计费状态的业务结果。
+ *
+ * 该结果不直接返回完整订单计费数据,只返回订单匹配结果和相关状态事实。
+ */
+@Data
+public class TmsOrderBillStatusResult {
+ /**
+ * 是否查询到计费订单。
+ */
+ private boolean found;
+
+ /**
+ * 匹配到的记录数,最多返回 2 条用于判断是否存在歧义。
+ */
+ private int matchedCount;
+
+ /**
+ * 命中类型:ORDER_NO/ TRACKING_NO/TRANSFER_NO/MULTIPLE。
+ */
+ private String matchType;
+
+ /**
+ * 计费订单标识,仅在唯一命中时返回。
+ */
+ private String billId;
+
+ private String orderNo;
+ private String trackingNo;
+ private String transferNo;
+ private Long customerId;
+ private String customerCode;
+
+ /**
+ * 客户生效状态:ACTIVE/MISSING/DISABLED。
+ */
+ private String customerActiveStatus;
+
+ /**
+ * 额度欠费状态:NORMAL/ARREARS。
+ */
+ private String creditArrearsStatus;
+}
diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/BillCreditArrearsStatus.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/BillCreditArrearsStatus.java
index c7336f51..cb2851ff 100644
--- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/BillCreditArrearsStatus.java
+++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/BillCreditArrearsStatus.java
@@ -20,4 +20,21 @@ public enum BillCreditArrearsStatus
{
return code;
}
+
+ /** 将数据库或接口中的状态值转换为枚举;未知值返回 null。 */
+ public static BillCreditArrearsStatus of(String code)
+ {
+ if (code == null)
+ {
+ return null;
+ }
+ for (BillCreditArrearsStatus status : values())
+ {
+ if (status.code.equalsIgnoreCase(code.trim()))
+ {
+ return status;
+ }
+ }
+ return null;
+ }
}
diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/BillCustomerActiveStatus.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/BillCustomerActiveStatus.java
index 9d4be226..aa68f813 100644
--- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/BillCustomerActiveStatus.java
+++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/BillCustomerActiveStatus.java
@@ -21,4 +21,21 @@ public enum BillCustomerActiveStatus
{
return code;
}
+
+ /** 将数据库或接口中的状态值转换为枚举;未知值返回 null。 */
+ public static BillCustomerActiveStatus of(String code)
+ {
+ if (code == null)
+ {
+ return null;
+ }
+ for (BillCustomerActiveStatus status : values())
+ {
+ if (status.code.equalsIgnoreCase(code.trim()))
+ {
+ return status;
+ }
+ }
+ return null;
+ }
}
diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/TmsOrderBillMatchType.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/TmsOrderBillMatchType.java
new file mode 100644
index 00000000..9bbbc9a8
--- /dev/null
+++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/enums/TmsOrderBillMatchType.java
@@ -0,0 +1,39 @@
+package com.ruoyi.price.enums;
+
+/**
+ * TMS 查询订单计费数据时的标识匹配类型。
+ */
+public enum TmsOrderBillMatchType {
+ /**
+ * 按订单号匹配。
+ */
+ ORDER_NO("ORDER_NO", "订单号"),
+ /**
+ * 按运单号匹配。
+ */
+ TRACKING_NO("TRACKING_NO", "运单号"),
+ /**
+ * 按转单号匹配。
+ */
+ TRANSFER_NO("TRANSFER_NO", "转单号"),
+ /**
+ * 同一标识匹配到多条记录,无法唯一确定订单。
+ */
+ MULTIPLE("MULTIPLE", "多条匹配");
+
+ private final String code;
+ private final String label;
+
+ TmsOrderBillMatchType(String code, String label) {
+ this.code = code;
+ this.label = label;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+}
diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcOrderBillMapper.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcOrderBillMapper.java
index 2d22ea67..265d85a7 100644
--- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcOrderBillMapper.java
+++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/mapper/PrcOrderBillMapper.java
@@ -13,6 +13,9 @@ public interface PrcOrderBillMapper
public PrcOrderBill selectPrcOrderBillByTransferNo(String transferNo);
+ /** TMS 状态查询:按订单号、运单号或转单号精确匹配,最多返回两条用于识别歧义。 */
+ List selectPrcOrderBillByTmsIdentifier(@Param("identifier") String identifier);
+
public PrcOrderBill selectPrcOrderBillById(String billId);
public List selectPrcOrderBillList(PrcOrderBill bill);
diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IPrcOrderBillService.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IPrcOrderBillService.java
index 2c9f7cd6..9a03e35c 100644
--- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IPrcOrderBillService.java
+++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/IPrcOrderBillService.java
@@ -4,6 +4,7 @@ import java.util.List;
import com.ruoyi.price.domain.PrcOrderBill;
import com.ruoyi.price.domain.vo.TmsPriceBillRequest;
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
+import com.ruoyi.price.domain.vo.TmsOrderBillStatusResult;
/** 订单计费:落库 prc_order_bill + prc_income_bill/+fee + prc_pay_bill/+fee。 */
public interface IPrcOrderBillService
@@ -24,4 +25,7 @@ public interface IPrcOrderBillService
TmsPriceQuoteResult getBill(String billId);
TmsPriceQuoteResult getBillByOrderNo(String orderNo);
+
+ /** 提供给 TMS 判断订单是否可以继续处理的客户状态查询。 */
+ TmsOrderBillStatusResult queryTmsOrderBillStatus(String identifier);
}
diff --git a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcOrderBillServiceImpl.java b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcOrderBillServiceImpl.java
index aff29cef..bf8cff81 100644
--- a/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcOrderBillServiceImpl.java
+++ b/bms-back/ruoyi-system/src/main/java/com/ruoyi/price/service/impl/PrcOrderBillServiceImpl.java
@@ -27,10 +27,12 @@ import com.ruoyi.price.domain.PrcOrderBillExt;
import com.ruoyi.price.domain.PrcPayBill;
import com.ruoyi.price.domain.PrcPayBillFee;
import com.ruoyi.price.domain.vo.TmsPriceBillRequest;
+import com.ruoyi.price.domain.vo.TmsOrderBillStatusResult;
import com.ruoyi.price.domain.vo.TmsPriceQuoteFeeLine;
import com.ruoyi.price.domain.vo.TmsPriceQuoteResult;
import com.ruoyi.price.enums.BillCreditArrearsStatus;
import com.ruoyi.price.enums.BillCustomerActiveStatus;
+import com.ruoyi.price.enums.TmsOrderBillMatchType;
import com.ruoyi.price.mapper.PrcIncomeBillFeeMapper;
import com.ruoyi.price.mapper.PrcIncomeBillMapper;
import com.ruoyi.price.mapper.PrcOrderBillExtMapper;
@@ -199,6 +201,85 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
payBillFeeMapper.selectByBillId(bill.getBillId()));
}
+ /** 查询 TMS 后续流程所需的客户状态,只返回订单数据和状态事实,不代替上游做业务决策。 */
+ @Override
+ @Transactional(readOnly = true)
+ public TmsOrderBillStatusResult queryTmsOrderBillStatus(String identifier)
+ {
+ String value = StringUtils.trim(identifier);
+ if (StringUtils.isEmpty(value))
+ {
+ throw new ServiceException("订单号、运单号或转单号不能为空");
+ }
+ if (value.length() > 64)
+ {
+ throw new ServiceException("订单号、运单号或转单号长度不能超过64个字符");
+ }
+
+ List bills = orderBillMapper.selectPrcOrderBillByTmsIdentifier(value);
+ if (bills == null || bills.isEmpty())
+ {
+ TmsOrderBillStatusResult result = new TmsOrderBillStatusResult();
+ result.setMatchedCount(0);
+ return result;
+ }
+ if (bills.size() > 1)
+ {
+ TmsOrderBillStatusResult result = new TmsOrderBillStatusResult();
+ result.setFound(true);
+ result.setMatchedCount(bills.size());
+ result.setMatchType(TmsOrderBillMatchType.MULTIPLE.getCode());
+ return result;
+ }
+
+ PrcOrderBill bill = bills.get(0);
+ TmsOrderBillStatusResult result = new TmsOrderBillStatusResult();
+ result.setFound(true);
+ result.setMatchedCount(1);
+ result.setMatchType(resolveTmsMatchType(value, bill).getCode());
+ result.setBillId(bill.getBillId());
+ result.setOrderNo(bill.getOrderNo());
+ result.setTrackingNo(bill.getTrackingNo());
+ result.setTransferNo(bill.getTransferNo());
+ result.setCustomerId(bill.getCustomerId());
+ result.setCustomerCode(bill.getCustomerCode());
+ result.setCustomerActiveStatus(normalizeCustomerActiveStatus(bill.getCustomerActiveStatus()));
+ result.setCreditArrearsStatus(normalizeCreditArrearsStatus(bill.getCreditArrearsStatus()));
+ return result;
+ }
+
+ private TmsOrderBillMatchType resolveTmsMatchType(String identifier, PrcOrderBill bill)
+ {
+ if (identifier.equalsIgnoreCase(StringUtils.trim(bill.getOrderNo())))
+ {
+ return TmsOrderBillMatchType.ORDER_NO;
+ }
+ if (identifier.equalsIgnoreCase(StringUtils.trim(bill.getTrackingNo())))
+ {
+ return TmsOrderBillMatchType.TRACKING_NO;
+ }
+ return TmsOrderBillMatchType.TRANSFER_NO;
+ }
+
+ private String normalizeCustomerActiveStatus(String status)
+ {
+ BillCustomerActiveStatus statusEnum = BillCustomerActiveStatus.of(status);
+ return statusEnum == null ? normalizeUnknownStatus(status) : statusEnum.getCode();
+ }
+
+ private String normalizeCreditArrearsStatus(String status)
+ {
+ BillCreditArrearsStatus statusEnum = BillCreditArrearsStatus.of(status);
+ return statusEnum == null ? normalizeUnknownStatus(status) : statusEnum.getCode();
+ }
+
+ /** 保留未知状态的原始值(统一为大写),便于上游识别新状态或脏数据。 */
+ private String normalizeUnknownStatus(String status)
+ {
+ String normalized = StringUtils.trim(status);
+ return StringUtils.isEmpty(normalized) ? null : normalized.toUpperCase();
+ }
+
private void persistBill(TmsPriceBillRequest request, BigDecimal weight, Date bizDate,
TmsPriceQuoteResult result)
{
@@ -390,7 +471,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
cmd.setAmount(delta.abs());
customerAccountApi.releaseCredit(cmd);
}
- saveCreditEsLog(bill, cmd, action, true, markArrears ? "ARREARS" : null);
+ saveCreditEsLog(bill, cmd, action, true,
+ markArrears ? BillCreditArrearsStatus.ARREARS.getCode() : null);
return markArrears ? arrears : normal;
}
catch (RuntimeException e)
diff --git a/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml b/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml
index de1eb16a..53a93b78 100644
--- a/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml
+++ b/bms-back/ruoyi-system/src/main/resources/mapper/price/PrcOrderBillMapper.xml
@@ -104,6 +104,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
limit 1
+
+
+