优化
This commit is contained in:
@@ -4,6 +4,8 @@ import com.baiyue.common.core.api.ApiResponse;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@@ -24,6 +26,20 @@ public interface FirstlegFeeSourceClient {
|
||||
@PathVariable long containerId, @PathVariable long feeId,
|
||||
@RequestParam int sourceVersion);
|
||||
|
||||
@PostMapping("/fee-sources/commands/void")
|
||||
ApiResponse<CommandResult> voidSource(
|
||||
@RequestHeader("X-Internal-Service-Token") String token,
|
||||
@RequestHeader("Idempotency-Key") String idempotencyKey,
|
||||
@RequestHeader("X-Request-Hash") String requestHash,
|
||||
@RequestBody VoidCommand request);
|
||||
|
||||
record VoidCommand(String sourceType, long sourceFeeId, int sourceVersion,
|
||||
long customerId, Long inboundId, Long containerId,
|
||||
long operatorId, String operatorName, String reason) {}
|
||||
|
||||
record CommandResult(String sourceType, long sourceFeeId, int sourceVersion,
|
||||
String status, boolean replayed) {}
|
||||
|
||||
record Snapshot(long tenantId, String sourceType, long feeId, int feeVersion,
|
||||
Long inboundId, String inboundCode, Long orderId, Long containerId,
|
||||
Long containerBookingId, String containerNo, long customerId,
|
||||
|
||||
+2
-1
@@ -121,6 +121,7 @@ public class FinancePhase1OperationController {
|
||||
@PathVariable long feeSourceId,
|
||||
@RequestHeader("Idempotency-Key") String key,
|
||||
@Valid @RequestBody FeeSourceActionRequest request) {
|
||||
return FinanceResponses.success(service.voidFinanceFeeSource(feeSourceId, user.userId(), key, request));
|
||||
return FinanceResponses.success(service.voidFinanceFeeSource(
|
||||
feeSourceId, user.userId(), user.username(), key, request));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class FinanceFeeSourceEventService {
|
||||
}
|
||||
long id = ((Number) row.get("id")).longValue();
|
||||
if ("FIRSTLEG_FEE_VOIDED".equals(event.eventType())) {
|
||||
jdbc.update("UPDATE fin_fee_source SET status='VOIDED' WHERE id=?", id);
|
||||
jdbc.update("UPDATE fin_fee_source SET status='VOIDED',operation_version=operation_version+1 WHERE id=? AND status<>'VOIDED'", id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
+57
-4
@@ -9,8 +9,12 @@ import com.baiyue.finance.dto.FinancePhase1Dtos.PageEnvelope;
|
||||
import com.baiyue.finance.dto.FinancePhase1Dtos.ReviewRequest;
|
||||
import com.baiyue.finance.dto.FinancePhase1Dtos.FeeSourceActionRequest;
|
||||
import com.baiyue.finance.dto.FinancePhase1Dtos.FeeSourceEditRequest;
|
||||
import com.baiyue.finance.client.FirstlegFeeSourceClient;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import feign.FeignException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -36,10 +40,21 @@ public class FinancePhase1OperationService {
|
||||
private static final String CUSTOMER_IDS = "SELECT tenant_id customer_id FROM fin_customer_settlement_profile UNION SELECT tenant_id FROM fin_bill UNION SELECT customer_id FROM fin_fee_source UNION SELECT tenant_id FROM fin_cost";
|
||||
private final JdbcTemplate jdbc;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final FirstlegFeeSourceClient firstleg;
|
||||
private final String internalToken;
|
||||
|
||||
public FinancePhase1OperationService(JdbcTemplate jdbc, ObjectMapper objectMapper) {
|
||||
@Autowired
|
||||
public FinancePhase1OperationService(JdbcTemplate jdbc, ObjectMapper objectMapper,
|
||||
FirstlegFeeSourceClient firstleg,
|
||||
@Value("${baiyue.security.internal-service-token}") String internalToken) {
|
||||
this.jdbc = jdbc;
|
||||
this.objectMapper = objectMapper;
|
||||
this.firstleg = firstleg;
|
||||
this.internalToken = internalToken;
|
||||
}
|
||||
|
||||
public FinancePhase1OperationService(JdbcTemplate jdbc, ObjectMapper objectMapper) {
|
||||
this(jdbc, objectMapper, null, "");
|
||||
}
|
||||
|
||||
public PageEnvelope<AccountSummary> listDerivedCustomerAccounts(int page, int pageSize, String customerName, String status) {
|
||||
@@ -164,6 +179,9 @@ public class FinancePhase1OperationService {
|
||||
throw error("FIN-REQ-400", adjustment ? "费用调整请求无效" : "费用编辑请求无效");
|
||||
}
|
||||
FeeSourceDetail before = getFinanceFeeSource(feeSourceId);
|
||||
if ("FIRSTLEG".equals(before.sourceSystem())) {
|
||||
throw error("FIN-FEE-409", "Firstleg费用必须在入库或柜次费用明细中调整,Finance不可直接修改业务金额");
|
||||
}
|
||||
String operation = adjustment ? "FEE_SOURCE_ADJUST" : "FEE_SOURCE_EDIT";
|
||||
String path = "/api/platform/finance/v1/fee-sources/" + feeSourceId + (adjustment ? "/adjust" : "");
|
||||
Long replay = reserve(before.customerId(), operatorId, operation, key,
|
||||
@@ -191,10 +209,45 @@ public class FinancePhase1OperationService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public FeeSourceDetail voidFinanceFeeSource(long feeSourceId, long operatorId, String key,
|
||||
public FeeSourceDetail voidFinanceFeeSource(long feeSourceId, long operatorId,
|
||||
String operatorName, String key,
|
||||
FeeSourceActionRequest request) {
|
||||
return transitionFeeSource(feeSourceId, operatorId, key, request, "FEE_SOURCE_VOID",
|
||||
"VOIDED", List.of("DRAFT", "PENDING", "REJECTED", "APPROVED"), true);
|
||||
if (request == null || request.expectedVersion() < 0 || !StringUtils.hasText(request.reason())) {
|
||||
throw error("FIN-REQ-400", "费用操作请求无效");
|
||||
}
|
||||
FeeSourceDetail before = getFinanceFeeSource(feeSourceId);
|
||||
if (!"FIRSTLEG".equals(before.sourceSystem())) {
|
||||
return transitionFeeSource(feeSourceId, operatorId, key, request, "FEE_SOURCE_VOID",
|
||||
"VOIDED", List.of("DRAFT", "PENDING", "REJECTED", "APPROVED"), true);
|
||||
}
|
||||
Long replay = reserve(before.customerId(), operatorId, "FEE_SOURCE_VOID", key, "POST",
|
||||
"/api/platform/finance/v1/fee-sources/" + feeSourceId + "/void", request);
|
||||
if (replay != null) return getFinanceFeeSource(replay);
|
||||
if (before.statementGenerated()) throw error("FIN-FEE-409", "已生成单据的费用来源不可作废");
|
||||
if (before.operationVersion() != request.expectedVersion()) {
|
||||
throw error("FIN-FEE-409", "费用来源状态或版本已变化");
|
||||
}
|
||||
if (!List.of("DRAFT", "PENDING", "REJECTED", "APPROVED").contains(before.reviewStatus())
|
||||
|| !"AVAILABLE".equals(before.sourceStatus())) {
|
||||
throw error("FIN-FEE-409", "费用来源当前状态不允许作废");
|
||||
}
|
||||
FirstlegFeeSourceClient.VoidCommand command = new FirstlegFeeSourceClient.VoidCommand(
|
||||
before.sourceType(), before.sourceFeeId(), before.sourceVersion(), before.customerId(),
|
||||
before.inboundId(), before.containerId(), operatorId,
|
||||
StringUtils.hasText(operatorName) ? operatorName.trim() : "SYSTEM", request.reason().trim());
|
||||
try {
|
||||
var response = firstleg.voidSource(internalToken, key, hash(command), command);
|
||||
if (response == null || !response.successful() || response.data() == null
|
||||
|| !"VOIDED".equals(response.data().status())) {
|
||||
throw error("FIN-UPSTREAM-502", "Firstleg未确认费用来源作废");
|
||||
}
|
||||
} catch (FeignException exception) {
|
||||
throw error("FIN-UPSTREAM-503", "Firstleg费用作废服务暂不可用");
|
||||
}
|
||||
audit(before.customerId(), feeSourceId, "FEE_SOURCE_VOID_REQUESTED",
|
||||
request.reason(), before, before, operatorId);
|
||||
complete(before.customerId(), operatorId, "FEE_SOURCE_VOID", key, feeSourceId);
|
||||
return getFinanceFeeSource(feeSourceId);
|
||||
}
|
||||
|
||||
private FeeSourceDetail transitionFeeSource(long feeSourceId, long operatorId, String key,
|
||||
|
||||
Reference in New Issue
Block a user