296 lines
7.8 KiB
TypeScript
296 lines
7.8 KiB
TypeScript
import { apiClient } from '@/shared/api/client'
|
|
import type { ApiResponse, PaginatedResult } from '@/shared/types/types'
|
|
|
|
export interface ChatParticipant {
|
|
id: number
|
|
conversation_id: number
|
|
participant_type: 'user' | 'admin'
|
|
participant_id: number
|
|
role: 'renter' | 'owner' | 'support' | 'customer' | 'admin'
|
|
remark: string
|
|
display_name: string
|
|
avatar_url: string
|
|
last_read_at?: string
|
|
joined_at: string
|
|
}
|
|
|
|
export type SupportScene = 'general' | 'mohong'
|
|
|
|
export interface ChatConversation {
|
|
id: number
|
|
order_id: number | null
|
|
listing_id?: number | null
|
|
latest_order_id?: number | null
|
|
latest_order_no?: string
|
|
latest_order_status?: string
|
|
latest_handoff_status?: string
|
|
latest_refund_status?: string
|
|
type: string
|
|
support_scene?: string
|
|
title: string
|
|
status: string
|
|
role: 'renter' | 'owner' | 'support' | 'customer' | 'admin'
|
|
participants?: ChatParticipant[]
|
|
last_message_id?: number
|
|
last_message_preview: string
|
|
last_message_at?: string
|
|
last_sender_type?: 'user' | 'admin' | 'system'
|
|
last_sender_id?: number
|
|
last_sender_role?: string
|
|
unread_count: number
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
/** 根据当前路由推断客服场景:摸大红页 → mohong,其它 → general */
|
|
export function resolveSupportScene(path: string): SupportScene {
|
|
if (
|
|
path === '/mohong' ||
|
|
path.startsWith('/mohong/') ||
|
|
path === '/m/mohong' ||
|
|
path.startsWith('/m/mohong/')
|
|
) {
|
|
return 'mohong'
|
|
}
|
|
return 'general'
|
|
}
|
|
|
|
export interface AdminChatCounts {
|
|
ownership?: Record<string, number>
|
|
stages?: Record<string, number>
|
|
}
|
|
|
|
export interface AdminChatQuery {
|
|
filter?: string
|
|
stage?: string
|
|
keyword?: string
|
|
}
|
|
|
|
export type AdminChatResult = PaginatedResult<ChatConversation> & {
|
|
counts?: AdminChatCounts
|
|
}
|
|
|
|
export interface ChatMessage {
|
|
id: number
|
|
conversation_id: number
|
|
sender_type: 'user' | 'admin' | 'system'
|
|
sender_id: number
|
|
sender_role: 'renter' | 'owner' | 'support' | 'customer' | 'system' | 'admin'
|
|
sender_name: string
|
|
sender_avatar: string
|
|
is_self: boolean
|
|
is_read: boolean
|
|
content_type: 'text' | 'image' | 'system'
|
|
content: string
|
|
attachment_urls: string[]
|
|
created_at: string
|
|
}
|
|
|
|
export async function fetchChats(page = 1, pageSize = 20) {
|
|
const { data } = await apiClient.get<ApiResponse<PaginatedResult<ChatConversation>>>('/chats', {
|
|
params: { page, page_size: pageSize },
|
|
})
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchUnreadChatCount() {
|
|
const { data } = await apiClient.get<ApiResponse<{ unread_count: number }>>(
|
|
'/chats/unread-count',
|
|
{
|
|
silent: true,
|
|
}
|
|
)
|
|
return data.data.unread_count
|
|
}
|
|
|
|
export async function fetchChat(id: number) {
|
|
const { data } = await apiClient.get<ApiResponse<ChatConversation>>(`/chats/${id}`)
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchOrderChat(orderId: number) {
|
|
const { data } = await apiClient.get<ApiResponse<ChatConversation>>(`/orders/${orderId}/chat`)
|
|
return data.data
|
|
}
|
|
|
|
export async function ensureSupportChat(scene: SupportScene | string = 'general') {
|
|
const { data } = await apiClient.post<ApiResponse<ChatConversation>>('/chats/support', {
|
|
scene: scene || 'general',
|
|
})
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchChatMessages(id: number, page = 1, pageSize = 100) {
|
|
const { data } = await apiClient.get<ApiResponse<PaginatedResult<ChatMessage>>>(
|
|
`/chats/${id}/messages`,
|
|
{
|
|
params: { page, page_size: pageSize },
|
|
}
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function sendChatMessage(id: number, content: string, attachmentUrls: string[] = []) {
|
|
const { data } = await apiClient.post<ApiResponse<ChatMessage>>(`/chats/${id}/messages`, {
|
|
content,
|
|
attachment_urls: attachmentUrls,
|
|
})
|
|
return data.data
|
|
}
|
|
|
|
export async function markChatRead(id: number) {
|
|
const { data } = await apiClient.post<ApiResponse<{ read: boolean }>>(`/chats/${id}/read`)
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchAdminChats(page = 1, pageSize = 50, query: AdminChatQuery = {}) {
|
|
const params = Object.fromEntries(
|
|
Object.entries({ page, page_size: pageSize, ...query }).filter(
|
|
([, value]) => value !== '' && value !== undefined
|
|
)
|
|
)
|
|
const { data } = await apiClient.get<ApiResponse<AdminChatResult>>('/admin/chats', {
|
|
params,
|
|
})
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchAdminChat(id: number) {
|
|
const { data } = await apiClient.get<ApiResponse<ChatConversation>>(`/admin/chats/${id}`)
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchAdminChatMessages(id: number, page = 1, pageSize = 100) {
|
|
const { data } = await apiClient.get<ApiResponse<PaginatedResult<ChatMessage>>>(
|
|
`/admin/chats/${id}/messages`,
|
|
{
|
|
params: { page, page_size: pageSize },
|
|
}
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function sendAdminChatMessage(
|
|
id: number,
|
|
content: string,
|
|
attachmentUrls: string[] = []
|
|
) {
|
|
const { data } = await apiClient.post<ApiResponse<ChatMessage>>(`/admin/chats/${id}/messages`, {
|
|
content,
|
|
attachment_urls: attachmentUrls,
|
|
})
|
|
return data.data
|
|
}
|
|
|
|
export async function markAdminChatRead(id: number) {
|
|
const { data } = await apiClient.post<ApiResponse<{ read: boolean }>>(`/admin/chats/${id}/read`)
|
|
return data.data
|
|
}
|
|
|
|
export interface SupportAdmin {
|
|
id: number
|
|
nickname: string
|
|
support_status: 'online' | 'offline' | 'busy'
|
|
chat_count: number
|
|
}
|
|
|
|
export async function fetchSupportAdmins() {
|
|
const { data } = await apiClient.get<ApiResponse<SupportAdmin[]>>('/admin/chats/support-admins')
|
|
return data.data
|
|
}
|
|
|
|
export async function transferChat(id: number, toAdminId: number) {
|
|
const { data } = await apiClient.post<ApiResponse<{ transferred: boolean }>>(
|
|
`/admin/chats/${id}/transfer`,
|
|
{
|
|
to_admin_id: toAdminId,
|
|
}
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function updateChatRemark(id: number, remark: string) {
|
|
const { data } = await apiClient.put<ApiResponse<{ updated: boolean }>>(
|
|
`/admin/chats/${id}/remark`,
|
|
{ remark }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export interface QuickReply {
|
|
id: number
|
|
admin_user_id: number
|
|
title: string
|
|
content: string
|
|
sort_order: number
|
|
is_global: boolean
|
|
}
|
|
|
|
export async function fetchQuickReplies() {
|
|
const { data } = await apiClient.get<ApiResponse<QuickReply[]>>('/admin/chats/quick-replies')
|
|
return data.data
|
|
}
|
|
|
|
export async function createQuickReply(
|
|
title: string,
|
|
content: string,
|
|
sortOrder = 0,
|
|
isGlobal = false
|
|
) {
|
|
const { data } = await apiClient.post<ApiResponse<QuickReply>>('/admin/chats/quick-replies', {
|
|
title,
|
|
content,
|
|
sort_order: sortOrder,
|
|
is_global: isGlobal,
|
|
})
|
|
return data.data
|
|
}
|
|
|
|
export async function updateQuickReply(
|
|
id: number,
|
|
updates: { title?: string; content?: string; sort_order?: number }
|
|
) {
|
|
const { data } = await apiClient.put<ApiResponse<{ updated: boolean }>>(
|
|
`/admin/chats/quick-replies/${id}`,
|
|
updates
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function deleteQuickReply(id: number) {
|
|
const { data } = await apiClient.delete<ApiResponse<{ deleted: boolean }>>(
|
|
`/admin/chats/quick-replies/${id}`
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchAutoWelcomeMessage() {
|
|
const { data } = await apiClient.get<ApiResponse<{ message: string }>>(
|
|
'/admin/chats/auto-welcome'
|
|
)
|
|
return data.data.message
|
|
}
|
|
|
|
export async function updateAutoWelcomeMessage(message: string) {
|
|
const { data } = await apiClient.put<ApiResponse<{ updated: boolean }>>(
|
|
'/admin/chats/auto-welcome',
|
|
{ message }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchListingGroupWelcomeMessage() {
|
|
const { data } = await apiClient.get<ApiResponse<{ message: string }>>(
|
|
'/admin/chats/listing-group-welcome'
|
|
)
|
|
return data.data.message
|
|
}
|
|
|
|
export async function updateListingGroupWelcomeMessage(message: string) {
|
|
const { data } = await apiClient.put<ApiResponse<{ updated: boolean }>>(
|
|
'/admin/chats/listing-group-welcome',
|
|
{ message }
|
|
)
|
|
return data.data
|
|
}
|