费用提交

This commit is contained in:
2026-10-26 13:41:43 +08:00
parent 0fe1d10566
commit 761fe3686e
171 changed files with 8750 additions and 386 deletions
@@ -0,0 +1,58 @@
package com.baiyue.firstleg.controller;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.service.SharedFeeCatalogService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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 java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.List;
/** 公共费用目录内部只读入口,禁止借用平台员工令牌跨服务查询。 */
@RestController
@RequestMapping("/internal/firstleg/v1/fee-items")
public class InternalFeeCatalogController {
private final SharedFeeCatalogService service;
private final byte[] expectedToken;
/** 绑定费用目录服务与内部身份凭证,空凭证始终拒绝访问。 */
public InternalFeeCatalogController(SharedFeeCatalogService service,
@Value("${baiyue.security.internal-service-token:}") String token) {
this.service = service;
this.expectedToken = token.getBytes(StandardCharsets.UTF_8);
}
/** 返回适用海外仓收付方向的启用项目。 */
@GetMapping
public ApiResponse<List<SharedFeeCatalogService.Item>> list(
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
@RequestParam(defaultValue = "RECEIVABLE") String direction) {
requireToken(token);
return ApiResponse.success(service.list(direction));
}
/** 在费用创建时验证实时项目资格。 */
@GetMapping("/{code}")
public ApiResponse<SharedFeeCatalogService.Item> detail(
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
@PathVariable String code, @RequestParam String direction) {
requireToken(token);
return ApiResponse.success(service.requireActive(code, direction));
}
/** 使用常量时间比较内部凭证,未配置凭证时关闭入口。 */
private void requireToken(String token) {
if (expectedToken.length == 0 || token == null
|| !MessageDigest.isEqual(expectedToken, token.getBytes(StandardCharsets.UTF_8))) {
throw new BizException(ResultCode.FORBIDDEN, "内部服务凭证无效");
}
}
}
@@ -54,6 +54,18 @@ public class FeeItemRepository {
.eq(FeeItemEntity::getFeeCode, feeCode));
}
/** 海外录费仅提供平台启用的国际仓库收付费项目,不复制费用目录。 */
public List<FeeItemEntity> listActiveInternationalWarehouseItems(String direction) {
return feeItems.selectList(new LambdaQueryWrapper<FeeItemEntity>()
.eq(FeeItemEntity::getTenantId, PLATFORM_TENANT_ID)
.eq(FeeItemEntity::getStatus, "ACTIVE")
.eq(FeeItemEntity::getTradeScope, "INTERNATIONAL")
.eq("RECEIVABLE".equals(direction), FeeItemEntity::getWarehouseReceivable, true)
.eq("PAYABLE".equals(direction), FeeItemEntity::getWarehousePayable, true)
.orderByAsc(FeeItemEntity::getCategory)
.orderByAsc(FeeItemEntity::getFeeTypeName));
}
public boolean itemNameExists(String tradeScope, String feeTypeName, Long excludedId) {
return feeItems.selectCount(new LambdaQueryWrapper<FeeItemEntity>()
.eq(FeeItemEntity::getTenantId, PLATFORM_TENANT_ID)
@@ -0,0 +1,53 @@
package com.baiyue.firstleg.service;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.firstleg.entity.FeeItemEntity;
import com.baiyue.firstleg.repository.FeeItemRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
/** 向海外业务提供公共费用目录的只读切片,项目维护仍归头程主数据。 */
@Service
@RequiredArgsConstructor
public class SharedFeeCatalogService {
private final FeeItemRepository repository;
/** 按收付方向返回当前可使用的费用项目。 */
public List<Item> list(String direction) {
requireDirection(direction);
return repository.listActiveInternationalWarehouseItems(direction).stream()
.map(SharedFeeCatalogService::snapshot).toList();
}
/** 写入业务费用前重新验证项目资格,停用或方向变化不得沿用旧选项。 */
public Item requireActive(String code, String direction) {
requireDirection(direction);
FeeItemEntity item = repository.findItemByCode(code);
if (item == null || !"ACTIVE".equals(item.getStatus())
|| !"INTERNATIONAL".equals(item.getTradeScope())
|| !("RECEIVABLE".equals(direction)
? Boolean.TRUE.equals(item.getWarehouseReceivable())
: Boolean.TRUE.equals(item.getWarehousePayable()))) {
throw new BizException(ResultCode.CONFLICT, "费用项目未启用或不适用于本次收费");
}
return snapshot(item);
}
/** 校验显式收付方向,防止空方向绕过目录过滤。 */
private static void requireDirection(String direction) {
if (!"RECEIVABLE".equals(direction) && !"PAYABLE".equals(direction)) {
throw new BizException(ResultCode.BAD_REQUEST, "请选择费用收付方向");
}
}
/** 返回有限字段,不向其他服务暴露目录实体或审计属性。 */
private static Item snapshot(FeeItemEntity item) {
return new Item(item.getFeeCode(), item.getFeeTypeName(), item.getDefaultPrice());
}
public record Item(String code, String name, BigDecimal defaultPrice) { }
}