front/src/api/customer/index.ts
zzh 072142d7bb
All checks were successful
Build and Deploy Front / deploy (push) Successful in 6m30s
feat: 代理门户返佣预计态与客户绑定代理
运单列表/工作台/返佣四态展示,注册归因与客户表单改绑代理,侧栏与占位邮箱优化。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-30 11:07:07 +08:00

331 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 客户管理 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
agentId?: number | null
agentCode?: string
agentName?: string
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
agentId?: number
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<CustomerDto>) {
const body: Record<string, unknown> = {
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,
// 空串表示解绑;有值则按编号绑定
agentCode: data.agentCode !== undefined ? String(data.agentCode ?? '').trim() : undefined,
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<CustomerPagedResult> {
const res = await axiosInstance.get(omsUrl('/customer/list'), { params })
return parseListPayload(res.data)
},
async getById(id: string | number): Promise<CustomerDto> {
const res = await axiosInstance.get(omsUrl('/customer/get'), { params: { id } })
return unwrapData<CustomerDto>(res.data)
},
async getServiceAdvisor(): Promise<CustomerServiceAdvisorDto> {
const res = await axiosInstance.get(omsUrl('/customer/service-advisor'))
return unwrapData<CustomerServiceAdvisorDto>(res.data)
},
async createSub(
data: Partial<CustomerDto> & { password?: string; permissions?: string[] }
): Promise<void> {
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<CustomerDto> & {
password?: string
inviteCode?: string
advisorCode?: string
agentCode?: string
},
): Promise<void> {
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,
agentCode: data.agentCode?.trim() || undefined,
}
const r = await axiosInstance.post(omsUrl('/customer/create'), body)
assertBusinessOk(r.data)
},
async update(id: string | number, data: Partial<CustomerDto>): Promise<void> {
const r = await axiosInstance.post(omsUrl('/customer/update'), mapUpdateBody(id, data))
assertBusinessOk(r.data)
},
async delete(id: string | number): Promise<void> {
const r = await axiosInstance.post(omsUrl('/customer/delete'), { id })
assertBusinessOk(r.data)
},
async resetPassword(id: string | number, newPassword: string): Promise<void> {
const r = await axiosInstance.post(omsUrl('/customer/reset-password'), { id, newPassword })
assertBusinessOk(r.data)
},
async getProfileCompleteness(): Promise<CustomerProfileCompleteness> {
const r = await axiosInstance.get(omsUrl('/customer/profile-completeness'))
const data = unwrapData<Record<string, unknown>>(r.data) || (r.data as Record<string, unknown>)
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<void> {
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<any>(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<CustomerOpenApiCredentialDto> {
const r = await axiosInstance.get(omsUrl('/customer/openapi-credential'))
return unwrapData<CustomerOpenApiCredentialDto>(r.data)
},
async ensureOpenApiCredential(): Promise<CustomerOpenApiCredentialDto> {
const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/ensure'))
return unwrapData<CustomerOpenApiCredentialDto>(r.data)
},
async resetOpenApiSecret(): Promise<CustomerOpenApiCredentialDto> {
const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/reset-secret'))
return unwrapData<CustomerOpenApiCredentialDto>(r.data)
},
async setOpenApiEnabled(enabled: boolean): Promise<CustomerOpenApiCredentialDto> {
const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/set-enabled'), {
enabled,
})
return unwrapData<CustomerOpenApiCredentialDto>(r.data)
},
async adminGetOpenApiCredential(
customerId: string | number,
): Promise<CustomerOpenApiCredentialDto> {
const r = await axiosInstance.get(omsUrl('/customer/openapi-credential/admin/get'), {
params: { customerId: Number(customerId) },
})
return unwrapData<CustomerOpenApiCredentialDto>(r.data)
},
async adminEnsureOpenApiCredential(
customerId: string | number,
): Promise<CustomerOpenApiCredentialDto> {
const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/admin/ensure'), {
customerId: Number(customerId),
})
return unwrapData<CustomerOpenApiCredentialDto>(r.data)
},
async adminResetOpenApiSecret(
customerId: string | number,
): Promise<CustomerOpenApiCredentialDto> {
const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/admin/reset-secret'), {
customerId: Number(customerId),
})
return unwrapData<CustomerOpenApiCredentialDto>(r.data)
},
async adminSetOpenApiEnabled(
customerId: string | number,
enabled: boolean,
): Promise<CustomerOpenApiCredentialDto> {
const r = await axiosInstance.post(omsUrl('/customer/openapi-credential/admin/set-enabled'), {
customerId: Number(customerId),
enabled,
})
return unwrapData<CustomerOpenApiCredentialDto>(r.data)
},
}