优化构架
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
const source = readFileSync(new URL('./merchantIam.ts', import.meta.url), 'utf8')
|
||||
const storeSource = readFileSync(new URL('../stores/tenantAdministration.ts', import.meta.url), 'utf8')
|
||||
const authSource = readFileSync(new URL('../stores/auth.ts', import.meta.url), 'utf8')
|
||||
|
||||
test('declares the documented merchant role and member endpoints', () => {
|
||||
assert.match(source, /url: '\/merchant\/v1\/iam\/roles'/)
|
||||
assert.match(source, /method: 'PUT', url: `\/merchant\/v1\/iam\/roles\/\$\{roleId\}`/)
|
||||
assert.match(source, /method: 'PUT', url: `\/merchant\/v1\/iam\/roles\/\$\{roleId\}\/status`/)
|
||||
assert.match(source, /method: 'DELETE', url: `\/merchant\/v1\/iam\/roles\/\$\{roleId\}`/)
|
||||
assert.match(source, /url: '\/merchant\/v1\/iam\/roles\/permissions'/)
|
||||
assert.match(source, /url: '\/merchant\/v1\/iam\/members'/)
|
||||
assert.match(source, /url: '\/merchant\/v1\/iam\/members\/roles'/)
|
||||
assert.match(source, /url: `\/merchant\/v1\/iam\/members\/\$\{membershipId\}`/)
|
||||
assert.match(source, /url: `\/merchant\/v1\/iam\/members\/\$\{membershipId\}\/status`/)
|
||||
assert.match(source, /url: `\/merchant\/v1\/iam\/members\/\$\{membershipId\}\/password`/)
|
||||
assert.match(source, /url: '\/merchant\/v1\/iam\/profile'/)
|
||||
})
|
||||
|
||||
test('supports tenant profile updates and authenticated qualification downloads', () => {
|
||||
assert.match(source, /method: 'PUT', url: '\/merchant\/v1\/iam\/tenant\/profile'/)
|
||||
assert.match(source, /apiBlobRequest\(\{ method: 'GET', url \}\)/)
|
||||
assert.match(source, /url\.startsWith\('\/merchant\/v1\/files\/qualifications\/'\)/)
|
||||
assert.match(storeSource, /await updateTenantProfile/)
|
||||
})
|
||||
|
||||
test('uses membershipId and full replacement arrays in authorization writes', () => {
|
||||
assert.match(source, /data: \{ roleId, permissionIds \}/)
|
||||
assert.match(source, /data: \{ membershipId, roleIds \}/)
|
||||
assert.doesNotMatch(source, /data: \{ userId, roleIds \}/)
|
||||
})
|
||||
|
||||
test('starts empty and only loads server role and member records', () => {
|
||||
assert.match(storeSource, /const members = ref<TenantMember\[]>\(\[\]\)/)
|
||||
assert.match(storeSource, /const roles = ref<TenantRole\[]>\(\[\]\)/)
|
||||
assert.match(storeSource, /async function loadRoles\(\) \{\s+roles\.value = \[\]/)
|
||||
assert.match(storeSource, /async function loadMembers\(\) \{\s+members\.value = \[\]/)
|
||||
assert.doesNotMatch(storeSource, /initialRoles|initialMembers|示例贸易|演示大道/)
|
||||
assert.match(storeSource, /await createMerchantRole/)
|
||||
assert.match(storeSource, /await updateMerchantRole/)
|
||||
assert.match(storeSource, /await changeMerchantRoleStatus/)
|
||||
assert.match(storeSource, /await deleteMerchantRole/)
|
||||
assert.match(storeSource, /await createMerchantMember/)
|
||||
assert.match(storeSource, /await updateMerchantMember/)
|
||||
assert.match(storeSource, /await changeMerchantMemberStatus/)
|
||||
assert.match(storeSource, /await resetMerchantMemberPassword/)
|
||||
})
|
||||
|
||||
test('shares the merchant permission map and preserves resources not rendered by the role editor', () => {
|
||||
assert.match(storeSource, /export const FRONTEND_TO_MERCHANT_PERMISSION_CODES/)
|
||||
assert.match(storeSource, /'order:first-leg:cancel': \['merchant:order:cancel'\]/)
|
||||
assert.match(authSource, /FRONTEND_TO_MERCHANT_PERMISSION_CODES/)
|
||||
assert.match(authSource, /Object\.entries\(FRONTEND_TO_MERCHANT_PERMISSION_CODES\)/)
|
||||
assert.match(storeSource, /const preservedPermissionIds = role\.permissionIds\.filter/)
|
||||
assert.match(storeSource, /const nextPermissionIds = \[\.\.\.new Set\(\[\.\.\.preservedPermissionIds, \.\.\.selectedPermissionIds\]\)\]/)
|
||||
assert.match(storeSource, /await replaceMerchantRolePermissions\(id, nextPermissionIds\)/)
|
||||
})
|
||||
@@ -0,0 +1,205 @@
|
||||
import { apiBlobRequest, apiRequest } from '@/api/http'
|
||||
|
||||
export type MerchantTenantStatus = 'PROFILE_REQUIRED' | 'PENDING' | 'ACTIVE' | 'REJECTED' | 'DISABLED'
|
||||
export type QualificationStatus = 'PENDING' | 'APPROVED' | 'REJECTED'
|
||||
|
||||
export interface MerchantTenantProfile {
|
||||
id: number | string
|
||||
tenantCode: string
|
||||
tenantName: string
|
||||
status: MerchantTenantStatus
|
||||
legalName: string
|
||||
unifiedSocialCreditCode: string | null
|
||||
contactName: string
|
||||
contactPhone: string
|
||||
contactEmail: string | null
|
||||
}
|
||||
|
||||
export interface UpdateMerchantTenantProfileRequest {
|
||||
tenantName: string
|
||||
legalName: string
|
||||
contactName: string
|
||||
contactPhone: string
|
||||
contactEmail: string
|
||||
}
|
||||
|
||||
export interface MerchantQualification {
|
||||
id: number | string
|
||||
tenantId: number | string
|
||||
qualificationType: 'BUSINESS_LICENSE'
|
||||
certificateNo: string | null
|
||||
documentUrl: string
|
||||
status: QualificationStatus
|
||||
reviewComment: string | null
|
||||
}
|
||||
|
||||
export interface MerchantPermissionResource {
|
||||
id: number | string
|
||||
tenantId: number | string
|
||||
appCode: 'MERCHANT'
|
||||
permissionType: 'MENU' | 'BUTTON'
|
||||
parentId: number | string
|
||||
permissionCode: string
|
||||
permissionName: string
|
||||
routePath: string | null
|
||||
httpMethod: string | null
|
||||
sortNo: number
|
||||
status: 'ACTIVE' | string
|
||||
}
|
||||
|
||||
export interface QualificationUploadResult {
|
||||
qualificationId: number | string
|
||||
reviewId: number | string
|
||||
tenantStatus: 'PENDING'
|
||||
documentUrl: string
|
||||
}
|
||||
|
||||
export interface MerchantRole {
|
||||
id: number | string
|
||||
tenantId: number | string
|
||||
appCode: 'MERCHANT'
|
||||
roleCode: string
|
||||
roleName: string
|
||||
roleType: 'SYSTEM' | 'CUSTOM' | string
|
||||
status: 'ACTIVE' | 'INACTIVE' | string
|
||||
description: string | null
|
||||
permissionIds: Array<number | string>
|
||||
}
|
||||
|
||||
export interface CreateMerchantRoleRequest {
|
||||
roleCode: string
|
||||
roleName: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface UpdateMerchantRoleRequest {
|
||||
roleName: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface MerchantMember {
|
||||
membershipId: number | string
|
||||
userId: number | string
|
||||
authUserId: number | string
|
||||
displayName: string
|
||||
phone: string | null
|
||||
email: string | null
|
||||
memberType: 'OWNER' | string
|
||||
memberNo: string
|
||||
status: 'ACTIVE' | 'INACTIVE' | string
|
||||
roleIds: Array<number | string>
|
||||
}
|
||||
|
||||
export interface MerchantPersonalProfile {
|
||||
userId: number | string
|
||||
authUserId: number | string
|
||||
username: string
|
||||
displayName: string
|
||||
phone: string | null
|
||||
email: string | null
|
||||
}
|
||||
|
||||
export interface CreateMerchantMemberRequest {
|
||||
username: string
|
||||
password: string
|
||||
displayName: string
|
||||
phone: string
|
||||
email: string
|
||||
memberType: 'EMPLOYEE'
|
||||
memberNo: string
|
||||
roleIds: Array<number | string>
|
||||
}
|
||||
|
||||
export interface UpdateMerchantMemberRequest {
|
||||
displayName: string
|
||||
phone: string
|
||||
email: string
|
||||
memberType: string
|
||||
memberNo: string
|
||||
roleIds: Array<number | string>
|
||||
}
|
||||
|
||||
export function getTenantProfile() {
|
||||
return apiRequest<MerchantTenantProfile>({ method: 'GET', url: '/merchant/v1/iam/tenant/profile' })
|
||||
}
|
||||
|
||||
export function updateTenantProfile(payload: UpdateMerchantTenantProfileRequest) {
|
||||
return apiRequest<MerchantTenantProfile>({ method: 'PUT', url: '/merchant/v1/iam/tenant/profile', data: payload })
|
||||
}
|
||||
|
||||
export function getTenantQualifications() {
|
||||
return apiRequest<MerchantQualification[]>({ method: 'GET', url: '/merchant/v1/iam/tenant/qualifications' })
|
||||
}
|
||||
|
||||
export function getMerchantPermissionResources() {
|
||||
return apiRequest<MerchantPermissionResource[]>({ method: 'GET', url: '/merchant/v1/iam/permissions' })
|
||||
}
|
||||
|
||||
export function getMerchantPersonalProfile() {
|
||||
return apiRequest<MerchantPersonalProfile>({ method: 'GET', url: '/merchant/v1/iam/profile' })
|
||||
}
|
||||
|
||||
export function updateMerchantPersonalProfile(payload: Pick<MerchantPersonalProfile, 'displayName' | 'phone' | 'email'>) {
|
||||
return apiRequest<MerchantPersonalProfile>({ method: 'PUT', url: '/merchant/v1/iam/profile', data: payload })
|
||||
}
|
||||
|
||||
export function uploadBusinessLicense(file: File, certificateNo?: string) {
|
||||
const data = new FormData()
|
||||
data.append('file', file)
|
||||
if (certificateNo) data.append('certificateNo', certificateNo)
|
||||
return apiRequest<QualificationUploadResult>({ method: 'POST', url: '/merchant/v1/files/qualifications', data })
|
||||
}
|
||||
|
||||
export function downloadQualificationImage(documentUrl: string) {
|
||||
const url = documentUrl.startsWith('/api/') ? documentUrl.slice(4) : documentUrl
|
||||
if (!url.startsWith('/merchant/v1/files/qualifications/')) throw new Error('资质文件地址无效')
|
||||
return apiBlobRequest({ method: 'GET', url })
|
||||
}
|
||||
|
||||
export function getMerchantRoles() {
|
||||
return apiRequest<MerchantRole[]>({ method: 'GET', url: '/merchant/v1/iam/roles' })
|
||||
}
|
||||
|
||||
export function createMerchantRole(payload: CreateMerchantRoleRequest) {
|
||||
return apiRequest<MerchantRole>({ method: 'POST', url: '/merchant/v1/iam/roles', data: payload })
|
||||
}
|
||||
|
||||
export function updateMerchantRole(roleId: number | string, payload: UpdateMerchantRoleRequest) {
|
||||
return apiRequest<MerchantRole>({ method: 'PUT', url: `/merchant/v1/iam/roles/${roleId}`, data: payload })
|
||||
}
|
||||
|
||||
export function changeMerchantRoleStatus(roleId: number | string, status: 'ACTIVE' | 'DISABLED') {
|
||||
return apiRequest<MerchantRole>({ method: 'PUT', url: `/merchant/v1/iam/roles/${roleId}/status`, data: { status } })
|
||||
}
|
||||
|
||||
export function deleteMerchantRole(roleId: number | string) {
|
||||
return apiRequest<void>({ method: 'DELETE', url: `/merchant/v1/iam/roles/${roleId}` })
|
||||
}
|
||||
|
||||
export function replaceMerchantRolePermissions(roleId: number | string, permissionIds: Array<number | string>) {
|
||||
return apiRequest<void>({ method: 'PUT', url: '/merchant/v1/iam/roles/permissions', data: { roleId, permissionIds } })
|
||||
}
|
||||
|
||||
export function getMerchantMembers() {
|
||||
return apiRequest<MerchantMember[]>({ method: 'GET', url: '/merchant/v1/iam/members' })
|
||||
}
|
||||
|
||||
export function createMerchantMember(payload: CreateMerchantMemberRequest) {
|
||||
return apiRequest<MerchantMember>({ method: 'POST', url: '/merchant/v1/iam/members', data: payload })
|
||||
}
|
||||
|
||||
export function updateMerchantMember(membershipId: number | string, payload: UpdateMerchantMemberRequest) {
|
||||
return apiRequest<MerchantMember>({ method: 'PUT', url: `/merchant/v1/iam/members/${membershipId}`, data: payload })
|
||||
}
|
||||
|
||||
export function changeMerchantMemberStatus(membershipId: number | string, status: 'ACTIVE' | 'DISABLED') {
|
||||
return apiRequest<MerchantMember>({ method: 'PUT', url: `/merchant/v1/iam/members/${membershipId}/status`, data: { status } })
|
||||
}
|
||||
|
||||
export function resetMerchantMemberPassword(membershipId: number | string, newPassword: string) {
|
||||
return apiRequest<void>({ method: 'PUT', url: `/merchant/v1/iam/members/${membershipId}/password`, data: { newPassword } })
|
||||
}
|
||||
|
||||
export function replaceMerchantMemberRoles(membershipId: number | string, roleIds: Array<number | string>) {
|
||||
return apiRequest<void>({ method: 'PUT', url: '/merchant/v1/iam/members/roles', data: { membershipId, roleIds } })
|
||||
}
|
||||
Reference in New Issue
Block a user