diff --git a/deploy/sql/头程数据库统一增量-MySQL8.sql b/deploy/sql/头程数据库统一增量-MySQL8.sql index 80d2d6bde..c9e2d8e6b 100644 --- a/deploy/sql/头程数据库统一增量-MySQL8.sql +++ b/deploy/sql/头程数据库统一增量-MySQL8.sql @@ -3896,9 +3896,38 @@ CREATE TABLE IF NOT EXISTS fl_price_book_version ( KEY idx_price_book_source_hash (tenant_id, source_sha256) ) ENGINE=InnoDB COMMENT='平台公共船期价格整表版本'; +-- Warehouse 增量段已在自身结束时删除同名过程;Firstleg 价格版本的存量补列需在当前库重新定义。 +DROP PROCEDURE IF EXISTS add_column_if_missing; +DELIMITER $$ +CREATE PROCEDURE add_column_if_missing( + IN p_table_name VARCHAR(64), + IN p_column_name VARCHAR(64), + IN p_column_definition TEXT +) +BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = DATABASE() + AND table_name = p_table_name + AND column_name = p_column_name + ) THEN + SET @add_column_sql = CONCAT( + 'ALTER TABLE `', REPLACE(p_table_name, '`', '``'), + '` ADD COLUMN `', REPLACE(p_column_name, '`', '``'), + '` ', p_column_definition + ); + PREPARE add_column_statement FROM @add_column_sql; + EXECUTE add_column_statement; + DEALLOCATE PREPARE add_column_statement; + END IF; +END$$ +DELIMITER ; + CALL add_column_if_missing('fl_price_book_version', 'voided_by', 'BIGINT UNSIGNED NULL COMMENT ''作废操作人ID'' AFTER superseded_at'); CALL add_column_if_missing('fl_price_book_version', 'voided_at', 'DATETIME(3) NULL COMMENT ''作废时间'' AFTER voided_by'); CALL add_column_if_missing('fl_price_book_version', 'void_reason', 'VARCHAR(500) NULL COMMENT ''作废原因'' AFTER voided_at'); +DROP PROCEDURE IF EXISTS add_column_if_missing; CREATE TABLE IF NOT EXISTS fl_price_book_channel ( id BIGINT UNSIGNED NOT NULL COMMENT '版本渠道快照ID', diff --git a/file/src/main/java/com/baiyue/file/client/IamQualificationClient.java b/file/src/main/java/com/baiyue/file/client/IamQualificationClient.java index a0c63a603..181269c14 100644 --- a/file/src/main/java/com/baiyue/file/client/IamQualificationClient.java +++ b/file/src/main/java/com/baiyue/file/client/IamQualificationClient.java @@ -31,6 +31,7 @@ public interface IamQualificationClient { record QualificationRequest(Long tenantId, String qualificationType, String certificateNo, String documentUrl, + String originalFileName, java.time.LocalDate validTo, boolean replace) { } record QualificationResponse(Long qualificationId, Long reviewId, String tenantStatus, String replacedDocumentUrl) { } @@ -38,12 +39,15 @@ public interface IamQualificationClient { String replacedDocumentUrl) { } record PlatformQualificationRequest(Long tenantId, String qualificationType, String certificateNo, String documentUrl, + String originalFileName, java.time.LocalDate validTo, boolean replace) { } record RequiredPackageRequest(Long tenantId, String unifiedSocialCreditCode, String businessLicenseDocumentUrl, + String businessLicenseOriginalFileName, java.time.LocalDate businessLicenseValidTo, boolean replaceBusinessLicense, String legalPersonIdDocumentUrl, + String legalPersonIdOriginalFileName, java.time.LocalDate legalPersonIdValidTo, boolean replaceLegalPersonId) { } record RequiredPackageResponse(Long businessLicenseQualificationId, diff --git a/file/src/main/java/com/baiyue/file/controller/PlatformQualificationFileController.java b/file/src/main/java/com/baiyue/file/controller/PlatformQualificationFileController.java index 44ed58565..e4694bede 100644 --- a/file/src/main/java/com/baiyue/file/controller/PlatformQualificationFileController.java +++ b/file/src/main/java/com/baiyue/file/controller/PlatformQualificationFileController.java @@ -53,7 +53,8 @@ public class PlatformQualificationFileController { try { var response = iamClient.submitPlatform(internalToken, new IamQualificationClient.PlatformQualificationRequest( - tenantId, qualificationType, certificateNo, stored.documentUrl(), validTo, replace)); + tenantId, qualificationType, certificateNo, stored.documentUrl(), + file.getOriginalFilename(), validTo, replace)); if (response == null) { throw new IllegalStateException("IAM qualification response is empty"); } diff --git a/file/src/main/java/com/baiyue/file/controller/QualificationFileController.java b/file/src/main/java/com/baiyue/file/controller/QualificationFileController.java index d47487852..fd8cf524b 100644 --- a/file/src/main/java/com/baiyue/file/controller/QualificationFileController.java +++ b/file/src/main/java/com/baiyue/file/controller/QualificationFileController.java @@ -45,7 +45,7 @@ public class QualificationFileController { try { var response = iamClient.submit(internalToken, new IamQualificationClient.QualificationRequest(tenantId, qualificationType, - certificateNo, stored.documentUrl(), validTo, replace)); + certificateNo, stored.documentUrl(), file.getOriginalFilename(), validTo, replace)); if (response == null) { throw new IllegalStateException("IAM qualification response is empty"); } diff --git a/file/src/main/java/com/baiyue/file/service/QualificationPackageService.java b/file/src/main/java/com/baiyue/file/service/QualificationPackageService.java index 63d2c00aa..5519973f0 100644 --- a/file/src/main/java/com/baiyue/file/service/QualificationPackageService.java +++ b/file/src/main/java/com/baiyue/file/service/QualificationPackageService.java @@ -45,8 +45,10 @@ public class QualificationPackageService { String legalPersonIdDocumentUrl = storedLegalPersonId == null ? null : storedLegalPersonId.documentUrl(); var request = new IamQualificationClient.RequiredPackageRequest( - tenantId, normalizedCreditCode, storedBusinessLicense.documentUrl(), businessLicenseValidTo, - replaceBusinessLicense, legalPersonIdDocumentUrl, legalPersonIdValidTo, replaceLegalPersonId); + tenantId, normalizedCreditCode, storedBusinessLicense.documentUrl(), businessLicense.getOriginalFilename(), + businessLicenseValidTo, replaceBusinessLicense, legalPersonIdDocumentUrl, + legalPersonId == null ? null : legalPersonId.getOriginalFilename(), + legalPersonIdValidTo, replaceLegalPersonId); try { var response = platform ? iamClient.submitPlatformRequiredPackage(internalToken, request) diff --git a/file/src/test/java/com/baiyue/file/service/QualificationPackageServiceTest.java b/file/src/test/java/com/baiyue/file/service/QualificationPackageServiceTest.java index 2157f6b76..43d1b1944 100644 --- a/file/src/test/java/com/baiyue/file/service/QualificationPackageServiceTest.java +++ b/file/src/test/java/com/baiyue/file/service/QualificationPackageServiceTest.java @@ -57,6 +57,8 @@ class QualificationPackageServiceTest { var captor = ArgumentCaptor.forClass(IamQualificationClient.RequiredPackageRequest.class); ordered.verify(fixture.iamClient).submitRequiredPackage(eq("internal-token"), captor.capture()); assertEquals("91310101ABCDE12345", captor.getValue().unifiedSocialCreditCode()); + assertEquals("license.pdf", captor.getValue().businessLicenseOriginalFileName()); + assertEquals("legal-person.png", captor.getValue().legalPersonIdOriginalFileName()); assertEquals(BUSINESS_URL, result.businessLicenseDocumentUrl()); assertEquals(LEGAL_PERSON_URL, result.legalPersonIdDocumentUrl()); } @@ -74,6 +76,7 @@ class QualificationPackageServiceTest { var captor = ArgumentCaptor.forClass(IamQualificationClient.RequiredPackageRequest.class); verify(fixture.iamClient).submitRequiredPackage(eq("internal-token"), captor.capture()); assertNull(captor.getValue().legalPersonIdDocumentUrl()); + assertNull(captor.getValue().legalPersonIdOriginalFileName()); assertNull(captor.getValue().legalPersonIdValidTo()); assertNull(result.legalPersonIdQualificationId()); assertNull(result.legalPersonIdDocumentUrl()); diff --git a/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs b/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs index d362a9e4c..b0efc204c 100644 --- a/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs +++ b/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs @@ -67,6 +67,19 @@ test('国内统一初始化在注释门禁前创建订单申报快照表', () => } }) +test('国内统一增量在 Firstleg 补列前重建已由 Warehouse 段删除的过程', () => { + const warehouseDrop = sql.indexOf('DROP PROCEDURE IF EXISTS add_column_if_missing;', + sql.indexOf('CALL assert_platform_public_resource_scope();')) + const firstlegCall = sql.indexOf("CALL add_column_if_missing('fl_price_book_version', 'voided_by'") + const firstlegCreate = sql.lastIndexOf('CREATE PROCEDURE add_column_if_missing(', firstlegCall) + const firstlegDrop = sql.indexOf('DROP PROCEDURE IF EXISTS add_column_if_missing;', firstlegCall) + + assert.ok(warehouseDrop >= 0, 'Warehouse migration must clean up its helper procedure') + assert.ok(firstlegCreate > warehouseDrop, 'Firstleg must recreate the helper after the Warehouse cleanup') + assert.ok(firstlegCreate < firstlegCall, 'Firstleg helper must exist before the price-version ALTER calls') + assert.ok(firstlegDrop > firstlegCall, 'Firstleg must clean up the helper after its ALTER calls') +}) + test('海外统一初始化内联空库基线且不依赖模块 SQL', () => { assert.match(overseasSql, /BEGIN INLINE EMPTY-DATABASE BASELINE: LASTLEG/) assert.match(overseasSql, /BEGIN INLINE EMPTY-DATABASE BASELINE: OVERSEAS WAREHOUSE/) diff --git a/scripts/admin-test-env-modules/merchant-qualification-fixture-contract.test.mjs b/scripts/admin-test-env-modules/merchant-qualification-fixture-contract.test.mjs index b4d30a2b0..2b29d2014 100644 --- a/scripts/admin-test-env-modules/merchant-qualification-fixture-contract.test.mjs +++ b/scripts/admin-test-env-modules/merchant-qualification-fixture-contract.test.mjs @@ -15,6 +15,8 @@ test('默认全量验收先准备已审核商户再运行订单链路', () => { test('商户夹具通过正式 File 和 IAM 审核流程建立下单资格', () => { assert.match(fixture, /files\/qualifications\/\$\{encodeURIComponent\(tenantId\)\}\/required-package/) + assert.match(fixture, /required-package\?unifiedSocialCreditCode=\$\{encodeURIComponent\(unifiedSocialCreditCode\)\}/) + assert.doesNotMatch(fixture, /packageForm\.append\('unifiedSocialCreditCode'/) assert.match(fixture, /iam\/tenants\/\$\{encodeURIComponent\(tenantId\)\}\/submit-review/) assert.match(fixture, /iam\/tenants\/\$\{encodeURIComponent\(tenantId\)\}\/dimension-initialization/) assert.match(fixture, /status === 'READY'/) diff --git a/scripts/admin-test-env-modules/merchant-qualification-fixture.mjs b/scripts/admin-test-env-modules/merchant-qualification-fixture.mjs index 168dac189..a2a5e94fd 100644 --- a/scripts/admin-test-env-modules/merchant-qualification-fixture.mjs +++ b/scripts/admin-test-env-modules/merchant-qualification-fixture.mjs @@ -62,9 +62,10 @@ export async function run(context) { const packageForm = new FormData() packageForm.append('businessLicense', new Blob([QUALIFICATION_IMAGE], { type: 'image/png' }), `business-${suffix}.png`) packageForm.append('legalPersonId', new Blob([QUALIFICATION_IMAGE], { type: 'image/png' }), `legal-person-${suffix}.png`) - packageForm.append('unifiedSocialCreditCode', `91440300${suffix}`) + const unifiedSocialCreditCode = `91440300${suffix}` const packageResponse = await context.requestWithBody('POST', - `/api/platform/v1/files/qualifications/${encodeURIComponent(tenantId)}/required-package`, packageForm) + `/api/platform/v1/files/qualifications/${encodeURIComponent(tenantId)}/required-package?unifiedSocialCreditCode=${encodeURIComponent(unifiedSocialCreditCode)}`, + packageForm) context.apiSuccess(MODULE, '通过 File 上传真实必填资质包', packageResponse, `tenantId=${tenantId}`, { deploymentBlocker: true })