西藏巴青项目

aiConsult.js 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. import { resolveResourceUrl } from '@/utils/resourceUrl'
  2. /** 与后端 ConsultSessionRules 一致 */
  3. export const SENDER_ROLE_USER = 1
  4. export const SENDER_ROLE_VET = 2
  5. export const SENDER_ROLE_AI = 3
  6. export const MSG_TYPE_TEXT = 1
  7. export const MSG_TYPE_IMAGE = 2
  8. export const MSG_TYPE_VIDEO = 3
  9. export const MSG_TYPE_VOICE = 4
  10. export const DEFAULT_SESSION_TITLE = '新会话'
  11. export const MESSAGE_PAGE_SIZE = 50
  12. /** 移动端快捷分类:问诊 / 咨询 */
  13. export const AI_CATEGORY = {
  14. advise: 1,
  15. consult: 2,
  16. medicine: 3,
  17. device: 4
  18. }
  19. function resolveConsultIdFromIdFields(session) {
  20. if (!session) return null
  21. if (session.id != null && session.id !== '') {
  22. return String(session.id)
  23. }
  24. if (session.realSessionId != null && session.realSessionId !== '') {
  25. return String(session.realSessionId)
  26. }
  27. return null
  28. }
  29. /**
  30. * 若依问诊会话主键(listAiConsultMessages / sendAiConsultMessage 路径参数)。
  31. * 与改造前一致:id → 列表 sessionId → realSessionId。
  32. */
  33. export function resolveConsultSessionId(session) {
  34. if (!session) return null
  35. if (typeof session === 'string' || typeof session === 'number') {
  36. return String(session)
  37. }
  38. const fromId = resolveConsultIdFromIdFields(session)
  39. if (fromId) {
  40. return fromId
  41. }
  42. if (session.sessionId != null && session.sessionId !== '') {
  43. return String(session.sessionId)
  44. }
  45. return null
  46. }
  47. /** @deprecated 使用 resolveConsultSessionId */
  48. export function resolveSessionId(session) {
  49. return resolveConsultSessionId(session)
  50. }
  51. /** 列表 rows[].sessionId:与若依主键不同时,为大模型网关 sessionId */
  52. export function resolveLlmSessionIdFromRow(session) {
  53. if (!session) return null
  54. if (session.llmSessionId != null && session.llmSessionId !== '') {
  55. return String(session.llmSessionId)
  56. }
  57. const consultId = resolveConsultIdFromIdFields(session)
  58. if (session.sessionId != null && session.sessionId !== '') {
  59. const sid = String(session.sessionId)
  60. if (!consultId || sid !== consultId) {
  61. return sid
  62. }
  63. }
  64. return null
  65. }
  66. /** 解析当前会话的大模型 sessionId(行内字段 + 可选本地缓存) */
  67. export function resolveLlmSessionId(session, loadFromStorage) {
  68. if (!session) return null
  69. const consultId = resolveConsultSessionId(session)
  70. const fromRow =
  71. resolveLlmSessionIdFromRow(session) ||
  72. (session.llmSessionId != null && session.llmSessionId !== '' ? String(session.llmSessionId) : null)
  73. if (fromRow) return fromRow
  74. if (consultId && typeof loadFromStorage === 'function') {
  75. const stored = loadFromStorage(consultId)
  76. if (stored) return String(stored)
  77. }
  78. return null
  79. }
  80. /** 列表行标准化:realSessionId → id;sessionId(列表) → llmSessionId */
  81. export function normalizeSessionRow(row, llmByConsultId) {
  82. const consultId = resolveConsultSessionId(row)
  83. if (!consultId) {
  84. return row
  85. }
  86. const map = llmByConsultId && typeof llmByConsultId === 'object' ? llmByConsultId : {}
  87. const llm =
  88. resolveLlmSessionIdFromRow(row) ||
  89. (row.llmSessionId != null && row.llmSessionId !== '' ? String(row.llmSessionId) : null) ||
  90. map[consultId] ||
  91. null
  92. return {
  93. ...row,
  94. id: consultId,
  95. realSessionId: row.realSessionId != null && row.realSessionId !== '' ? String(row.realSessionId) : consultId,
  96. llmSessionId: llm
  97. }
  98. }
  99. export function mergeSessionsPreserveLlmId(rows, prevSessions, loadFromStorage) {
  100. const prevMap = {}
  101. ;(prevSessions || []).forEach((s) => {
  102. const id = resolveSessionId(s)
  103. const llm = resolveLlmSessionIdFromRow(s) || s.llmSessionId
  104. if (id && llm) {
  105. prevMap[id] = String(llm)
  106. }
  107. })
  108. return (rows || []).map((row) => {
  109. const consultId = resolveConsultSessionId(row)
  110. if (!consultId) {
  111. return row
  112. }
  113. const stored = typeof loadFromStorage === 'function' ? loadFromStorage(consultId) : null
  114. const llm =
  115. resolveLlmSessionIdFromRow(row) ||
  116. prevMap[consultId] ||
  117. stored ||
  118. null
  119. return normalizeSessionRow(row, llm ? { [consultId]: llm } : prevMap)
  120. })
  121. }
  122. export function isUserMessage(m) {
  123. return m && m.senderRole === SENDER_ROLE_USER
  124. }
  125. export function isAiMessage(m) {
  126. return m && m.senderRole === SENDER_ROLE_AI
  127. }
  128. export function isVetMessage(m) {
  129. return m && m.senderRole === SENDER_ROLE_VET
  130. }
  131. export function mediaUrl(url) {
  132. return resolveResourceUrl(url)
  133. }
  134. export function normalizeMessage(m) {
  135. return {
  136. id: m.id || m.messageId,
  137. sessionId: m.sessionId,
  138. senderRole: m.senderRole,
  139. senderName: m.senderName,
  140. msgType: m.msgType,
  141. content: m.content,
  142. mediaDuration: m.mediaDuration,
  143. sendTime: m.sendTime
  144. }
  145. }
  146. export function sessionTitle(s, fallback) {
  147. if (!s) return fallback || ''
  148. const t = s.sessionTitle
  149. if (!t || t === DEFAULT_SESSION_TITLE) {
  150. return fallback || t || ''
  151. }
  152. return t
  153. }
  154. export function formatListDate(time) {
  155. if (!time) return ''
  156. const d = new Date(time)
  157. if (isNaN(d.getTime())) return ''
  158. const now = new Date()
  159. const isToday =
  160. d.getFullYear() === now.getFullYear() &&
  161. d.getMonth() === now.getMonth() &&
  162. d.getDate() === now.getDate()
  163. if (isToday) {
  164. const h = String(d.getHours()).padStart(2, '0')
  165. const min = String(d.getMinutes()).padStart(2, '0')
  166. return `${h}:${min}`
  167. }
  168. return `${d.getMonth() + 1}/${d.getDate()}`
  169. }
  170. export function formatMsgTime(time) {
  171. if (!time) return ''
  172. const d = new Date(time)
  173. if (isNaN(d.getTime())) return ''
  174. const h = String(d.getHours()).padStart(2, '0')
  175. const min = String(d.getMinutes()).padStart(2, '0')
  176. return `${h}:${min}`
  177. }
  178. export function dateKey(time) {
  179. if (!time) return ''
  180. const d = new Date(time)
  181. if (isNaN(d.getTime())) return ''
  182. return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`
  183. }
  184. export function formatDateDivider(key, tFn) {
  185. if (!key) return ''
  186. const parts = key.split('-')
  187. if (parts.length >= 3) {
  188. const month = Number(parts[1])
  189. const day = Number(parts[2])
  190. if (tFn) {
  191. return tFn('aiOnlineConsult.dateDivider', { month, day })
  192. }
  193. return `${month}月${day}日`
  194. }
  195. return key
  196. }
  197. export function formatVoiceDuration(sec) {
  198. const s = Math.max(1, Math.round(Number(sec) || 1))
  199. return s + '"'
  200. }
  201. export function buildMessageGroups(messages, tFn) {
  202. const groups = []
  203. let currentKey = null
  204. let currentItems = []
  205. for (const m of messages) {
  206. const key = dateKey(m.sendTime)
  207. if (key !== currentKey) {
  208. if (currentItems.length) {
  209. groups.push({
  210. dateLabel: formatDateDivider(currentKey, tFn),
  211. items: currentItems
  212. })
  213. }
  214. currentKey = key
  215. currentItems = [m]
  216. } else {
  217. currentItems.push(m)
  218. }
  219. }
  220. if (currentItems.length) {
  221. groups.push({
  222. dateLabel: formatDateDivider(currentKey, tFn),
  223. items: currentItems
  224. })
  225. }
  226. return groups
  227. }
  228. export function previewText(msgType, content, tFn) {
  229. if (msgType === MSG_TYPE_IMAGE) return tFn ? tFn('aiOnlineConsult.msgImage') : '[图片]'
  230. if (msgType === MSG_TYPE_VIDEO) return tFn ? tFn('aiOnlineConsult.msgVideo') : '[视频]'
  231. if (msgType === MSG_TYPE_VOICE) return tFn ? tFn('aiOnlineConsult.msgVoice') : '[语音]'
  232. return content || ''
  233. }