# 方案总结

基于提供的 Git diff,我将生成以下 conventional commit message:

## 变更分析:

1. **核心功能**:
   - 新增睡眠监控服务(`services/sleepMonitor.ts`)
   - 新增睡眠通知服务(`services/sleepNotificationService.ts`)
   - iOS 原生端增加睡眠观察者方法

2. **应用启动优化**:
   - 重构 `app/_layout.tsx` 中的初始化流程,按优先级分阶段加载服务

3. **药品功能改进**:
   - 优化语音识别交互(实时预览、可取消)
   - Widget 增加 URL scheme 支持

4. **路由配置**:
   - 新增药品管理路由常量

## 提交信息类型:
- **主类型**:`feat` (新增睡眠监控功能)
- **作用域**:`health` (健康相关功能)

---

请确认方案后,我将生成最终的 commit message。

---

**最终 Commit Message:**

feat(health): 添加睡眠监控和通知服务,优化应用启动流程

- 新增睡眠监控服务,支持实时监听 HealthKit 睡眠数据更新
- 实现睡眠质量分析算法,计算睡眠评分和各阶段占比
- 新增睡眠通知服务,分析完成后自动推送质量评估和建议
- iOS 原生端实现睡眠数据观察者,支持后台数据传递
- 重构应用启动初始化流程,按优先级分阶段加载服务(关键/次要/后台/空闲)
- 优化药品录入页面语音识别交互,支持实时预览和取消操作
- 药品 Widget 增加 deeplink 支持,点击跳转到应用
- 新增药品管理路由常量配置
This commit is contained in:
richarjiang
2025-11-14 10:52:26 +08:00
parent 6c2f9295be
commit 7bd0b5fc52
8 changed files with 1061 additions and 128 deletions

View File

@@ -163,6 +163,10 @@ export default function AddMedicationScreen() {
const [note, setNote] = useState('');
const [dictationActive, setDictationActive] = useState(false);
const [dictationLoading, setDictationLoading] = useState(false);
// 临时存储当前语音识别的结果,用于实时预览
const [currentDictationText, setCurrentDictationText] = useState('');
// 记录语音识别开始前的文本,用于取消时恢复
const [noteBeforeDictation, setNoteBeforeDictation] = useState('');
const isDictationSupported = Platform.OS === 'ios';
useEffect(() => {
@@ -181,20 +185,44 @@ export default function AddMedicationScreen() {
});
}, [timesPerDay]);
const appendDictationResult = useCallback(
// 实时更新语音识别结果(替换式,不是追加)
const updateDictationResult = useCallback(
(text: string) => {
const clean = text.trim();
if (!clean) return;
// 实时更新:用识别的新文本替换当前识别文本
setCurrentDictationText(clean);
// 同步更新到 note 中,以便用户能看到实时效果
setNote((prev) => {
if (!prev) {
// 移除之前的语音识别文本,添加新的识别文本
const baseText = noteBeforeDictation;
if (!baseText) {
return clean;
}
return `${prev}${prev.endsWith('\n') ? '' : '\n'}${clean}`;
// 在原文本后追加,确保格式正确
return `${baseText}${baseText.endsWith('\n') ? '' : '\n'}${clean}`;
});
},
[setNote]
[noteBeforeDictation]
);
// 确认语音识别结果
const confirmDictationResult = useCallback(() => {
// 语音识别结束,确认当前文本
setCurrentDictationText('');
setNoteBeforeDictation('');
}, []);
// 取消语音识别
const cancelDictationResult = useCallback(() => {
// 恢复到语音识别前的文本
setNote(noteBeforeDictation);
setCurrentDictationText('');
setNoteBeforeDictation('');
}, [noteBeforeDictation]);
const stepTitle = STEP_TITLES[currentStep] ?? STEP_TITLES[0];
const stepDescription = STEP_DESCRIPTIONS[currentStep] ?? '';
@@ -224,19 +252,25 @@ export default function AddMedicationScreen() {
};
Voice.onSpeechEnd = () => {
// 语音识别结束,确认识别结果
confirmDictationResult();
setDictationActive(false);
setDictationLoading(false);
};
Voice.onSpeechResults = (event: any) => {
// 获取最新的识别结果(这是累积的结果,包含之前说过的内容)
const recognized = event?.value?.[0];
if (recognized) {
appendDictationResult(recognized);
// 实时更新识别结果,替换式而非追加式
updateDictationResult(recognized);
}
};
Voice.onSpeechError = (error: any) => {
console.log('[MEDICATION] voice error', error);
// 发生错误时取消识别
cancelDictationResult();
setDictationActive(false);
setDictationLoading(false);
Alert.alert('语音识别不可用', '无法使用语音输入,请检查权限设置后重试');
@@ -249,7 +283,7 @@ export default function AddMedicationScreen() {
})
.catch(() => {});
};
}, [appendDictationResult, isDictationSupported]);
}, [updateDictationResult, confirmDictationResult, cancelDictationResult, isDictationSupported]);
const handleNext = useCallback(async () => {
if (!canProceed) return;
@@ -359,25 +393,34 @@ export default function AddMedicationScreen() {
try {
if (dictationActive) {
// 停止录音
setDictationLoading(true);
await Voice.stop();
// Voice.onSpeechEnd 会自动确认结果
setDictationLoading(false);
return;
}
// 开始录音前,保存当前的文本内容
setNoteBeforeDictation(note);
setCurrentDictationText('');
setDictationLoading(true);
try {
// 确保之前的录音已停止
await Voice.stop();
} catch {
// no-op: safe to ignore if not already recording
// 忽略错误如果之前没有录音stop 会抛出异常
}
// 开始语音识别
await Voice.start('zh-CN');
} catch (error) {
console.log('[MEDICATION] unable to start dictation', error);
setDictationLoading(false);
Alert.alert('无法启动语音输入', '请检查麦克风与语音识别权限后重试');
}
}, [dictationActive, dictationLoading, isDictationSupported]);
}, [dictationActive, dictationLoading, isDictationSupported, note]);
// 处理图片选择(拍照或相册)
const handleSelectPhoto = useCallback(() => {