Files
hms/apps/miniprogram/src/services/ai-chat.ts
iven 345e46002a fix(mp): 行业标准第二轮审计修复 — 安全存储+UX+合规
- 安全:AI聊天历史、患者档案、设备同步数据统一走 secureSet/secureGet 加密存储
- 合规:TabBar "消息" 改为 "助手" 消除命名误导
- 合规:新增 .env.production 模板配置 HTTPS API URL
- UX:AI发送按钮 40→44px、反馈按钮 32→44px、协议勾选框 44px 点击热区
- UX:5处硬编码 10-12px 字号替换为 design token(DoctorTabBar/ShortcutButton/TodoAlert/mall)
- UX:6处安全区域写法统一(全部使用 --tk-page-padding/--tk-tabbar-space + env fallback)
- 新增 safe-bottom-padded / safe-bottom-tabbar 两个 mixin

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 14:06:29 +08:00

95 lines
2.8 KiB
TypeScript

import { api } from './request';
import { secureGet, secureSet } from '@/utils/secure-storage';
export interface AiChatMessage {
id: string;
role: 'user' | 'assistant';
content: string;
created_at: string;
}
export interface AiChatResponse {
reply: string;
message_id: string;
display_hints?: DisplayHint[];
}
export type DisplayHint =
| { type: 'vital_card'; indicator_type: string; values: [string, number][]; unit: string }
| { type: 'lab_report_card'; report_date: string; abnormal_count: number }
| { type: 'action_confirm'; action_type: string; summary: string; confirm_payload: unknown }
| { type: 'risk_alert'; level: string; message: string }
| { type: 'trend_chart'; metrics: string[]; period: string; summary: string }
| { type: 'insight_card'; title: string; severity: string; items: string[] }
| { type: 'patient_profile'; chronic_conditions: string[]; medication_count: number }
| { type: 'text' };
/** 发送消息给 AI 客服 */
export async function sendAiMessage(
message: string,
history?: AiChatMessage[],
patientId?: string,
sessionId?: string,
): Promise<AiChatResponse> {
const body: Record<string, unknown> = {
message,
history: history?.slice(-10),
};
if (patientId) {
body.patient_id = patientId;
}
if (sessionId) {
body.session_id = sessionId;
}
const resp = await api.post<AiChatResponse>('/ai/chat', body);
return resp;
}
// === 会话 API ===
export interface AiChatSession {
id: string;
title: string | null;
patient_id: string | null;
status: string;
created_at: string;
updated_at: string;
}
export async function createSession(patientId?: string, title?: string): Promise<AiChatSession> {
const body: Record<string, unknown> = {};
if (patientId) body.patient_id = patientId;
if (title) body.title = title;
return api.post<AiChatSession>('/ai/chat/sessions', body);
}
export async function listSessions(): Promise<AiChatSession[]> {
return api.get<AiChatSession[]>('/ai/chat/sessions');
}
export async function renameSession(sessionId: string, title: string): Promise<void> {
await api.put(`/ai/chat/sessions/${sessionId}/rename`, { title });
}
export async function closeSession(sessionId: string): Promise<void> {
await api.post(`/ai/chat/sessions/${sessionId}/close`, {});
}
/** 获取聊天历史(本地加密缓存) */
export function getLocalHistory(): AiChatMessage[] {
try {
const raw = secureGet('ai_chat_history');
return raw ? JSON.parse(raw) : [];
} catch (err) {
console.warn('[ai-chat] 读取本地聊天历史失败:', err);
return [];
}
}
/** 保存聊天历史到本地(加密存储) */
export function saveLocalHistory(messages: AiChatMessage[]): void {
try {
secureSet('ai_chat_history', JSON.stringify(messages.slice(-100)));
} catch (err) { console.warn('[ai-chat] 保存本地聊天历史失败:', err); }
}