订单客户状态查询接口
This commit is contained in:
+59
@@ -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 订单客户状态查询接口。
|
||||
*
|
||||
* <p>该接口不要求登录 Token,但必须携带 X-Api-Code。
|
||||
* identifier 会按订单号、运单号、转单号进行精确匹配。</p>
|
||||
*/
|
||||
@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;
|
||||
|
||||
/**
|
||||
* 查询订单对应客户是否正常、额度是否欠费。
|
||||
*
|
||||
* <p>接口只返回查询事实和状态字段,不判断上游是否继续处理。</p>
|
||||
*/
|
||||
@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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.ruoyi.price.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* TMS 查询订单计费状态的业务结果。
|
||||
*
|
||||
* <p>该结果不直接返回完整订单计费数据,只返回订单匹配结果和相关状态事实。</p>
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
+17
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@ public interface PrcOrderBillMapper
|
||||
|
||||
public PrcOrderBill selectPrcOrderBillByTransferNo(String transferNo);
|
||||
|
||||
/** TMS 状态查询:按订单号、运单号或转单号精确匹配,最多返回两条用于识别歧义。 */
|
||||
List<PrcOrderBill> selectPrcOrderBillByTmsIdentifier(@Param("identifier") String identifier);
|
||||
|
||||
public PrcOrderBill selectPrcOrderBillById(String billId);
|
||||
|
||||
public List<PrcOrderBill> selectPrcOrderBillList(PrcOrderBill bill);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+83
-1
@@ -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<PrcOrderBill> 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)
|
||||
|
||||
@@ -104,6 +104,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<!-- TMS 状态查询使用精确匹配,避免模糊查询把多个订单误判为同一条。 -->
|
||||
<select id="selectPrcOrderBillByTmsIdentifier" resultMap="PrcOrderBillResult">
|
||||
<include refid="selectPrcOrderBillVo"/>
|
||||
where order_no = #{identifier}
|
||||
or tracking_no = #{identifier}
|
||||
or transfer_no = #{identifier}
|
||||
order by
|
||||
case
|
||||
when order_no = #{identifier} then 1
|
||||
when tracking_no = #{identifier} then 2
|
||||
when transfer_no = #{identifier} then 3
|
||||
else 4
|
||||
end,
|
||||
create_time desc,
|
||||
bill_id desc
|
||||
limit 2
|
||||
</select>
|
||||
|
||||
<select id="selectPrcOrderBillById" parameterType="String" resultMap="PrcOrderBillResult">
|
||||
<include refid="selectPrcOrderBillVo"/>
|
||||
where bill_id = #{billId}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
-- TMS 订单状态查询接口:为运单号精确查询补充索引。
|
||||
-- 订单号已有唯一索引,转单号已有索引;当前表结构通常缺少 tracking_no 索引。
|
||||
-- 脚本可重复执行。
|
||||
SET NAMES utf8mb4;
|
||||
SET @db_name := DATABASE();
|
||||
|
||||
SET @has_tracking_index := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db_name
|
||||
AND TABLE_NAME = 'prc_order_bill'
|
||||
AND INDEX_NAME = 'idx_order_bill_tracking_no'
|
||||
);
|
||||
SET @sql := IF(@has_tracking_index = 0,
|
||||
'ALTER TABLE prc_order_bill ADD INDEX idx_order_bill_tracking_no (tracking_no)',
|
||||
'SELECT 1');
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SELECT
|
||||
TABLE_NAME,
|
||||
INDEX_NAME,
|
||||
COLUMN_NAME,
|
||||
NON_UNIQUE
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = @db_name
|
||||
AND TABLE_NAME = 'prc_order_bill'
|
||||
AND INDEX_NAME IN ('uk_bill_order', 'idx_order_bill_tracking_no', 'idx_order_bill_transfer_no')
|
||||
ORDER BY INDEX_NAME, SEQ_IN_INDEX;
|
||||
Reference in New Issue
Block a user