优化
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.baiyue.finance.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
@@ -22,6 +23,7 @@ public class FinanceDocumentNumberGenerator {
|
||||
private final JdbcTemplate jdbc;
|
||||
private final Clock clock;
|
||||
|
||||
@Autowired
|
||||
public FinanceDocumentNumberGenerator(JdbcTemplate jdbc) {
|
||||
this(jdbc, Clock.system(BUSINESS_ZONE));
|
||||
}
|
||||
|
||||
@@ -41,6 +41,14 @@ CALL finance_fee_source_add_column('fin_fee_source', 'operation_version',
|
||||
"INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '费用编辑、提交、审核、调整和作废操作版本号' AFTER review_reason");
|
||||
CALL finance_fee_source_add_column('fin_fee_source', 'updated_at',
|
||||
"DATETIME(3) NULL COMMENT '来源状态最后更新时间,数据修改时自动刷新' AFTER created_at");
|
||||
CALL finance_fee_source_add_column('fin_bill', 'customer_confirmation_status',
|
||||
"VARCHAR(24) NOT NULL DEFAULT 'NOT_REQUIRED' COMMENT '客户确认状态:NOT_REQUIRED无需确认、PENDING_CONFIRMATION待确认、CONFIRMED已确认' AFTER issued_by");
|
||||
CALL finance_fee_source_add_column('fin_bill', 'customer_confirmed_by',
|
||||
"BIGINT UNSIGNED NULL COMMENT '客户确认人用户ID' AFTER customer_confirmation_status");
|
||||
CALL finance_fee_source_add_column('fin_bill', 'customer_confirmed_by_name',
|
||||
"VARCHAR(128) NULL COMMENT '客户确认人姓名快照' AFTER customer_confirmed_by");
|
||||
CALL finance_fee_source_add_column('fin_bill', 'customer_confirmed_at',
|
||||
"DATETIME(3) NULL COMMENT '客户确认时间' AFTER customer_confirmed_by_name");
|
||||
CALL finance_fee_source_add_column('fin_inbox_event', 'payload_hash',
|
||||
"CHAR(64) NULL COMMENT '规范化事件内容SHA-256哈希,用于同事件ID冲突检测' AFTER payload");
|
||||
CALL finance_fee_source_add_column('fin_inbox_event', 'result_resource_id',
|
||||
@@ -91,6 +99,12 @@ BEGIN
|
||||
AND column_name IN ('payload_hash','result_resource_id','error_code')) <> 3 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Finance费用来源迁移失败:Inbox幂等字段不完整';
|
||||
END IF;
|
||||
IF (SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE() AND table_name = 'fin_bill'
|
||||
AND column_name IN ('customer_confirmation_status','customer_confirmed_by',
|
||||
'customer_confirmed_by_name','customer_confirmed_at')) <> 4 THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Finance客户对账单确认字段不完整';
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
CALL finance_fee_source_assert_schema();
|
||||
|
||||
+13
@@ -1,6 +1,7 @@
|
||||
package com.baiyue.finance.service;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import java.time.Clock;
|
||||
@@ -18,6 +19,18 @@ class FinanceDocumentNumberGeneratorTest {
|
||||
private static final Clock CLOCK = Clock.fixed(
|
||||
Instant.parse("2026-08-18T02:00:00Z"), ZoneId.of("Asia/Shanghai"));
|
||||
|
||||
@Test
|
||||
void startsAsASpringBeanWhenTheTestClockConstructorAlsoExists() {
|
||||
JdbcTemplate jdbc = mock(JdbcTemplate.class);
|
||||
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
|
||||
context.registerBean(JdbcTemplate.class, () -> jdbc);
|
||||
context.register(FinanceDocumentNumberGenerator.class);
|
||||
context.refresh();
|
||||
|
||||
assertThat(context.getBean(FinanceDocumentNumberGenerator.class)).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatsShortDailySequenceForAllThreeDocuments() {
|
||||
JdbcTemplate jdbc = mock(JdbcTemplate.class);
|
||||
|
||||
+2
@@ -2,6 +2,7 @@ package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
@@ -24,6 +25,7 @@ public class FirstlegDocumentNumberGenerator {
|
||||
private final JdbcTemplate jdbc;
|
||||
private final Clock clock;
|
||||
|
||||
@Autowired
|
||||
public FirstlegDocumentNumberGenerator(JdbcTemplate jdbc) {
|
||||
this(jdbc, Clock.system(BUSINESS_ZONE));
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ CREATE TABLE IF NOT EXISTS fl_container_fee_detail (
|
||||
direction VARCHAR(16) NOT NULL COMMENT '费用方向:RECEIVABLE应收、PAYABLE应付',
|
||||
supplier_id BIGINT UNSIGNED NULL COMMENT '应付Warehouse供应商ID快照;应收为空',
|
||||
supplier_name VARCHAR(200) NULL COMMENT '应付Warehouse供应商名称快照;应收为空',
|
||||
supplier_category VARCHAR(16) NULL COMMENT '费用项目供应商类别快照;应收为空',
|
||||
supplier_category VARCHAR(16) NULL COMMENT '费用项目供应商类别约束快照;为空表示任意启用供应商',
|
||||
business_date DATE NOT NULL COMMENT '费用业务日期',
|
||||
amount DECIMAL(18,2) NOT NULL COMMENT 'CNY费用金额',
|
||||
currency CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种,首期固定CNY',
|
||||
@@ -100,37 +100,43 @@ CREATE TABLE IF NOT EXISTS fl_container_fee_detail (
|
||||
) ENGINE=InnoDB COMMENT='柜次级人工应收应付费用来源;审核通过后自动同步Finance';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fl_container_fee_source_snapshot (
|
||||
id BIGINT UNSIGNED NOT NULL,
|
||||
source_fee_id BIGINT UNSIGNED NOT NULL,
|
||||
source_version INT UNSIGNED NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
loading_plan_id BIGINT UNSIGNED NOT NULL,
|
||||
container_booking_id BIGINT UNSIGNED NULL,
|
||||
container_no VARCHAR(64) NULL,
|
||||
fee_item_id BIGINT UNSIGNED NOT NULL,
|
||||
fee_item_code VARCHAR(50) NOT NULL,
|
||||
fee_item_name VARCHAR(128) NOT NULL,
|
||||
direction VARCHAR(16) NOT NULL,
|
||||
supplier_id BIGINT UNSIGNED NULL,
|
||||
supplier_name VARCHAR(200) NULL,
|
||||
supplier_category VARCHAR(16) NULL,
|
||||
business_date DATE NOT NULL,
|
||||
amount DECIMAL(18,2) NOT NULL,
|
||||
currency CHAR(3) NOT NULL DEFAULT 'CNY',
|
||||
review_status VARCHAR(16) NOT NULL,
|
||||
reviewed_at DATETIME(3) NULL,
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
id BIGINT UNSIGNED NOT NULL COMMENT '柜次费用快照ID',
|
||||
source_fee_id BIGINT UNSIGNED NOT NULL COMMENT '柜次费用来源ID',
|
||||
source_version INT UNSIGNED NOT NULL COMMENT '费用来源版本',
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '客户ID和数据隔离键',
|
||||
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '柜次ID',
|
||||
container_booking_id BIGINT UNSIGNED NULL COMMENT '订舱柜ID快照',
|
||||
container_no VARCHAR(64) NULL COMMENT '柜号快照',
|
||||
fee_item_id BIGINT UNSIGNED NOT NULL COMMENT '费用项目ID快照',
|
||||
fee_item_code VARCHAR(50) NOT NULL COMMENT '费用项目编码快照',
|
||||
fee_item_name VARCHAR(128) NOT NULL COMMENT '费用项目名称快照',
|
||||
direction VARCHAR(16) NOT NULL COMMENT '费用方向',
|
||||
supplier_id BIGINT UNSIGNED NULL COMMENT 'Warehouse供应商ID快照',
|
||||
supplier_name VARCHAR(200) NULL COMMENT 'Warehouse供应商名称快照',
|
||||
supplier_category VARCHAR(16) NULL COMMENT '费用项目供应商类别约束快照;为空表示任意启用供应商',
|
||||
business_date DATE NOT NULL COMMENT '费用业务日期',
|
||||
amount DECIMAL(18,2) NOT NULL COMMENT 'CNY费用金额',
|
||||
currency CHAR(3) NOT NULL DEFAULT 'CNY' COMMENT '币种,首期固定CNY',
|
||||
review_status VARCHAR(16) NOT NULL COMMENT '审核状态快照',
|
||||
reviewed_at DATETIME(3) NULL COMMENT '审核时间快照',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '快照创建时间',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_container_fee_source_version (loading_plan_id, source_fee_id, source_version),
|
||||
KEY idx_container_fee_snapshot_tenant (tenant_id, loading_plan_id)
|
||||
) ENGINE=InnoDB COMMENT='柜次费用不可变版本快照';
|
||||
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'supplier_id', "BIGINT UNSIGNED NULL COMMENT '应付Warehouse供应商ID快照;应收为空' AFTER direction");
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'supplier_name', "VARCHAR(200) NULL COMMENT '应付Warehouse供应商名称快照;应收为空' AFTER supplier_id");
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'supplier_category', "VARCHAR(16) NULL COMMENT '费用项目供应商类别快照;应收为空' AFTER supplier_name");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'supplier_id', "BIGINT UNSIGNED NULL COMMENT '应付Warehouse供应商ID快照;应收为空' AFTER direction");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'supplier_name', "VARCHAR(200) NULL COMMENT '应付Warehouse供应商名称快照;应收为空' AFTER supplier_id");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'supplier_category', "VARCHAR(16) NULL COMMENT '费用项目供应商类别快照;应收为空' AFTER supplier_name");
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'supplier_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '应付Warehouse供应商ID快照;应收为空' AFTER direction");
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'supplier_name',
|
||||
"VARCHAR(200) NULL COMMENT '应付Warehouse供应商名称快照;应收为空' AFTER supplier_id");
|
||||
CALL firstleg_finance_add_column('fl_container_fee_detail', 'supplier_category',
|
||||
"VARCHAR(16) NULL COMMENT '费用项目供应商类别约束快照;为空表示任意启用供应商' AFTER supplier_name");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'supplier_id',
|
||||
"BIGINT UNSIGNED NULL COMMENT '应付Warehouse供应商ID快照;应收为空' AFTER direction");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'supplier_name',
|
||||
"VARCHAR(200) NULL COMMENT '应付Warehouse供应商名称快照;应收为空' AFTER supplier_id");
|
||||
CALL firstleg_finance_add_column('fl_inbound_fee_detail', 'supplier_category',
|
||||
"VARCHAR(16) NULL COMMENT '费用项目供应商类别约束快照;为空表示任意启用供应商' AFTER supplier_name");
|
||||
DROP PROCEDURE IF EXISTS firstleg_finance_add_column;
|
||||
|
||||
DROP PROCEDURE IF EXISTS firstleg_finance_assert_schema;
|
||||
|
||||
+13
@@ -2,6 +2,7 @@ package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import java.time.Clock;
|
||||
@@ -19,6 +20,18 @@ class FirstlegDocumentNumberGeneratorTest {
|
||||
private static final Clock CLOCK = Clock.fixed(
|
||||
Instant.parse("2026-08-18T02:00:00Z"), ZoneId.of("Asia/Shanghai"));
|
||||
|
||||
@Test
|
||||
void startsAsASpringBeanWhenTheTestClockConstructorAlsoExists() {
|
||||
JdbcTemplate jdbc = mock(JdbcTemplate.class);
|
||||
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
|
||||
context.registerBean(JdbcTemplate.class, () -> jdbc);
|
||||
context.register(FirstlegDocumentNumberGenerator.class);
|
||||
context.refresh();
|
||||
|
||||
assertThat(context.getBean(FirstlegDocumentNumberGenerator.class)).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatsAllOperationalDocumentPrefixes() {
|
||||
JdbcTemplate jdbc = mock(JdbcTemplate.class);
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ class FinanceG3G4PermissionSqlContractTest {
|
||||
assertThat(sql)
|
||||
.contains("platform:finance:settlement-ar:list")
|
||||
.contains("platform:finance:settlement-ap:list")
|
||||
.contains("permission_code LIKE 'platform:finance:%' AND status='ACTIVE' AND deleted=0) <> 27")
|
||||
.contains("permission_code LIKE 'platform:finance:%' AND status='ACTIVE' AND deleted=0) <> 32")
|
||||
.contains("rp.role_id=4000001")
|
||||
.contains("LIKE 'platform:finance:%')) <> 28");
|
||||
}
|
||||
|
||||
@@ -49,5 +49,6 @@ test('扩展财务验收输出可回传资源与柜次部署阻断', () => {
|
||||
test('按柜 PAYABLE 费用携带 Warehouse 供应商', () => {
|
||||
assert.match(fixtureSource, /supplierId: id\(supplier\.id\)/)
|
||||
assert.doesNotMatch(fixtureSource, /partnerId|partnerName/)
|
||||
assert.match(fixtureSource, /\/api\/platform\/v1\/suppliers\?page=1&size=200&status=ACTIVE&category=BOOKING/)
|
||||
assert.match(fixtureSource, /POST', '\/api\/platform\/v1\/suppliers\?tenantId=0'/)
|
||||
assert.match(fixtureSource, /!category \|\| detail\?\.category === category/)
|
||||
})
|
||||
|
||||
@@ -47,7 +47,7 @@ test('Finance PAYABLE fixture creates an isolated ACTIVE supplier through the fo
|
||||
assert.equal(body.status, 'ACTIVE')
|
||||
assert.match(body.name, /^财务验收供应商-/)
|
||||
assert.match(source, /POST', '\/api\/platform\/v1\/suppliers\?tenantId=0'/)
|
||||
assert.match(source, /detail\?\.status === 'ACTIVE' && detail\?\.category === category/)
|
||||
assert.match(source, /detail\?\.status === 'ACTIVE' && \(!category \|\| detail\?\.category === category\)/)
|
||||
})
|
||||
|
||||
test('fee detail writes preserve 19-digit fee item IDs as strings', () => {
|
||||
|
||||
@@ -253,7 +253,7 @@ test('run 发布一条日期链、三张独立已出账单、三张 AR 和一张
|
||||
assert.equal(fixture.payableSettlements.length, 1)
|
||||
assert.equal(fixture.payableSettlements[0].supplierId, '501')
|
||||
assert.equal(fixture.payableSettlements[0].supplierId, '501')
|
||||
assert.equal(fixture.payableSettlements[0].supplierName, 'Fixture Partner')
|
||||
assert.equal(fixture.payableSettlements[0].supplierName, 'Fixture Supplier')
|
||||
assert.equal(context.coverageGaps.some((gap) => gap.category === 'incomplete-container-cost-attempt'
|
||||
&& gap.reason.includes('containerBookingId=CB-FAILED')
|
||||
&& gap.reason.includes('containerBookingId=CB-COST-RESPONSE')), true)
|
||||
|
||||
Reference in New Issue
Block a user