Files
YaoXiang/docs/architecture.md
T
2026-06-15 14:50:15 +08:00

729 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 系统架构设计文档
## 1. 系统概述
家庭药品与应急物资管理系统(YaoXiang)是一个基于 Web 的家庭药品库存管理系统,支持 AI 自动录入、智能搜索、到期提醒等功能。系统采用前后端分离架构,支持 Docker 单容器部署。
## 2. 技术栈
### 前端
- **框架**: React 18+
- **语言**: TypeScript
- **构建工具**: Vite
- **UI 库**: Ant Design Mobile 5.x(移动端优化)
- **状态管理**: Zustand
- **路由**: React Router 6
- **HTTP 客户端**: Axios
- **PWA**: vite-plugin-pwa
### 后端
- **框架**: FastAPI
- **语言**: Python 3.11+
- **ORM**: SQLAlchemy 2.0
- **数据库迁移**: Alembic
- **文件存储**: 本地文件系统
- **任务队列**: 无(采用异步任务)
### 数据库
- **主数据库**: SQLite(可升级 PostgreSQL
- **缓存**: 无(可选 Redis
### AI Provider
- **多模态模型**: OpenAI GPT-4o / Gemini / Claude
- **文本模型**: DeepSeek / Ollama
- **抽象层**: 统一 Provider 接口
### 部署
- **容器化**: Docker
- **编排**: Docker Compose
- **反向代理**: Nginx(前端静态文件)
## 3. 系统架构图
```
┌─────────────────────────────────────────────────────────────┐
│ 客户端层 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 手机浏览器 │ │ 平板浏览器 │ │ PC 浏览器 │ │
│ │ (PWA) │ │ (大屏模式) │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 前端应用层 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ React App │ │ PWA 配置 │ │ 状态管理 │ │
│ │ (Vite) │ │ Service Worker │ │ (Zustand) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 页面组件 │ │ 业务组件 │ │ API 调用层 │ │
│ │ (Router) │ │ (Ant Design)│ │ (Axios) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ API 通信层 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ RESTful API │ │
│ │ POST /api/auth/login │ │
│ │ GET /api/medicines │ │
│ │ POST /api/medicines │ │
│ │ POST /api/medicines/recognize │ │
│ │ ... │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 后端应用层 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ FastAPI │ │ 路由层 │ │ 中间件 │ │
│ │ (Router) │ │ (APIRouter)│ │ (Auth) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 服务层 │ │ 数据访问层 │ │ 模型层 │ │
│ │ (Service) │ │ (Repository)│ │ (Model) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ AI Provider│ │ 通知系统 │ │ 文件存储 │ │
│ │ (Abstract) │ │ (Provider) │ │ (Local) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 数据存储层 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ SQLite │ │ 文件系统 │ │ 缓存 │ │
│ │ (Database) │ │ (Uploads) │ │ (可选) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## 4. 模块划分
### 4.1 前端模块
```
frontend/
├── src/
│ ├── api/ # API 调用层
│ │ ├── client.ts # Axios 实例配置
│ │ ├── auth.ts # 认证相关 API
│ │ ├── medicines.ts # 药品管理 API
│ │ ├── categories.ts # 分类管理 API
│ │ ├── batches.ts # 批次管理 API
│ │ └── notifications.ts # 通知相关 API
│ ├── components/ # 业务组件
│ │ ├── MedicineCard/ # 药品卡片
│ │ ├── BatchForm/ # 批次表单
│ │ ├── CategoryTree/ # 分类树
│ │ └── SearchBar/ # 搜索栏
│ ├── pages/ # 页面组件
│ │ ├── Home/ # 首页(库存概览)
│ │ ├── MedicineList/ # 药品列表
│ │ ├── MedicineDetail/ # 药品详情
│ │ ├── AddMedicine/ # 添加药品
│ │ ├── QuickDispense/ # 快速取药(大屏模式)
│ │ ├── Scanner/ # AI 识别
│ │ ├── Search/ # 搜索页面
│ │ ├── Notifications/ # 通知中心
│ │ ├── Settings/ # 设置页面
│ │ └── Login/ # 登录页面
│ ├── stores/ # 状态管理
│ │ ├── authStore.ts # 认证状态
│ │ ├── medicineStore.ts # 药品状态
│ │ └── uiStore.ts # UI 状态
│ ├── hooks/ # 自定义 Hooks
│ │ ├── useAuth.ts # 认证 Hook
│ │ ├── useMedicine.ts # 药品 Hook
│ │ └── useCamera.ts # 摄像头 Hook
│ ├── utils/ # 工具函数
│ │ ├── date.ts # 日期处理
│ │ ├── storage.ts # 本地存储
│ │ └── validators.ts # 表单验证
│ ├── types/ # TypeScript 类型
│ │ ├── medicine.ts # 药品类型
│ │ ├── batch.ts # 批次类型
│ │ ├── user.ts # 用户类型
│ │ └── api.ts # API 响应类型
│ ├── styles/ # 样式文件
│ │ ├── global.css # 全局样式
│ │ └── variables.css # CSS 变量
│ ├── App.tsx # 根组件
│ ├── main.tsx # 入口文件
│ └── router.tsx # 路由配置
├── public/ # 静态资源
├── index.html # HTML 模板
├── vite.config.ts # Vite 配置
├── tsconfig.json # TypeScript 配置
└── package.json # 依赖配置
```
### 4.2 后端模块
```
backend/
├── app/
│ ├── api/ # 路由层
│ │ ├── v1/ # API 版本
│ │ │ ├── auth.py # 认证路由
│ │ │ ├── medicines.py # 药品路由
│ │ │ ├── batches.py # 批次路由
│ │ │ ├── categories.py # 分类路由
│ │ │ ├── search.py # 搜索路由
│ │ │ ├── notifications.py # 通知路由
│ │ │ ├── ai.py # AI 识别路由
│ │ │ └── users.py # 用户管理路由
│ │ └── router.py # 路由汇总
│ ├── core/ # 核心配置
│ │ ├── config.py # 配置管理
│ │ ├── security.py # 安全工具
│ │ └── deps.py # 依赖注入
│ ├── models/ # 数据模型
│ │ ├── medicine.py # 药品模型
│ │ ├── batch.py # 批次模型
│ │ ├── category.py # 分类模型
│ │ ├── user.py # 用户模型
│ │ ├── notification.py # 通知模型
│ │ └── audit.py # 审计日志模型
│ ├── schemas/ # Pydantic 模型
│ │ ├── medicine.py # 药品 Schema
│ │ ├── batch.py # 批次 Schema
│ │ ├── category.py # 分类 Schema
│ │ ├── user.py # 用户 Schema
│ │ └── auth.py # 认证 Schema
│ ├── services/ # 服务层
│ │ ├── medicine.py # 药品服务
│ │ ├── batch.py # 批次服务
│ │ ├── category.py # 分类服务
│ │ ├── user.py # 用户服务
│ │ ├── auth.py # 认证服务
│ │ ├── notification.py # 通知服务
│ │ └── search.py # 搜索服务
│ ├── repositories/ # 数据访问层
│ │ ├── medicine.py # 药品仓库
│ │ ├── batch.py # 批次仓库
│ │ ├── category.py # 分类仓库
│ │ └── user.py # 用户仓库
│ ├── ai/ # AI Provider
│ │ ├── provider.py # 抽象基类
│ │ ├── openai.py # OpenAI 实现
│ │ ├── gemini.py # Gemini 实现
│ │ ├── claude.py # Claude 实现
│ │ ├── deepseek.py # DeepSeek 实现
│ │ ├── ollama.py # Ollama 实现
│ │ └── manager.py # Provider 管理器
│ ├── notifications/ # 通知系统
│ │ ├── provider.py # 抽象基类
│ │ ├── serverchan.py # Server酱
│ │ ├── pushplus.py # PushPlus
│ │ ├── bark.py # Bark
│ │ ├── wechat.py # 企业微信
│ │ ├── telegram.py # Telegram
│ │ ├── email.py # 邮件
│ │ └── manager.py # 通知管理器
│ ├── storage/ # 文件存储
│ │ ├── local.py # 本地存储
│ │ └── manager.py # 存储管理器
│ ├── tasks/ # 异步任务
│ │ ├── expiry_check.py # 到期检查任务
│ │ └── stock_check.py # 库存检查任务
│ ├── database.py # 数据库连接
│ └── main.py # 应用入口
├── alembic/ # 数据库迁移
│ ├── versions/
│ └── env.py
├── tests/ # 测试文件
├── requirements.txt # 依赖配置
├── alembic.ini # Alembic 配置
├── Dockerfile # Docker 配置
└── .env.example # 环境变量示例
```
## 5. 数据流设计
### 5.1 AI 自动录入流程
```
用户上传图片
┌─────────────────┐
│ 图片预处理 │
│ (压缩/格式化) │
└─────────────────┘
┌─────────────────┐
│ 调用 Vision │
│ Provider │
└─────────────────┘
┌─────────────────┐
│ 返回识别结果 │
│ (JSON) │
└─────────────────┘
┌─────────────────┐
│ 用户确认/编辑 │
└─────────────────┘
┌─────────────────┐
│ 保存到数据库 │
│ + 保存图片 │
└─────────────────┘
```
### 5.2 快速取药流程
```
用户选择药品
┌─────────────────┐
│ 显示药品详情 │
│ (可用批次列表) │
└─────────────────┘
┌─────────────────┐
│ 用户选择批次 │
│ 输入取药数量 │
└─────────────────┘
┌─────────────────┐
│ 扣减库存 │
│ 记录审计日志 │
└─────────────────┘
┌─────────────────┐
│ 检查库存阈值 │
│ 触发通知(可选) │
└─────────────────┘
```
### 5.3 到期提醒流程
```
定时任务触发
┌─────────────────┐
│ 查询即将过期 │
│ 批次 (90/30/7天)│
└─────────────────┘
┌─────────────────┐
│ 生成提醒内容 │
└─────────────────┘
┌─────────────────┐
│ 调用通知系统 │
│ 发送提醒 │
└─────────────────┘
```
## 6. AI Provider 抽象层设计
### 6.1 抽象接口
```python
from abc import ABC, abstractmethod
from typing import Optional
from pydantic import BaseModel
class VisionResult(BaseModel):
"""视觉识别结果"""
generic_name: str
brand_name: Optional[str]
manufacturer: Optional[str]
specification: Optional[str]
class DateResult(BaseModel):
"""日期识别结果"""
production_date: Optional[str]
expiry_date: Optional[str]
class LeafletResult(BaseModel):
"""说明书识别结果"""
indications: str
adult_dose: str
child_dose: Optional[str]
contraindications: str
notes: Optional[str]
class VisionProvider(ABC):
"""视觉模型提供者抽象基类"""
@abstractmethod
async def recognize_medicine(self, image_bytes: bytes) -> VisionResult:
"""识别药盒信息"""
pass
@abstractmethod
async def recognize_dates(self, image_bytes: bytes) -> DateResult:
"""识别日期信息"""
pass
class TextProvider(ABC):
"""文本模型提供者抽象基类"""
@abstractmethod
async def summarize_leaflet(self, text: str) -> LeafletResult:
"""总结说明书内容"""
pass
@abstractmethod
async def natural_language_search(self, query: str, medicines: list) -> list:
"""自然语言搜索"""
pass
```
### 6.2 Provider 管理器
```python
class AIManager:
"""AI Provider 管理器"""
def __init__(self):
self.vision_providers: dict[str, VisionProvider] = {}
self.text_providers: dict[str, TextProvider] = {}
def register_vision_provider(self, name: str, provider: VisionProvider):
"""注册视觉模型提供者"""
self.vision_providers[name] = provider
def register_text_provider(self, name: str, provider: TextProvider):
"""注册文本模型提供者"""
self.text_providers[name] = provider
def get_vision_provider(self, name: str) -> VisionProvider:
"""获取视觉模型提供者"""
return self.vision_providers.get(name)
def get_text_provider(self, name: str) -> TextProvider:
"""获取文本模型提供者"""
return self.text_providers.get(name)
```
## 7. 通知系统设计
### 7.1 通知 Provider 抽象
```python
from abc import ABC, abstractmethod
class NotificationProvider(ABC):
"""通知提供者抽象基类"""
@abstractmethod
async def send(self, title: str, content: str) -> bool:
"""发送通知"""
pass
@abstractmethod
def validate_config(self) -> bool:
"""验证配置"""
pass
```
### 7.2 通知管理器
```python
class NotificationManager:
"""通知管理器"""
def __init__(self):
self.providers: list[NotificationProvider] = []
def add_provider(self, provider: NotificationProvider):
"""添加通知提供者"""
self.providers.append(provider)
async def send_notification(self, title: str, content: str):
"""发送通知到所有提供者"""
for provider in self.providers:
try:
await provider.send(title, content)
except Exception as e:
# 记录错误但不中断
pass
```
## 8. 认证授权设计
### 8.1 用户角色
- **admin**: 管理员,拥有所有权限
- **user**: 普通用户,可查看、添加库存、取药
- **readonly**: 只读用户,仅可查看
### 8.2 权限矩阵
| 功能 | admin | user | readonly |
|------|-------|------|----------|
| 查看药品 | ✓ | ✓ | ✓ |
| 添加药品 | ✓ | ✓ | ✗ |
| 修改药品 | ✓ | ✓ | ✗ |
| 删除药品 | ✓ | ✗ | ✗ |
| 取药 | ✓ | ✓ | ✗ |
| 用户管理 | ✓ | ✗ | ✗ |
| 系统设置 | ✓ | ✗ | ✗ |
| 通知管理 | ✓ | ✓ | ✗ |
### 8.3 JWT 认证流程
```
用户登录
┌─────────────────┐
│ 验证用户名密码 │
└─────────────────┘
┌─────────────────┐
│ 生成 JWT Token │
│ (Access Token) │
└─────────────────┘
┌─────────────────┐
│ 返回 Token │
└─────────────────┘
┌─────────────────┐
│ 前端存储 Token │
│ (localStorage) │
└─────────────────┘
┌─────────────────┐
│ 后续请求携带 │
│ Authorization │
│ Header │
└─────────────────┘
```
## 9. 插件系统设计
### 9.1 插件接口
```python
from abc import ABC, abstractmethod
class Plugin(ABC):
"""插件抽象基类"""
@abstractmethod
def get_name(self) -> str:
"""获取插件名称"""
pass
@abstractmethod
def get_description(self) -> str:
"""获取插件描述"""
pass
@abstractmethod
def initialize(self, app):
"""初始化插件"""
pass
```
### 9.2 插件管理器
```python
class PluginManager:
"""插件管理器"""
def __init__(self):
self.plugins: dict[str, Plugin] = {}
def register_plugin(self, plugin: Plugin):
"""注册插件"""
name = plugin.get_name()
self.plugins[name] = plugin
def get_plugin(self, name: str) -> Plugin:
"""获取插件"""
return self.plugins.get(name)
def list_plugins(self) -> list[str]:
"""列出所有插件"""
return list(self.plugins.keys())
```
## 10. MCP 协议支持
### 10.1 MCP 工具定义
```python
from mcp import Tool
# 查询库存工具
query_inventory_tool = Tool(
name="query_inventory",
description="查询家庭药品库存",
inputSchema={
"type": "object",
"properties": {
"medicine_name": {
"type": "string",
"description": "药品名称"
}
}
}
)
# 取药工具
dispense_medicine_tool = Tool(
name="dispense_medicine",
description="取药操作",
inputSchema={
"type": "object",
"properties": {
"medicine_id": {
"type": "integer",
"description": "药品ID"
},
"quantity": {
"type": "integer",
"description": "取药数量"
}
},
"required": ["medicine_id", "quantity"]
}
)
```
## 11. 部署架构
```
┌─────────────────────────────────────────┐
│ Docker 容器 │
├─────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Nginx │ │ FastAPI │ │
│ │ (静态文件) │ │ (后端) │ │
│ │ :80 │ │ :8000 │ │
│ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ SQLite │ │ 文件存储 │ │
│ │ (数据库) │ │ (图片) │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────┘
```
## 12. 环境变量配置
```bash
# 数据库配置
DATABASE_URL=sqlite:///./data/yaoxiang.db
# AI Provider 配置
AI_PROVIDER=openai
OPENAI_API_KEY=sk-xxx
OPENAI_MODEL=gpt-4o
# 通知配置
NOTIFICATION_PROVIDERS=serverchan,pushplus
SERVERCHAN_KEY=xxx
PUSHPLUS_TOKEN=xxx
# 安全配置
JWT_SECRET_KEY=xxx
JWT_ALGORITHM=HS256
JWT_EXPIRATION_HOURS=24
# 文件存储配置
UPLOAD_DIR=./data/uploads
MAX_UPLOAD_SIZE=10485760 # 10MB
# 应用配置
APP_NAME=药箱
APP_VERSION=1.0.0
DEBUG=false
```
## 13. 开发规范
### 13.1 代码风格
- **Python**: 遵循 PEP 8,使用 Black 格式化
- **TypeScript**: 遵循 ESLint 规则,使用 Prettier 格式化
- **Git**: 使用 Conventional Commits 规范
### 13.2 分支管理
- `main`: 生产分支
- `develop`: 开发分支
- `feature/*`: 功能分支
- `bugfix/*`: 修复分支
- `release/*`: 发布分支
### 13.3 提交规范
```
feat: 新功能
fix: 修复 bug
docs: 文档更新
style: 代码格式调整
refactor: 重构
test: 测试相关
chore: 构建/工具相关
```
## 14. 性能优化
### 14.1 前端优化
- 路由懒加载
- 图片懒加载
- 虚拟列表(长列表优化)
- Service Worker 缓存
### 14.2 后端优化
- 数据库连接池
- 查询优化(索引、分页)
- 异步处理耗时任务
- 响应缓存
## 15. 安全设计
### 15.1 认证安全
- 密码使用 bcrypt 加密存储
- JWT Token 定期轮换
- 登录失败次数限制
### 15.2 数据安全
- 敏感配置使用环境变量
- 文件上传类型验证
- SQL 注入防护(ORM
- XSS 防护(前端)
### 15.3 传输安全
- 支持 HTTPS
- CORS 配置
- 请求限流