费用提交
This commit is contained in:
+58
@@ -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) { }
|
||||
}
|
||||
Reference in New Issue
Block a user