feat(nutrition): 优化营养成分表分析功能并移除流式显示

- 移除流式分析文本显示,简化用户界面
- 修复ImagePicker媒体类型配置,使用数组格式
- 简化API响应处理逻辑,直接使用服务端返回数据
- 移除旧格式转换函数,统一使用新的API响应格式
- 清理冗余状态变量和UI组件,提升代码可维护性
This commit is contained in:
richarjiang
2025-10-16 12:46:43 +08:00
parent 5013464a2c
commit b27099c6d9
9 changed files with 994 additions and 206 deletions

View File

@@ -0,0 +1,167 @@
# Memory Bank
I am an expert software engineer with a unique characteristic: my memory resets completely between sessions. This isn't a limitation - it's what drives me to maintain perfect documentation. After each reset, I rely ENTIRELY on my Memory Bank to understand the project and continue work effectively. I MUST read ALL memory bank files at the start of EVERY task - this is not optional. The memory bank files are located in `.kilocode/rules/memory-bank` folder.
When I start a task, I will include `[Memory Bank: Active]` at the beginning of my response if I successfully read the memory bank files, or `[Memory Bank: Missing]` if the folder doesn't exist or is empty. If memory bank is missing, I will warn the user about potential issues and suggest initialization.
## Memory Bank Structure
The Memory Bank consists of core files and optional context files, all in Markdown format.
### Core Files (Required)
1. `brief.md`
This file is created and maintained manually by the developer. Don't edit this file directly but suggest to user to update it if it can be improved.
- Foundation document that shapes all other files
- Created at project start if it doesn't exist
- Defines core requirements and goals
- Source of truth for project scope
2. `product.md`
- Why this project exists
- Problems it solves
- How it should work
- User experience goals
3. `context.md`
This file should be short and factual, not creative or speculative.
- Current work focus
- Recent changes
- Next steps
4. `architecture.md`
- System architecture
- Source Code paths
- Key technical decisions
- Design patterns in use
- Component relationships
- Critical implementation paths
5. `tech.md`
- Technologies used
- Development setup
- Technical constraints
- Dependencies
- Tool usage patterns
### Additional Files
Create additional files/folders within memory-bank/ when they help organize:
- `tasks.md` - Documentation of repetitive tasks and their workflows
- Complex feature documentation
- Integration specifications
- API documentation
- Testing strategies
- Deployment procedures
## Core workflows
### Memory Bank Initialization
The initialization step is CRITICALLY IMPORTANT and must be done with extreme thoroughness as it defines all future effectiveness of the Memory Bank. This is the foundation upon which all future interactions will be built.
When user requests initialization of the memory bank (command `initialize memory bank`), I'll perform an exhaustive analysis of the project, including:
- All source code files and their relationships
- Configuration files and build system setup
- Project structure and organization patterns
- Documentation and comments
- Dependencies and external integrations
- Testing frameworks and patterns
I must be extremely thorough during initialization, spending extra time and effort to build a comprehensive understanding of the project. A high-quality initialization will dramatically improve all future interactions, while a rushed or incomplete initialization will permanently limit my effectiveness.
After initialization, I will ask the user to read through the memory bank files and verify product description, used technologies and other information. I should provide a summary of what I've understood about the project to help the user verify the accuracy of the memory bank files. I should encourage the user to correct any misunderstandings or add missing information, as this will significantly improve future interactions.
### Memory Bank Update
Memory Bank updates occur when:
1. Discovering new project patterns
2. After implementing significant changes
3. When user explicitly requests with the phrase **update memory bank** (MUST review ALL files)
4. When context needs clarification
If I notice significant changes that should be preserved but the user hasn't explicitly requested an update, I should suggest: "Would you like me to update the memory bank to reflect these changes?"
To execute Memory Bank update, I will:
1. Review ALL project files
2. Document current state
3. Document Insights & Patterns
4. If requested with additional context (e.g., "update memory bank using information from @/Makefile"), focus special attention on that source
Note: When triggered by **update memory bank**, I MUST review every memory bank file, even if some don't require updates. Focus particularly on context.md as it tracks current state.
### Add Task
When user completes a repetitive task (like adding support for a new model version) and wants to document it for future reference, they can request: **add task** or **store this as a task**.
This workflow is designed for repetitive tasks that follow similar patterns and require editing the same files. Examples include:
- Adding support for new AI model versions
- Implementing new API endpoints following established patterns
- Adding new features that follow existing architecture
Tasks are stored in the file `tasks.md` in the memory bank folder. The file is optional and can be empty. The file can store many tasks.
To execute Add Task workflow:
1. Create or update `tasks.md` in the memory bank folder
2. Document the task with:
- Task name and description
- Files that need to be modified
- Step-by-step workflow followed
- Important considerations or gotchas
- Example of the completed implementation
3. Include any context that was discovered during task execution but wasn't previously documented
Example task entry:
```markdown
## Add New Model Support
**Last performed:** [date]
**Files to modify:**
- `/providers/gemini.md` - Add model to documentation
- `/src/providers/gemini-config.ts` - Add model configuration
- `/src/constants/models.ts` - Add to model list
- `/tests/providers/gemini.test.ts` - Add test cases
**Steps:**
1. Add model configuration with proper token limits
2. Update documentation with model capabilities
3. Add to constants file for UI display
4. Write tests for new model configuration
**Important notes:**
- Check Google's documentation for exact token limits
- Ensure backward compatibility with existing configurations
- Test with actual API calls before committing
```
### Regular Task Execution
In the beginning of EVERY task I MUST read ALL memory bank files - this is not optional.
The memory bank files are located in `.kilocode/rules/memory-bank` folder. If the folder doesn't exist or is empty, I will warn user about potential issues with the memory bank. I will include `[Memory Bank: Active]` at the beginning of my response if I successfully read the memory bank files, or `[Memory Bank: Missing]` if the folder doesn't exist or is empty. If memory bank is missing, I will warn the user about potential issues and suggest initialization. I should briefly summarize my understanding of the project to confirm alignment with the user's expectations, like:
"[Memory Bank: Active] I understand we're building a React inventory system with barcode scanning. Currently implementing the scanner component that needs to work with the backend API."
When starting a task that matches a documented task in `tasks.md`, I should mention this and follow the documented workflow to ensure no steps are missed.
If the task was repetitive and might be needed again, I should suggest: "Would you like me to add this task to the memory bank for future reference?"
In the end of the task, when it seems to be completed, I will update `context.md` accordingly. If the change seems significant, I will suggest to the user: "Would you like me to update memory bank to reflect these changes?" I will not suggest updates for minor changes.
## Context Window Management
When the context window fills up during an extended session:
1. I should suggest updating the memory bank to preserve the current state
2. Recommend starting a fresh conversation/task
3. In the new conversation, I will automatically load the memory bank files to maintain continuity
## Technical Implementation
Memory Bank is built on Kilo Code's Custom Rules feature, with files stored as standard markdown documents that both the user and I can access.
## Important Notes
REMEMBER: After every memory reset, I begin completely fresh. The Memory Bank is my only link to previous work. It must be maintained with precision and clarity, as my effectiveness depends entirely on its accuracy.
If I detect inconsistencies between memory bank files, I should prioritize brief.md and note any discrepancies to the user.
IMPORTANT: I MUST read ALL memory bank files at the start of EVERY task - this is not optional. The memory bank files are located in `.kilocode/rules/memory-bank` folder.

