fix: 全面修复 - 实时通讯、报告AI流程、健康概览、用药运动提醒
- SignalR Hub消息持久化+防重复+跨端广播 - 报告VLM+LLM真实AI流程(验证→提取→解读) - 医生审核页重构(严重程度/建议模板/综合评语) - 健康概览五合一趋势图(fl_chart+指标切换) - 用药提醒时区修复+跨午夜+过期提醒 - 运动打卡DayOfWeek映射修复+计划覆盖查询 - 体重与其他四指标同级(Abnormal检查/确认卡牌) - AI Agent支持多种类多时段批量录入 - 删除硬编码假数据(张三/假医生/假AI解读) - 随访/报告审核数据链路全部打通
This commit is contained in:
@@ -164,9 +164,13 @@ class ConsultationChatNotifier extends Notifier<ConsultationChatState> {
|
||||
: DateTime.now(),
|
||||
);
|
||||
|
||||
// 去重
|
||||
final existingIds = state.messages.map((m) => m.id).toSet();
|
||||
if (!existingIds.contains(msg.id)) {
|
||||
// 去重:按 ID + 内容+时间窗口(防止本地消息和服务器消息重复)
|
||||
final isDuplicate = state.messages.any((m) =>
|
||||
m.id == msg.id ||
|
||||
(m.senderType == msg.senderType &&
|
||||
m.content == msg.content &&
|
||||
msg.createdAt.difference(m.createdAt).inSeconds.abs() < 3));
|
||||
if (!isDuplicate) {
|
||||
state = state.copyWith(messages: [...state.messages, msg]);
|
||||
}
|
||||
});
|
||||
@@ -220,8 +224,9 @@ class ConsultationChatNotifier extends Notifier<ConsultationChatState> {
|
||||
Future<void> sendMessage(String text) async {
|
||||
if (text.trim().isEmpty || state.isSending || state.consultationId == null) return;
|
||||
|
||||
final localId = '${DateTime.now().millisecondsSinceEpoch}';
|
||||
final userMsg = ConsultationMsg(
|
||||
id: '${DateTime.now().millisecondsSinceEpoch}',
|
||||
id: localId,
|
||||
senderType: 'User',
|
||||
content: text,
|
||||
createdAt: DateTime.now(),
|
||||
@@ -234,13 +239,50 @@ class ConsultationChatNotifier extends Notifier<ConsultationChatState> {
|
||||
try {
|
||||
// 优先通过 SignalR 发送
|
||||
if (_hub != null && _hub!.state == HubConnectionState.Connected) {
|
||||
await _hub!.invoke('SendMessage',
|
||||
final result = await _hub!.invoke('SendMessage',
|
||||
args: <Object>[state.consultationId!, 'User', '', text]);
|
||||
// 用服务器返回的真实 ID 更新本地消息,防止后续轮询重复
|
||||
if (result != null) {
|
||||
final serverId = (result as Map<String, dynamic>)['id']?.toString();
|
||||
if (serverId != null && serverId != localId) {
|
||||
final updated = state.messages.map((m) {
|
||||
if (m.id == localId) {
|
||||
return ConsultationMsg(
|
||||
id: serverId,
|
||||
senderType: m.senderType,
|
||||
senderName: m.senderName,
|
||||
content: m.content,
|
||||
createdAt: m.createdAt,
|
||||
);
|
||||
}
|
||||
return m;
|
||||
}).toList();
|
||||
state = state.copyWith(messages: updated);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 回退到 HTTP
|
||||
final api = ref.read(apiClientProvider);
|
||||
await api.post('/api/consultations/${state.consultationId}/messages',
|
||||
final res = await api.post('/api/consultations/${state.consultationId}/messages',
|
||||
data: {'content': text});
|
||||
// 用服务器返回的真实 ID 更新本地消息
|
||||
final dataMap = res.data['data'] as Map<String, dynamic>?;
|
||||
final serverId = dataMap?['id']?.toString();
|
||||
if (serverId != null && serverId != localId) {
|
||||
final updated = state.messages.map((m) {
|
||||
if (m.id == localId) {
|
||||
return ConsultationMsg(
|
||||
id: serverId,
|
||||
senderType: m.senderType,
|
||||
senderName: m.senderName,
|
||||
content: m.content,
|
||||
createdAt: m.createdAt,
|
||||
);
|
||||
}
|
||||
return m;
|
||||
}).toList();
|
||||
state = state.copyWith(messages: updated);
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
// 静默失败,消息已显示在本地
|
||||
|
||||
Reference in New Issue
Block a user