优化构架
This commit is contained in:
@@ -0,0 +1,326 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { http } from '@/shared/api/http'
|
||||
import {
|
||||
assignPlatformInternalUserRoles,
|
||||
changePlatformManagedUserStatus,
|
||||
changePlatformInternalUserStatus,
|
||||
changePlatformTenantStatus,
|
||||
changePlatformPermissionStatus,
|
||||
changePlatformRoleStatus,
|
||||
createPlatformPermission,
|
||||
createPlatformManagedUser,
|
||||
createPlatformRole,
|
||||
createPlatformTenant,
|
||||
deletePlatformPermission,
|
||||
deletePlatformRole,
|
||||
downloadTenantQualification,
|
||||
listPlatformTenants,
|
||||
listPlatformTenantOrderCounts,
|
||||
listTenantQualifications,
|
||||
listPlatformPermissions,
|
||||
listPlatformRoles,
|
||||
listPlatformInternalUsers,
|
||||
listPlatformManagedUsers,
|
||||
getPlatformUserOptions,
|
||||
qualificationFileName,
|
||||
replacePlatformRolePermissions,
|
||||
resetPlatformInternalUserPassword,
|
||||
resetPlatformManagedUserPassword,
|
||||
submitPlatformTenantReview,
|
||||
updatePlatformInternalUser,
|
||||
updatePlatformManagedUser,
|
||||
updatePlatformPermission,
|
||||
updatePlatformRole,
|
||||
updatePlatformTenant,
|
||||
uploadPlatformTenantQualification,
|
||||
type PlatformPermissionResource,
|
||||
type PlatformInternalUser,
|
||||
type PlatformManagedUser,
|
||||
type PlatformRoleResource,
|
||||
} from './platformIam'
|
||||
|
||||
vi.mock('@/shared/api/http', () => ({
|
||||
http: { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn() },
|
||||
unwrapApiResponse: <T>(response: { data: T }) => response.data,
|
||||
}))
|
||||
|
||||
const role: PlatformRoleResource = {
|
||||
id: 4000002,
|
||||
tenantId: 0,
|
||||
appCode: 'PLATFORM',
|
||||
roleCode: 'CUSTOMER_REVIEWER',
|
||||
roleName: '企业审核员',
|
||||
roleType: 'SYSTEM',
|
||||
status: 'ACTIVE',
|
||||
description: '企业资料与入驻审核',
|
||||
permissionIds: ['5100010'],
|
||||
}
|
||||
|
||||
const permission: PlatformPermissionResource = {
|
||||
id: 5100010,
|
||||
tenantId: 0,
|
||||
appCode: 'PLATFORM',
|
||||
permissionType: 'MENU',
|
||||
parentId: 5100000,
|
||||
permissionCode: 'platform:customer:list',
|
||||
permissionName: '企业客户',
|
||||
routePath: '/customers',
|
||||
httpMethod: null,
|
||||
sortNo: 10,
|
||||
status: 'ACTIVE',
|
||||
builtIn: false,
|
||||
}
|
||||
|
||||
const internalUser: PlatformInternalUser = {
|
||||
membershipId: 3000001,
|
||||
userId: 2000001,
|
||||
authUserId: 2000001,
|
||||
username: 'platform_admin',
|
||||
displayName: '平台管理员',
|
||||
phone: '13800138000',
|
||||
email: 'admin@example.com',
|
||||
memberType: 'INTERNAL',
|
||||
memberNo: 'PLATFORM-0001',
|
||||
status: 'ACTIVE',
|
||||
roleIds: [4000001],
|
||||
}
|
||||
|
||||
const managedUser: PlatformManagedUser = {
|
||||
membershipId: '3000002',
|
||||
userId: '2000002',
|
||||
authUserId: '1000002',
|
||||
username: 'zhangwei',
|
||||
displayName: '张伟',
|
||||
phone: '13800138000',
|
||||
email: 'zhangwei@example.com',
|
||||
tenantId: '10001',
|
||||
tenantName: '华远贸易',
|
||||
memberType: 'EMPLOYEE',
|
||||
memberNo: 'MEMBER-3000002',
|
||||
status: 'ACTIVE',
|
||||
roleIds: ['40001'],
|
||||
roleNames: ['租户管理员'],
|
||||
lastLoginAt: '2026-06-01T10:30:00',
|
||||
protectedAccount: false,
|
||||
}
|
||||
|
||||
describe('platform IAM roles', () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(http.get).mockReset()
|
||||
vi.mocked(http.post).mockReset()
|
||||
vi.mocked(http.put).mockReset()
|
||||
vi.mocked(http.delete).mockReset()
|
||||
})
|
||||
|
||||
it('loads roles from the platform IAM prefix', async () => {
|
||||
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: [role] })
|
||||
|
||||
await expect(listPlatformRoles()).resolves.toEqual([role])
|
||||
expect(http.get).toHaveBeenCalledWith('/platform/v1/iam/roles')
|
||||
})
|
||||
|
||||
it('creates a platform role with the documented payload', async () => {
|
||||
const payload = {
|
||||
roleCode: 'CUSTOMER_REVIEWER',
|
||||
roleName: '企业审核员',
|
||||
description: '企业资料与入驻审核',
|
||||
}
|
||||
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: role })
|
||||
|
||||
await expect(createPlatformRole(payload)).resolves.toEqual(role)
|
||||
expect(http.post).toHaveBeenCalledWith('/platform/v1/iam/roles', payload)
|
||||
})
|
||||
|
||||
it('updates status, permissions, and deletes roles with the implemented IAM routes', async () => {
|
||||
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: role })
|
||||
vi.mocked(http.delete).mockResolvedValue({ code: '200', message: 'success', data: null })
|
||||
|
||||
await expect(updatePlatformRole(role.id, { roleName: '审核主管', description: '负责企业审核' })).resolves.toEqual(role)
|
||||
await expect(changePlatformRoleStatus(role.id, 'DISABLED')).resolves.toEqual(role)
|
||||
await expect(replacePlatformRolePermissions(role.id, ['5100010'])).resolves.toEqual(role)
|
||||
await expect(deletePlatformRole(role.id)).resolves.toBeNull()
|
||||
|
||||
expect(http.put).toHaveBeenNthCalledWith(1, `/platform/v1/iam/roles/${role.id}`, { roleName: '审核主管', description: '负责企业审核' })
|
||||
expect(http.put).toHaveBeenNthCalledWith(2, `/platform/v1/iam/roles/${role.id}/status`, { status: 'DISABLED' })
|
||||
expect(http.put).toHaveBeenNthCalledWith(3, '/platform/v1/iam/roles/permissions', { roleId: role.id, permissionIds: ['5100010'] })
|
||||
expect(http.delete).toHaveBeenCalledWith(`/platform/v1/iam/roles/${role.id}`)
|
||||
})
|
||||
|
||||
it('loads and creates permission resources under the platform IAM prefix', async () => {
|
||||
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: [permission] })
|
||||
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: permission })
|
||||
const payload = {
|
||||
type: 'MENU' as const,
|
||||
code: permission.permissionCode,
|
||||
name: permission.permissionName,
|
||||
parentId: permission.parentId,
|
||||
routePath: permission.routePath,
|
||||
httpMethod: null,
|
||||
sortNo: permission.sortNo,
|
||||
}
|
||||
|
||||
await expect(listPlatformPermissions()).resolves.toEqual([permission])
|
||||
await expect(createPlatformPermission(payload)).resolves.toEqual(permission)
|
||||
expect(http.get).toHaveBeenCalledWith('/platform/v1/iam/permissions')
|
||||
expect(http.post).toHaveBeenCalledWith('/platform/v1/iam/permissions', payload)
|
||||
})
|
||||
|
||||
it('updates, changes status, and deletes custom permission resources with IAM routes', async () => {
|
||||
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: permission })
|
||||
vi.mocked(http.delete).mockResolvedValue({ code: '200', message: 'success', data: null })
|
||||
const payload = { name: '企业客户', parentId: permission.parentId, routePath: '/customers', httpMethod: null, sortNo: 10 }
|
||||
|
||||
await expect(updatePlatformPermission(permission.id, payload)).resolves.toEqual(permission)
|
||||
await expect(changePlatformPermissionStatus(permission.id, 'DISABLED')).resolves.toEqual(permission)
|
||||
await expect(deletePlatformPermission(permission.id)).resolves.toBeNull()
|
||||
|
||||
expect(http.put).toHaveBeenNthCalledWith(1, `/platform/v1/iam/permissions/${permission.id}`, payload)
|
||||
expect(http.put).toHaveBeenNthCalledWith(2, `/platform/v1/iam/permissions/${permission.id}/status`, { status: 'DISABLED' })
|
||||
expect(http.delete).toHaveBeenCalledWith(`/platform/v1/iam/permissions/${permission.id}`)
|
||||
})
|
||||
|
||||
it('maintains internal users through their public membership ID', async () => {
|
||||
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: [internalUser] })
|
||||
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: internalUser })
|
||||
const update = {
|
||||
displayName: '平台管理员',
|
||||
phone: '13800138000',
|
||||
email: 'admin@example.com',
|
||||
memberNo: 'PLATFORM-0001',
|
||||
roleIds: [4000001],
|
||||
}
|
||||
|
||||
await expect(listPlatformInternalUsers()).resolves.toEqual([internalUser])
|
||||
await expect(updatePlatformInternalUser(internalUser.membershipId, update)).resolves.toEqual(internalUser)
|
||||
await expect(changePlatformInternalUserStatus(internalUser.membershipId, 'DISABLED')).resolves.toEqual(internalUser)
|
||||
await expect(resetPlatformInternalUserPassword(internalUser.membershipId, 'ChangeMe123')).resolves.toEqual(internalUser)
|
||||
await expect(assignPlatformInternalUserRoles(internalUser.membershipId, [4000001])).resolves.toEqual(internalUser)
|
||||
|
||||
expect(http.get).toHaveBeenCalledWith('/platform/v1/iam/internal-users')
|
||||
expect(http.put).toHaveBeenNthCalledWith(1, `/platform/v1/iam/internal-users/${internalUser.membershipId}`, update)
|
||||
expect(http.put).toHaveBeenNthCalledWith(2, `/platform/v1/iam/internal-users/${internalUser.membershipId}/status`, { status: 'DISABLED' })
|
||||
expect(http.put).toHaveBeenNthCalledWith(3, `/platform/v1/iam/internal-users/${internalUser.membershipId}/password`, { newPassword: 'ChangeMe123' })
|
||||
expect(http.put).toHaveBeenNthCalledWith(4, `/platform/v1/iam/internal-users/${internalUser.membershipId}/roles`, { roleIds: [4000001] })
|
||||
})
|
||||
|
||||
it('maintains platform and tenant users through the unified user contract', async () => {
|
||||
const userOptions = {
|
||||
tenants: [{ id: '10001', name: '华远贸易', status: 'ACTIVE' }],
|
||||
roles: [{ id: '40001', tenantId: '10001', name: '租户管理员', status: 'ACTIVE' }],
|
||||
}
|
||||
const create = {
|
||||
username: 'zhangwei', displayName: '张伟', phone: '13800138000', email: 'zhangwei@example.com',
|
||||
tenantId: '10001', roleIds: ['40001'], password: 'Password1', status: 'ACTIVE' as const,
|
||||
}
|
||||
const update = {
|
||||
displayName: '张伟', phone: '13800138000', email: 'zhangwei@example.com',
|
||||
tenantId: '10001', roleIds: ['40001'],
|
||||
}
|
||||
vi.mocked(http.get).mockResolvedValueOnce({ code: '200', message: 'success', data: [managedUser] })
|
||||
.mockResolvedValueOnce({ code: '200', message: 'success', data: userOptions })
|
||||
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: managedUser })
|
||||
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: managedUser })
|
||||
|
||||
await expect(listPlatformManagedUsers()).resolves.toEqual([managedUser])
|
||||
await expect(getPlatformUserOptions()).resolves.toEqual(userOptions)
|
||||
await expect(createPlatformManagedUser(create)).resolves.toEqual(managedUser)
|
||||
await expect(updatePlatformManagedUser(managedUser.membershipId, update)).resolves.toEqual(managedUser)
|
||||
await expect(changePlatformManagedUserStatus(managedUser.membershipId, 'DISABLED')).resolves.toEqual(managedUser)
|
||||
await expect(resetPlatformManagedUserPassword(managedUser.membershipId, 'ChangeMe123')).resolves.toEqual(managedUser)
|
||||
|
||||
expect(http.get).toHaveBeenNthCalledWith(1, '/platform/v1/iam/users')
|
||||
expect(http.get).toHaveBeenNthCalledWith(2, '/platform/v1/iam/users/options')
|
||||
expect(http.post).toHaveBeenCalledWith('/platform/v1/iam/users', create)
|
||||
expect(http.put).toHaveBeenNthCalledWith(1, `/platform/v1/iam/users/${managedUser.membershipId}`, update)
|
||||
expect(http.put).toHaveBeenNthCalledWith(2, `/platform/v1/iam/users/${managedUser.membershipId}/status`, { status: 'DISABLED' })
|
||||
expect(http.put).toHaveBeenNthCalledWith(3, `/platform/v1/iam/users/${managedUser.membershipId}/password`, { newPassword: 'ChangeMe123' })
|
||||
})
|
||||
|
||||
it('loads qualifications for the selected tenant', async () => {
|
||||
const qualification = {
|
||||
id: 12,
|
||||
tenantId: 8,
|
||||
qualificationType: 'BUSINESS_LICENSE',
|
||||
certificateNo: '91440300XXXXXXXXXX',
|
||||
documentUrl: '/api/merchant/v1/files/qualifications/license.png',
|
||||
status: 'PENDING' as const,
|
||||
reviewComment: null,
|
||||
}
|
||||
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: [qualification] })
|
||||
|
||||
await expect(listTenantQualifications(8)).resolves.toEqual([qualification])
|
||||
expect(http.get).toHaveBeenCalledWith('/platform/v1/iam/tenants/8/qualifications')
|
||||
})
|
||||
|
||||
it('loads paginated tenants with the documented filters', async () => {
|
||||
const page = { records: [], page: 2, size: 10, total: 11, pages: 2 }
|
||||
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: { ...page, page: '2', size: '10', total: '11', pages: '2' } })
|
||||
|
||||
await expect(listPlatformTenants({ page: 2, size: 10, keyword: '柏悦', status: 'ACTIVE' })).resolves.toEqual(page)
|
||||
expect(http.get).toHaveBeenCalledWith('/platform/v1/iam/tenants', {
|
||||
params: { page: 2, size: 10, keyword: '柏悦', status: 'ACTIVE' },
|
||||
})
|
||||
})
|
||||
|
||||
it('creates, updates, and changes customer status with the platform tenant contract', async () => {
|
||||
const tenant = { id: '8', tenantCode: 'TEN8', tenantName: '柏悦物流', status: 'ACTIVE' }
|
||||
const create = {
|
||||
tenantName: '柏悦物流', legalName: '柏悦物流有限公司', unifiedSocialCreditCode: '91440300MA5ABCDE12',
|
||||
contactName: '张三', contactPhone: '13800138000', contactEmail: 'ops@example.com', servicePackage: '标准版',
|
||||
contactWechat: 'baiyue-ops', salesOwnerName: '李四', paymentTermDays: 30, enterpriseType: '有限责任公司',
|
||||
establishedDate: '2020-01-01', registeredCapital: 1000, registeredCapitalCurrency: 'CNY',
|
||||
registeredAddress: '深圳市宝安区', internalRemark: '重点客户',
|
||||
}
|
||||
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: tenant })
|
||||
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: tenant })
|
||||
|
||||
await expect(createPlatformTenant(create)).resolves.toEqual(tenant)
|
||||
const { unifiedSocialCreditCode: _creditCode, ...update } = create
|
||||
await expect(updatePlatformTenant('8', update)).resolves.toEqual(tenant)
|
||||
await expect(changePlatformTenantStatus('8', 'DISABLED')).resolves.toEqual(tenant)
|
||||
|
||||
expect(http.post).toHaveBeenCalledWith('/platform/v1/iam/tenants', create)
|
||||
expect(http.put).toHaveBeenNthCalledWith(1, '/platform/v1/iam/tenants/8', update)
|
||||
expect(http.put).toHaveBeenNthCalledWith(2, '/platform/v1/iam/tenants/8/status', { status: 'DISABLED' })
|
||||
})
|
||||
|
||||
it('loads real order totals for the visible customer tenants', async () => {
|
||||
const counts = [{ tenantId: '8', orderCount: 12 }, { tenantId: '9', orderCount: 0 }]
|
||||
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: counts })
|
||||
await expect(listPlatformTenantOrderCounts(['8', '9'])).resolves.toEqual(counts)
|
||||
expect(http.get).toHaveBeenCalledWith('/platform/v1/orders/first-leg/tenant-counts?tenantIds=8,9')
|
||||
})
|
||||
|
||||
it('uploads qualification metadata before explicitly submitting the customer review', async () => {
|
||||
const uploadResult = { qualificationId: '91', reviewId: null, tenantStatus: 'PROFILE_REQUIRED', documentUrl: '/file.pdf' }
|
||||
const tenant = { id: '8', tenantCode: 'TEN8', tenantName: '柏悦物流', status: 'PENDING' }
|
||||
vi.mocked(http.post).mockResolvedValueOnce({ code: '200', message: 'success', data: uploadResult })
|
||||
.mockResolvedValueOnce({ code: '200', message: 'success', data: tenant })
|
||||
const file = new File(['%PDF-1.4'], '营业执照.pdf', { type: 'application/pdf' })
|
||||
|
||||
await expect(uploadPlatformTenantQualification('8', 'BUSINESS_LICENSE', file, '2030-12-31', '91440300MA5ABCDE12', true)).resolves.toEqual(uploadResult)
|
||||
await expect(submitPlatformTenantReview('8')).resolves.toEqual(tenant)
|
||||
|
||||
const form = vi.mocked(http.post).mock.calls[0]?.[1] as FormData
|
||||
expect(http.post).toHaveBeenNthCalledWith(1, '/platform/v1/files/qualifications/8', expect.any(FormData))
|
||||
expect(form.get('file')).toBe(file)
|
||||
expect(form.get('qualificationType')).toBe('BUSINESS_LICENSE')
|
||||
expect(form.get('validTo')).toBe('2030-12-31')
|
||||
expect(form.get('certificateNo')).toBe('91440300MA5ABCDE12')
|
||||
expect(form.get('replace')).toBe('true')
|
||||
expect(http.post).toHaveBeenNthCalledWith(2, '/platform/v1/iam/tenants/8/submit-review', {})
|
||||
})
|
||||
|
||||
it('downloads a qualification with the platform file endpoint and a safe filename', async () => {
|
||||
const blob = new Blob(['image'], { type: 'image/png' })
|
||||
vi.mocked(http.get).mockResolvedValue(blob)
|
||||
|
||||
expect(qualificationFileName('/api/merchant/v1/files/qualifications/%E8%90%A5%E4%B8%9A%E6%89%A7%E7%85%A7.png?token=ignored')).toBe('营业执照.png')
|
||||
await expect(downloadTenantQualification(8, '/api/merchant/v1/files/qualifications/%E8%90%A5%E4%B8%9A%E6%89%A7%E7%85%A7.png')).resolves.toBe(blob)
|
||||
expect(http.get).toHaveBeenCalledWith(
|
||||
'/platform/v1/files/qualifications/8/%E8%90%A5%E4%B8%9A%E6%89%A7%E7%85%A7.png',
|
||||
{ responseType: 'blob' },
|
||||
)
|
||||
expect(() => qualificationFileName('/api/merchant/v1/files/qualifications/%2Fetc%2Fpasswd')).toThrow('资质文件地址无效')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,446 @@
|
||||
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
|
||||
import type { PlatformPermissionResource } from '@/shared/types/platformPermission'
|
||||
|
||||
export type { PlatformPermissionResource } from '@/shared/types/platformPermission'
|
||||
|
||||
export interface EnterpriseReview {
|
||||
id: number
|
||||
tenantId: number
|
||||
status: 'PENDING' | 'APPROVED' | 'REJECTED'
|
||||
tenantName?: string | null
|
||||
tenantCode?: string | null
|
||||
qualificationId?: number | null
|
||||
qualificationType?: string | null
|
||||
certificateNo?: string | null
|
||||
documentUrl?: string | null
|
||||
contactName?: string | null
|
||||
contactPhone?: string | null
|
||||
contactEmail?: string | null
|
||||
submittedAt?: string | null
|
||||
createdAt?: string | null
|
||||
reviewComment?: string | null
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export type PlatformTenantStatus = 'PENDING' | 'ACTIVE' | 'REJECTED' | 'INACTIVE' | string
|
||||
|
||||
export interface PlatformTenantResource {
|
||||
id: number | string
|
||||
tenantCode: string
|
||||
tenantName: string
|
||||
status: PlatformTenantStatus
|
||||
legalName: string | null
|
||||
unifiedSocialCreditCode: string | null
|
||||
contactName: string | null
|
||||
contactPhone: string | null
|
||||
contactEmail: string | null
|
||||
servicePackage: string | null
|
||||
contactWechat: string | null
|
||||
salesOwnerName: string | null
|
||||
paymentTermDays: number | null
|
||||
enterpriseType: string | null
|
||||
establishedDate: string | null
|
||||
registeredCapital: number | string | null
|
||||
registeredCapitalCurrency: string | null
|
||||
registeredAddress: string | null
|
||||
internalRemark: string | null
|
||||
createdAt: string | null
|
||||
approvedAt: string | null
|
||||
}
|
||||
|
||||
export interface CreatePlatformTenantRequest {
|
||||
tenantName: string
|
||||
legalName: string
|
||||
unifiedSocialCreditCode: string
|
||||
contactName: string
|
||||
contactPhone: string
|
||||
contactEmail: string
|
||||
servicePackage: string
|
||||
contactWechat: string
|
||||
salesOwnerName: string
|
||||
paymentTermDays: number
|
||||
enterpriseType: string
|
||||
establishedDate: string | null
|
||||
registeredCapital: number | null
|
||||
registeredCapitalCurrency: string
|
||||
registeredAddress: string
|
||||
internalRemark: string
|
||||
}
|
||||
|
||||
export type UpdatePlatformTenantRequest = Omit<CreatePlatformTenantRequest, 'unifiedSocialCreditCode'>
|
||||
|
||||
export interface PlatformTenantOrderCount {
|
||||
tenantId: number | string
|
||||
orderCount: number
|
||||
}
|
||||
|
||||
export interface PlatformTenantPage {
|
||||
records: PlatformTenantResource[]
|
||||
page: number
|
||||
size: number
|
||||
total: number
|
||||
pages: number
|
||||
}
|
||||
|
||||
export interface PlatformTenantQuery {
|
||||
page: number
|
||||
size: number
|
||||
keyword?: string
|
||||
status?: PlatformTenantStatus
|
||||
}
|
||||
|
||||
export interface PlatformTenantQualification {
|
||||
id: number | string
|
||||
tenantId: number | string
|
||||
qualificationType: 'BUSINESS_LICENSE' | string
|
||||
certificateNo: string | null
|
||||
documentUrl: string
|
||||
status: 'PENDING' | 'APPROVED' | 'REJECTED'
|
||||
reviewComment: string | null
|
||||
validTo?: string | null
|
||||
}
|
||||
|
||||
export type PlatformQualificationType = 'BUSINESS_LICENSE' | 'LEGAL_PERSON_ID' | 'FREIGHT_FORWARDER_CERTIFICATE' | 'BANK_ACCOUNT_LICENSE'
|
||||
|
||||
export interface PlatformQualificationUploadResponse {
|
||||
qualificationId: number | string
|
||||
reviewId: number | string | null
|
||||
tenantStatus: PlatformTenantStatus
|
||||
documentUrl: string
|
||||
replacedDocumentUrl?: string | null
|
||||
}
|
||||
|
||||
export interface PlatformRoleResource {
|
||||
id: number | string
|
||||
tenantId: number | string
|
||||
appCode: 'PLATFORM'
|
||||
roleCode: string
|
||||
roleName: string
|
||||
roleType: 'SYSTEM' | 'CUSTOM' | string
|
||||
status: 'ACTIVE' | 'INACTIVE' | string
|
||||
description: string | null
|
||||
permissionIds?: Array<number | string>
|
||||
}
|
||||
|
||||
export interface CreatePlatformRoleRequest {
|
||||
roleCode: string
|
||||
roleName: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface UpdatePlatformRoleRequest {
|
||||
roleName: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface CreatePlatformPermissionRequest {
|
||||
type: 'MENU' | 'BUTTON'
|
||||
code: string
|
||||
name: string
|
||||
parentId: number | string
|
||||
routePath: string | null
|
||||
httpMethod: null
|
||||
sortNo: number
|
||||
}
|
||||
|
||||
export interface UpdatePlatformPermissionRequest {
|
||||
name: string
|
||||
parentId: number | string | null
|
||||
routePath: string | null
|
||||
httpMethod: string | null
|
||||
sortNo: number
|
||||
}
|
||||
|
||||
export interface PlatformInternalUser {
|
||||
membershipId: number | string
|
||||
userId: number | string
|
||||
authUserId: number | string
|
||||
username: string
|
||||
displayName: string
|
||||
phone: string | null
|
||||
email: string | null
|
||||
memberType: string
|
||||
memberNo: string | null
|
||||
status: 'ACTIVE' | 'DISABLED' | string
|
||||
roleIds: Array<number | string>
|
||||
}
|
||||
|
||||
export interface CreatePlatformInternalUserRequest {
|
||||
authUserId: number | string
|
||||
userType: string
|
||||
displayName: string
|
||||
phone: string
|
||||
email: string
|
||||
memberNo?: string
|
||||
roleIds?: Array<number | string>
|
||||
}
|
||||
|
||||
export interface UpdatePlatformInternalUserRequest {
|
||||
displayName: string
|
||||
phone: string
|
||||
email: string
|
||||
memberNo: string
|
||||
roleIds: Array<number | string>
|
||||
}
|
||||
|
||||
export interface PlatformManagedUser {
|
||||
membershipId: number | string
|
||||
userId: number | string
|
||||
authUserId: number | string
|
||||
username: string
|
||||
displayName: string
|
||||
phone: string | null
|
||||
email: string | null
|
||||
tenantId: number | string
|
||||
tenantName: string
|
||||
memberType: string
|
||||
memberNo: string | null
|
||||
status: 'ACTIVE' | 'DISABLED'
|
||||
roleIds: Array<number | string>
|
||||
roleNames: string[]
|
||||
lastLoginAt: string | null
|
||||
protectedAccount: boolean
|
||||
}
|
||||
|
||||
export interface PlatformUserOptions {
|
||||
tenants: Array<{ id: number | string; name: string; status: string }>
|
||||
roles: Array<{ id: number | string; tenantId: number | string; name: string; status: string }>
|
||||
}
|
||||
|
||||
export interface CreatePlatformManagedUserRequest {
|
||||
username: string
|
||||
displayName: string
|
||||
phone: string
|
||||
email: string
|
||||
tenantId: number | string
|
||||
roleIds: Array<number | string>
|
||||
password: string
|
||||
status: 'ACTIVE' | 'DISABLED'
|
||||
}
|
||||
|
||||
export interface UpdatePlatformManagedUserRequest {
|
||||
displayName: string
|
||||
phone: string
|
||||
email: string
|
||||
tenantId: number | string
|
||||
roleIds: Array<number | string>
|
||||
}
|
||||
|
||||
async function getData<T>(path: string): Promise<T> {
|
||||
const response = await http.get<ApiEnvelope<T>, ApiEnvelope<T>>(path)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
async function postData<T>(path: string, body: unknown): Promise<T> {
|
||||
const response = await http.post<ApiEnvelope<T>, ApiEnvelope<T>>(path, body)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
async function putData<T>(path: string, body: unknown): Promise<T> {
|
||||
const response = await http.put<ApiEnvelope<T>, ApiEnvelope<T>>(path, body)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
async function deleteData<T>(path: string): Promise<T> {
|
||||
const response = await http.delete<ApiEnvelope<T>, ApiEnvelope<T>>(path)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export function listPlatformPermissions() {
|
||||
return getData<PlatformPermissionResource[]>('/platform/v1/iam/permissions')
|
||||
}
|
||||
|
||||
export function listEnterpriseReviews() {
|
||||
return getData<EnterpriseReview[]>('/platform/v1/iam/reviews')
|
||||
}
|
||||
|
||||
export async function listPlatformTenants(query: PlatformTenantQuery) {
|
||||
const response = await http.get<ApiEnvelope<PlatformTenantPage>, ApiEnvelope<PlatformTenantPage>>(
|
||||
'/platform/v1/iam/tenants',
|
||||
{
|
||||
params: {
|
||||
page: query.page,
|
||||
size: query.size,
|
||||
...(query.keyword ? { keyword: query.keyword } : {}),
|
||||
...(query.status ? { status: query.status } : {}),
|
||||
},
|
||||
},
|
||||
)
|
||||
const data = unwrapApiResponse(response)
|
||||
return {
|
||||
...data,
|
||||
page: Number(data.page),
|
||||
size: Number(data.size),
|
||||
total: Number(data.total),
|
||||
pages: Number(data.pages),
|
||||
}
|
||||
}
|
||||
|
||||
export function getPlatformTenant(tenantId: number | string) {
|
||||
return getData<PlatformTenantResource>(`/platform/v1/iam/tenants/${tenantId}`)
|
||||
}
|
||||
|
||||
export function createPlatformTenant(payload: CreatePlatformTenantRequest) {
|
||||
return postData<PlatformTenantResource>('/platform/v1/iam/tenants', payload)
|
||||
}
|
||||
|
||||
export function updatePlatformTenant(tenantId: number | string, payload: UpdatePlatformTenantRequest) {
|
||||
return putData<PlatformTenantResource>(`/platform/v1/iam/tenants/${tenantId}`, payload)
|
||||
}
|
||||
|
||||
export function changePlatformTenantStatus(tenantId: number | string, status: 'ACTIVE' | 'DISABLED') {
|
||||
return putData<PlatformTenantResource>(`/platform/v1/iam/tenants/${tenantId}/status`, { status })
|
||||
}
|
||||
|
||||
export function submitPlatformTenantReview(tenantId: number | string) {
|
||||
return postData<PlatformTenantResource>(`/platform/v1/iam/tenants/${tenantId}/submit-review`, {})
|
||||
}
|
||||
|
||||
export async function uploadPlatformTenantQualification(
|
||||
tenantId: number | string,
|
||||
qualificationType: PlatformQualificationType,
|
||||
file: File,
|
||||
validTo?: string,
|
||||
certificateNo?: string,
|
||||
replace = false,
|
||||
) {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
form.append('qualificationType', qualificationType)
|
||||
if (validTo) form.append('validTo', validTo)
|
||||
if (certificateNo) form.append('certificateNo', certificateNo)
|
||||
if (replace) form.append('replace', 'true')
|
||||
const response = await http.post<ApiEnvelope<PlatformQualificationUploadResponse>, ApiEnvelope<PlatformQualificationUploadResponse>>(
|
||||
`/platform/v1/files/qualifications/${tenantId}`,
|
||||
form,
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export function listPlatformTenantOrderCounts(tenantIds: Array<number | string>) {
|
||||
const ids = tenantIds.map((id) => encodeURIComponent(String(id))).join(',')
|
||||
return getData<PlatformTenantOrderCount[]>(`/platform/v1/orders/first-leg/tenant-counts?tenantIds=${ids}`)
|
||||
}
|
||||
|
||||
export function listTenantQualifications(tenantId: number | string) {
|
||||
return getData<PlatformTenantQualification[]>(`/platform/v1/iam/tenants/${tenantId}/qualifications`)
|
||||
}
|
||||
|
||||
export function qualificationFileName(documentUrl: string) {
|
||||
const path = documentUrl.split(/[?#]/, 1)[0]
|
||||
const encodedName = path.split('/').filter(Boolean).at(-1)
|
||||
if (!encodedName) throw new Error('资质文件地址无效')
|
||||
let fileName: string
|
||||
try {
|
||||
fileName = decodeURIComponent(encodedName)
|
||||
} catch {
|
||||
throw new Error('资质文件地址无效')
|
||||
}
|
||||
if (!fileName || fileName === '.' || fileName === '..' || /[\\/]/.test(fileName)) {
|
||||
throw new Error('资质文件地址无效')
|
||||
}
|
||||
return fileName
|
||||
}
|
||||
|
||||
export function downloadTenantQualification(tenantId: number | string, documentUrl: string) {
|
||||
const fileName = qualificationFileName(documentUrl)
|
||||
return http.get<Blob, Blob>(`/platform/v1/files/qualifications/${tenantId}/${encodeURIComponent(fileName)}`, {
|
||||
responseType: 'blob',
|
||||
})
|
||||
}
|
||||
|
||||
export function listPlatformRoles() {
|
||||
return getData<PlatformRoleResource[]>('/platform/v1/iam/roles')
|
||||
}
|
||||
|
||||
export function createPlatformRole(payload: CreatePlatformRoleRequest) {
|
||||
return postData<PlatformRoleResource>('/platform/v1/iam/roles', payload)
|
||||
}
|
||||
|
||||
export function updatePlatformRole(roleId: number | string, payload: UpdatePlatformRoleRequest) {
|
||||
return putData<PlatformRoleResource>(`/platform/v1/iam/roles/${roleId}`, payload)
|
||||
}
|
||||
|
||||
export function changePlatformRoleStatus(roleId: number | string, status: 'ACTIVE' | 'DISABLED') {
|
||||
return putData<PlatformRoleResource>(`/platform/v1/iam/roles/${roleId}/status`, { status })
|
||||
}
|
||||
|
||||
export function deletePlatformRole(roleId: number | string) {
|
||||
return deleteData<void>(`/platform/v1/iam/roles/${roleId}`)
|
||||
}
|
||||
|
||||
export function replacePlatformRolePermissions(roleId: number | string, permissionIds: Array<number | string>) {
|
||||
return putData<void>('/platform/v1/iam/roles/permissions', { roleId, permissionIds })
|
||||
}
|
||||
|
||||
export function createPlatformPermission(payload: CreatePlatformPermissionRequest) {
|
||||
return postData<PlatformPermissionResource>('/platform/v1/iam/permissions', payload)
|
||||
}
|
||||
|
||||
export function updatePlatformPermission(permissionId: number | string, payload: UpdatePlatformPermissionRequest) {
|
||||
return putData<PlatformPermissionResource>(`/platform/v1/iam/permissions/${permissionId}`, payload)
|
||||
}
|
||||
|
||||
export function changePlatformPermissionStatus(permissionId: number | string, status: 'ACTIVE' | 'DISABLED') {
|
||||
return putData<PlatformPermissionResource>(`/platform/v1/iam/permissions/${permissionId}/status`, { status })
|
||||
}
|
||||
|
||||
export function deletePlatformPermission(permissionId: number | string) {
|
||||
return deleteData<void>(`/platform/v1/iam/permissions/${permissionId}`)
|
||||
}
|
||||
|
||||
export function listPlatformInternalUsers() {
|
||||
return getData<PlatformInternalUser[]>('/platform/v1/iam/internal-users')
|
||||
}
|
||||
|
||||
export function createPlatformInternalUser(payload: CreatePlatformInternalUserRequest) {
|
||||
return postData<PlatformInternalUser>('/platform/v1/iam/internal-users', payload)
|
||||
}
|
||||
|
||||
export function updatePlatformInternalUser(membershipId: number | string, payload: UpdatePlatformInternalUserRequest) {
|
||||
return putData<PlatformInternalUser>(`/platform/v1/iam/internal-users/${membershipId}`, payload)
|
||||
}
|
||||
|
||||
export function changePlatformInternalUserStatus(membershipId: number | string, status: 'ACTIVE' | 'DISABLED') {
|
||||
return putData<PlatformInternalUser>(`/platform/v1/iam/internal-users/${membershipId}/status`, { status })
|
||||
}
|
||||
|
||||
export function resetPlatformInternalUserPassword(membershipId: number | string, newPassword: string) {
|
||||
return putData<void>(`/platform/v1/iam/internal-users/${membershipId}/password`, { newPassword })
|
||||
}
|
||||
|
||||
export function assignPlatformInternalUserRoles(membershipId: number | string, roleIds: Array<number | string>) {
|
||||
return putData<PlatformInternalUser>(`/platform/v1/iam/internal-users/${membershipId}/roles`, { roleIds })
|
||||
}
|
||||
|
||||
export function listPlatformManagedUsers() {
|
||||
return getData<PlatformManagedUser[]>('/platform/v1/iam/users')
|
||||
}
|
||||
|
||||
export function getPlatformUserOptions() {
|
||||
return getData<PlatformUserOptions>('/platform/v1/iam/users/options')
|
||||
}
|
||||
|
||||
export function createPlatformManagedUser(payload: CreatePlatformManagedUserRequest) {
|
||||
return postData<PlatformManagedUser>('/platform/v1/iam/users', payload)
|
||||
}
|
||||
|
||||
export function updatePlatformManagedUser(membershipId: number | string, payload: UpdatePlatformManagedUserRequest) {
|
||||
return putData<PlatformManagedUser>(`/platform/v1/iam/users/${membershipId}`, payload)
|
||||
}
|
||||
|
||||
export function changePlatformManagedUserStatus(membershipId: number | string, status: 'ACTIVE' | 'DISABLED') {
|
||||
return putData<PlatformManagedUser>(`/platform/v1/iam/users/${membershipId}/status`, { status })
|
||||
}
|
||||
|
||||
export function resetPlatformManagedUserPassword(membershipId: number | string, newPassword: string) {
|
||||
return putData<void>(`/platform/v1/iam/users/${membershipId}/password`, { newPassword })
|
||||
}
|
||||
|
||||
export function approveEnterpriseReview(reviewId: number, comment: string) {
|
||||
return postData<unknown>(`/platform/v1/iam/reviews/${reviewId}/approve`, { comment })
|
||||
}
|
||||
|
||||
export function rejectEnterpriseReview(reviewId: number, comment: string) {
|
||||
return postData<unknown>(`/platform/v1/iam/reviews/${reviewId}/reject`, { comment })
|
||||
}
|
||||
Reference in New Issue
Block a user