Files
admin-web/src/modules/iam/api/platformIam.ts
T
2026-09-11 16:17:21 +08:00

665 lines
21 KiB
TypeScript

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 | string
tenantId: number | string
status: 'PENDING' | 'APPROVED' | 'REJECTED'
tenantName?: string | null
tenantCode?: string | null
qualificationId?: number | string | 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 = 'PROFILE_REQUIRED' | 'PENDING' | 'ACTIVE' | 'REJECTED' | 'DISABLED' | 'ARCHIVED' | string
export interface PlatformTenantResource {
id: number | string
tenantCode: string
tenantName: string
status: PlatformTenantStatus
dimensionInitializationStatus?: 'PENDING' | 'SENDING' | 'READY' | 'FAILED' | string
legalName: string | null
legalRepresentative: 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
legalRepresentative?: 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 = CreatePlatformTenantRequest
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' | 'SUPERSEDED'
reviewComment: string | null
validTo?: string | null
}
export interface PlatformTenantMember {
membershipId: number | string
userId: number | string
authUserId: number | string
username?: string | null
displayName: string | null
phone: string | null
email: string | null
memberType: string
memberNo: string | null
status: 'ACTIVE' | 'DISABLED' | string
roleIds?: Array<number | string>
roleNames?: string[]
}
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 PlatformRequiredQualificationPackageUploadResponse {
businessLicenseQualificationId: number | string
legalPersonIdQualificationId: number | string | null
tenantStatus: PlatformTenantStatus
businessLicenseDocumentUrl: string
legalPersonIdDocumentUrl: string | null
}
export interface EnterpriseApprovalResponse {
tenantId: number | string
administratorCreated: boolean
administratorUsername: string | null
temporaryPassword: string | null
}
export interface PlatformTenantCreationResponse {
tenant: PlatformTenantResource
administratorUsername: string
temporaryPassword: string
}
export interface CustomerDimensionInitializationStatus {
tenantId: number | string
status: 'PENDING' | 'SENDING' | 'READY' | 'FAILED' | string
attemptCount: number
nextRetryAt: string | null
lastErrorCode: string | null
lastErrorMessage: string | null
updatedAt: string | null
}
export interface ResetPlatformTenantOwnerPasswordRequest {
newPassword: string
forceChangePassword: boolean
}
export interface CreatePlatformTenantImpersonationRequest {
reason: string
}
export interface PlatformTenantImpersonationResponse {
code: string
ticketId: string
expiresIn: number
}
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
memberCount: number
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' | 'PAGE' | 'BUTTON' | 'API'
code: string
name: string
parentId: number | string
routePath: string | null
httpMethod: string | 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
overseasWarehouseId: number | string | null
}
export interface PlatformManagedUserPage {
records: PlatformManagedUser[]
page: number
size: number
total: number
pages: number
}
export interface PlatformManagedUserQuery {
page: number
size: number
keyword?: string
status?: PlatformManagedUser['status']
}
export interface PlatformUserOptions {
tenants: Array<{ id: number | string; name: string; status: string }>
roles: Array<{ id: number | string; tenantId: number | string; roleCode: string; name: string; status: string }>
}
interface PlatformUserOptionsPayload {
tenants: PlatformUserOptions['tenants']
roles: Array<Omit<PlatformUserOptions['roles'][number], 'roleCode'>>
}
export interface SharedReceivingOverseasWarehouseOption {
id: string
warehouseCode: string
warehouseName: string
countryCode: string
}
export interface CreatePlatformManagedUserRequest {
username: string
displayName: string
phone: string
email: string
tenantId: number | string
roleIds: Array<number | string>
overseasWarehouseId: number | string | null
password: string
status: 'ACTIVE' | 'DISABLED'
}
export interface UpdatePlatformManagedUserRequest {
displayName: string
phone: string
email: string
tenantId: number | string
roleIds: Array<number | string>
overseasWarehouseId: number | string | null
}
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 getPlatformTenantDimensionInitializationStatus(tenantId: number | string) {
return getData<CustomerDimensionInitializationStatus>(
`/platform/v1/iam/tenants/${tenantId}/dimension-initialization`,
)
}
export async function checkPlatformTenantContactPhone(contactPhone: string, excludeTenantId?: number | string) {
const response = await http.get<ApiEnvelope<boolean>, ApiEnvelope<boolean>>(
'/platform/v1/iam/tenants/contact-phone-availability',
{ params: { contactPhone, ...(excludeTenantId ? { excludeTenantId } : {}) } },
)
return unwrapApiResponse(response)
}
export function createPlatformTenant(payload: CreatePlatformTenantRequest) {
return postData<PlatformTenantCreationResponse>('/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 function resetPlatformTenantOwnerPassword(
tenantId: number | string,
payload: ResetPlatformTenantOwnerPasswordRequest,
) {
return putData<void>(`/platform/v1/iam/tenants/${tenantId}/owner/password`, payload)
}
export function archivePlatformTenant(tenantId: number | string) {
return postData<PlatformTenantResource>(`/platform/v1/iam/tenants/${tenantId}/archive`, {})
}
export function createPlatformTenantImpersonation(
tenantId: number | string,
payload: CreatePlatformTenantImpersonationRequest,
) {
return postData<PlatformTenantImpersonationResponse>(`/platform/v1/iam/tenants/${tenantId}/impersonation`, payload)
}
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 async function uploadPlatformTenantRequiredQualificationPackage(
tenantId: number | string,
businessLicense: File,
legalPersonId: File | null,
unifiedSocialCreditCode: string,
replaceBusinessLicense = false,
replaceLegalPersonId = false,
) {
const form = new FormData()
form.append('businessLicense', businessLicense)
if (legalPersonId) form.append('legalPersonId', legalPersonId)
form.append('unifiedSocialCreditCode', unifiedSocialCreditCode)
if (replaceBusinessLicense) form.append('replaceBusinessLicense', 'true')
if (replaceLegalPersonId) form.append('replaceLegalPersonId', 'true')
const response = await http.post<ApiEnvelope<PlatformRequiredQualificationPackageUploadResponse>, ApiEnvelope<PlatformRequiredQualificationPackageUploadResponse>>(
`/platform/v1/files/qualifications/${tenantId}/required-package`,
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 listPlatformTenantMembers(tenantId: number | string) {
return getData<PlatformTenantMember[]>(`/platform/v1/iam/tenants/${tenantId}/members`)
}
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 async function listPlatformManagedUsers(query: PlatformManagedUserQuery) {
const response = await http.get<ApiEnvelope<PlatformManagedUserPage>, ApiEnvelope<PlatformManagedUserPage>>(
'/platform/v1/iam/users',
{
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 async function getPlatformUserOptions(): Promise<PlatformUserOptions> {
const [optionsResponse, rolesResponse] = await Promise.all([
http.get<ApiEnvelope<PlatformUserOptionsPayload>, ApiEnvelope<PlatformUserOptionsPayload>>('/platform/v1/iam/users/options'),
http.get<ApiEnvelope<PlatformRoleResource[]>, ApiEnvelope<PlatformRoleResource[]>>('/platform/v1/iam/roles'),
])
const options = unwrapApiResponse(optionsResponse)
const roleCodes = new Map(unwrapApiResponse(rolesResponse).map((role) => [String(role.id), role.roleCode]))
return {
tenants: options.tenants,
roles: options.roles.map((role) => ({
...role,
roleCode: roleCodes.get(String(role.id)) ?? '',
})),
}
}
export async function listSharedReceivingOverseasWarehouseOptions() {
interface WarehousePage {
records: Array<{
id: number | string
tenantId: number | string
warehouseCode: string
warehouseName: string
warehouseType: string
countryCode: string
status: string
}>
}
const response = await http.get<ApiEnvelope<WarehousePage>, ApiEnvelope<WarehousePage>>(
'/platform/v1/warehouse-admin',
{
params: {
tenantId: '0',
page: 1,
size: 200,
capabilityCode: 'RECEIVING',
warehouseType: 'OVERSEAS',
status: 'ACTIVE',
},
},
)
return unwrapApiResponse(response).records
.filter((warehouse) => String(warehouse.tenantId) === '0'
&& warehouse.warehouseType === 'OVERSEAS'
&& warehouse.status === 'ACTIVE')
.map((warehouse) => ({
id: String(warehouse.id),
warehouseCode: warehouse.warehouseCode,
warehouseName: warehouse.warehouseName,
countryCode: warehouse.countryCode,
}))
}
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 | string, comment: string) {
return postData<EnterpriseApprovalResponse>(`/platform/v1/iam/reviews/${reviewId}/approve`, { comment })
}
export function rejectEnterpriseReview(reviewId: number | string, comment: string) {
return postData<void>(`/platform/v1/iam/reviews/${reviewId}/reject`, { comment })
}