conversationApi.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import { apiUrl, getAgentHeaders } from "@/lib/config";
  2. import {
  3. ConversationDetail,
  4. ConversationSummary,
  5. } from "../types";
  6. export type ConversationListType = "visitor" | "internal";
  7. export type ConversationStatus = "open" | "closed";
  8. export async function fetchConversations(
  9. userId?: number,
  10. opts?: { type?: ConversationListType; status?: ConversationStatus }
  11. ): Promise<ConversationSummary[]> {
  12. const params = new URLSearchParams();
  13. if (userId) params.set("user_id", String(userId));
  14. if (opts?.type) params.set("type", opts.type);
  15. if (opts?.status) params.set("status", opts.status);
  16. const url = `${apiUrl("/conversations")}?${params.toString()}`;
  17. const res = await fetch(url, { cache: "no-store", headers: getAgentHeaders() });
  18. if (!res.ok) {
  19. throw new Error("获取对话列表失败");
  20. }
  21. const data = await res.json();
  22. if (!Array.isArray(data)) {
  23. return [];
  24. }
  25. return data.map((item) => ({
  26. ...item,
  27. unread_count: item.unread_count ?? 0,
  28. has_participated: item.has_participated ?? false,
  29. }));
  30. }
  31. /** 创建一条内部对话(知识库测试),返回新对话 ID */
  32. export async function initInternalConversation(userId: number): Promise<{ conversation_id: number }> {
  33. const res = await fetch(`${apiUrl("/conversations/internal")}?user_id=${userId}`, {
  34. method: "POST",
  35. headers: { "Content-Type": "application/json", ...getAgentHeaders() },
  36. });
  37. if (!res.ok) {
  38. const err = await res.json().catch(() => ({}));
  39. throw new Error((err as { error?: string }).error || "创建内部对话失败");
  40. }
  41. const data = await res.json();
  42. return { conversation_id: data.conversation_id };
  43. }
  44. export async function searchConversations(
  45. query: string,
  46. userId?: number,
  47. opts?: { status?: ConversationStatus }
  48. ): Promise<ConversationSummary[]> {
  49. const status = opts?.status ?? "open";
  50. const url = userId
  51. ? `${apiUrl("/conversations/search")}?q=${encodeURIComponent(query)}&user_id=${userId}&status=${status}`
  52. : `${apiUrl("/conversations/search")}?q=${encodeURIComponent(query)}&status=${status}`;
  53. const res = await fetch(url, {
  54. cache: "no-store",
  55. headers: getAgentHeaders(),
  56. });
  57. if (!res.ok) {
  58. throw new Error("搜索对话失败");
  59. }
  60. const data = await res.json();
  61. if (!Array.isArray(data)) {
  62. return [];
  63. }
  64. return data.map((item) => ({
  65. ...item,
  66. unread_count: item.unread_count ?? 0,
  67. has_participated: item.has_participated ?? false,
  68. }));
  69. }
  70. export async function fetchConversationDetail(
  71. conversationId: number,
  72. userId?: number
  73. ): Promise<ConversationDetail | null> {
  74. const url = userId
  75. ? `${apiUrl(`/conversations/${conversationId}`)}?user_id=${userId}`
  76. : apiUrl(`/conversations/${conversationId}`);
  77. const res = await fetch(url, { cache: "no-store", headers: getAgentHeaders() });
  78. if (!res.ok) {
  79. return null;
  80. }
  81. const data = await res.json();
  82. return {
  83. ...data,
  84. unread_count: data.unread_count ?? 0,
  85. };
  86. }
  87. /** 关闭会话(进入历史/归档)。访客再次发消息会自动 reopen(B 方案)。 */
  88. export async function closeConversation(conversationId: number): Promise<void> {
  89. const res = await fetch(apiUrl(`/conversations/${conversationId}/close`), {
  90. method: "POST",
  91. headers: getAgentHeaders(),
  92. });
  93. if (!res.ok) {
  94. const j = await res.json().catch(() => ({}));
  95. throw new Error((j as { error?: string }).error || `关闭会话失败(${res.status})`);
  96. }
  97. }
  98. export interface UpdateConversationContactPayload {
  99. email?: string;
  100. phone?: string;
  101. notes?: string;
  102. }
  103. export interface UpdateConversationContactResult {
  104. email: string;
  105. phone: string;
  106. notes: string;
  107. }
  108. export async function updateConversationContact(
  109. conversationId: number,
  110. payload: UpdateConversationContactPayload
  111. ): Promise<UpdateConversationContactResult> {
  112. const res = await fetch(
  113. apiUrl(`/conversations/${conversationId}/contact`),
  114. {
  115. method: "PUT",
  116. headers: { "Content-Type": "application/json", ...getAgentHeaders() },
  117. body: JSON.stringify(payload),
  118. }
  119. );
  120. if (!res.ok) {
  121. throw new Error("更新访客联系信息失败");
  122. }
  123. const data = await res.json();
  124. return {
  125. email: data.email ?? "",
  126. phone: data.phone ?? "",
  127. notes: data.notes ?? "",
  128. };
  129. }