优化
This commit is contained in:
@@ -13,7 +13,7 @@ server {
|
||||
|
||||
# 前端统一请求 /api,保持原始路径转发给 Gateway。
|
||||
location /api/ {
|
||||
client_max_body_size 6m;
|
||||
client_max_body_size 12m;
|
||||
proxy_pass ${API_UPSTREAM};
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
@@ -5,5 +5,5 @@ import test from 'node:test'
|
||||
const config = readFileSync(new URL('../../nginx/default.conf.template', import.meta.url), 'utf8')
|
||||
|
||||
test('allows qualification uploads with multipart overhead through the API proxy', () => {
|
||||
assert.match(config, /location \/api\/ \{[\s\S]*?client_max_body_size 6m;/)
|
||||
assert.match(config, /location \/api\/ \{[\s\S]*?client_max_body_size 12m;/)
|
||||
})
|
||||
|
||||
@@ -34,6 +34,13 @@ test('supports tenant profile updates and authenticated qualification downloads'
|
||||
assert.match(storeSource, /normalizeQualificationStatus\(item\.status\)/)
|
||||
})
|
||||
|
||||
test('uploads the three required qualification fields through one multipart package request', () => {
|
||||
assert.match(source, /url: '\/merchant\/v1\/files\/qualifications\/required-package'/)
|
||||
assert.match(source, /data\.append\('businessLicense', businessLicense\)/)
|
||||
assert.match(source, /data\.append\('legalPersonId', legalPersonId\)/)
|
||||
assert.match(source, /params: \{ unifiedSocialCreditCode, replaceBusinessLicense, replaceLegalPersonId \}/)
|
||||
})
|
||||
|
||||
test('uses membershipId and full replacement arrays in authorization writes', () => {
|
||||
assert.match(source, /data: \{ roleId, permissionIds \}/)
|
||||
assert.match(source, /data: \{ membershipId, roleIds \}/)
|
||||
|
||||
@@ -56,6 +56,14 @@ export interface QualificationUploadResult {
|
||||
documentUrl: string
|
||||
}
|
||||
|
||||
export interface RequiredQualificationPackageUploadResult {
|
||||
businessLicenseQualificationId: number | string
|
||||
legalPersonIdQualificationId: number | string
|
||||
tenantStatus: MerchantTenantStatus
|
||||
businessLicenseDocumentUrl: string
|
||||
legalPersonIdDocumentUrl: string
|
||||
}
|
||||
|
||||
export interface MerchantRole {
|
||||
id: number | string
|
||||
tenantId: number | string
|
||||
@@ -161,6 +169,24 @@ export function uploadMerchantQualification(
|
||||
})
|
||||
}
|
||||
|
||||
export function uploadMerchantRequiredQualificationPackage(
|
||||
businessLicense: File,
|
||||
legalPersonId: File,
|
||||
unifiedSocialCreditCode: string,
|
||||
replaceBusinessLicense = false,
|
||||
replaceLegalPersonId = false,
|
||||
) {
|
||||
const data = new FormData()
|
||||
data.append('businessLicense', businessLicense)
|
||||
data.append('legalPersonId', legalPersonId)
|
||||
return apiRequest<RequiredQualificationPackageUploadResult>({
|
||||
method: 'POST',
|
||||
url: '/merchant/v1/files/qualifications/required-package',
|
||||
params: { unifiedSocialCreditCode, replaceBusinessLicense, replaceLegalPersonId },
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function submitTenantQualificationReview() {
|
||||
return apiRequest<MerchantTenantProfile>({ method: 'POST', url: '/merchant/v1/iam/tenant/qualifications/submit-review' })
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
downloadQualificationImage,
|
||||
submitTenantQualificationReview,
|
||||
uploadMerchantQualification,
|
||||
uploadMerchantRequiredQualificationPackage,
|
||||
type MerchantQualificationType,
|
||||
} from '@/modules/tenant-management/api/merchantIam'
|
||||
|
||||
@@ -80,11 +81,15 @@ const companyCreditCodeValid = computed(() => unifiedSocialCreditCodePattern.tes
|
||||
const normalizedQualificationCertificateNo = computed(() => qualificationCertificateNo.value.trim().toUpperCase())
|
||||
const draftCreditCodeValid = computed(() => unifiedSocialCreditCodePattern.test(normalizedQualificationCertificateNo.value))
|
||||
const businessLicenseFileSelected = computed(() => Boolean(qualificationDraftItem('BUSINESS_LICENSE')?.file))
|
||||
const qualificationCreditCodeReady = computed(() => companyCreditCodeValid.value
|
||||
|| (businessLicenseFileSelected.value && draftCreditCodeValid.value))
|
||||
const legalPersonIdFileSelected = computed(() => Boolean(qualificationDraftItem('LEGAL_PERSON_ID')?.file))
|
||||
const requiredPackageChangeSelected = computed(() => businessLicenseFileSelected.value || legalPersonIdFileSelected.value)
|
||||
const requiredPackageFilesSelected = computed(() => businessLicenseFileSelected.value && legalPersonIdFileSelected.value)
|
||||
const qualificationCreditCodeReady = computed(() => requiredPackageChangeSelected.value
|
||||
? requiredPackageFilesSelected.value && (companyCreditCodeValid.value || draftCreditCodeValid.value)
|
||||
: companyCreditCodeValid.value)
|
||||
const qualificationCreditCodeError = computed(() => {
|
||||
if (qualificationCreditCodeReady.value) return ''
|
||||
if (draftCreditCodeValid.value && !businessLicenseFileSelected.value) return '统一社会信用代码必须随营业执照文件共同保存'
|
||||
if (draftCreditCodeValid.value && !requiredPackageFilesSelected.value) return '统一社会信用代码必须随营业执照和法人身份证共同保存'
|
||||
return '请输入18位字母或数字组成的统一社会信用代码'
|
||||
})
|
||||
const canUploadQualification = computed(() => ['PROFILE_REQUIRED', 'REJECTED'].includes(store.company.auditStatus))
|
||||
@@ -92,13 +97,15 @@ const uploadedQualificationTypes = computed(() => new Set(store.qualifications
|
||||
.filter((item) => item.status === 'PENDING')
|
||||
.map((item) => qualificationTypes.find((type) => type.label === item.type)?.value)
|
||||
.filter((type): type is MerchantQualificationType => Boolean(type))))
|
||||
const requiredPackageFilesReady = computed(() => requiredQualificationTypes.every((type) => (
|
||||
uploadedQualificationTypes.value.has(type.value) || Boolean(qualificationDraftItem(type.value)?.file)
|
||||
)))
|
||||
const requiredPackageFilesReady = computed(() => requiredPackageChangeSelected.value
|
||||
? requiredPackageFilesSelected.value
|
||||
: requiredQualificationTypes.every((type) => uploadedQualificationTypes.value.has(type.value)))
|
||||
const requiredPackageComplete = computed(() => qualificationCreditCodeReady.value && requiredPackageFilesReady.value)
|
||||
const missingRequiredPackageItems = computed(() => {
|
||||
const missing = requiredQualificationTypes
|
||||
.filter((type) => !uploadedQualificationTypes.value.has(type.value) && !qualificationDraftItem(type.value)?.file)
|
||||
.filter((type) => requiredPackageChangeSelected.value
|
||||
? !qualificationDraftItem(type.value)?.file
|
||||
: !uploadedQualificationTypes.value.has(type.value))
|
||||
.map((type) => type.label)
|
||||
if (!qualificationCreditCodeReady.value) missing.splice(1, 0, '18位统一社会信用代码')
|
||||
return missing
|
||||
@@ -330,12 +337,46 @@ async function submitQualification() {
|
||||
ElMessage.warning(`请完整保存必填业务项:${missingRequiredPackageItems.value.join('、')}`)
|
||||
return
|
||||
}
|
||||
const pendingItems = qualificationDrafts.filter((item) => item.file)
|
||||
const optionalItems = qualificationDrafts.filter((item) => !item.required && item.file)
|
||||
qualificationSubmitting.value = true
|
||||
let uploadedCount = 0
|
||||
const failures: string[] = []
|
||||
try {
|
||||
for (const item of pendingItems) {
|
||||
if (requiredPackageChangeSelected.value) {
|
||||
const businessLicense = qualificationDraftItem('BUSINESS_LICENSE')
|
||||
const legalPersonId = qualificationDraftItem('LEGAL_PERSON_ID')
|
||||
for (const item of [businessLicense, legalPersonId]) {
|
||||
if (!item) continue
|
||||
item.error = ''
|
||||
item.uploadState = 'uploading'
|
||||
}
|
||||
try {
|
||||
await uploadMerchantRequiredQualificationPackage(
|
||||
businessLicense?.file as File,
|
||||
legalPersonId?.file as File,
|
||||
normalizedQualificationCertificateNo.value,
|
||||
Boolean(replaceableQualification('BUSINESS_LICENSE')),
|
||||
Boolean(replaceableQualification('LEGAL_PERSON_ID')),
|
||||
)
|
||||
for (const item of [businessLicense, legalPersonId]) {
|
||||
if (!item) continue
|
||||
releaseQualificationPreview(item)
|
||||
item.file = null
|
||||
item.fileName = ''
|
||||
item.fileSize = 0
|
||||
item.fileType = ''
|
||||
item.inputVersion += 1
|
||||
item.uploadState = 'uploaded'
|
||||
}
|
||||
uploadedCount += 2
|
||||
} catch (error) {
|
||||
const message = qualificationUploadErrorMessage(error)
|
||||
if (businessLicense) { businessLicense.error = message; businessLicense.uploadState = 'error' }
|
||||
if (legalPersonId) { legalPersonId.error = message; legalPersonId.uploadState = 'error' }
|
||||
failures.push(`必填资质包:${message}`)
|
||||
}
|
||||
}
|
||||
for (const item of failures.length ? [] : optionalItems) {
|
||||
const file = item.file
|
||||
if (!file) continue
|
||||
item.error = ''
|
||||
@@ -344,7 +385,7 @@ async function submitQualification() {
|
||||
await uploadMerchantQualification(
|
||||
file,
|
||||
item.value,
|
||||
item.value === 'BUSINESS_LICENSE' ? normalizedQualificationCertificateNo.value : undefined,
|
||||
undefined,
|
||||
Boolean(replaceableQualification(item.value)),
|
||||
)
|
||||
releaseQualificationPreview(item)
|
||||
|
||||
@@ -33,7 +33,10 @@ test('implements company details, contact information and qualification workflow
|
||||
assert.match(profileSource, /class="qualification-upload-grid"/)
|
||||
assert.match(profileSource, /v-for="item in qualificationDrafts"/)
|
||||
assert.match(profileSource, /保存全部资质/)
|
||||
assert.match(profileSource, /for \(const item of pendingItems\)/)
|
||||
assert.match(profileSource, /uploadMerchantRequiredQualificationPackage/)
|
||||
assert.match(profileSource, /const requiredPackageFilesSelected = computed/)
|
||||
assert.match(profileSource, /item\.uploadState = 'uploading'/)
|
||||
assert.match(profileSource, /for \(const item of failures\.length \? \[\] : optionalItems\)/)
|
||||
assert.match(profileSource, /item\.fileName = ''[\s\S]*item\.uploadState = 'uploaded'/)
|
||||
assert.match(profileSource, /const canSaveQualificationBatch = computed/)
|
||||
assert.match(profileSource, /const qualificationCreditCodeReady = computed/)
|
||||
|
||||
Reference in New Issue
Block a user