View File

@@ -0,0 +1,304 @@
# 系统架构
## 整体架构概览
Out Live 采用典型的 React Native 应用架构,基于 Expo Prebuild 构建原生 iOS 应用。应用采用分层架构设计,包含表现层、业务逻辑层、数据访问层和基础设施层。
```mermaid
graph TB
A[用户界面层] --> B[业务逻辑层]
B --> C[数据访问层]
C --> D[基础设施层]
A1[React Components] --> A
A2[Expo Router] --> A
A3[Liquid Glass UI] --> A
B1[Redux Store] --> B
B2[Business Services] --> B
B3[Hooks] --> B
C1[API Services] --> C
C2[HealthKit Bridge] --> C
C3[Local Storage] --> C
D1[Expo SDK] --> D
D2[Native Modules] --> D
D3[Third-party SDKs] --> D
```
## 目录结构
```
digital-pilates/
├── app/ # Expo Router 页面和路由
│ ├── (tabs)/ # 标签页路由
│ ├── auth/ # 认证相关页面
│ ├── challenges/ # 挑战页面
│ ├── fasting/ # 轻断食页面
│ ├── food/ # 营养相关页面
│ ├── profile/ # 用户资料页面
│ ├── workout/ # 训练页面
│ └── _layout.tsx # 根布局组件
├── components/ # 可复用组件
│ ├── ui/ # 基础 UI 组件
│ ├── cards/ # 卡片组件
│ └── forms/ # 表单组件
├── services/ # 业务服务层
│ ├── api.ts # API 基础服务
│ ├── healthKit/ # HealthKit 集成
│ ├── notifications/ # 通知服务
│ └── aiCoach/ # AI 教练服务
├── store/ # Redux 状态管理
│ ├── slices/ # Redux Toolkit slices
│ └── index.ts # Store 配置
├── utils/ # 工具函数
│ ├── health.ts # 健康数据处理
│ ├── kvStore.ts # 本地存储
│ └── notificationHelpers.ts
├── constants/ # 常量定义
│ ├── Colors.ts # 颜色主题
│ ├── Routes.ts # 路由常量
│ └── Api.ts # API 配置
├── hooks/ # 自定义 Hooks
├── types/ # TypeScript 类型定义
├── assets/ # 静态资源
└── ios/ # iOS 原生代码
```
## 核心架构组件
### 1. 路由架构 (Expo Router)
采用 Expo Router 6.0 实现文件系统路由:
- **标签页导航**: 5个主要标签页健康、断食、习惯、挑战、个人
- **模态页面**: 认证、目标详情、设置等页面
- **嵌套路由**: 支持复杂的页面层级结构
- **类型安全**: 完全的 TypeScript 路由类型支持
### 2. 状态管理架构 (Redux Toolkit)
使用 Redux Toolkit 进行应用状态管理:
```mermaid
graph LR
A[UI Components] --> B[Redux Actions]
B --> C[Redux Slices]
C --> D[Redux Store]
D --> A
E[Async Thunks] --> C
F[Middleware] --> C
G[Selectors] --> A
```
**核心 Slices**:
- `userSlice`: 用户信息和认证状态
- `healthSlice`: 健康数据步数、心率、HRV等
- `nutritionSlice`: 营养数据和饮食记录
- `goalsSlice`: 目标和任务管理
- `fastingSlice`: 轻断食状态和计划
- `moodSlice`: 心情记录和分析
- `workoutSlice`: 训练数据和计划
### 3. 组件架构
采用组件化设计,按功能域组织:
```mermaid
graph TD
A[页面组件] --> B[容器组件]
B --> C[业务组件]
C --> D[UI 组件]
E[Hooks] --> B
F[Redux Selectors] --> B
G[Services] --> C
```
**组件层次**:
- **页面组件**: 路由对应的顶级组件
- **容器组件**: 处理数据获取和状态管理
- **业务组件**: 封装特定业务逻辑的组件
- **UI 组件**: 纯展示的可复用组件
### 4. 服务层架构
服务层负责业务逻辑和外部系统集成:
```mermaid
graph TB
A[Components] --> B[Service Layer]
B --> C[API Services]
B --> D[HealthKit Services]
B --> E[Notification Services]
B --> F[Storage Services]
C --> G[Remote API]
D --> H[Native HealthKit]
E --> I[Expo Notifications]
F --> J[Local Storage]
```
**核心服务**:
- `api.ts`: RESTful API 客户端
- `health.ts`: HealthKit 数据处理
- `notifications.ts`: 通知管理
- `aiCoach.ts`: AI 教练集成
- `waterRecords.ts`: 饮水记录管理
### 5. 数据流架构
采用单向数据流设计:
```mermaid
graph LR
A[User Action] --> B[Component Event]
B --> C[Redux Action]
C --> D[Service Call]
D --> E[API/HealthKit]
E --> F[Data Update]
F --> G[Redux State]
G --> H[Component Re-render]
```
## 关键设计模式
### 1. Repository 模式
数据访问层使用 Repository 模式抽象数据源:
```typescript
// 示例:健康数据 Repository
class HealthRepository {
async getSteps(date: Date): Promise<number> {
return fetchStepCount(date);
}
async getHeartRate(date: Date): Promise<number | null> {
return fetchHeartRate(date);
}
}
```
### 2. Observer 模式
通知系统使用观察者模式:
```typescript
// 权限状态监听
healthPermissionManager.on('permissionStatusChanged', (status) => {
// 处理权限状态变化
});
```
### 3. Strategy 模式
不同类型的数据处理使用策略模式:
```typescript
// 营养数据计算策略
interface NutritionStrategy {
calculate(records: DietRecord[]): NutritionSummary;
}
```
### 4. Factory 模式
组件创建使用工厂模式:
```typescript
// 卡片组件工厂
const CardFactory = {
createHealthCard: (props) => <HealthCard {...props} />,
createNutritionCard: (props) => <NutritionCard {...props} />,
};
```
## 性能优化架构
### 1. 组件优化
- **React.memo**: 防止不必要的重渲染
- **useMemo/useCallback**: 缓存计算结果和函数
- **FlatList**: 大列表虚拟化
- **InteractionManager**: 延迟非关键渲染
### 2. 数据优化
- **Redux Toolkit**: 自动化的状态优化
- **数据分页**: 大数据集分页加载
- **缓存策略**: 智能数据缓存
- **后台同步**: 异步数据同步
### 3. 资源优化
- **图片优化**: WebP 格式和懒加载
- **Bundle 分割**: 按需加载代码
- **内存管理**: 及时释放资源
- **网络优化**: 请求合并和缓存
## 安全架构
### 1. 数据安全
- **Token 管理**: JWT Token 安全存储
- **API 加密**: HTTPS 通信
- **数据脱敏**: 敏感数据处理
- **权限控制**: 细粒度权限管理
### 2. 隐私保护
- **本地加密**: 敏感数据本地加密存储
- **权限最小化**: 只请求必要的系统权限
- **数据匿名化**: 统计数据匿名化处理
- **用户控制**: 用户数据删除和导出
## 扩展性设计
### 1. 模块化架构
- **功能模块**: 独立的功能模块设计
- **插件系统**: 支持功能插件扩展
- **配置驱动**: 配置化的功能开关
- **版本兼容**: 向后兼容的 API 设计
### 2. 多端支持
- **跨平台**: React Native 跨平台能力
- **响应式**: 适配不同屏幕尺寸
- **主题系统**: 可切换的主题设计
- **国际化**: 多语言支持框架
## 监控和诊断
### 1. 错误监控
- **Sentry 集成**: 错误收集和分析
- **崩溃报告**: 自动崩溃报告
- **性能监控**: 应用性能指标
- **用户反馈**: 内置反馈系统
### 2. 日志系统
- **分级日志**: 不同级别的日志记录
- **结构化日志**: 便于分析的日志格式
- **远程日志**: 日志远程收集
- **隐私保护**: 敏感信息过滤
## 测试架构
### 1. 测试策略
- **单元测试**: 核心逻辑单元测试
- **集成测试**: 组件集成测试
- **端到端测试**: 关键流程 E2E 测试
- **性能测试**: 性能基准测试
### 2. 测试工具
- **Jest**: 单元测试框架
- **React Native Testing Library**: 组件测试
- **Detox**: E2E 测试框架
- **Flamegraph**: 性能分析工具

