fix(file): allow standalone legal-person ID upload

This commit is contained in:
2026-09-02 12:07:32 +08:00
parent f0da6e5110
commit 9e70f4339a
6 changed files with 203 additions and 18 deletions
@@ -40,7 +40,7 @@ public class QualificationFileController {
@RequestParam(required = false) LocalDate validTo,
@RequestParam(defaultValue = "false") boolean replace) {
long tenantId = merchantTenant(user);
qualificationType = QualificationTypes.requireOptional(qualificationType);
qualificationType = QualificationTypes.requireSingleUpload(qualificationType);
var stored = storage.store(tenantId, file);
try {
var response = iamClient.submit(internalToken,
@@ -21,11 +21,22 @@ final class QualificationTypes {
return qualificationType;
}
static String requireSingleUpload(String qualificationType) {
String type = requireSupported(qualificationType);
if ("BUSINESS_LICENSE".equals(type)) {
throw new BizException(ResultCode.BAD_REQUEST,
"The business license must be uploaded with its unified social credit code"
+ " / 营业执照必须与统一社会信用代码一并上传");
}
return type;
}
static String requireOptional(String qualificationType) {
String type = requireSupported(qualificationType);
if ("BUSINESS_LICENSE".equals(type) || "LEGAL_PERSON_ID".equals(type)) {
throw new BizException(ResultCode.BAD_REQUEST,
"Required qualifications must be uploaded as one package / 营业执照、统一社会信用代码和法人身份证必须整包上传");
"Business license and legal-person ID use the platform qualification package"
+ " / 平台端营业执照与法人身份证通过资质包上传");
}
return type;
}
@@ -4,6 +4,43 @@ info:
version: 1.0.0
description: 营业执照为必填资质;法人身份证为选填资质,缺省时不创建、不替换法人身份证记录。
paths:
/api/merchant/v1/files/qualifications:
post:
summary: 客户单独上传或替换选填资质
description: >-
需要 merchant:qualification:upload;客户范围只取登录 Token,不接受客户ID。
LEGAL_PERSON_ID 可在已有营业执照后单独新增或替换,不要求重新上传营业执照。
BUSINESS_LICENSE 不允许走此入口,必须与18位统一社会信用代码通过 required-package 一并上传。
security: [{bearerAuth: []}]
parameters:
- {$ref: '#/components/parameters/QualificationType'}
- {$ref: '#/components/parameters/CertificateNo'}
- {$ref: '#/components/parameters/ValidTo'}
- {$ref: '#/components/parameters/Replace'}
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: string
format: binary
maxLength: 5242880
description: PDF/JPG/PNG,最大5MB,声明媒体类型必须与文件签名一致
responses:
'200':
description: 成功或业务失败,失败时按 code 判断并保留 requestId/traceId
content:
application/json:
schema:
oneOf:
- {$ref: '#/components/schemas/QualificationUploadSuccess'}
- {$ref: '#/components/schemas/ErrorResponse'}
'401': {$ref: '#/components/responses/Error'}
'403': {$ref: '#/components/responses/Error'}
/api/merchant/v1/files/qualifications/required-package:
post:
summary: 客户上传必需资质包
@@ -37,6 +74,27 @@ components:
securitySchemes:
bearerAuth: {type: http, scheme: bearer, bearerFormat: JWT}
parameters:
QualificationType:
name: qualificationType
in: query
required: true
schema: {type: string, enum: [LEGAL_PERSON_ID, FREIGHT_FORWARDER_CERTIFICATE, BANK_ACCOUNT_LICENSE]}
CertificateNo:
name: certificateNo
in: query
required: false
schema: {type: string, maxLength: 128}
ValidTo:
name: validTo
in: query
required: false
schema: {type: string, format: date}
Replace:
name: replace
in: query
required: false
description: 同类有当前资质时必须显式为true;成功后旧文件元数据标记为已替代
schema: {type: boolean, default: false}
TenantId:
name: tenantId
in: path
@@ -88,6 +146,11 @@ components:
nullable: true
description: 法人身份证,选填;PDF/JPG/PNG,最大5MB
responses:
Error:
description: 认证或授权失败,响应包含请求关联元数据
content:
application/json:
schema: {$ref: '#/components/schemas/ErrorResponse'}
PackageResult:
description: 资质包上传结果
content:
@@ -100,6 +163,37 @@ components:
data: {$ref: '#/components/schemas/PackageUploadResult'}
schemas:
Id: {type: string, pattern: '^[1-9][0-9]*$'}
QualificationUploadSuccess:
type: object
required: [code, message, data, timestamp]
properties:
code: {type: string, enum: ['200']}
message: {type: string}
data: {$ref: '#/components/schemas/QualificationUploadResult'}
requestId: {type: string, nullable: true}
traceId: {type: string, nullable: true}
timestamp: {type: string, format: date-time}
ErrorResponse:
type: object
required: [code, message, requestId, traceId, timestamp]
properties:
code: {type: string, example: A0403}
message: {type: string}
data: {nullable: true}
requestId: {type: string, minLength: 1}
traceId: {type: string, minLength: 1}
timestamp: {type: string, format: date-time}
QualificationUploadResult:
type: object
required: [qualificationId, tenantStatus, documentUrl]
properties:
qualificationId: {$ref: '#/components/schemas/Id'}
reviewId:
allOf: [{$ref: '#/components/schemas/Id'}]
nullable: true
description: 上传不自动送审,当前固定为null
tenantStatus: {type: string, enum: [PROFILE_REQUIRED, REJECTED]}
documentUrl: {type: string, minLength: 1}
PackageUploadResult:
type: object
required: [businessLicenseQualificationId, tenantStatus, businessLicenseDocumentUrl]