feat(ai): Phase 2B 洞察→推送→反馈闭环 — 风险评分+通知+建议反馈
- 风险评分引擎 load_patient_data 实装(体征+化验异常)
- refresh_all_patients 高风险自动创建洞察+事件推送
- erp-message 订阅 copilot.insight.created 推送医护通知
- 每日 cron 增加洞察过期清理+建议过期清理
- POST /ai/suggestions/{id}/feedback 建议反馈端点
- SuggestionFeedbackService 反馈服务层
- 小程序健康页建议卡片增加采纳/忽略/咨询医生按钮
This commit is contained in:
@@ -12,6 +12,7 @@ import SegmentTabs from '../../components/SegmentTabs';
|
||||
import PageShell from '@/components/ui/PageShell';
|
||||
import ContentCard from '@/components/ui/ContentCard';
|
||||
import { useHealthData, VITAL_TABS, type VitalType } from './useHealthData';
|
||||
import { submitSuggestionFeedback } from '../../services/ai-analysis';
|
||||
import './index.scss';
|
||||
|
||||
function buildRefRange(t: HealthThreshold[]): Record<VitalType, string> {
|
||||
@@ -171,16 +172,7 @@ export default function Health() {
|
||||
</View>
|
||||
|
||||
{aiSuggestions.length > 0 && (
|
||||
<View className='ai-suggestion-card' onClick={() => {
|
||||
const first = aiSuggestions[0];
|
||||
if (first?.suggestion_type === 'appointment') {
|
||||
safeNavigateTo(`/pages/appointment/create/index`);
|
||||
} else if (first?.suggestion_type === 'followup') {
|
||||
safeNavigateTo('/pages/pkg-profile/followups/index');
|
||||
} else {
|
||||
Taro.switchTab({ url: '/pages/health/index' });
|
||||
}
|
||||
}}>
|
||||
<View className='ai-suggestion-card'>
|
||||
<View className='ai-card-header'>
|
||||
<Text className='ai-card-title'>AI 健康建议</Text>
|
||||
<Text className='ai-card-count'>{aiSuggestions.length} 条待查看</Text>
|
||||
@@ -192,8 +184,44 @@ export default function Health() {
|
||||
const reason = (params?.reason as string) || (params?.message as string) || typeLabel;
|
||||
return (
|
||||
<View key={s.id} className='ai-suggestion-item'>
|
||||
<View className={`ai-risk-dot ${riskCls}`} />
|
||||
<Text className='ai-suggestion-text'>{reason.slice(0, 40)}</Text>
|
||||
<View className='ai-suggestion-main' onClick={() => {
|
||||
if (s.suggestion_type === 'appointment') {
|
||||
safeNavigateTo(`/pages/appointment/create/index`);
|
||||
} else if (s.suggestion_type === 'followup') {
|
||||
safeNavigateTo('/pages/pkg-profile/followups/index');
|
||||
}
|
||||
}}>
|
||||
<View className={`ai-risk-dot ${riskCls}`} />
|
||||
<Text className='ai-suggestion-text'>{reason.slice(0, 40)}</Text>
|
||||
</View>
|
||||
<View className='ai-feedback-row'>
|
||||
<View className='ai-feedback-btn ai-feedback-adopt' onClick={async () => {
|
||||
try {
|
||||
await submitSuggestionFeedback(s.id, 'adopt');
|
||||
Taro.showToast({ title: '已采纳', icon: 'success' });
|
||||
fetchData();
|
||||
} catch { Taro.showToast({ title: '操作失败', icon: 'none' }); }
|
||||
}}>
|
||||
<Text className='ai-feedback-btn-text'>采纳</Text>
|
||||
</View>
|
||||
<View className='ai-feedback-btn ai-feedback-ignore' onClick={async () => {
|
||||
try {
|
||||
await submitSuggestionFeedback(s.id, 'ignore');
|
||||
Taro.showToast({ title: '已忽略', icon: 'success' });
|
||||
fetchData();
|
||||
} catch { Taro.showToast({ title: '操作失败', icon: 'none' }); }
|
||||
}}>
|
||||
<Text className='ai-feedback-btn-text'>忽略</Text>
|
||||
</View>
|
||||
<View className='ai-feedback-btn ai-feedback-consult' onClick={async () => {
|
||||
try {
|
||||
await submitSuggestionFeedback(s.id, 'consult');
|
||||
safeNavigateTo('/pages/consultation/index');
|
||||
} catch { Taro.showToast({ title: '操作失败', icon: 'none' }); }
|
||||
}}>
|
||||
<Text className='ai-feedback-btn-text'>咨询医生</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user