14 KiB
“食话食说”项目设计文档
文档摘要
本文档是“食话食说”项目的综合技术设计与分析报告。通过对项目前后端代码库的系统性梳理,本文档详细记录了项目的整体架构、前后端技术栈、数据库模型、API接口规范、前端路由与状态管理模式,以及核心的用户交互流程。旨在为项目的后续开发、维护和团队协作提供一份清晰、准确、全面的技术参考。
目录
1. 项目总体分析
1.1. 项目简介
本文档旨在对“食话食说”项目进行全面的技术分析和设计记录。项目采用前后端分离架构,前端基于 Vue.js 3 构建用户界面,后端使用 FastAPI 提供 API 服务,实现了包括用户认证、食品信息查询、扫码识别等核心功能。
1.2. 技术栈
1.2.1. 前端 (shihuashishuo-ui)
- 核心框架: Vue.js 3 (
vue: ^3.5.17
) - 路由管理: Vue Router (
vue-router: ^4.5.1
) - 状态管理: Pinia (
pinia: ^3.0.3
) - 开发与构建工具: Vite (
vite: npm:rolldown-vite@^7.0.9
) - 编程语言: TypeScript (
typescript: ~5.8.0
) - 单元测试: Vitest (
vitest: ^3.2.4
) - 端到端测试: Playwright (
@playwright/test: ^1.53.1
) - 代码规范与格式化: ESLint, Prettier, Oxlint
1.2.2. 后端 (shihuashishuo-api)
- 核心框架: FastAPI
- Web 服务器: Uvicorn
- ORM (对象关系映射): SQLAlchemy
- 数据校验: Pydantic
- 安全与认证: python-jose[cryptography], passlib, bcrypt
- 文件上传支持: python-multipart
- 数据库: SQLite (根据
shihuashishuo.db
文件推断)
2. 后端 (API) 设计
2.1. 数据库模型 (SQLAlchemy)
数据库模型定义在 shihuashishuo-api/models.py
中,使用 SQLAlchemy ORM。
表名 (__tablename__ ) |
模型类 (class ) |
描述 | 关键字段 |
---|---|---|---|
users |
User |
存储用户信息 | id , phone_number , hashed_password , avatar_url |
user_preferences |
UserPreference |
存储用户的个人偏好,如过敏原、健康状况等 | id , category , value , user_id |
foods |
Food |
存储食品的基本信息 | id , barcode , name , brand , ingredients |
search_history |
SearchHistory |
记录用户的搜索历史 | id , query , user_id |
2.2. API 数据结构 (Pydantic Schemas)
API的数据输入输出规范定义在 shihuashishuo-api/schemas.py
中,使用 Pydantic 模型进行数据校验和序列化。
- 用户相关 (
User*
):UserBase
: 用户基础信息(手机号、头像)。UserCreate
: 用于创建用户,包含密码。UserCodeLogin
: 用于验证码登录。UserPasswordLogin
: 用于密码登录。User
: API返回的用户信息,包含id和创建时间。PasswordSet
: 用于设置或修改密码。
- 认证相关 (
Token
):Token
: 登录成功后返回的access_token
。
- 食品相关 (
Food*
):FoodBase
: 食品基础信息。FoodCreate
: 用于创建食品条目。Food
: API返回的食品信息。
- 用户偏好相关 (
UserPreference*
,OnboardingPreferences
):UserPreferenceBase
: 用户偏好基础信息。UserPreferenceCreate
: 用于创建用户偏好。UserPreference
: API返回的用户偏好信息。OnboardingPreferences
: 用于处理引导流程中用户提交的批量偏好。
- 搜索历史相关 (
SearchHistory*
):SearchHistoryBase
: 搜索历史基础信息。SearchHistoryCreate
: 用于创建搜索历史。SearchHistory
: API返回的搜索历史信息。
2.3. API 端点 (Endpoints)
API 的主要逻辑和路由定义在 shihuashishuo-api/main.py
中。
2.3.1. 认证路由 (/api/v1/auth
)
方法 | 路径 | 函数名 | 描述 | 是否需要认证 |
---|---|---|---|---|
POST |
/send-verification-code |
send_verification_code |
发送手机验证码(当前为模拟) | 否 |
POST |
/login |
login |
使用手机和验证码登录或注册 | 否 |
POST |
/login/password |
login_with_password |
使用手机和密码登录 | 否 |
PUT |
/users/me/password |
set_password_for_current_user |
为当前登录用户设置密码 | 是 |
2.3.2. 其他核心路由
方法 | 路径 | 函数名 | 描述 | 是否需要认证 |
---|---|---|---|---|
GET |
/ |
read_root |
API 根路径,返回欢迎信息 | 否 |
GET |
/api/v1/users/check-phone-existence/ |
check_phone_existence |
检查手机号是否已注册 | 否 |
POST |
/api/v1/food/ |
create_food_entry |
创建新的食品条目 | 否 |
GET |
/api/v1/food/{barcode} |
read_food_by_barcode |
根据条形码查询食品信息 | 否 |
POST |
/api/v1/users/me/preferences |
update_user_preferences |
为当前用户批量更新偏好设置 | 是 |
POST |
/api/v1/search/history |
create_search_history_entry |
创建一条搜索历史记录 | 可选 |
2.4. 业务逻辑层 (CRUD)
数据库的增、删、改、查(CRUD)操作被封装在 shihuashishuo-api/crud.py
文件中,供 API 路由调用。
get_user_by_phone_number
: 根据手机号查询用户。create_user
: 创建新用户,如果提供了密码,则进行哈希存储。authenticate_user
: 验证用户密码是否正确。set_user_password
: 为指定用户设置或更新密码。get_food_by_barcode
: 根据条形码查询食品。create_food
: 创建新的食品条目。create_user_preferences
: 批量创建用户偏好,会先删除旧记录。create_search_history
: 创建搜索历史记录。
2.5. 安全与认证机制
项目的认证与安全相关功能定义在 shihuashishuo-api/security.py
中。
- 认证方式: 基于 JWT (JSON Web Tokens) 的 Bearer Token 认证。
- Token 生成:
- 使用
python-jose
库生成 HS256 签名的 JWT。 - 访问令牌默认有效期为 30 分钟 (
ACCESS_TOKEN_EXPIRE_MINUTES
)。
- 使用
- 密码处理:
- 使用
passlib
和bcrypt
算法对用户密码进行哈希处理和验证。
- 使用
- 依赖注入:
get_current_user
: 一个 FastAPI 依赖项,用于从请求头中解析 Token,验证并获取当前登录的用户信息。需要认证的路由都会依赖此函数。get_current_user_optional
:get_current_user
的可选版本,在没有提供 Token 时返回None
而非抛出异常。
- 安全风险提示:
SECRET_KEY
目前是硬编码在代码中的,在生产环境中应使用环境变量等更安全的方式进行管理。
3. 前端 (UI) 设计
3.1. 路由管理 (Vue Router)
前端路由配置在 shihuashishuo-ui/src/router/index.ts
中,负责管理页面导航。
- 路由模式: HTML5 History 模式 (
createWebHistory
)。 - 主要路由分组:
- 通用基础页: 包含闪屏页 (
/splash
)、登录页 (/login
)、引导页 (/onboarding
) 等独立页面。 - 主应用布局 (
/app
): 包含一个带有底部导航栏的MainLayout
,嵌套了首页 (/home
)、发现 (/discover
)、我的 (/me
) 等核心功能页面。 - 独立功能页: 包含扫码页 (
/scan
)、搜索页 (/search
)、结果页 (/result/:id
) 等无底部导航栏的页面。
- 通用基础页: 包含闪屏页 (
- 全局路由守卫 (
router.beforeEach
):- 认证检查: 保护需要登录才能访问的页面,未登录用户会被重定向到登录页。
- 引导流程检查: 已登录但未完成引导流程的用户会被强制导向引导页。
- 状态一致性: 防止已登录用户重复访问登录页或引导页。
3.2. 状态管理 (Pinia)
全局状态管理由 Pinia 实现,核心是 shihuashishuo-ui/src/stores/auth.ts
。
- 持久化: 关键认证状态(如
token
,isLoggedIn
)通过localStorage
进行持久化,防止刷新页面后状态丢失。 - 核心状态 (
state
):token
: 存储用户的 JWT 访问令牌。isLoggedIn
: 布尔值,表示用户当前的登录状态。hasCompletedOnboarding
: 布尔值,表示用户是否已完成首次引导流程。isNewUser
: 布尔值,用于判断用户在登录后是否为新注册用户,以决定是否跳转到引导页。
- 核心动作 (
actions
):setToken
: 登录成功后调用,保存 token 并更新状态。logout
: 清除所有认证状态和本地存储。completeOnboarding
: 标记引导流程完成。setPassword
,passwordLogin
: 封装了调用后端认证相关 API 的fetch
请求。
3.3. 组件结构与 API 交互
- 组件化: 项目遵循 Vue 的组件化思想,将页面拆分为可复用的组件。页面级组件存放在
src/views
,通用组件可以放在src/components
(当前项目暂未大量使用)。 - 页面结构:
通用基础页
: 存放登录、注册、引导、闪屏等与核心业务逻辑解耦的页面。核心体验页
: 存放与食品查询相关的核心功能页面。- 根目录
views
: 存放构成主应用导航的五个一级页面。
- API 交互模式 (以
PasswordLoginView.vue
为例):- 视图层 (View): 负责渲染 UI 和接收用户输入(如手机号、密码)。
- 触发动作: 用户操作(如点击登录按钮)会调用一个在
<script setup>
中定义的方法。 - 调用状态管理 (Store): 该方法不直接发起 API 请求,而是调用 Pinia Store (
authStore
) 中对应的action
(如authStore.passwordLogin()
),并将用户输入作为参数传入。 - Store 执行 API 请求: 在
authStore
的action
内部,使用fetch
向后端发送请求,并处理响应。 - 更新状态: API 请求成功后,
action
会更新 Store 中的状态(如token
,isLoggedIn
)。 - 响应式更新: 由于组件依赖于 Store 中的状态,状态的改变会通过 Vue 的响应式系统自动反馈到 UI 上。
- 导航:
action
执行完毕后,视图层根据返回结果或更新后的状态,使用vue-router
进行页面跳转。
这种模式将视图渲染、状态管理和 API 通信清晰地分离开来,是 Vue 生态中的最佳实践之一。
4. 核心功能与交互流程
本章节将整合前后端的分析结果,对关键的用户功能流程进行详细描述。
4.1. 流程一:手机验证码登录 / 注册
这是应用最核心的用户入口流程,涵盖了新用户注册和老用户登录两种情况。
4.1.1. 流程描述
- 用户输入手机号: 用户在登录页 (
LoginView
) 输入手机号码,点击“发送验证码”。 - 前端请求验证码: 前端调用后端
POST /api/v1/auth/send-verification-code
接口。 - 后端发送验证码: 后端收到请求,(当前为模拟)生成一个验证码(如 "111111")并返回给前端。在生产环境中,此处应调用短信服务商API。
- 用户输入验证码: 用户收到验证码后,在页面上输入,并点击“登录”。
- 前端执行登录:
- 前端调用 Pinia Store 中的
authStore.setToken
(或类似逻辑),该action
会向后端POST /api/v1/auth/login
发送请求,请求体包含手机号和验证码。
- 前端调用 Pinia Store 中的
- 后端处理登录:
- 后端验证验证码是否正确。
- 验证通过后,调用
crud.get_user_by_phone_number
检查该手机号是否存在。 - 如果用户不存在 (新用户): 调用
crud.create_user
创建一个新用户记录,并标记is_new_user = True
。 - 如果用户已存在 (老用户): 直接获取该用户信息,
is_new_user = False
。 - 使用
security.create_access_token
为该用户生成一个 JWT。 - 将
access_token
和is_new_user
标志返回给前端。
- 前端处理响应:
authStore
将收到的token
和is_new_user
状态存入localStorage
。- 如果是新用户:
isNewUser
状态为true
,路由守卫将用户重定向到引导页 (/onboarding
)。 - 如果是老用户:
isNewUser
状态为false
,路由守卫将用户重定向到主页 (/app/home
)。
4.1.2. 序列图 (Mermaid.js)
sequenceDiagram
participant User as 用户
participant Frontend as 前端 (Vue)
participant Backend as 后端 (FastAPI)
participant DB as 数据库
User->>Frontend: 1. 输入手机号,点击发送验证码
Frontend->>Backend: 2. POST /api/v1/auth/send-verification-code
Backend-->>Frontend: 3. 返回模拟验证码 "111111"
User->>Frontend: 4. 输入验证码,点击登录
Frontend->>Backend: 5. POST /api/v1/auth/login (含手机号和验证码)
Backend->>Backend: 6.1. 校验验证码
Backend->>DB: 6.2. get_user_by_phone_number()
alt 新用户
DB-->>Backend: 6.3. 用户不存在
Backend->>DB: 6.4. create_user()
DB-->>Backend: 6.5. 返回新用户信息
Backend->>Backend: 6.6. 生成JWT, is_new_user = true
else 老用户
DB-->>Backend: 6.3. 返回用户信息
Backend->>Backend: 6.6. 生成JWT, is_new_user = false
end
Backend-->>Frontend: 7. 返回 { access_token, is_new_user }
Frontend->>Frontend: 8. 保存Token, 更新Store状态
alt is_new_user is true
Frontend->>User: 9. 重定向到引导页 (/onboarding)
else is_new_user is false
Frontend->>User: 9. 重定向到主页 (/app/home)
end