fix qualification upload contract and test baseline

This commit is contained in:
2026-09-12 12:27:56 +08:00
parent dcd079b5b7
commit 2191bacbfe
9 changed files with 61 additions and 6 deletions
@@ -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',
@@ -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,
@@ -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");
}
@@ -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");
}
@@ -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)
@@ -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());
@@ -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/)
@@ -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'/)
@@ -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 })