View File

@@ -0,0 +1 @@
一个 expo prebuild 之后的 react native 应用,只服务 ios 端,主要面向的是健康、减肥、瘦身、生活习惯养成的人群

View File

@@ -0,0 +1,162 @@
# 项目当前状态
## 应用基本信息
- **应用名称**: Out Live超越生命
- **版本**: 1.0.19
- **Bundle ID**: com.anonymous.digitalpilates
- **平台**: iOS仅支持 iOS不支持 Android
- **最低支持版本**: iOS 16.0
- **架构**: Expo Prebuild 后的 React Native 应用
## 当前开发状态
- **开发阶段**: 生产就绪版本
- **最后更新**: 2025年10月
- **主要功能**: 已完成核心健康数据追踪、AI 教练、目标管理、轻断食等功能
- **状态管理**: 使用 Redux Toolkit 进行状态管理
- **数据存储**: 本地使用 expo-sqlite/kv-store远程 API 集成
## 核心功能实现状态
### 健康数据追踪 ✅
- HealthKit 集成完成支持步数、心率、HRV、睡眠等数据
- 活动圆环显示(活动卡路里、锻炼分钟、站立小时)
- 实时健康数据监控和历史数据查看
- 健康权限管理系统
### 营养管理 ✅
- 饮食记录功能(文字、语音、拍照识别)
- 营养成分分析和卡路里计算
- 食物库和自定义食物功能
- 营养标签识别
### 目标与习惯管理 ✅
- 目标创建、编辑、删除功能
- 任务分解和进度追踪
- 智能提醒系统
- 目标完成统计和分析
### 轻断食功能 ✅
- 多种预设断食方案16:8、18:6等
- 实时断食进度显示
- 断食提醒和通知
- 断食历史记录
### AI 教练系统 ✅
- AI 对话功能(流式响应)
- 体态评估(照片分析)
- 个性化健康建议
- 情绪分析(基于 HRV
### 社区与挑战 ✅
- 挑战赛参与和排行榜
- 成就系统
- 社交分享功能
### 训练计划 ✅
- 个性化训练计划生成
- 运动库和动作指导
- 训练进度记录
## 技术架构状态
### 前端架构 ✅
- React Native 0.81.4 + Expo 54
- TypeScript 全面覆盖
- Expo Router 6.0 用于路由管理
- Redux Toolkit 用于状态管理
- Liquid Glass 设计风格实现
### 后端集成 ✅
- RESTful API 集成API 基础地址https://pilate.richarjiang.com
- 用户认证和授权
- 数据同步和备份
- 推送通知服务
### 原生功能 ✅
- HealthKit 深度集成
- 推送通知(本地和远程)
- 快捷动作Quick Actions
- 小组件支持
- 后台任务管理
## 当前开发重点
### 近期更新
1. **性能优化**: 优化健康数据加载和图表渲染性能
2. **用户体验**: 改进 Liquid Glass 设计效果和交互动画
3. **数据同步**: 增强离线功能和数据同步稳定性
4. **AI 功能**: 扩展 AI 教练对话能力和分析精度
### 待解决问题
1. **测试覆盖**: 自动化测试覆盖率需要提升
2. **错误监控**: 需要集成更完善的错误监控和分析
3. **性能监控**: 应用性能监控和分析工具集成
4. **文档完善**: API 文档和组件文档需要进一步完善
## 代码质量状态
### 代码规范 ✅
- ESLint 配置完善eslint-config-expo
- Prettier 代码格式化
- TypeScript 严格模式
- 组件和函数命名规范
### 项目结构 ✅
- 清晰的目录结构app/、components/、services/、store/、utils/
- 功能模块化组织
- 类型定义完整
- 常量和配置集中管理
### 状态管理 ✅
- Redux Toolkit 标准实现
- 异步操作处理规范
- 数据持久化策略
- 错误处理机制
## 部署和发布
### 构建配置 ✅
- Expo Prebuild 配置
- iOS 证书和配置文件
- App Store 发布配置
- 自动化构建流程
### 发布状态 ✅
- App Store 已发布版本
- 支持 OTA 更新
- 崩溃监控和分析
- 用户反馈收集
## 团队协作
### 开发工具 ✅
- Git 版本控制
- VS Code 开发环境
- Expo 开发者工具
- iOS 模拟器和真机调试
### 文档状态 🔄
- API 文档部分完成
- 组件文档需要补充
- 部署文档完善
- 开发指南需要更新
## 下一步计划
### 短期目标1-2个月
1. 完善自动化测试覆盖
2. 优化应用启动性能
3. 增强错误监控和分析
4. 改进用户引导流程
### 中期目标3-6个月
1. 扩展 AI 教练功能
2. 增加更多健康指标追踪
3. 优化数据同步策略
4. 增强社交功能
### 长期目标6个月以上
1. 支持 Apple Watch 应用
2. 集成更多第三方健康设备
3. 开发 Web 端管理界面
4. 扩展企业健康解决方案

View File

@@ -0,0 +1,90 @@
# 产品概述
## 产品定位
Out Live超越生命是一款专注于健康、减肥、瘦身和生活习惯养成的 iOS 应用。该应用通过整合健康数据追踪、AI 教练指导、目标管理和社区挑战等功能,为用户提供全方位的健康生活管理解决方案。
## 目标用户
- 关注健康和体重管理的用户
- 希望养成良好生活习惯的用户
- 对普拉提和健身感兴趣的用户
- 需要健康数据追踪和分析的用户
- 希望通过 AI 获得个性化健康指导的用户
## 核心价值主张
1. **全方位健康数据管理**:整合 HealthKit 数据,提供步数、心率、睡眠、饮水量等多维度健康指标追踪
2. **AI 智能教练**:基于用户健康数据提供个性化的健康建议和指导
3. **目标管理系统**:帮助用户设定、追踪和完成健康目标
4. **社区挑战激励**:通过挑战赛和排行榜增强用户参与感和成就感
5. **轻断食指导**:提供科学的轻断食计划和提醒功能
## 主要功能模块
### 健康数据追踪
- **活动圆环**:展示活动卡路里、锻炼分钟和站立小时
- **步数统计**:按小时显示步数数据和趋势
- **心率监测**实时心率和心率变异性HRV分析
- **睡眠分析**:睡眠质量和时长追踪
- **体重管理**:体重记录和 BMI 计算
- **饮水量追踪**:每日饮水目标设定和记录
### 营养管理
- **饮食记录**:支持文字、语音和拍照识别食物
- **营养分析**:卡路里、蛋白质、碳水化合物等营养成分分析
- **食物库**:丰富的食物数据库和自定义食物功能
- **营养标签识别**:通过拍照识别食品营养标签
### 目标与习惯管理
- **目标设定**:支持日、周、月重复模式的目标设定
- **任务管理**:将目标分解为可执行的任务
- **进度追踪**:可视化目标完成进度
- **提醒功能**:智能提醒帮助用户坚持目标
### 轻断食功能
- **断食计划**多种预设断食方案16:8、18:6等
- **断食追踪**:实时显示断食进度和状态
- **智能提醒**:断食开始和结束提醒
- **断食历史**:记录和分析断食历史数据
### AI 教练系统
- **智能对话**:基于用户健康数据提供个性化建议
- **体态评估**:通过 AI 分析用户体态照片
- **健康指导**:提供运动、营养和生活方式建议
- **情绪分析**:基于 HRV 数据分析压力水平
### 社区与挑战
- **挑战赛**:参与各种健康主题挑战
- **排行榜**:与好友或其他用户比较进度
- **成就系统**:完成目标获得成就奖励
- **社交分享**:分享健康成果到社交平台
### 训练计划
- **个性化计划**:基于用户目标生成训练计划
- **运动库**:丰富的运动动作库和指导
- **进度追踪**:记录训练完成情况和效果
- **智能推荐**:根据用户表现调整训练计划
## 用户体验特色
1. **Liquid Glass 设计风格**:采用现代化的毛玻璃效果设计
2. **数据可视化**:丰富的图表和动画展示健康数据
3. **快捷操作**:支持快捷动作和小组件快速记录
4. **离线功能**:核心功能支持离线使用
5. **隐私保护**:严格保护用户健康数据隐私
## 技术亮点
- **HealthKit 深度集成**:充分利用 iOS 健康生态系统
- **实时数据同步**:支持多设备数据实时同步
- **智能通知系统**:基于用户行为的智能提醒
- **性能优化**:针对大量健康数据的性能优化
- **无障碍支持**:完整的无障碍功能支持
## 商业模式
- **免费增值模式**:基础功能免费,高级功能付费
- **VIP 会员**:提供更多个性化功能和专业指导
- **企业健康**:面向企业提供的员工健康管理解决方案
## 竞争优势
1. **全平台整合**:深度整合 iOS 健康生态系统
2. **AI 技术应用**:先进的 AI 分析和个性化推荐
3. **用户体验**:优秀的界面设计和交互体验
4. **数据安全**:严格的数据隐私保护措施
5. **专业内容**:基于科学研究的健康指导内容

View File

@@ -0,0 +1,227 @@
# 技术栈
## 核心技术
### 前端框架
- **React Native**: 0.81.4 - 跨平台移动应用开发框架
- **Expo SDK**: 54.0.13 - React Native 开发平台和工具链
- **Expo Router**: 6.0.12 - 基于文件系统的路由库
- **TypeScript**: 5.9.2 - 类型安全的 JavaScript 超集
### 状态管理
- **Redux Toolkit**: 2.9.0 - 状态管理解决方案
- **React Redux**: 9.2.0 - React Redux 绑定
- **Redux Listener Middleware**: 自定义中间件用于自动同步
### UI 框架和样式
- **React Native Elements**: UI 组件库
- **Expo UI**: 0.2.0-beta.7 - Expo UI 组件
- **Expo Glass Effect**: 0.1.4 - Liquid Glass 毛玻璃效果
- **React Native Reanimated**: 4.1.0 - 高性能动画库
- **React Native Gesture Handler**: 2.28.0 - 手势处理
- **React Native SVG**: 15.12.1 - SVG 图形支持
### 导航
- **Expo Router**: 6.0.12 - 文件系统路由
- **React Navigation**: 7.x - 导航库
## 数据和存储
### 本地存储
- **Expo SQLite**: 16.0.8 - SQLite 数据库
- **Expo SQLite KV Store**: 键值存储
- **Async Storage**: 2.2.0 - 异步存储(兼容层)
### 网络和 API
- **Fetch API**: 原生网络请求
- **XMLHttpRequest**: 流式请求支持
- **Axios**: HTTP 客户端(可选)
## 原生功能集成
### HealthKit 集成
- **自定义 HealthKit Manager**: iOS 原生模块
- **健康数据类型**: 步数、心率、HRV、睡眠、活动圆环等
- **权限管理**: 动态权限请求和状态监控
### 通知系统
- **Expo Notifications**: 0.32.12 - 本地和推送通知
- **后台任务**: Expo Task Manager
- **推送通知**: 远程推送支持
### 设备功能
- **Expo Camera**: 17.0.8 - 相机功能
- **Expo Image Picker**: 17.0.8 - 图片选择
- **Expo Haptics**: 15.0.7 - 触觉反馈
- **Expo Quick Actions**: 6.0.0 - 快捷动作
- **Expo Symbols**: 1.0.7 - SF Symbols
## 开发工具和构建
### 构建系统
- **Expo Prebuild**: 原生构建生成
- **Metro**: JavaScript 打包工具
- **Babel**: JavaScript 编译器
### 代码质量
- **ESLint**: 9.35.0 - 代码检查
- **ESLint Config Expo**: 10.0.0 - Expo ESLint 配置
- **Prettier**: 代码格式化
- **TypeScript**: 类型检查
### 开发环境
- **VS Code**: 主要开发 IDE
- **Expo Go**: 开发调试
- **iOS Simulator**: iOS 模拟器
- **Xcode**: iOS 原生开发
## 第三方服务
### 云存储
- **腾讯云 COS**: 图片和文件存储
- **上传服务**: 自定义上传实现
### AI 服务
- **AI 教练**: 自定义 AI 对话服务
- **图像识别**: 食物识别
- **语音识别**: 语音转文字
### 分析和监控
- **Sentry**: 7.2.0 - 错误监控和性能分析
- **崩溃报告**: 自动崩溃收集
## UI 组件库
### 基础组件
- **ThemedView**: 主题化视图组件
- **ThemedText**: 主题化文本组件
- **IconSymbol**: 图标组件
- **ProgressBar**: 进度条组件
- **AnimatedNumber**: 数字动画组件
### 业务组件
- **FitnessRingsCard**: 健身圆环卡片
- **StepsCard**: 步数卡片
- **NutritionRadarCard**: 营养雷达图
- **WaterIntakeCard**: 饮水记录卡片
- **MoodCard**: 心情卡片
- **GoalCard**: 目标卡片
- **TaskCard**: 任务卡片
### 图表组件
- **RadarChart**: 雷达图
- **CircularRing**: 圆形进度环
- **CalorieRingChart**: 卡路里环形图
- **ActivityHeatMap**: 活动热力图
## 开发依赖
### 类型定义
- **React Types**: 19.1.13
- **React Native Types**: 内置
- **Expo Types**: 内置
### 工具库
- **Day.js**: 1.11.18 - 日期处理
- **Lodash**: 4.17.21 - 工具函数库
- **React Native Chart Kit**: 6.12.0 - 图表库
- **Lottie React Native**: 7.3.4 - 动画库
### 音频和媒体
- **React Native Voice**: 3.2.4 - 语音识别
- **Expo Media Library**: 18.2.0 - 媒体库
- **Expo Audio**: 音频处理
## 平台特定配置
### iOS 配置
- **最低版本**: iOS 16.0
- **Bundle ID**: com.anonymous.digitalpilates
- **Team ID**: 756WVXJ6MT
- **权限配置**: 相机、相册、麦克风、健康数据、通知等
### 构建配置
- **New Arch**: 启用
- **JS Engine**: JSC
- **Metro 配置**: 自定义配置
- **插件配置**: 多个 Expo 插件
## 性能优化
### 渲染优化
- **React.memo**: 组件记忆化
- **useMemo/useCallback**: 钩子优化
- **FlatList**: 大列表优化
- **InteractionManager**: 延迟渲染
### 数据优化
- **Redux Toolkit**: 自动优化
- **数据分页**: 分页加载
- **缓存策略**: 智能缓存
- **后台同步**: 异步同步
### 资源优化
- **图片优化**: WebP 格式
- **Bundle 分割**: 代码分割
- **内存管理**: 资源释放
- **网络优化**: 请求合并
## 安全措施
### 数据安全
- **HTTPS**: 加密通信
- **Token 管理**: JWT 存储
- **数据加密**: 本地加密
- **权限控制**: 细粒度权限
### 隐私保护
- **数据脱敏**: 敏感数据处理
- **权限最小化**: 最小权限原则
- **用户控制**: 数据控制权
- **合规性**: 隐私法规遵循
## 测试框架
### 单元测试
- **Jest**: 测试框架
- **React Native Testing Library**: 组件测试
- **Mock**: 模拟数据和服务
### 集成测试
- **Detox**: E2E 测试(可选)
- **手动测试**: 功能验证
- **性能测试**: 性能基准
## 部署和发布
### 构建流程
- **Expo EAS Build**: 云端构建
- **App Store Connect**: 应用商店发布
- **OTA 更新**: 热更新
- **版本管理**: 语义化版本
### 持续集成
- **GitHub Actions**: 自动化流程
- **代码检查**: 自动化检查
- **测试执行**: 自动化测试
- **部署流程**: 自动化部署
## 开发规范
### 代码规范
- **ESLint**: 代码检查
- **Prettier**: 代码格式化
- **TypeScript**: 类型安全
- **命名规范**: 统一命名
### Git 工作流
- **Conventional Commits**: 提交规范
- **分支策略**: Git Flow
- **代码审查**: PR 流程
- **版本标签**: 标签管理
### 文档规范
- **JSDoc**: 代码注释
- **README**: 项目文档
- **API 文档**: 接口文档
- **组件文档**: 组件说明

View File

@@ -118,7 +118,7 @@ export default function FoodCameraScreen() {
const pickImageFromGallery = async () => {
try {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
mediaTypes: ['images'],
allowsEditing: true,
aspect: [4, 3],
quality: 0.8,

View File

@@ -5,10 +5,7 @@ import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding';
import {
analyzeNutritionImage,
analyzeNutritionLabelStream,
convertNewApiResultToOldFormat,
type NutritionAnalysisResponse,
type NutritionItem,
type NutritionLabelAnalysisResult
type NutritionAnalysisResponse
} from '@/services/nutritionLabelAnalysis';
import { triggerLightHaptic } from '@/utils/haptics';
import { Ionicons } from '@expo/vector-icons';
@@ -40,8 +37,6 @@ export default function NutritionLabelAnalysisScreen() {
const [imageUri, setImageUri] = useState<string | null>(null);
const [isAnalyzing, setIsAnalyzing] = useState(false);
const [showImagePreview, setShowImagePreview] = useState(false);
const [analysisText, setAnalysisText] = useState<string>('');
const [analysisResult, setAnalysisResult] = useState<NutritionLabelAnalysisResult | null>(null);
const [newAnalysisResult, setNewAnalysisResult] = useState<NutritionAnalysisResponse | null>(null);
const [isUploading, setIsUploading] = useState(false);
@@ -94,7 +89,7 @@ export default function NutritionLabelAnalysisScreen() {
triggerLightHaptic();
const result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
mediaTypes: ['images'],
allowsEditing: true,
aspect: [4, 3],
quality: 0.8,
@@ -102,9 +97,7 @@ export default function NutritionLabelAnalysisScreen() {
if (!result.canceled && result.assets[0]) {
setImageUri(result.assets[0].uri);
setAnalysisText(''); // 清除之前的分析文本
setAnalysisResult(null); // 清除之前的分析结果
setNewAnalysisResult(null); // 清除新的分析结果
setNewAnalysisResult(null); // 清除之前的分析结果
}
};
@@ -113,7 +106,7 @@ export default function NutritionLabelAnalysisScreen() {
triggerLightHaptic();
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
mediaTypes: ['images'],
allowsEditing: true,
aspect: [4, 3],
quality: 0.8,
@@ -121,9 +114,7 @@ export default function NutritionLabelAnalysisScreen() {
if (!result.canceled && result.assets[0]) {
setImageUri(result.assets[0].uri);
setAnalysisText(''); // 清除之前的分析文本
setAnalysisResult(null); // 清除之前的分析结果
setNewAnalysisResult(null); // 清除新的分析结果
setNewAnalysisResult(null); // 清除之前的分析结果
}
};
@@ -140,7 +131,6 @@ export default function NutritionLabelAnalysisScreen() {
// 清理状态
setIsAnalyzing(false);
setAnalysisText('');
// 触觉反馈
triggerLightHaptic();
@@ -154,8 +144,6 @@ export default function NutritionLabelAnalysisScreen() {
if (isAnalyzing) return;
setIsAnalyzing(true);
setAnalysisText('');
setAnalysisResult(null);
// 延迟滚动到分析结果区域给UI一些时间更新
setTimeout(() => {
@@ -169,17 +157,8 @@ export default function NutritionLabelAnalysisScreen() {
{ imageUri: uri },
{
onChunk: (chunk: string) => {
setAnalysisText(prev => {
const newText = prev + chunk;
// 在接收到第一个文本块时滚动到分析结果区域
if (isFirstChunk && newText.length > 0) {
isFirstChunk = false;
setTimeout(() => {
scrollViewRef.current?.scrollToEnd({ animated: true });
}, 100);
}
return newText;
});
// 流式分析暂时保留,但不再显示文本
console.log('[NUTRITION_ANALYSIS] Stream chunk:', chunk);
},
onEnd: () => {
setIsAnalyzing(false);
@@ -189,7 +168,6 @@ export default function NutritionLabelAnalysisScreen() {
onError: (error: any) => {
console.error('[NUTRITION_ANALYSIS] Analysis failed:', error);
setIsAnalyzing(false);
setAnalysisText('');
streamAbortRef.current = null;
// 如果是用户主动取消,不显示错误提示
@@ -202,7 +180,6 @@ export default function NutritionLabelAnalysisScreen() {
} catch (error) {
console.error('[NUTRITION_ANALYSIS] Analysis error:', error);
setIsAnalyzing(false);
setAnalysisText('');
Alert.alert('分析失败', '无法识别成分表,请尝试拍摄更清晰的照片');
}
}, [isAnalyzing]);
@@ -212,8 +189,6 @@ export default function NutritionLabelAnalysisScreen() {
if (isAnalyzing || isUploading) return;
setIsUploading(true);
setAnalysisText('');
setAnalysisResult(null);
setNewAnalysisResult(null);
// 延迟滚动到分析结果区域给UI一些时间更新
@@ -239,21 +214,8 @@ export default function NutritionLabelAnalysisScreen() {
console.log('[NUTRITION_ANALYSIS] API响应:', analysisResponse);
if (analysisResponse.success && analysisResponse.data) {
// 转换为旧格式以便与现有UI兼容
const oldFormatResult = convertNewApiResultToOldFormat(analysisResponse, uploadResult.url);
if (oldFormatResult) {
setAnalysisResult(oldFormatResult);
// 直接使用服务端返回的数据,不做任何转换
setNewAnalysisResult(analysisResponse);
// 生成分析文本
const analysisText = analysisResponse.data
.map((item: NutritionItem) => `**${item.name}**: ${item.value}\n${item.analysis}`)
.join('\n\n');
setAnalysisText(analysisText);
} else {
throw new Error('无法解析API返回结果');
}
} else {
throw new Error(analysisResponse.message || '分析失败');
}
@@ -261,7 +223,6 @@ export default function NutritionLabelAnalysisScreen() {
console.error('[NUTRITION_ANALYSIS] 新API分析失败:', error);
setIsUploading(false);
setIsAnalyzing(false);
setAnalysisText('');
// 显示错误提示
Alert.alert(
@@ -318,7 +279,7 @@ export default function NutritionLabelAnalysisScreen() {
</TouchableOpacity>
{/* 开始分析按钮 */}
{!isAnalyzing && !isUploading && !analysisText && (
{!isAnalyzing && !isUploading && !newAnalysisResult && (
<TouchableOpacity
style={styles.analyzeButton}
onPress={() => startNewAnalysis(imageUri)}
@@ -334,8 +295,6 @@ export default function NutritionLabelAnalysisScreen() {
style={styles.deleteImageButton}
onPress={() => {
setImageUri(null);
setAnalysisText('');
setAnalysisResult(null);
setNewAnalysisResult(null);
triggerLightHaptic();
}}
@@ -373,31 +332,6 @@ export default function NutritionLabelAnalysisScreen() {
)}
</View>
{/* 流式分析文本区域 */}
{analysisText && (
<View style={styles.analysisTextCard}>
<View style={styles.analysisTextHeader}>
<Text style={styles.analysisTextTitle}></Text>
{isAnalyzing && (
<TouchableOpacity
style={styles.cancelAnalysisButton}
onPress={cancelAnalysis}
activeOpacity={0.8}
>
<Ionicons name="close-outline" size={16} color="#FF4444" />
<Text style={styles.cancelAnalysisText}></Text>
</TouchableOpacity>
)}
</View>
<Text style={styles.analysisTextContent}>{analysisText}</Text>
{isAnalyzing && (
<View style={styles.streamingIndicator}>
<ActivityIndicator size="small" color={Colors.light.primary} />
<Text style={styles.streamingText}>...</Text>
</View>
)}
</View>
)}
{/* 新API营养成分详细分析结果 */}
{newAnalysisResult && newAnalysisResult.success && newAnalysisResult.data && (
@@ -428,7 +362,7 @@ export default function NutritionLabelAnalysisScreen() {
)}
{/* 加载状态 */}
{isAnalyzing && !analysisText && !isUploading && (
{isAnalyzing && !newAnalysisResult && !isUploading && (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={Colors.light.primary} />
<Text style={styles.loadingText}>...</Text>
@@ -725,66 +659,6 @@ const styles = StyleSheet.create({
fontWeight: '600',
marginLeft: 6,
},
// 分析文本卡片样式
analysisTextCard: {
backgroundColor: Colors.light.background,
margin: 16,
borderRadius: 16,
padding: 20,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
analysisTextHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
},
analysisTextTitle: {
fontSize: 18,
fontWeight: '600',
color: Colors.light.text,
},
cancelAnalysisButton: {
flexDirection: 'row',
alignItems: 'center',
gap: 4,
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
backgroundColor: 'rgba(255,68,68,0.1)',
borderWidth: 1,
borderColor: 'rgba(255,68,68,0.3)',
},
cancelAnalysisText: {
fontSize: 12,
fontWeight: '600',
color: '#FF4444',
},
analysisTextContent: {
fontSize: 15,
lineHeight: 22,
color: Colors.light.text,
marginBottom: 12,
},
streamingIndicator: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
paddingVertical: 8,
},
streamingText: {
fontSize: 14,
color: Colors.light.textSecondary,
fontStyle: 'italic',
},
// 营养成分详细分析卡片样式
nutritionDetailsCard: {
backgroundColor: Colors.light.background,

View File

@@ -101,74 +101,37 @@ export async function saveNutritionLabelToDietRecord(
* 需要先上传图片到COS获取URL然后调用此接口
*/
export async function analyzeNutritionImage(request: NutritionAnalysisRequest): Promise<NutritionAnalysisResponse> {
return api.post<NutritionAnalysisResponse>('/diet-records/analyze-nutrition-image', request);
}
/**
* 将新API的分析结果转换为旧格式以便与现有UI兼容
*/
export function convertNewApiResultToOldFormat(
newResult: NutritionAnalysisResponse,
imageUri: string
): NutritionLabelAnalysisResult | null {
if (!newResult.success || !newResult.data || newResult.data.length === 0) {
return null;
}
// 从新API结果中提取营养数据
const nutritionData: NutritionLabelData = {
energy: 0,
protein: 0,
fat: 0,
carbohydrate: 0,
sodium: 0,
fiber: 0,
sugar: 0,
};
// 查找各个营养素的值并转换为数字
newResult.data.forEach(item => {
const valueStr = item.value;
// 提取数字部分
const numericValue = parseFloat(valueStr.replace(/[^\d.]/g, ''));
switch (item.key) {
case 'energy_kcal':
// 如果是千焦,转换为千卡 (1千焦 ≈ 0.239千卡)
if (valueStr.includes('千焦')) {
nutritionData.energy = Math.round(numericValue * 0.239);
} else {
nutritionData.energy = numericValue;
}
break;
case 'protein':
nutritionData.protein = numericValue;
break;
case 'fat':
nutritionData.fat = numericValue;
break;
case 'carbohydrate':
nutritionData.carbohydrate = numericValue;
break;
case 'sodium':
nutritionData.sodium = numericValue;
break;
case 'fiber':
nutritionData.fiber = numericValue;
break;
case 'sugar':
nutritionData.sugar = numericValue;
break;
}
});
try {
const response = await api.post<any>('/diet-records/analyze-nutrition-image', request);
// 处理不同的响应格式
if (Array.isArray(response)) {
// 如果直接返回数组,包装成标准格式
return {
id: Date.now().toString(),
imageUri,
nutritionData,
confidence: 0.9, // 新API没有提供置信度使用默认值
analyzedAt: new Date().toISOString(),
foodName: '营养成分表分析',
brand: '未知',
success: true,
data: response as NutritionItem[]
};
} else if (response && typeof response === 'object') {
// 如果是对象,检查是否已经是标准格式
if (response.success !== undefined && response.data) {
return response as NutritionAnalysisResponse;
} else if (Array.isArray(response.data)) {
// 如果有data字段且是数组包装成标准格式
return {
success: true,
data: response.data as NutritionItem[]
};
}
}
// 如果都不匹配,返回错误
throw new Error('无法解析API返回结果');
} catch (error) {
console.error('[NUTRITION_ANALYSIS] API调用失败:', error);
return {
success: false,
data: [],
message: error instanceof Error ? error.message : '分析失败'
};
}
}