/** * 客户管理 API(文档:GET/POST /customer/*,admin 同源) */ import { axiosInstance } from '@/api/rbac' import { assertBusinessOk, omsUrl, parseListPayload, unwrapData } from '@/api/oms/common' export type CustomerStatus = 0 | 1 | 2 export type CustomerGroup = 0 | 1 | 2 | 3 | 4 | 5 export interface CustomerDto { id?: string | number parentCustomerId?: number | null userId?: number customerCode?: string customerName?: string wechatAvatar?: string wechatAvatarUrl?: string wechatNickname?: string email?: string phone?: string balance?: number accountBalance?: number totalQuota?: number usedQuota?: number status?: CustomerStatus remark?: string createTime?: string createdAt?: string createBy?: string createdBy?: string updateTime?: string updateBy?: string salesman?: string | number salesPerson?: string | number customerService?: string | number customerGroup?: CustomerGroup memberLevelId?: number | null memberLevelName?: string priceTierKey?: string | null lastLoginIp?: string lastOnlineIp?: string lastLoginTime?: string lastOnlineTime?: string role?: string | number } export interface CustomerListParams { page?: number pageSize?: number keyword?: string status?: number customerGroup?: number salesman?: string customerService?: string sortField?: string sortOrder?: string } export interface CustomerPagedResult { items: CustomerDto[] total: number } export interface CustomerProfileCompleteness { phoneFilled: boolean emailFilled: boolean needContact: boolean customerCode: string phone?: string email?: string } function mapUpdateBody(id: string | number, data: Partial) { const body: Record = { id, customerCode: data.customerCode, customerName: data.customerName, wechatAvatar: data.wechatAvatarUrl ?? data.wechatAvatar, wechatNickname: data.wechatNickname, email: data.email, phone: data.phone, balance: data.accountBalance ?? data.balance, totalQuota: data.totalQuota, usedQuota: data.usedQuota, status: data.status, role: data.role, remark: data.remark, salesman: data.salesman ?? data.salesPerson, customerService: data.customerService, customerGroup: data.customerGroup, memberLevelId: data.memberLevelId, priceTierKey: data.priceTierKey, } // 去掉 undefined,避免局部字段被序列化成 null 后把后端原值冲掉 return Object.fromEntries(Object.entries(body).filter(([, v]) => v !== undefined)) } export interface CustomerServiceAdvisorDto { name?: string wechatId?: string wechatQrCode?: string wecomKfUrl?: string } /** 客户 OpenAPI 机机凭证 */ export interface CustomerOpenApiCredentialDto { id?: number customerId?: number appKey?: string appSecret?: string enabled?: boolean hasCredential?: boolean secretRevealed?: boolean lastUsedAt?: string | null lastUsedIp?: string | null createTime?: string | null updateTime?: string | null } export const CustomerService = { async getList(params: CustomerListParams): Promise { const res = await axiosInstance.get(omsUrl('/customer/list'), { params }) return parseListPayload(res.data) }, async getById(id: string | number): Promise { const res = await axiosInstance.get(omsUrl('/customer/get'), { params: { id } }) return unwrapData(res.data) }, async getServiceAdvisor(): Promise { const res = await axiosInstance.get(omsUrl('/customer/service-advisor')) return unwrapData(res.data) }, async createSub( data: Partial & { password?: string; permissions?: string[] } ): Promise { const body = { customerCode: data.customerCode, customerName: data.customerName, wechatAvatar: data.wechatAvatarUrl ?? data.wechatAvatar, wechatNickname: data.wechatNickname, phone: data.phone, email: data.email, password: data.password, balance: data.accountBalance ?? data.balance ?? 0, totalQuota: data.totalQuota ?? 0, usedQuota: data.usedQuota ?? 0, status: data.status ?? 1, remark: data.remark, salesman: data.salesman ?? data.salesPerson, customerService: data.customerService, customerGroup: data.customerGroup ?? 0, memberLevelId: data.memberLevelId, priceTierKey: data.priceTierKey, permissions: data.permissions ?? [], } const r = await axiosInstance.post(omsUrl('/customer/sub/create'), body) assertBusinessOk(r.data) }, async create( data: Partial & { password?: string; inviteCode?: string; advisorCode?: string }, ): Promise { const body = { customerCode: data.customerCode || undefined, customerName: data.customerName, wechatAvatar: data.wechatAvatarUrl ?? data.wechatAvatar, wechatNickname: data.wechatNickname, phone: data.phone, email: data.email, password: data.password, balance: data.accountBalance ?? data.balance, totalQuota: data.totalQuota, usedQuota: data.usedQuota, status: data.status ?? 1, role: data.role, remark: data.remark, salesman: data.salesman ?? data.salesPerson, customerService: data.customerService, customerGroup: data.customerGroup ?? 0, memberLevelId: data.memberLevelId, priceTierKey: data.priceTierKey, inviteCode: data.inviteCode?.trim() || undefined, advisorCode: data.advisorCode?.trim() || undefined, } const r = await axiosInstance.post(omsUrl('/customer/create'), body) assertBusinessOk(r.data) }, async update(id: string | number, data: Partial): Promise { const r = await axiosInstance.post(omsUrl('/customer/update'), mapUpdateBody(id, data)) assertBusinessOk(r.data) }, async delete(id: string | number): Promise { const r = await axiosInstance.post(omsUrl('/customer/delete'), { id }) assertBusinessOk(r.data) }, async resetPassword(id: string | number, newPassword: string): Promise { const r = await axiosInstance.post(omsUrl('/customer/reset-password'), { id, newPassword }) assertBusinessOk(r.data) }, async getProfileCompleteness(): Promise { const r = await axiosInstance.get(omsUrl('/customer/profile-completeness')) const data = unwrapData>(r.data) || (r.data as Record) return { phoneFilled: Boolean(data.phoneFilled ?? data.PhoneFilled), emailFilled: Boolean(data.emailFilled ?? data.EmailFilled), needContact: Boolean(data.needContact ?? data.NeedContact), customerCode: String(data.customerCode ?? data.CustomerCode ?? ''), phone: (data.phone ?? data.Phone) as string | undefined, email: (data.email ?? data.Email) as string | undefined, } }, async completeContact(input: { phone: string; email: string }): Promise { const r = await axiosInstance.post(omsUrl('/customer/complete-contact'), { phone: input.phone.trim(), email: input.email.trim(), }) assertBusinessOk(r.data) }, async omsLogin(customerId: string | number): Promise<{ accessToken: string refreshToken?: string | null expiresIn?: number }> { const r = await axiosInstance.post(omsUrl('/rbac/auth/customer-oms-login'), { customerId: Number(customerId), }) const data = unwrapData(r.data) return { accessToken: data.accessToken ?? data.AccessToken ?? '', refreshToken: data.refreshToken ?? data.RefreshToken ?? null, expiresIn: data.expiresIn ?? data.ExpiresIn, } }, buildCustomerSsoUrl(result: { accessToken: string refreshToken?: string | null }): string { const params = new URLSearchParams({ sso: '1', accessToken: result.accessToken, }) if (result.refreshToken) { params.set('refreshToken', result.refreshToken) } return `${window.location.origin}/?${params.toString()}` }, async getOpenApiCredential(): Promise { const r = await axiosInstance.get(omsUrl('/customer/openapi-credential')) return unwrapData(r.data) }, async ensureOpenApiCredential(): Promise { const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/ensure')) return unwrapData(r.data) }, async resetOpenApiSecret(): Promise { const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/reset-secret')) return unwrapData(r.data) }, async setOpenApiEnabled(enabled: boolean): Promise { const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/set-enabled'), { enabled, }) return unwrapData(r.data) }, async adminGetOpenApiCredential( customerId: string | number, ): Promise { const r = await axiosInstance.get(omsUrl('/customer/openapi-credential/admin/get'), { params: { customerId: Number(customerId) }, }) return unwrapData(r.data) }, async adminEnsureOpenApiCredential( customerId: string | number, ): Promise { const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/admin/ensure'), { customerId: Number(customerId), }) return unwrapData(r.data) }, async adminResetOpenApiSecret( customerId: string | number, ): Promise { const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/admin/reset-secret'), { customerId: Number(customerId), }) return unwrapData(r.data) }, async adminSetOpenApiEnabled( customerId: string | number, enabled: boolean, ): Promise { const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/admin/set-enabled'), { customerId: Number(customerId), enabled, }) return unwrapData(r.data) }, }