2026-10-13 16:28:11 +08:00
|
|
|
|
/**
|
2026-10-13 17:31:12 +08:00
|
|
|
|
* 会话与线索 API(Phase 3 Task 3 骨架;Task 5 实装调用函数并按实际响应修正类型)
|
2026-10-13 16:28:11 +08:00
|
|
|
|
*
|
2026-10-13 17:31:12 +08:00
|
|
|
|
* 端点与响应事实源:app/platform/routes/data_routes.py
|
|
|
|
|
|
* - 列表:{total, page, page_size, items},行含 preview/intent_level/contact_info/
|
|
|
|
|
|
* email_sent/message_count/country/site 等;筛选 intent 白名单(interested/
|
|
|
|
|
|
* not_interested/pending,非法 400)/site 精确/client_info["site"]/from-to 闭区间
|
|
|
|
|
|
* (日期-only 的 to 含当日全天)
|
|
|
|
|
|
* - 详情:扁平结构(元数据 + evaluation/profile/messages 三块),消息行字段为
|
|
|
|
|
|
* time(非 created_at);profile.chips 为 raw_info 去 _ 前缀键
|
|
|
|
|
|
* - 删除:{ok: true};不存在 404(级联消息/画像/评估/推送日志)
|
|
|
|
|
|
* - push-logs:列表不回大 payload(load_only 排除 request_payload),带
|
|
|
|
|
|
* payload_size 服务端长度标记;status 白名单 pending/success/failed/skipped
|
2026-10-13 16:28:11 +08:00
|
|
|
|
*/
|
2026-10-13 17:31:12 +08:00
|
|
|
|
import { http } from './client'
|
2026-10-13 16:28:11 +08:00
|
|
|
|
|
2026-10-13 17:31:12 +08:00
|
|
|
|
/** 意向层级(Evaluation.intent_level 枚举;列表筛选白名单同值) */
|
|
|
|
|
|
export type IntentLevel = 'interested' | 'not_interested' | 'pending'
|
2026-10-13 16:28:11 +08:00
|
|
|
|
|
2026-10-13 17:31:12 +08:00
|
|
|
|
/** 推送状态(LeadPushLog.status;筛选白名单同值) */
|
|
|
|
|
|
export type PushLogStatus = 'pending' | 'success' | 'failed' | 'skipped'
|
|
|
|
|
|
|
|
|
|
|
|
/** 联系方式(评估产出,留资三选一) */
|
2026-10-13 16:28:11 +08:00
|
|
|
|
export interface ContactInfo {
|
|
|
|
|
|
email?: string | null
|
|
|
|
|
|
whatsapp?: string | null
|
|
|
|
|
|
phone?: string | null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 会话列表行(GET /api/platform/tenants/{slug}/conversations) */
|
|
|
|
|
|
export interface ConversationListItem {
|
|
|
|
|
|
id: string
|
|
|
|
|
|
session_id: string
|
|
|
|
|
|
status: string
|
|
|
|
|
|
source: string
|
|
|
|
|
|
site: string | null
|
|
|
|
|
|
ip: string
|
|
|
|
|
|
device: string
|
|
|
|
|
|
country: string | null
|
2026-10-13 17:31:12 +08:00
|
|
|
|
intent_level: IntentLevel | null
|
2026-10-13 16:28:11 +08:00
|
|
|
|
reasoning: string | null
|
|
|
|
|
|
contact_info: ContactInfo | null
|
|
|
|
|
|
email_sent: boolean
|
|
|
|
|
|
message_count: number
|
|
|
|
|
|
preview: string
|
|
|
|
|
|
created_at: string | null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-13 17:31:12 +08:00
|
|
|
|
/** 会话列表查询参数(intent 白名单校验在后端;from/to 为闭区间日期 YYYY-MM-DD) */
|
2026-10-13 16:28:11 +08:00
|
|
|
|
export interface ConversationListParams {
|
2026-10-13 17:31:12 +08:00
|
|
|
|
intent?: IntentLevel
|
2026-10-13 16:28:11 +08:00
|
|
|
|
site?: string
|
|
|
|
|
|
from?: string
|
|
|
|
|
|
to?: string
|
|
|
|
|
|
page?: number
|
|
|
|
|
|
page_size?: number
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 会话列表响应 */
|
|
|
|
|
|
export interface ConversationListResponse {
|
|
|
|
|
|
total: number
|
|
|
|
|
|
page: number
|
|
|
|
|
|
page_size: number
|
|
|
|
|
|
items: ConversationListItem[]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-13 17:31:12 +08:00
|
|
|
|
/** 会话详情消息行(时间升序;字段为 time) */
|
2026-10-13 16:28:11 +08:00
|
|
|
|
export interface ConversationMessage {
|
|
|
|
|
|
role: 'user' | 'assistant' | 'system' | string
|
|
|
|
|
|
content: string
|
2026-10-13 17:31:12 +08:00
|
|
|
|
time: string | null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 评估块(详情内嵌,未评估为 null) */
|
|
|
|
|
|
export interface ConversationEvaluation {
|
|
|
|
|
|
intent_level: IntentLevel | null
|
|
|
|
|
|
reasoning: string | null
|
|
|
|
|
|
contact_info: ContactInfo | null
|
|
|
|
|
|
email_sent: boolean
|
|
|
|
|
|
correction_count: number | null
|
|
|
|
|
|
evaluated_at: string | null
|
2026-10-13 16:28:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-13 17:31:12 +08:00
|
|
|
|
/** 会话详情响应(GET .../conversations/{convId}——扁平,与列表行不同构) */
|
|
|
|
|
|
export interface ConversationDetail {
|
|
|
|
|
|
id: string
|
|
|
|
|
|
session_id: string
|
|
|
|
|
|
status: string
|
|
|
|
|
|
source: string
|
2026-10-13 16:28:11 +08:00
|
|
|
|
client_info: Record<string, unknown>
|
2026-10-13 17:31:12 +08:00
|
|
|
|
site: string | null
|
|
|
|
|
|
ip: string
|
|
|
|
|
|
device: string
|
|
|
|
|
|
country: string | null
|
|
|
|
|
|
created_at: string | null
|
|
|
|
|
|
updated_at: string | null
|
|
|
|
|
|
evaluation: ConversationEvaluation | null
|
2026-10-13 16:28:11 +08:00
|
|
|
|
profile: {
|
|
|
|
|
|
chips: Record<string, unknown>
|
|
|
|
|
|
summary: string | null
|
|
|
|
|
|
} | null
|
2026-10-13 17:31:12 +08:00
|
|
|
|
messages: ConversationMessage[]
|
2026-10-13 16:28:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-13 17:31:12 +08:00
|
|
|
|
/** 推送日志行(GET .../push-logs——无 request_payload,仅 payload_size 长度标记) */
|
2026-10-13 16:28:11 +08:00
|
|
|
|
export interface PushLogItem {
|
2026-10-13 17:31:12 +08:00
|
|
|
|
id: string
|
|
|
|
|
|
evaluation_id: string
|
2026-10-13 16:28:11 +08:00
|
|
|
|
conversation_id: string
|
2026-10-13 17:31:12 +08:00
|
|
|
|
push_type: string
|
|
|
|
|
|
status: PushLogStatus | string
|
|
|
|
|
|
attempts: number
|
|
|
|
|
|
outcome: string | null
|
|
|
|
|
|
response_status: number | null
|
|
|
|
|
|
error_message: string | null
|
|
|
|
|
|
payload_size: number | null
|
2026-10-13 16:28:11 +08:00
|
|
|
|
created_at: string | null
|
2026-10-13 17:31:12 +08:00
|
|
|
|
updated_at: string | null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 推送日志查询参数(status 白名单校验在后端) */
|
|
|
|
|
|
export interface PushLogParams {
|
|
|
|
|
|
status?: PushLogStatus
|
|
|
|
|
|
conversation_id?: string
|
|
|
|
|
|
page?: number
|
|
|
|
|
|
page_size?: number
|
2026-10-13 16:28:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 推送日志响应 */
|
|
|
|
|
|
export interface PushLogResponse {
|
|
|
|
|
|
total: number
|
|
|
|
|
|
page: number
|
|
|
|
|
|
page_size: number
|
|
|
|
|
|
items: PushLogItem[]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 会话与线索端点 */
|
|
|
|
|
|
export const CONVERSATION_ENDPOINTS = {
|
|
|
|
|
|
list: (slug: string) => `/api/platform/tenants/${slug}/conversations`,
|
|
|
|
|
|
detail: (slug: string, convId: string) =>
|
|
|
|
|
|
`/api/platform/tenants/${slug}/conversations/${convId}`,
|
|
|
|
|
|
remove: (slug: string, convId: string) =>
|
|
|
|
|
|
`/api/platform/tenants/${slug}/conversations/${convId}`,
|
|
|
|
|
|
pushLogs: (slug: string) => `/api/platform/tenants/${slug}/push-logs`
|
|
|
|
|
|
} as const
|
2026-10-13 17:31:12 +08:00
|
|
|
|
|
|
|
|
|
|
// ---- 展示映射(枚举值事实源 data_routes.py INTENTS/PUSH_STATUSES 与 models.py) ----
|
|
|
|
|
|
|
|
|
|
|
|
/** 意向筛选/展示选项 */
|
|
|
|
|
|
export const INTENT_OPTIONS: Array<{ value: IntentLevel; label: string }> = [
|
|
|
|
|
|
{ value: 'interested', label: '有意向' },
|
|
|
|
|
|
{ value: 'not_interested', label: '无意向' },
|
|
|
|
|
|
{ value: 'pending', label: '待定' }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
/** 意向展示文案(未知值回退原样) */
|
|
|
|
|
|
export function intentLabel(level: string | null | undefined): string {
|
|
|
|
|
|
if (!level) return '未评估'
|
|
|
|
|
|
return INTENT_OPTIONS.find((item) => item.value === level)?.label ?? level
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 推送状态筛选/展示选项 */
|
|
|
|
|
|
export const PUSH_STATUS_OPTIONS: Array<{ value: PushLogStatus; label: string }> = [
|
|
|
|
|
|
{ value: 'pending', label: '待推送' },
|
|
|
|
|
|
{ value: 'success', label: '成功' },
|
|
|
|
|
|
{ value: 'failed', label: '失败' },
|
|
|
|
|
|
{ value: 'skipped', label: '跳过' }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
/** 推送状态展示文案 */
|
|
|
|
|
|
export function pushStatusLabel(status: string | null | undefined): string {
|
|
|
|
|
|
if (!status) return '—'
|
|
|
|
|
|
return PUSH_STATUS_OPTIONS.find((item) => item.value === status)?.label ?? status
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 推送类型展示文案(LeadPushLog.push_type:lead/returning/correction) */
|
|
|
|
|
|
export function pushTypeLabel(type: string | null | undefined): string {
|
|
|
|
|
|
const labels: Record<string, string> = {
|
|
|
|
|
|
lead: '新线索',
|
|
|
|
|
|
returning: '回访',
|
|
|
|
|
|
correction: '更正'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!type) return '—'
|
|
|
|
|
|
return labels[type] ?? type
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 会话状态展示文案(ConversationStatus) */
|
|
|
|
|
|
export function conversationStatusLabel(status: string | null | undefined): string {
|
|
|
|
|
|
const labels: Record<string, string> = {
|
|
|
|
|
|
active: '进行中',
|
|
|
|
|
|
completed: '已完成',
|
|
|
|
|
|
abandoned: '已放弃'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!status) return '—'
|
|
|
|
|
|
return labels[status] ?? status
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 会话来源展示文案(ConversationSource) */
|
|
|
|
|
|
export function conversationSourceLabel(source: string | null | undefined): string {
|
|
|
|
|
|
const labels: Record<string, string> = {
|
|
|
|
|
|
standalone: '独立站',
|
|
|
|
|
|
embedded: '内嵌页',
|
|
|
|
|
|
widget: '挂件',
|
|
|
|
|
|
app: '应用'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!source) return '—'
|
|
|
|
|
|
return labels[source] ?? source
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 调用函数 -------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
/** 会话列表(分页 + intent/site/日期区间筛选) */
|
|
|
|
|
|
export function fetchConversations(
|
|
|
|
|
|
slug: string,
|
|
|
|
|
|
params?: ConversationListParams
|
|
|
|
|
|
): Promise<ConversationListResponse> {
|
|
|
|
|
|
return http.get<ConversationListResponse>(CONVERSATION_ENDPOINTS.list(slug), { params })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 会话详情(元数据 + 画像 chips + 评估 + 消息全量) */
|
|
|
|
|
|
export function fetchConversationDetail(
|
|
|
|
|
|
slug: string,
|
|
|
|
|
|
convId: string
|
|
|
|
|
|
): Promise<ConversationDetail> {
|
|
|
|
|
|
return http.get<ConversationDetail>(CONVERSATION_ENDPOINTS.detail(slug, convId))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 删除会话(级联消息/画像/评估/推送日志;不存在 404) */
|
|
|
|
|
|
export function deleteConversation(
|
|
|
|
|
|
slug: string,
|
|
|
|
|
|
convId: string
|
|
|
|
|
|
): Promise<{ ok: boolean }> {
|
|
|
|
|
|
return http.del<{ ok: boolean }>(CONVERSATION_ENDPOINTS.remove(slug, convId))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 推送日志列表(status/conversation_id 筛选 + 分页;不回大 payload) */
|
|
|
|
|
|
export function fetchPushLogs(slug: string, params?: PushLogParams): Promise<PushLogResponse> {
|
|
|
|
|
|
return http.get<PushLogResponse>(CONVERSATION_ENDPOINTS.pushLogs(slug), { params })
|
|
|
|
|
|
}
|