init
This commit is contained in:
2
.cursor/commands/myspec.plan.md
Normal file
2
.cursor/commands/myspec.plan.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
你是spec kit专家
|
||||||
|
根据对应的spec.md 生成plan.md
|
||||||
4
.cursor/commands/myspec.spec.md
Normal file
4
.cursor/commands/myspec.spec.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
你是spec kit专家
|
||||||
|
现在是高层规范spec阶段
|
||||||
|
- spec_kit/下新建目录 生成需求名称 长度不超过四个单词
|
||||||
|
生成spec.md
|
||||||
14
.cursor/commands/myspec.split-spec.md
Normal file
14
.cursor/commands/myspec.split-spec.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
当前有一个很大的 spec.md(大需求规范),需要按业务逻辑拆分成多个子模块规范。
|
||||||
|
|
||||||
|
请按以下规则拆分:
|
||||||
|
|
||||||
|
1. 从大 spec 中提取可以独立成 **子模块规范** 的部分,每个子模块输出一个小 spec。
|
||||||
|
2. 拆分后每个小 spec 需包含:
|
||||||
|
- 子模块名称
|
||||||
|
- 目标描述(本模块要解决什么)
|
||||||
|
- 输入/输出定义
|
||||||
|
- 验收标准(可验证)
|
||||||
|
3. 拆分后输出一个 `modules/` 目录结构列表,并为每个模块生成对应 spec 内容。
|
||||||
|
4. 保留大 spec.md 的高层背景/总览到 overview 部分。
|
||||||
|
5. 子模块之间按逻辑关系关联。
|
||||||
|
6. 不生成 plan.md 或 tasks.md,仅拆出子模块 spec。
|
||||||
4
.cursor/commands/myspec.task.md
Normal file
4
.cursor/commands/myspec.task.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
你是spec kit专家
|
||||||
|
根据对应的plan.md 生成task.md
|
||||||
|
任务清单详细可执行
|
||||||
|
执行完要标记
|
||||||
30
.cursor/rules/rule.mdc
Normal file
30
.cursor/rules/rule.mdc
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
你是一名 Spec Kit 系统专家。
|
||||||
|
要求:
|
||||||
|
1. 在生成 plan.md 或 tasks.md 之前,若有不确定的点,请先输出“待确认问题清单”,等待确认再继续,可反复追问。
|
||||||
|
2. 所有文件使用 Markdown 格式,目录结构为:
|
||||||
|
spec_kit/
|
||||||
|
├ {需求名称}/
|
||||||
|
│ ├ spec.md # 大需求高层规范
|
||||||
|
│ ├ plan.md # 大需求技术计划
|
||||||
|
│ ├ tasks.md # 大需求任务清单
|
||||||
|
│ ├ overflow.md # 大需求补充说明
|
||||||
|
│ └ modules/ # 子模块集合
|
||||||
|
│ ├ submodule-A/
|
||||||
|
│ │ ├ spec.md
|
||||||
|
│ │ ├ plan.md
|
||||||
|
│ │ ├ tasks.md
|
||||||
|
│ │ ├ overflow.md # 针对子模块的overflow.md
|
||||||
|
│ │ └ modules/ # 子模块内可再拆分
|
||||||
|
│ │ ├ sub-A1/
|
||||||
|
│ │ │ ├ spec.md
|
||||||
|
│ │ │ ├ plan.md
|
||||||
|
│ │ │ ├ overflow.md
|
||||||
|
│ │ │ └ tasks.md
|
||||||
|
│ │ └ …
|
||||||
|
modules/ 可嵌套 modules/,每层都独立规范。
|
||||||
|
输出时根据这个结构生成内容时,请保持文件职责清晰。
|
||||||
|
简短记录项目的该层每个spec的内容 ,每次编码完成后更新overview.md
|
||||||
|
可以通过nvm 切换node版本
|
||||||
20
.envrc
Normal file
20
.envrc
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# 本项目代理(仅本目录生效;可用 direnv 自动加载,或手动 source)
|
||||||
|
# 目标:将网络请求代理到本机 127.0.0.1:7897
|
||||||
|
|
||||||
|
export PROXY_HOST="127.0.0.1"
|
||||||
|
export PROXY_PORT="7897"
|
||||||
|
|
||||||
|
# HTTP/HTTPS 代理(多数 CLI 工具识别)
|
||||||
|
export http_proxy="http://${PROXY_HOST}:${PROXY_PORT}"
|
||||||
|
export https_proxy="http://${PROXY_HOST}:${PROXY_PORT}"
|
||||||
|
export HTTP_PROXY="http://${PROXY_HOST}:${PROXY_PORT}"
|
||||||
|
export HTTPS_PROXY="http://${PROXY_HOST}:${PROXY_PORT}"
|
||||||
|
|
||||||
|
# SOCKS5 代理(部分工具使用 ALL_PROXY)
|
||||||
|
export all_proxy="socks5h://${PROXY_HOST}:${PROXY_PORT}"
|
||||||
|
export ALL_PROXY="socks5h://${PROXY_HOST}:${PROXY_PORT}"
|
||||||
|
|
||||||
|
# 本地地址不走代理(避免本地联调被误代理)
|
||||||
|
export no_proxy="localhost,127.0.0.1,::1"
|
||||||
|
export NO_PROXY="localhost,127.0.0.1,::1"
|
||||||
|
|
||||||
144
README.md
Normal file
144
README.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# Mindfulness(正念 APP)📱
|
||||||
|
|
||||||
|
本项目面向宝妈群体,提供情绪价值与正念练习支持。
|
||||||
|
客户端采用 React Native + Expo,后端采用 Python + FastAPI,数据库 MySQL,任务调度 Celery,推送/定时等功能完整支持。
|
||||||
|
|
||||||
|
# 目录结构
|
||||||
|
|
||||||
|
/mindfulness
|
||||||
|
├── client/ # 客户端(React Native + Expo)
|
||||||
|
├── server/ # 后端(FastAPI)
|
||||||
|
├── infra/ # 基础设施(Docker / k8s / nginx 等)
|
||||||
|
├── scripts/ # 构建、部署脚本
|
||||||
|
├── .gitea/ # Gitea Runner CI/CD 配置
|
||||||
|
└── README.md # 本文档
|
||||||
|
|
||||||
|
|
||||||
|
# 1. 功能概览
|
||||||
|
## 1.1 一期功能
|
||||||
|
1)APP-PUSH 推送功能
|
||||||
|
|
||||||
|
定时推送情绪文字(如:正念短句、温柔提醒)
|
||||||
|
|
||||||
|
支持推送策略:
|
||||||
|
|
||||||
|
固定时间推送
|
||||||
|
|
||||||
|
用户可自定义推送时间
|
||||||
|
|
||||||
|
推送内容可由后台配置与更新
|
||||||
|
|
||||||
|
2)iOS 小组件(Widget)
|
||||||
|
|
||||||
|
小组件展示:
|
||||||
|
|
||||||
|
当前情绪文字
|
||||||
|
|
||||||
|
背景图片/渐变色
|
||||||
|
|
||||||
|
支持多种尺寸(小/中/大)
|
||||||
|
|
||||||
|
可配置刷新频率(例如:每小时/每天)
|
||||||
|
|
||||||
|
3)情绪卡片滑动功能
|
||||||
|
|
||||||
|
APP 内展示情绪卡片列表
|
||||||
|
|
||||||
|
支持左右滑动(类似 Tinder 卡片)
|
||||||
|
|
||||||
|
滑动后显示下一张情绪卡片
|
||||||
|
|
||||||
|
可收藏/分享(可作为后续迭代功能)
|
||||||
|
|
||||||
|
# 2. 技术栈(建议版本)
|
||||||
|
## 2.1 客户端
|
||||||
|
|
||||||
|
React Native + Expo
|
||||||
|
|
||||||
|
TypeScript
|
||||||
|
|
||||||
|
React Navigation
|
||||||
|
|
||||||
|
Zustand(或 Redux Toolkit)
|
||||||
|
|
||||||
|
Axios(或 Fetch)
|
||||||
|
|
||||||
|
Expo Notifications(推送)
|
||||||
|
|
||||||
|
Expo Widgets(小组件)
|
||||||
|
|
||||||
|
Expo 使得 iOS 开发更轻量,适合你这种“AI 友好 + 快速迭代”的项目。
|
||||||
|
|
||||||
|
## 2.2 后端
|
||||||
|
|
||||||
|
Python + FastAPI
|
||||||
|
|
||||||
|
SQLAlchemy 2.x(Async)
|
||||||
|
|
||||||
|
MySQL 8.0
|
||||||
|
|
||||||
|
Alembic(数据库迁移)
|
||||||
|
|
||||||
|
Celery + Redis(定时推送任务)
|
||||||
|
|
||||||
|
Pydantic v2(请求/响应校验)
|
||||||
|
|
||||||
|
pytest(单元测试)
|
||||||
|
|
||||||
|
OpenAPI(自动文档)
|
||||||
|
|
||||||
|
# 3. 环境区分(dev / pro)
|
||||||
|
|
||||||
|
客户端使用 .env.dev 和 .env.prod 管理环境变量
|
||||||
|
|
||||||
|
后端使用 .env.dev 和 .env.prod 管理数据库、缓存、第三方服务等
|
||||||
|
|
||||||
|
.env 文件不提交到 Git,仓库中提供 .env.example 作为模板
|
||||||
|
|
||||||
|
打包/部署通过环境变量决定使用 dev 或 prod 配置
|
||||||
|
|
||||||
|
# 3.1 本项目代理(本机 7897)
|
||||||
|
|
||||||
|
如果你需要让本项目在安装依赖/拉取资源时走本机代理(如 Clash),本仓库根目录已提供 `.envrc`,默认指向 `127.0.0.1:7897`。
|
||||||
|
|
||||||
|
在仓库根目录执行(对当前终端会话生效):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ./.envrc
|
||||||
|
```
|
||||||
|
|
||||||
|
验证是否生效(任选其一):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo $HTTP_PROXY
|
||||||
|
echo $ALL_PROXY
|
||||||
|
```
|
||||||
|
|
||||||
|
# 4. CI / CD(推荐方案)
|
||||||
|
|
||||||
|
1. 客户端打包并推送到 dev/prod
|
||||||
|
|
||||||
|
dev 分支触发 dev 包构建(bundle id:com.damer.mindfulness.dev)
|
||||||
|
|
||||||
|
main 分支触发 prod 包构建(bundle id:com.damer.mindfulness)
|
||||||
|
|
||||||
|
2. 后端构建 Docker 镜像(dev/prod)
|
||||||
|
|
||||||
|
dev 分支构建 :dev 镜像
|
||||||
|
|
||||||
|
main 分支构建 :prod 镜像
|
||||||
|
|
||||||
|
3. 后端部署(dev/prod)
|
||||||
|
|
||||||
|
dev 分支部署到 dev 环境
|
||||||
|
|
||||||
|
main 分支部署到 prod 环境
|
||||||
|
## 目录
|
||||||
|
├── .gitea/workflows # gitea runner 配置(建议)
|
||||||
|
│ ├── client-build.yml # 打包并推送至 testflight
|
||||||
|
│ ├── server-build.yml # 后端构建镜像推送至docker hub
|
||||||
|
│ └── server-deploy.yml # 部署后端
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1
client
Submodule
1
client
Submodule
Submodule client added at e7237a8f3e
210
server/README.md
Normal file
210
server/README.md
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
# 正念 APP(后端)
|
||||||
|
|
||||||
|
本目录为正念 APP 的后端服务(FastAPI),负责:
|
||||||
|
|
||||||
|
- 提供业务 API(情绪文案/卡片、用户配置、推送配置等)
|
||||||
|
- 支撑定时任务(推送、内容刷新等)
|
||||||
|
- 对接数据库与缓存(MySQL / Redis)
|
||||||
|
|
||||||
|
> 说明:当前 `server/` 目录仅包含说明文档,尚未初始化具体代码与依赖文件。本文档按“推荐工程形态”编写,等代码落地后只需把入口模块名与配置字段名对齐即可。
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
- Python + FastAPI
|
||||||
|
- SQLAlchemy(建议 2.x,异步)+ MySQL 8.0
|
||||||
|
- Alembic(数据库迁移)
|
||||||
|
- Celery + Redis(任务调度/定时推送)
|
||||||
|
- Pydantic v2(数据校验)+ pydantic-settings(环境配置)
|
||||||
|
- pytest(测试)
|
||||||
|
|
||||||
|
## 本地开发
|
||||||
|
|
||||||
|
### 1. 环境要求
|
||||||
|
|
||||||
|
- Python:3.11+
|
||||||
|
- MySQL:8.0+
|
||||||
|
|
||||||
|
### 2. 创建虚拟环境
|
||||||
|
|
||||||
|
在 `server/` 目录下:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
python -m pip install -U pip
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 安装依赖
|
||||||
|
|
||||||
|
|
||||||
|
- 方式 :`requirements.txt`(示例)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 配置(dev / prod)
|
||||||
|
|
||||||
|
推荐使用 `.env.dev` 与 `.env.prod`,由 `pydantic-settings` 读取。
|
||||||
|
|
||||||
|
### 1. 环境变量示例
|
||||||
|
|
||||||
|
在 `server/` 下创建 `.env.dev`(示例字段,可按代码中的 Settings 字段名调整):
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
# 运行环境
|
||||||
|
APP_ENV=dev
|
||||||
|
APP_NAME=mindfulness-server
|
||||||
|
APP_HOST=0.0.0.0
|
||||||
|
APP_PORT=8000
|
||||||
|
|
||||||
|
# 数据库(推荐用统一的 DATABASE_URL,或拆分为 MYSQL_*)
|
||||||
|
DATABASE_URL=mysql+aiomysql://<用户名>:<密码>@<MySQL_HOST>:3306/mindfulness_dev?charset=utf8mb4
|
||||||
|
# MYSQL_HOST=127.0.0.1
|
||||||
|
# MYSQL_PORT=3306
|
||||||
|
# MYSQL_USER=root
|
||||||
|
# MYSQL_PASSWORD=password
|
||||||
|
# MYSQL_DB=mindfulness_dev
|
||||||
|
|
||||||
|
# Redis(缓存/任务队列)
|
||||||
|
REDIS_URL=redis://dev_user:devpassword@127.0.0.1:6379/0
|
||||||
|
最大的存储限制是256 请尽量少使用
|
||||||
|
# Celery(建议先只用 broker;如需结果存储请务必设置 TTL)
|
||||||
|
CELERY_BROKER_URL=redis://dev_user:devpassword@127.0.0.1:6379/0
|
||||||
|
# CELERY_RESULT_BACKEND=redis://dev_user:devpassword@127.0.0.1:6379/0
|
||||||
|
|
||||||
|
# 推送/第三方(按实际接入补充)
|
||||||
|
# PUSH_PROVIDER=expo
|
||||||
|
# EXPO_ACCESS_TOKEN=
|
||||||
|
```
|
||||||
|
|
||||||
|
`.env.prod` 同理,替换为生产环境地址与密钥即可。
|
||||||
|
|
||||||
|
### 1.1 MySQL 命名与 dev/pro 区分(约定)
|
||||||
|
|
||||||
|
- **生产库(prod)**:`mindfulness`
|
||||||
|
- **开发库(dev)**:`mindfulness_dev`
|
||||||
|
|
||||||
|
建议:
|
||||||
|
|
||||||
|
- **库名/表名/字段名**:统一小写 + 下划线(snake_case)
|
||||||
|
- **字符集**:统一 `utf8mb4`
|
||||||
|
|
||||||
|
`.env.dev` 指向 `mindfulness_dev`,`.env.prod` 指向 `mindfulness`:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
# prod 示例
|
||||||
|
DATABASE_URL=mysql+aiomysql://<用户名>:<密码>@<MySQL_HOST>:3306/mindfulness?charset=utf8mb4
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Redis:通过 ACL 限制 dev/pro 前缀访问
|
||||||
|
|
||||||
|
只启动 **一个 Redis 服务**,用 **ACL + key 前缀**隔离环境数据:
|
||||||
|
|
||||||
|
```text
|
||||||
|
# Dev 用户,只能访问 dev:* 前缀
|
||||||
|
user dev_user on >devpassword ~dev:* +@all
|
||||||
|
|
||||||
|
# Pro 用户,只能访问 pro:* 前缀
|
||||||
|
user pro_user on >propassword ~pro:* +@all
|
||||||
|
```
|
||||||
|
|
||||||
|
连接串示例(dev/pro 使用不同账号):
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
# dev
|
||||||
|
REDIS_URL=redis://dev_user:devpassword@127.0.0.1:6379/0
|
||||||
|
CELERY_BROKER_URL=redis://dev_user:devpassword@127.0.0.1:6379/0
|
||||||
|
|
||||||
|
# prod
|
||||||
|
# REDIS_URL=redis://pro_user:propassword@127.0.0.1:6379/0
|
||||||
|
# CELERY_BROKER_URL=redis://pro_user:propassword@127.0.0.1:6379/0
|
||||||
|
```
|
||||||
|
|
||||||
|
重要约定:
|
||||||
|
|
||||||
|
- **应用写入 Redis 的 key 必须带前缀**:dev 环境使用 `dev:`,prod 环境使用 `pro:`
|
||||||
|
- **Celery 队列/键也建议带环境前缀**(例如队列名 `dev:push` / `pro:push`),避免同机多环境混用时相互影响
|
||||||
|
|
||||||
|
### 2. 配置加载约定(建议)
|
||||||
|
|
||||||
|
- 通过 `APP_ENV` 决定加载 `.env.dev` 或 `.env.prod`
|
||||||
|
- 代码中使用 `pydantic-settings` 定义 `Settings`,并集中在 `app/core/config.py`(仅建议命名)
|
||||||
|
|
||||||
|
## 启动服务(FastAPI)
|
||||||
|
|
||||||
|
> 下方入口模块名为示例:假设你的 FastAPI 实例位于 `app/main.py` 中的 `app` 变量。若实际路径不同,请自行替换。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
启动后可访问:
|
||||||
|
|
||||||
|
- OpenAPI 文档:`/docs`
|
||||||
|
- ReDoc:`/redoc`
|
||||||
|
|
||||||
|
## 数据库迁移(Alembic)
|
||||||
|
|
||||||
|
> 若你采用 Alembic:建议把迁移脚本放在 `server/alembic/`,并在 `alembic.ini` 中配置数据库连接(或从环境变量读取)。
|
||||||
|
|
||||||
|
常用命令(示例):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
alembic revision --autogenerate -m "初始化表结构"
|
||||||
|
alembic upgrade head
|
||||||
|
```
|
||||||
|
|
||||||
|
## 任务调度(Celery + Redis)
|
||||||
|
|
||||||
|
> 下方入口模块名为示例:假设 Celery 实例位于 `app/worker.py` 中的 `celery_app` 变量。若实际路径不同,请自行替换。
|
||||||
|
|
||||||
|
启动 Worker(处理异步任务):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
celery -A app.worker:celery_app worker -l info
|
||||||
|
```
|
||||||
|
|
||||||
|
启动 Beat(定时任务调度,若你使用 celery beat):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
celery -A app.worker:celery_app beat -l info
|
||||||
|
```
|
||||||
|
|
||||||
|
## 运行测试(pytest)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest -q
|
||||||
|
```
|
||||||
|
|
||||||
|
## 推荐目录结构(建议)
|
||||||
|
|
||||||
|
> 仅为建议,便于后续协作与扩展。
|
||||||
|
|
||||||
|
```text
|
||||||
|
server/
|
||||||
|
app/
|
||||||
|
main.py # FastAPI 入口(app = FastAPI())
|
||||||
|
core/
|
||||||
|
config.py # Settings(pydantic-settings)
|
||||||
|
logging.py
|
||||||
|
db/
|
||||||
|
session.py # 数据库会话/引擎
|
||||||
|
models/ # ORM 模型
|
||||||
|
migrations/ # (如不使用默认 alembic 目录,可自定义)
|
||||||
|
api/
|
||||||
|
v1/
|
||||||
|
routes/
|
||||||
|
tasks/
|
||||||
|
push.py # 推送相关任务
|
||||||
|
worker.py # Celery 入口(celery_app = Celery(...))
|
||||||
|
tests/
|
||||||
|
README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
- **MySQL 连接报错/字符集问题**:建议统一使用 `utf8mb4`,并在连接串中带上 `charset=utf8mb4`。
|
||||||
|
- **Celery 无法连接 Redis**:检查 `CELERY_BROKER_URL` 与 Redis 是否可达、端口是否开放。
|
||||||
6
server/app/__init__.py
Normal file
6
server/app/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
"""
|
||||||
|
后端应用包入口。
|
||||||
|
|
||||||
|
说明:目前仅提供项目初始化骨架,后续会逐步补齐业务模块。
|
||||||
|
"""
|
||||||
|
|
||||||
71
server/app/core/config.py
Normal file
71
server/app/core/config.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from functools import lru_cache
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Literal, Optional
|
||||||
|
|
||||||
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
|
def _guess_env_file(app_env: str) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
根据 APP_ENV 选择 env 文件。
|
||||||
|
|
||||||
|
说明:
|
||||||
|
- `.env.dev` / `.env.prod` 不进入仓库,通常由运维在部署时放到 `server/` 目录
|
||||||
|
- 如果文件不存在,则返回 None,让系统从“真实环境变量”读取
|
||||||
|
"""
|
||||||
|
|
||||||
|
base_dir = Path(__file__).resolve().parents[2] # .../server/app -> .../server
|
||||||
|
candidates = {
|
||||||
|
"dev": base_dir / ".env.dev",
|
||||||
|
"prod": base_dir / ".env.prod",
|
||||||
|
}
|
||||||
|
p = candidates.get(app_env)
|
||||||
|
if p and p.exists():
|
||||||
|
return str(p)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
"""
|
||||||
|
应用配置(统一从环境变量读取)。
|
||||||
|
|
||||||
|
注意:请不要把真实账号密码写入仓库;使用 `.env.dev/.env.prod` 或部署系统注入。
|
||||||
|
"""
|
||||||
|
|
||||||
|
app_env: Literal["dev", "prod"] = "dev"
|
||||||
|
app_name: str = "mindfulness-server"
|
||||||
|
app_host: str = "0.0.0.0"
|
||||||
|
app_port: int = 8000
|
||||||
|
|
||||||
|
database_url: str
|
||||||
|
|
||||||
|
redis_url: str
|
||||||
|
celery_broker_url: str
|
||||||
|
celery_result_backend: Optional[str] = None
|
||||||
|
|
||||||
|
model_config = SettingsConfigDict(
|
||||||
|
env_prefix="",
|
||||||
|
case_sensitive=False,
|
||||||
|
extra="ignore",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
|
def get_settings() -> Settings:
|
||||||
|
"""
|
||||||
|
获取配置(带缓存)。
|
||||||
|
|
||||||
|
加载顺序:
|
||||||
|
- 优先使用 `.env.dev/.env.prod`(若存在)
|
||||||
|
- 否则使用系统环境变量
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
env = os.getenv("APP_ENV", "dev").strip() or "dev"
|
||||||
|
env_file = _guess_env_file(env)
|
||||||
|
|
||||||
|
return Settings(_env_file=env_file)
|
||||||
|
|
||||||
6
server/app/db/base.py
Normal file
6
server/app/db/base.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from sqlalchemy.orm import DeclarativeBase
|
||||||
|
|
||||||
|
|
||||||
|
class Base(DeclarativeBase):
|
||||||
|
"""SQLAlchemy ORM Base。"""
|
||||||
|
|
||||||
36
server/app/db/session.py
Normal file
36
server/app/db/session.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||||
|
|
||||||
|
from app.core.config import get_settings
|
||||||
|
|
||||||
|
|
||||||
|
def create_engine():
|
||||||
|
"""
|
||||||
|
创建异步 Engine。
|
||||||
|
|
||||||
|
注意:连接串从环境变量 `DATABASE_URL` 读取。
|
||||||
|
"""
|
||||||
|
|
||||||
|
settings = get_settings()
|
||||||
|
return create_async_engine(settings.database_url, pool_pre_ping=True)
|
||||||
|
|
||||||
|
|
||||||
|
engine = create_engine()
|
||||||
|
|
||||||
|
AsyncSessionLocal: async_sessionmaker[AsyncSession] = async_sessionmaker(
|
||||||
|
bind=engine,
|
||||||
|
expire_on_commit=False,
|
||||||
|
autoflush=False,
|
||||||
|
autocommit=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_db() -> AsyncSession:
|
||||||
|
"""
|
||||||
|
FastAPI 依赖:获取数据库会话。
|
||||||
|
"""
|
||||||
|
|
||||||
|
async with AsyncSessionLocal() as session:
|
||||||
|
yield session
|
||||||
|
|
||||||
25
server/app/main.py
Normal file
25
server/app/main.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
from app.core.config import get_settings
|
||||||
|
|
||||||
|
|
||||||
|
def create_app() -> FastAPI:
|
||||||
|
"""
|
||||||
|
创建 FastAPI 应用实例。
|
||||||
|
|
||||||
|
说明:目前仅提供最小可运行骨架(health check),后续逐步加入路由与中间件。
|
||||||
|
"""
|
||||||
|
|
||||||
|
settings = get_settings()
|
||||||
|
|
||||||
|
app = FastAPI(title=settings.app_name)
|
||||||
|
|
||||||
|
@app.get("/healthz")
|
||||||
|
async def healthz() -> dict:
|
||||||
|
return {"status": "ok", "env": settings.app_env}
|
||||||
|
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
|
||||||
4
server/app/tasks/__init__.py
Normal file
4
server/app/tasks/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
"""
|
||||||
|
Celery 任务集合。
|
||||||
|
"""
|
||||||
|
|
||||||
13
server/app/tasks/ping.py
Normal file
13
server/app/tasks/ping.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task(name="tasks.ping")
|
||||||
|
def ping(msg: str = "pong") -> str:
|
||||||
|
"""
|
||||||
|
示例任务:用于验证 Celery worker 可消费任务。
|
||||||
|
|
||||||
|
注意:任务参数请尽量保持小(只传 id/短文本),避免 Redis 队列膨胀。
|
||||||
|
"""
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
43
server/app/worker.py
Normal file
43
server/app/worker.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from celery import Celery
|
||||||
|
|
||||||
|
from app.core.config import get_settings
|
||||||
|
|
||||||
|
|
||||||
|
def _env_prefix(app_env: str) -> str:
|
||||||
|
"""
|
||||||
|
根据环境生成前缀:
|
||||||
|
- dev -> dev
|
||||||
|
- prod -> pro
|
||||||
|
|
||||||
|
说明:Redis ACL 限制使用 `dev:*` / `pro:*`。
|
||||||
|
"""
|
||||||
|
|
||||||
|
return "dev" if app_env == "dev" else "pro"
|
||||||
|
|
||||||
|
|
||||||
|
settings = get_settings()
|
||||||
|
prefix = _env_prefix(settings.app_env)
|
||||||
|
|
||||||
|
# 关键:使用 Redis transport 的全局 key 前缀,确保所有 broker key 都在 ACL 允许范围内
|
||||||
|
broker_transport_options = {"global_keyprefix": f"{prefix}:"}
|
||||||
|
|
||||||
|
celery_app = Celery(
|
||||||
|
"mindfulness",
|
||||||
|
broker=settings.celery_broker_url,
|
||||||
|
backend=settings.celery_result_backend,
|
||||||
|
broker_transport_options=broker_transport_options,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 默认不存结果(降低 Redis 占用)。如需结果存储,可在业务中显式开启并设置 TTL。
|
||||||
|
celery_app.conf.update(
|
||||||
|
task_ignore_result=True if not settings.celery_result_backend else False,
|
||||||
|
task_default_queue=f"{prefix}:celery",
|
||||||
|
task_default_exchange=f"{prefix}:celery",
|
||||||
|
task_default_routing_key=f"{prefix}:celery",
|
||||||
|
)
|
||||||
|
|
||||||
|
# 自动发现任务(app/tasks 下的 shared_task)
|
||||||
|
celery_app.autodiscover_tasks(["app.tasks"])
|
||||||
|
|
||||||
20
server/env.example
Normal file
20
server/env.example
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# 运行环境:dev 或 prod
|
||||||
|
APP_ENV=dev
|
||||||
|
|
||||||
|
# Web 服务
|
||||||
|
APP_NAME=mindfulness-server
|
||||||
|
APP_HOST=0.0.0.0
|
||||||
|
APP_PORT=8000
|
||||||
|
|
||||||
|
# 数据库(dev 指向 mindfulness_dev;prod 指向 mindfulness)
|
||||||
|
DATABASE_URL=mysql+aiomysql://<用户名>:<密码>@<MYSQL_HOST>:3306/mindfulness_dev?charset=utf8mb4
|
||||||
|
|
||||||
|
# Redis(使用 ACL 用户;并确保应用侧 key 带 dev:/pro: 前缀)
|
||||||
|
REDIS_URL=redis://<REDIS_USER>:<REDIS_PASSWORD>@<REDIS_HOST>:6379/0
|
||||||
|
|
||||||
|
# Celery(默认不启用结果存储,避免 Redis 内存压力)
|
||||||
|
CELERY_BROKER_URL=redis://<REDIS_USER>:<REDIS_PASSWORD>@<REDIS_HOST>:6379/0
|
||||||
|
# CELERY_RESULT_BACKEND=redis://<REDIS_USER>:<REDIS_PASSWORD>@<REDIS_HOST>:6379/0
|
||||||
|
|
||||||
|
# 推送(Expo)
|
||||||
|
# EXPO_ACCESS_TOKEN=
|
||||||
20
server/requirements.txt
Normal file
20
server/requirements.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
fastapi>=0.110
|
||||||
|
uvicorn[standard]>=0.27
|
||||||
|
|
||||||
|
# 数据库(SQLAlchemy 2.x 异步 + MySQL)
|
||||||
|
SQLAlchemy>=2.0
|
||||||
|
aiomysql>=0.2
|
||||||
|
|
||||||
|
# 配置
|
||||||
|
pydantic>=2.6
|
||||||
|
pydantic-settings>=2.2
|
||||||
|
|
||||||
|
# 迁移
|
||||||
|
alembic>=1.13
|
||||||
|
|
||||||
|
# 任务调度
|
||||||
|
celery>=5.3
|
||||||
|
redis>=5.0
|
||||||
|
|
||||||
|
# 测试
|
||||||
|
pytest>=8.0
|
||||||
127
spec_kit/Client Bootstrap/plan.md
Normal file
127
spec_kit/Client Bootstrap/plan.md
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
# Client Bootstrap(技术计划)
|
||||||
|
|
||||||
|
## 1. 计划目标
|
||||||
|
|
||||||
|
基于 `spec.md` 的约束,落地一套可执行的客户端工程初始化方案,保证:
|
||||||
|
|
||||||
|
- 开发者可本地启动与联调
|
||||||
|
- dev/prod 环境区分清晰
|
||||||
|
- i18n 支持 CN/EN/ES/PT/TC 并支持动态切换
|
||||||
|
- 目录结构标准化,可持续扩展
|
||||||
|
- EAS Build iOS 打包流程可执行
|
||||||
|
|
||||||
|
## 2. 默认技术决策(本计划采用)
|
||||||
|
|
||||||
|
- **Node.js**:建议 **20.x LTS**(最低 **18.x LTS**)
|
||||||
|
- **包管理器**:pnpm(建议 **9.x**,最低 **8.x**)
|
||||||
|
- **路由**:优先采用 `expo-router`(使用 `app/` 目录)
|
||||||
|
- 若项目已使用 React Navigation,则保留 `src/navigation/` + `src/screens/` 的方案(见“可替代方案”)
|
||||||
|
- **i18n**:`i18next` + `react-i18next` + `expo-localization`
|
||||||
|
- **环境变量**:`.env.dev` / `.env.prod` + `EXPO_PUBLIC_` 前缀
|
||||||
|
- **网络层**:统一封装 `src/services/`(Axios 或 Fetch 二选一,先按 Axios 预留结构)
|
||||||
|
- **状态管理**:Zustand(与现有 `spec.md` 一致)
|
||||||
|
|
||||||
|
## 3. 目录与模块规划
|
||||||
|
|
||||||
|
### 3.1 顶层目录(目标态)
|
||||||
|
|
||||||
|
- `app/`:路由入口(expo-router)
|
||||||
|
- `src/`:业务代码
|
||||||
|
- `src/features/`:按功能域拆分(push/widget/cards)
|
||||||
|
- `src/services/`:API 封装
|
||||||
|
- `src/store/`:状态管理
|
||||||
|
- `src/i18n/`:多语言资源与初始化
|
||||||
|
- `src/components/`、`src/hooks/`、`src/utils/`、`src/constants/`、`src/types/`
|
||||||
|
- `assets/`:静态资源
|
||||||
|
- `app.json` 或 `app.config.ts`:Expo 配置与环境区分入口
|
||||||
|
- `eas.json`:EAS 配置(如启用)
|
||||||
|
|
||||||
|
### 3.2 关键模块拆分
|
||||||
|
|
||||||
|
- **i18n 模块**(`src/i18n/`)
|
||||||
|
- `index.ts`:初始化(默认语言、回退语言、资源注册、与系统语言对齐)
|
||||||
|
- `locales/*.json`:`zh-CN/en/es/pt/zh-TW` 五份资源
|
||||||
|
- (可选)`types.ts`:语言码类型与 key 约束
|
||||||
|
- **Push 模块**(`src/features/push/`)
|
||||||
|
- 权限申请
|
||||||
|
- Token 获取与上报(接口对齐点在联调阶段确认)
|
||||||
|
- **Widget 模块**(`src/features/widget/`)
|
||||||
|
- 数据读取与缓存策略(先定义接口与缓存层,再落地 iOS 扩展实现)
|
||||||
|
- **Cards 模块**(`src/features/cards/`)
|
||||||
|
- 卡片滑动交互组件与数据模型
|
||||||
|
|
||||||
|
## 4. 环境区分与配置计划
|
||||||
|
|
||||||
|
### 4.1 环境变量与模板
|
||||||
|
|
||||||
|
- 提供 `.env.example`(至少包含 `EXPO_PUBLIC_API_BASE_URL`、`EXPO_PUBLIC_ENV`)
|
||||||
|
- 本地开发使用 `.env.dev`,生产构建使用 `.env.prod`
|
||||||
|
- README 明确:
|
||||||
|
- 真机联调时 `localhost` 替换为局域网 IP
|
||||||
|
- 环境变量不入库
|
||||||
|
|
||||||
|
### 4.2 Bundle ID 与构建标识
|
||||||
|
|
||||||
|
- dev:`com.damer.mindfulness.dev`
|
||||||
|
- prod:`com.damer.mindfulness`
|
||||||
|
- 在 `eas.json` 中按 profile 区分环境变量注入与 bundle id(当工程文件落地后执行)
|
||||||
|
|
||||||
|
## 5. 多语言(i18n)落地计划
|
||||||
|
|
||||||
|
### 5.1 语言码与策略
|
||||||
|
|
||||||
|
- 支持:`zh-CN` / `en` / `es` / `pt` / `zh-TW`
|
||||||
|
- 默认:优先使用用户当前**设备语言**(在支持列表内时生效)
|
||||||
|
- 回退:设备语言不在支持列表时,回退到 `zh-CN`(或团队指定的默认语言)
|
||||||
|
|
||||||
|
### 5.2 动态切换与持久化
|
||||||
|
|
||||||
|
- 提供“语言设置”入口(后续 UI 落地),允许用户手动切换语言
|
||||||
|
- 切换语言后写入本地存储(例如 AsyncStorage)
|
||||||
|
- App 启动时语言选择优先级:
|
||||||
|
- ① 用户设置(若存在)
|
||||||
|
- ② 设备语言(在支持列表内)
|
||||||
|
- ③ 默认回退(`zh-CN` 或团队指定默认)
|
||||||
|
|
||||||
|
### 5.3 文案规范
|
||||||
|
|
||||||
|
- key 点分层:`common.*`、`push.*`、`cards.*`、`widget.*`
|
||||||
|
- 插值:`{{name}}` 等形式统一
|
||||||
|
- 禁止代码内拼接长句(降低翻译成本)
|
||||||
|
|
||||||
|
## 6. 推送与小组件(一期能力)计划
|
||||||
|
|
||||||
|
### 6.1 推送
|
||||||
|
|
||||||
|
- 客户端:权限 -> token -> 上报
|
||||||
|
- 后端:保存 token -> 定时任务 -> 推送内容配置
|
||||||
|
- 联调产出:token 上报接口、字段、鉴权、错误码约定(后续任务阶段细化)
|
||||||
|
|
||||||
|
### 6.2 小组件
|
||||||
|
|
||||||
|
- 先定义“数据模型 + 缓存策略 + 刷新策略”的规范
|
||||||
|
- 明确限制:刷新频率受 iOS 系统控制,文档中强调体验目标为“尽力而为”
|
||||||
|
|
||||||
|
## 7. 执行步骤(落地顺序)
|
||||||
|
|
||||||
|
1. **补齐客户端 README**:确保启动/环境/i18n/目录/EAS 指引完整(已完成基础版本)
|
||||||
|
2. **初始化 Expo 工程(如尚未提交)**:生成 `package.json`、`app.json/app.config.*`、`assets/` 等
|
||||||
|
3. **建立标准目录结构**:创建 `app/`、`src/` 与各子目录
|
||||||
|
4. **接入 i18n 基座**:完成语言码、资源文件、初始化与切换策略
|
||||||
|
5. **接入环境变量读取**:确保 dev/prod 可影响 API Base URL
|
||||||
|
6. **预留 push/widget/cards 的模块骨架**:先放类型与接口占位,等待业务实现
|
||||||
|
7. **EAS 配置与 iOS 构建跑通**:编写 `eas.json` 并验证 development profile 构建链路
|
||||||
|
|
||||||
|
## 8. 可替代方案(不影响目标的变体)
|
||||||
|
|
||||||
|
- **路由不使用 expo-router**:使用 React Navigation
|
||||||
|
- 目录:`src/navigation/` + `src/screens/`
|
||||||
|
- 仍保留 `src/features/`、`src/services/`、`src/i18n/` 等不变
|
||||||
|
- **网络层使用 Fetch**:保持 `src/services/` 统一封装,替换实现即可
|
||||||
|
|
||||||
|
## 9. 风险与回滚策略
|
||||||
|
|
||||||
|
- **真机联调失败**:优先排查局域网 IP、代理/防火墙、后端端口映射;必要时使用内网穿透
|
||||||
|
- **推送配置复杂**:先在 dev profile 跑通 token->推送最小闭环,再扩展定时策略
|
||||||
|
- **小组件刷新不稳定**:在 UI/产品层面接受系统限制,并通过缓存提升体验
|
||||||
|
|
||||||
129
spec_kit/Client Bootstrap/spec.md
Normal file
129
spec_kit/Client Bootstrap/spec.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
# Client Bootstrap(高层规范)
|
||||||
|
|
||||||
|
## 1. 背景与目标
|
||||||
|
|
||||||
|
本需求用于初始化「正念 APP」客户端工程的高层规范,确保项目具备一致的工程结构、环境区分、国际化、多端调试与打包流程,并为后续推送、小组件、情绪卡片等一期功能落地提供清晰边界。
|
||||||
|
|
||||||
|
### 目标
|
||||||
|
|
||||||
|
- **工程可运行**:开发者可在本地启动 Expo 开发服务器并在模拟器/真机调试
|
||||||
|
- **环境可切换**:支持 dev/prod 两套环境变量与配置切换
|
||||||
|
- **多语言可扩展**:支持 CN/EN/ES/PT/TC 五种语言,并具备动态切换与回退策略
|
||||||
|
- **目录标准化**:采用标准 Expo + TypeScript 目录结构,便于持续扩展
|
||||||
|
- **打包可执行**:支持 EAS Build 的 iOS 打包流程(dev/prod 基本约定)
|
||||||
|
|
||||||
|
### 非目标(本阶段不做)
|
||||||
|
|
||||||
|
- 不在本阶段实现完整业务 UI/交互细节(以工程骨架与规范为主)
|
||||||
|
- 不在本阶段实现后端接口与数据库设计(由 `server/` 侧需求承接)
|
||||||
|
- 不保证小组件在所有刷新频率下的严格准时(受 iOS 系统限制)
|
||||||
|
|
||||||
|
## 2. 范围与交付物
|
||||||
|
|
||||||
|
### 范围
|
||||||
|
|
||||||
|
- 客户端工程初始化与文档化(`client/`)
|
||||||
|
- 推送/小组件/卡片滑动的高层能力约束与接口对齐点(不实现具体代码亦可)
|
||||||
|
- 国际化(i18n)规范与资源组织方式
|
||||||
|
|
||||||
|
### 交付物
|
||||||
|
|
||||||
|
- **`client/README.md`**:可运行、可联调、可打包、可扩展的客户端说明文档
|
||||||
|
- **客户端工程目录结构规范**:`app/`(可选 expo-router)+ `src/` 的职责划分
|
||||||
|
- **i18n 规范**:语言码、资源文件组织、回退策略、动态切换策略
|
||||||
|
|
||||||
|
## 3. 用户与核心使用场景
|
||||||
|
|
||||||
|
- **用户**:宝妈群体(情绪支持与正念练习)
|
||||||
|
- **开发者场景**:
|
||||||
|
- 新成员可按文档快速启动项目并联调后端
|
||||||
|
- 能在 dev/prod 之间切换并进行 iOS 构建
|
||||||
|
- 能新增一条文案并同步到五种语言资源中
|
||||||
|
|
||||||
|
## 4. 功能需求(高层)
|
||||||
|
|
||||||
|
### 4.1 基础工程能力
|
||||||
|
|
||||||
|
- 支持 Expo 本地启动与 iOS/Android 运行入口
|
||||||
|
- 支持网络请求基座(Axios 或 Fetch)与 API Base URL 配置
|
||||||
|
- 支持状态管理基座(Zustand )
|
||||||
|
|
||||||
|
### 4.2 环境区分(dev / prod)
|
||||||
|
|
||||||
|
- 客户端使用 `.env.dev` / `.env.prod` 管理环境变量
|
||||||
|
- `.env` 不提交到 Git,提供 `.env.example` 作为模板
|
||||||
|
- 推荐使用 `EXPO_PUBLIC_` 前缀的公开变量(如 `EXPO_PUBLIC_API_BASE_URL`)
|
||||||
|
- dev/prod 的 bundle id 约定:
|
||||||
|
- dev:`com.damer.mindfulness.dev`
|
||||||
|
- prod:`com.damer.mindfulness`
|
||||||
|
|
||||||
|
### 4.3 多语言(CN/EN/ES/PT/TC)
|
||||||
|
|
||||||
|
- 支持语言码:
|
||||||
|
- CN:`zh-CN`
|
||||||
|
- EN:`en`
|
||||||
|
- ES:`es`
|
||||||
|
- PT:`pt`
|
||||||
|
- TC:`zh-TW`
|
||||||
|
- 推荐方案:`i18next` + `react-i18next` + `expo-localization`
|
||||||
|
- 文案 key 需稳定(点分层),支持插值,避免字符串拼接
|
||||||
|
- **默认语言选择**:优先使用用户当前**设备语言**(在支持列表内时生效)
|
||||||
|
- **手动切换**:在“设置”中支持切换语言,切换后持久化(例如 AsyncStorage),并在后续启动中优先生效
|
||||||
|
- **回退策略**:设备语言不在支持列表时,回退到 `zh-CN`(或团队指定的默认语言)
|
||||||
|
- 如后端需要多语言(如推送文案),语言码需与客户端一致,并在接口中明确 `lang`
|
||||||
|
|
||||||
|
### 4.4 推送(一期能力约束)
|
||||||
|
|
||||||
|
- 客户端需能:
|
||||||
|
- 申请通知权限
|
||||||
|
- 获取 Push Token
|
||||||
|
- 将 Token 上报后端(接口字段与鉴权方式由联调阶段确认)
|
||||||
|
- 后端需能:
|
||||||
|
- 保存 Token
|
||||||
|
- 按策略触发定时推送(固定时间/用户自定义时间)
|
||||||
|
- 支持后台配置推送内容并更新
|
||||||
|
|
||||||
|
### 4.5 iOS 小组件(一期能力约束)
|
||||||
|
|
||||||
|
- 展示:当前情绪文字 + 背景图/渐变
|
||||||
|
- 支持:小/中/大三种尺寸
|
||||||
|
- 数据源:本地缓存或后端拉取(需定义刷新/缓存策略)
|
||||||
|
- 刷新:可配置刷新频率,但需接受 iOS 系统限制导致的不确定性
|
||||||
|
|
||||||
|
### 4.6 情绪卡片滑动(一期能力约束)
|
||||||
|
|
||||||
|
- 展示情绪卡片列表
|
||||||
|
- 左右滑动切换下一张(类似 Tinder)
|
||||||
|
- 收藏/分享作为后续迭代(不纳入本阶段验收)
|
||||||
|
|
||||||
|
## 5. 技术与工程规范
|
||||||
|
|
||||||
|
### 5.1 目录结构(标准建议)
|
||||||
|
|
||||||
|
- 推荐 `expo-router`:使用 `app/` 作为路由目录
|
||||||
|
- 业务代码统一放入 `src/`,并按功能域拆分 `features/`
|
||||||
|
- 资源放入 `assets/`
|
||||||
|
- 配置文件:`app.json` 或 `app.config.ts`、`eas.json`、`package.json`
|
||||||
|
|
||||||
|
### 5.2 依赖与工具(建议)
|
||||||
|
|
||||||
|
- Node.js:LTS
|
||||||
|
- 包管理器:pnpm(团队统一)
|
||||||
|
- Expo:推荐通过 `npx expo` 使用,避免全局版本漂移
|
||||||
|
- iOS 真机调试:macOS + Xcode(如需)
|
||||||
|
|
||||||
|
## 6. 验收标准
|
||||||
|
|
||||||
|
- 文档层面:
|
||||||
|
- `client/README.md` 明确:启动方式、环境变量、联调方式、EAS 构建方式、多语言规范、标准目录结构
|
||||||
|
- 工程层面(当客户端代码提交后):
|
||||||
|
- 可在本地 `pnpm install` 后启动并运行到 iOS/Android
|
||||||
|
- 可切换 dev/prod 环境变量并影响 API Base URL
|
||||||
|
- i18n 可在运行时切换到 `zh-CN/en/es/pt/zh-TW` 并生效
|
||||||
|
|
||||||
|
## 7. 风险与注意事项
|
||||||
|
|
||||||
|
- iOS 小组件刷新频率受系统限制:规范中应强调“尽力而为”的体验目标
|
||||||
|
- 真机联调 `localhost` 不可用:需使用局域网 IP 或内网穿透
|
||||||
|
- 推送与证书/配置相关:需在 EAS 配置与平台后台完成相应设置
|
||||||
|
|
||||||
130
spec_kit/Client Bootstrap/tasks.md
Normal file
130
spec_kit/Client Bootstrap/tasks.md
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# Client Bootstrap(任务清单)
|
||||||
|
|
||||||
|
> 说明:本清单根据 `plan.md` 拆解,要求“可执行、可验收、可标记”。
|
||||||
|
> 标记规则:执行完成后将对应项从 `- [ ]` 改为 `- [x]`,并把“状态”改为 **已完成**;进行中改为 **进行中**;阻塞写明原因与解除条件。
|
||||||
|
|
||||||
|
## 0. 清单状态说明
|
||||||
|
|
||||||
|
- **状态**:未开始 / 进行中 / 已完成 / 阻塞
|
||||||
|
- **阻塞**:必须写明阻塞点(例如“缺少 Expo 工程代码”)与下一步需要谁提供什么
|
||||||
|
|
||||||
|
## 1. 文档完善(README 为准入门)
|
||||||
|
|
||||||
|
- [x] **更新 `client/README.md` 的包管理器命令统一为 pnpm**
|
||||||
|
- **状态**:已完成
|
||||||
|
- **内容**:将 `npm install`、`npm run *` 改为 `pnpm install`、`pnpm *`(或在 README 中明确“团队统一 pnpm”且给出 pnpm 命令为主)
|
||||||
|
- **验收**:README 中安装/启动/平台命令不混用 npm/pnpm
|
||||||
|
|
||||||
|
- [x] **补充 i18n “设置页切换语言”的落地说明**
|
||||||
|
- **状态**:已完成
|
||||||
|
- **内容**:在多语言章节增加“语言优先级:用户设置 > 设备语言 > 默认回退(zh-CN)”与“设置入口建议位置”
|
||||||
|
- **验收**:README 的 i18n 规则与 `spec.md/plan.md` 完全一致
|
||||||
|
|
||||||
|
## 2. 初始化 Expo 工程(若仓库尚未提交客户端代码)
|
||||||
|
|
||||||
|
> 若 `client/` 下已经存在 Expo 工程(`package.json`、`app.json/app.config.*`),跳过本章节并在任务上标记“已完成(已存在)”。
|
||||||
|
|
||||||
|
- [x] **在 `client/` 初始化 Expo + TypeScript 工程**
|
||||||
|
- **状态**:已完成
|
||||||
|
- **命令**:
|
||||||
|
- `cd client`
|
||||||
|
- `npx create-expo-app@latest .`(按提示选择 TypeScript 模板)
|
||||||
|
- **产出**:生成 `package.json`、`app.json/app.config.*`、`assets/`、入口代码等
|
||||||
|
- **验收**:`client/` 下存在可运行的 Expo 工程基础文件
|
||||||
|
|
||||||
|
- [x] **切换为 pnpm 并安装依赖**
|
||||||
|
- **状态**:已完成
|
||||||
|
- **命令**:
|
||||||
|
- `cd client`
|
||||||
|
- `pnpm install`
|
||||||
|
- **验收**:依赖安装成功,可执行 `pnpm start`
|
||||||
|
|
||||||
|
## 3. 建立标准目录结构(目标态)
|
||||||
|
|
||||||
|
- [x] **创建标准目录骨架(`app/` + `src/` + `assets/`)**
|
||||||
|
- **状态**:已完成
|
||||||
|
- **目录**:
|
||||||
|
- `app/`(expo-router 路由目录,若采用)
|
||||||
|
- `src/components/`
|
||||||
|
- `src/constants/`
|
||||||
|
- `src/features/push/`
|
||||||
|
- `src/features/widget/`
|
||||||
|
- `src/features/cards/`
|
||||||
|
- `src/hooks/`
|
||||||
|
- `src/i18n/locales/`
|
||||||
|
- `src/services/`
|
||||||
|
- `src/store/`
|
||||||
|
- `src/types/`
|
||||||
|
- `src/utils/`
|
||||||
|
- **验收**:目录与 `client/README.md` 中的“标准目录结构”一致
|
||||||
|
|
||||||
|
## 4. 接入 i18n 基座(CN/EN/ES/PT/TC)
|
||||||
|
|
||||||
|
- [x] **引入 i18n 依赖并记录版本**
|
||||||
|
- **状态**:已完成(i18next@25.8.0 / react-i18next@16.5.4 / expo-localization@17.0.8 / AsyncStorage@2.2.0)
|
||||||
|
- **依赖**:`i18next`、`react-i18next`、`expo-localization`(如需持久化再加 `@react-native-async-storage/async-storage`)
|
||||||
|
- **验收**:`package.json` 中依赖齐全,且能正常打包/运行
|
||||||
|
|
||||||
|
- [x] **创建语言资源文件(5 份)**
|
||||||
|
- **状态**:已完成
|
||||||
|
- **文件**:
|
||||||
|
- `src/i18n/locales/zh-CN.json`
|
||||||
|
- `src/i18n/locales/en.json`
|
||||||
|
- `src/i18n/locales/es.json`
|
||||||
|
- `src/i18n/locales/pt.json`
|
||||||
|
- `src/i18n/locales/zh-TW.json`
|
||||||
|
- **内容要求**:至少包含 `common.ok/common.cancel` 等最小 key 集合(五种语言都要齐全)
|
||||||
|
- **验收**:同一 key 在 5 个文件都存在,不允许缺 key
|
||||||
|
|
||||||
|
- [x] **实现 `src/i18n/index.ts` 初始化与语言优先级**
|
||||||
|
- **状态**:已完成
|
||||||
|
- **规则**:
|
||||||
|
- ① 用户设置语言(存在则优先)
|
||||||
|
- ② 设备语言(在支持列表内)
|
||||||
|
- ③ 默认回退(`zh-CN` 或团队指定默认)
|
||||||
|
- **验收**:能在运行时切换语言并立即生效;重启后仍保持用户选择
|
||||||
|
|
||||||
|
- [x] **提供语言设置的状态存储与 API(供设置页调用)**
|
||||||
|
- **状态**:已完成(已提供 `getLanguagePreference/setLanguagePreference/changeLanguage/clearLanguagePreference`)
|
||||||
|
- **建议**:`src/store/` 或 `src/features/settings/`(如后续新增)暴露 `setLanguage/getLanguage`
|
||||||
|
- **验收**:设置页接入时只需调用一个清晰接口即可完成切换
|
||||||
|
|
||||||
|
## 5. 环境变量与联调基座
|
||||||
|
|
||||||
|
- [x] **补齐 `.env.example` 并规范 `.env.dev/.env.prod`**
|
||||||
|
- **状态**:已完成(已创建 `.env.example`;注意该文件在当前环境会被 globalignore 过滤,终端可见但编辑工具不可见)
|
||||||
|
- **字段**:`EXPO_PUBLIC_API_BASE_URL`、`EXPO_PUBLIC_ENV`
|
||||||
|
- **验收**:文档与实际读取方式一致;真机联调注意事项明确
|
||||||
|
|
||||||
|
- [ ] **实现 API 客户端封装(`src/services/`)**
|
||||||
|
- **状态**:未开始
|
||||||
|
- **要求**:从 `EXPO_PUBLIC_API_BASE_URL` 读取 baseURL;统一错误处理与超时策略(先给默认)
|
||||||
|
- **验收**:任意页面可通过统一 client 发起请求,且环境切换能影响 baseURL
|
||||||
|
|
||||||
|
## 6. 模块骨架(push/widget/cards)
|
||||||
|
|
||||||
|
- [ ] **Push 模块:权限/Token/上报接口占位**
|
||||||
|
- **状态**:未开始
|
||||||
|
- **要求**:提供最小 API(例如 `requestPermission() / getToken() / reportToken()`)
|
||||||
|
- **验收**:模块对外 API 清晰,后续联调只需补实现不需改结构
|
||||||
|
|
||||||
|
- [ ] **Widget 模块:数据模型/缓存/刷新策略文档化与占位**
|
||||||
|
- **状态**:未开始
|
||||||
|
- **要求**:定义“情绪文案数据结构”、缓存 key、刷新建议(强调 iOS 限制)
|
||||||
|
- **验收**:能明确后续 iOS 小组件实现需要的数据与刷新行为
|
||||||
|
|
||||||
|
- [ ] **Cards 模块:数据模型与卡片滑动组件占位**
|
||||||
|
- **状态**:未开始
|
||||||
|
- **验收**:后续 UI 实现可以直接在该模块内扩展,不破坏目录约定
|
||||||
|
|
||||||
|
## 7. EAS Build(iOS 打包链路)
|
||||||
|
|
||||||
|
- [ ] **创建并配置 `eas.json`(development profile)**
|
||||||
|
- **状态**:未开始
|
||||||
|
- **要求**:区分 dev/prod;注入对应环境变量;bundle id 符合约定
|
||||||
|
- **验收**:可触发 iOS development 构建并产出安装包/构建产物
|
||||||
|
|
||||||
|
- [ ] **验证 iOS 构建最小闭环**
|
||||||
|
- **状态**:未开始
|
||||||
|
- **验收**:构建成功;App 能启动;基础 i18n 与环境变量读取无崩溃
|
||||||
|
|
||||||
132
spec_kit/Onboarding App Shell/spec.md
Normal file
132
spec_kit/Onboarding App Shell/spec.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# Onboarding App Shell(高层规范)
|
||||||
|
|
||||||
|
## 1. 背景与目标
|
||||||
|
|
||||||
|
本需求用于落地客户端的“首次进入体验 + 主应用壳 + 关键入口”,包含:
|
||||||
|
|
||||||
|
- 3–5 页可跳过的 Onboarding(问题页均可跳过)
|
||||||
|
- Push 设置页(可跳过)
|
||||||
|
- 进入主 App:支持“点赞/讨厌”交互
|
||||||
|
- 收藏夹入口
|
||||||
|
- 通用设置入口:版本信息、iOS 小组件入口
|
||||||
|
|
||||||
|
### 目标
|
||||||
|
|
||||||
|
- **首次体验顺畅**:用户可完成或跳过 Onboarding 与 Push 设置,不阻塞进入主功能
|
||||||
|
- **主功能可用**:进入主 App 后可对内容进行“点赞/讨厌”,并能进入收藏夹与设置
|
||||||
|
- **入口清晰**:收藏夹与设置入口明显且可从主界面到达
|
||||||
|
- **状态可持久化**:Onboarding 完成状态、Push 设置选择(是否跳过)、点赞/讨厌记录、收藏记录可持久化
|
||||||
|
|
||||||
|
### 非目标(本阶段不做)
|
||||||
|
|
||||||
|
- 不实现完整推送业务闭环(仅做“设置入口/引导”,且可跳过)
|
||||||
|
- 不实现真实推荐算法(内容来源可先用本地 mock 或固定列表)
|
||||||
|
- iOS 小组件不要求完整上线交互(先做“入口/说明/引导”即可,实际小组件实现可拆分到后续模块)
|
||||||
|
|
||||||
|
## 2. 用户流程(高层)
|
||||||
|
|
||||||
|
### 2.1 首次启动
|
||||||
|
|
||||||
|
1. App 启动
|
||||||
|
2. Onboarding(3–5 页)
|
||||||
|
- 每页都提供 **跳过**(Skip)
|
||||||
|
- 支持上一页/下一页(可选)
|
||||||
|
3. Push 设置页(可选进入,且可跳过)
|
||||||
|
4. 进入主 App(内容卡片/列表)并可点赞/讨厌
|
||||||
|
|
||||||
|
### 2.2 非首次启动
|
||||||
|
|
||||||
|
- 若已完成(或跳过)Onboarding:直接进入主 App
|
||||||
|
|
||||||
|
## 3. 功能需求(高层)
|
||||||
|
|
||||||
|
### 3.1 Onboarding(3–5 页,问题可跳过)
|
||||||
|
|
||||||
|
- **页数**:3–5 页(可配置)
|
||||||
|
- **内容**:以“问题/偏好收集”为主(具体题目可后续在内容配置中迭代)
|
||||||
|
- **跳过策略**:
|
||||||
|
- 每页可跳过(不强制填写)
|
||||||
|
- 最终可一键跳过整个 Onboarding
|
||||||
|
- **视觉反馈**:
|
||||||
|
- 进度指示(例如 1/5 或进度条)
|
||||||
|
- 清晰的“下一步/跳过”按钮
|
||||||
|
|
||||||
|
### 3.2 Push 设置(可跳过)
|
||||||
|
|
||||||
|
- 提供一页“开启通知”的引导与说明
|
||||||
|
- 支持:
|
||||||
|
- **立即开启**(触发权限申请)
|
||||||
|
- **稍后**(跳过)
|
||||||
|
- 用户选择需持久化(用于后续在设置页再次引导)
|
||||||
|
|
||||||
|
### 3.3 主 App:点赞/讨厌
|
||||||
|
|
||||||
|
- 主界面展示一条“情绪文字/正念短句/卡片内容”
|
||||||
|
- 支持两种操作:
|
||||||
|
- **点赞(Like)**
|
||||||
|
- **讨厌(Dislike)**
|
||||||
|
- 行为结果:
|
||||||
|
- 记录用户操作(本地持久化)
|
||||||
|
- 点赞内容可进入收藏夹(或点赞即收藏,具体规则在实现时确认)
|
||||||
|
|
||||||
|
### 3.4 收藏夹入口
|
||||||
|
|
||||||
|
- 主界面提供“收藏夹”入口
|
||||||
|
- 收藏夹展示已收藏内容列表
|
||||||
|
- 支持取消收藏(可选)
|
||||||
|
|
||||||
|
### 3.5 通用设置入口
|
||||||
|
|
||||||
|
设置页至少包含:
|
||||||
|
|
||||||
|
- **版本信息**:展示 App 版本号(和可选的 build number)
|
||||||
|
- **iOS 小组件**:入口/说明
|
||||||
|
- 提供“如何添加小组件”的引导文案
|
||||||
|
- 若后续实现小组件配置页,可从此入口进入
|
||||||
|
- **通知设置**(可选,但建议有):
|
||||||
|
- 展示当前通知状态
|
||||||
|
- 允许用户再次进入系统设置开启通知(iOS/Android 跳转能力按平台支持情况实现)
|
||||||
|
|
||||||
|
## 4. 数据与持久化(高层)
|
||||||
|
|
||||||
|
需要持久化的最小集合:
|
||||||
|
|
||||||
|
- `onboarding.completed`: boolean
|
||||||
|
- `push.promptState`: `enabled | skipped | unknown`
|
||||||
|
- `content.reactions`: 记录内容 ID 与 like/dislike
|
||||||
|
- `favorites.items`: 收藏内容列表(按 ID 存储)
|
||||||
|
|
||||||
|
> 说明:本期可采用本地存储(例如 AsyncStorage);后续可扩展为与后端同步。
|
||||||
|
|
||||||
|
## 5. 页面与导航(建议)
|
||||||
|
|
||||||
|
- `Onboarding`(多页)
|
||||||
|
- `PushPrompt`(可跳过)
|
||||||
|
- `Home`(点赞/讨厌)
|
||||||
|
- `Favorites`(收藏夹)
|
||||||
|
- `Settings`(通用设置:版本、iOS 小组件)
|
||||||
|
|
||||||
|
若使用 `expo-router`:
|
||||||
|
|
||||||
|
- Onboarding 可用分组路由(例如 `(onboarding)`)管理,并在完成后跳转到 `(tabs)` 或 `Home`
|
||||||
|
|
||||||
|
## 6. 验收标准
|
||||||
|
|
||||||
|
- 首次进入:
|
||||||
|
- 可完成或跳过 Onboarding(任意页可跳过)
|
||||||
|
- Push 设置页可跳过
|
||||||
|
- 最终一定能进入主 App
|
||||||
|
- 主功能:
|
||||||
|
- 主界面可对内容执行点赞/讨厌并记录
|
||||||
|
- 存在收藏夹入口且可看到收藏内容
|
||||||
|
- 存在设置入口且能看到版本与 iOS 小组件入口/说明
|
||||||
|
- 持久化:
|
||||||
|
- 重启 App 后,Onboarding 不会重复出现(除非主动清除状态)
|
||||||
|
- 点赞/收藏记录仍存在
|
||||||
|
|
||||||
|
## 7. 风险与注意事项
|
||||||
|
|
||||||
|
- 通知权限申请时机:过早弹窗可能导致拒绝率上升,建议通过引导页解释后再触发
|
||||||
|
- iOS 小组件能力受系统限制:刷新频率与内容更新不保证严格实时
|
||||||
|
- 内容来源:若暂未接后端,需提供可替换的 mock 内容与唯一 ID 方案
|
||||||
|
|
||||||
152
spec_kit/Project Bootstrap/plan.md
Normal file
152
spec_kit/Project Bootstrap/plan.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# Project Bootstrap(技术计划)
|
||||||
|
|
||||||
|
## 1. 目标与约束(来自 spec)
|
||||||
|
|
||||||
|
- 目标:完成项目初始化可落地的工程与基础设施约定(client/server + dev/prod 隔离 + MySQL/Redis 命名与访问边界)。
|
||||||
|
- 关键选择:
|
||||||
|
- 后端依赖:`requirements.txt` + `pip`
|
||||||
|
- 定时/异步:Celery + Redis
|
||||||
|
- 环境:仅 dev / prod
|
||||||
|
- Redis 隔离:ACL + key 前缀(`dev:*` / `pro:*`)
|
||||||
|
- 部署:**不在本需求中提供 Docker 起 MySQL/Redis**(MySQL/Redis 已部署),后端通过环境变量连接
|
||||||
|
- 数据库迁移:Alembic
|
||||||
|
- Redis 生产环境:**maxmemory=256MB 硬限制**
|
||||||
|
|
||||||
|
## 2. 总体方案
|
||||||
|
|
||||||
|
### 2.1 MySQL(schema 隔离)
|
||||||
|
|
||||||
|
- prod schema:`mindfulness`
|
||||||
|
- dev schema:`mindfulness_dev`
|
||||||
|
- 字符集:`utf8mb4`
|
||||||
|
- 连接方式:后端通过 `DATABASE_URL`(SQLAlchemy Async)连接到对应 schema
|
||||||
|
|
||||||
|
### 2.2 Redis(ACL + 前缀隔离 + 内存限制)
|
||||||
|
|
||||||
|
- 单 Redis 实例(同机同容器可行)。
|
||||||
|
- 通过 **ACL 用户**限制不同环境只能访问各自前缀:
|
||||||
|
- dev 用户:只能访问 `dev:*`
|
||||||
|
- prod 用户:只能访问 `pro:*`
|
||||||
|
- 应用侧硬约束:所有 Redis key 必须带环境前缀(`dev:` / `pro:`)。
|
||||||
|
- 生产 maxmemory:`256mb`,并采用“少用 Redis、短 TTL、避免任务积压”的策略。
|
||||||
|
|
||||||
|
> 重要:Celery broker 以“可靠投递”为优先,计划中默认 **不启用** Celery result backend(或启用时必须设置 TTL)。
|
||||||
|
|
||||||
|
### 2.3 运行方式(不限定部署形态)
|
||||||
|
|
||||||
|
本需求只要求:
|
||||||
|
|
||||||
|
- MySQL 与 Redis **已可访问**
|
||||||
|
- 后端通过 `.env.dev/.env.prod`(或等价环境变量注入方式)获取连接信息
|
||||||
|
- API/Celery/Beat 以任意进程管理方式运行(Docker/systemd/进程托管均可),但需满足验收项
|
||||||
|
|
||||||
|
## 3. 目录与文件落地清单
|
||||||
|
|
||||||
|
> 本计划只定义“应该创建/调整什么”,具体文件内容在 tasks 阶段逐项实现。
|
||||||
|
|
||||||
|
### 3.1 后端依赖与启动入口(server)
|
||||||
|
|
||||||
|
- `server/requirements.txt`
|
||||||
|
- `server/app/main.py`(FastAPI 入口)
|
||||||
|
- `server/app/core/config.py`(pydantic-settings,按 `APP_ENV` 加载 `.env.dev/.env.prod`)
|
||||||
|
- `server/app/worker.py`(Celery app 入口)
|
||||||
|
- `server/alembic.ini` + `server/alembic/`(迁移脚手架)
|
||||||
|
- `server/.env.example`(字段结构,不含真实值)
|
||||||
|
|
||||||
|
### 3.2 基础设施(infra)
|
||||||
|
|
||||||
|
本需求不强制创建 `infra/`(因为 MySQL/Redis 已部署)。
|
||||||
|
|
||||||
|
可选新增(仅用于“文档化配置/备份参考”,不要求参与部署):
|
||||||
|
|
||||||
|
- `infra/redis/users.acl`(ACL 用户定义备份)
|
||||||
|
- `infra/redis/redis.prod.conf`(prod 配置备份,包含 maxmemory=256mb)
|
||||||
|
- `infra/mysql/init/00-create-schemas.sql`(创建 `mindfulness_dev/mindfulness` 的 SQL 备份)
|
||||||
|
|
||||||
|
## 4. Redis ACL 与 maxmemory 的落地方案
|
||||||
|
|
||||||
|
### 4.1 ACL 文件(`infra/redis/users.acl`)
|
||||||
|
|
||||||
|
> 若 Redis 已在服务器配置完成,本节用于“约定与验收”。
|
||||||
|
|
||||||
|
- 定义 `dev_user`、`pro_user` 两个账号
|
||||||
|
- 分别限制 key pattern:
|
||||||
|
- dev:`~dev:*`
|
||||||
|
- prod:`~pro:*`
|
||||||
|
- 建议禁用默认用户,避免未授权访问(推荐):`user default off`
|
||||||
|
|
||||||
|
### 4.2 Redis 配置(`infra/redis/redis.prod.conf`)
|
||||||
|
|
||||||
|
> 若 Redis 已在服务器配置完成,本节用于“约定与验收”。
|
||||||
|
|
||||||
|
- `maxmemory 256mb`
|
||||||
|
- `maxmemory-policy noeviction`(队列场景优先“不丢任务”,宁可让写入失败报警)
|
||||||
|
- 开启 ACL(例如 `aclfile .../users.acl`)
|
||||||
|
|
||||||
|
### 4.3 应用侧约束(在代码实现时落地)
|
||||||
|
|
||||||
|
为确保 ACL 真的起作用,需要做到:
|
||||||
|
|
||||||
|
- **所有业务 key 带前缀**:例如 `dev:cache:...` / `pro:cache:...`
|
||||||
|
- **Celery 队列名带前缀**:例如 `dev:push` / `pro:push`
|
||||||
|
- **禁用或限制 result backend**:默认不启用;如启用必须设置短 TTL(例如 1 小时/1 天)
|
||||||
|
- **任务参数保持小**:只传 id,不传长文本/大 payload,避免队列膨胀导致 Redis 触顶
|
||||||
|
|
||||||
|
## 5. 环境变量与配置约定
|
||||||
|
|
||||||
|
### 5.1 后端 `.env.dev/.env.prod`(字段示例)
|
||||||
|
|
||||||
|
- `APP_ENV=dev|prod`
|
||||||
|
- `DATABASE_URL=.../mindfulness_dev`(dev)或 `.../mindfulness`(prod)
|
||||||
|
- `REDIS_URL=redis://<user>:<pass>@redis:6379/0`
|
||||||
|
- `CELERY_BROKER_URL=redis://<user>:<pass>@redis:6379/0`
|
||||||
|
- `CELERY_RESULT_BACKEND`:默认不配置(或仅在 dev 开启)
|
||||||
|
|
||||||
|
### 5.2 密钥管理
|
||||||
|
|
||||||
|
- `.env.dev/.env.prod` 不进仓库
|
||||||
|
- 提供 `.env.example` 仅展示字段结构
|
||||||
|
|
||||||
|
## 6. 实施步骤(建议顺序)
|
||||||
|
|
||||||
|
### 阶段 A:验证现有 MySQL/Redis 与隔离策略
|
||||||
|
|
||||||
|
- Redis:ACL 生效验证(dev 用户无法访问 `pro:*`,prod 用户无法访问 `dev:*`)
|
||||||
|
- Redis:prod 配置验证(`maxmemory=256mb` 生效)
|
||||||
|
- MySQL:确认 `mindfulness_dev` 与 `mindfulness` 两个 schema 已创建(若未创建则创建)
|
||||||
|
|
||||||
|
### 阶段 B:后端最小可运行骨架
|
||||||
|
|
||||||
|
- `requirements.txt` 固化依赖
|
||||||
|
- FastAPI 最小健康检查接口(如 `/healthz`)
|
||||||
|
- SQLAlchemy 连接与基础会话
|
||||||
|
- Alembic 初始化与一次空迁移验证
|
||||||
|
|
||||||
|
### 阶段 C:Celery 最小闭环
|
||||||
|
|
||||||
|
- Celery app 初始化,broker 指向 Redis(按环境选择 dev_user/pro_user)
|
||||||
|
- 1 个示例任务(例如打印/写库)验证 worker 可消费
|
||||||
|
- 如需要定时:beat 定时触发任务验证
|
||||||
|
|
||||||
|
## 7. 验收与自检清单(plan 阶段定义)
|
||||||
|
|
||||||
|
- **环境隔离**
|
||||||
|
- dev 连接 `mindfulness_dev`,prod 连接 `mindfulness`
|
||||||
|
- dev 的 Redis 凭证无法读写 `pro:*`,反之亦然
|
||||||
|
- **资源约束**
|
||||||
|
- prod Redis `maxmemory=256mb` 生效
|
||||||
|
- 不启用(或短 TTL)result backend,避免长期堆积
|
||||||
|
- **可运行性**
|
||||||
|
- FastAPI 能启动并提供基础接口
|
||||||
|
- Celery worker 能消费任务,beat(如启用)能触发定时任务
|
||||||
|
|
||||||
|
## 8. 风险与应对
|
||||||
|
|
||||||
|
- Redis 256MB 触顶风险:
|
||||||
|
- 控制队列积压(worker 数量/并发)
|
||||||
|
- 禁用或缩短结果存储 TTL
|
||||||
|
- 任务 payload 只传 id
|
||||||
|
- 多环境同机误用风险:
|
||||||
|
- 强制不同 ACL 用户 + 前缀
|
||||||
|
- Celery 队列名带前缀,避免互相消费
|
||||||
|
|
||||||
155
spec_kit/Project Bootstrap/spec.md
Normal file
155
spec_kit/Project Bootstrap/spec.md
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
# Project Bootstrap(高层规范)
|
||||||
|
|
||||||
|
## 1. 背景与目标
|
||||||
|
|
||||||
|
本需求用于完成「正念 APP」项目的**仓库初始化与工程约定落地**,覆盖客户端(React Native + Expo)与后端(FastAPI)的基础目录结构、文档、环境区分与基础设施依赖(MySQL / Redis)隔离策略,为一期功能(推送、小组件、情绪卡片)后续迭代提供稳定底座。
|
||||||
|
|
||||||
|
### 目标
|
||||||
|
|
||||||
|
- **仓库结构清晰**:`client/`、`server/`、`infra/`、`scripts/`、`.gitea/` 等职责明确
|
||||||
|
- **环境可区分(dev / prod)**:客户端与后端均支持 dev/prod 两套配置,不混用资源
|
||||||
|
- **数据库命名可持续**:MySQL schema 命名统一、可扩展、可一键区分环境
|
||||||
|
- **Redis 隔离可控**:通过 Redis ACL + key 前缀隔离 dev/pro 数据访问,满足同机部署需求
|
||||||
|
- **文档可执行**:新人按 README 可完成本地启动、配置、联调(允许依赖尚未完全落地时使用占位说明)
|
||||||
|
|
||||||
|
### 非目标(本阶段不做)
|
||||||
|
|
||||||
|
- 不在本阶段实现完整业务接口与表结构(仅定义约定与边界)
|
||||||
|
- 不在本阶段实现推送业务全链路(仅确定任务调度与资源隔离策略)
|
||||||
|
- 不在本阶段完成生产级监控/告警/灰度(后续需求承接)
|
||||||
|
|
||||||
|
## 2. 范围与交付物
|
||||||
|
|
||||||
|
### 范围
|
||||||
|
|
||||||
|
- 仓库根目录结构与职责约定
|
||||||
|
- 客户端/后端 README 规范化(可运行步骤、配置说明、常见问题)
|
||||||
|
- dev/prod 环境区分策略(配置文件命名、变量命名、密钥管理)
|
||||||
|
- MySQL schema 命名约定
|
||||||
|
- Redis ACL 与 key 前缀隔离约定
|
||||||
|
|
||||||
|
### 交付物
|
||||||
|
|
||||||
|
- **根 README**:项目概览、目录结构、一期功能说明、技术栈与环境区分说明
|
||||||
|
- **`client/README.md`**:客户端启动/调试/环境切换/打包规范
|
||||||
|
- **`server/README.md`**:后端启动/配置/迁移/任务调度(Celery)说明
|
||||||
|
- **基础设施约定**(文档层面):
|
||||||
|
- MySQL:schema 命名与字符集约定
|
||||||
|
- Redis:ACL 用户与 key 前缀隔离方案(dev/pro)
|
||||||
|
|
||||||
|
## 3. 用户与核心使用场景
|
||||||
|
|
||||||
|
- **开发者(单人或小团队)**
|
||||||
|
- 能快速在本地启动 client/server 并联调
|
||||||
|
- 能明确 dev/prod 使用的数据库与 Redis 资源不互相污染
|
||||||
|
- **运维/部署执行者**
|
||||||
|
- 能根据文档完成最小化部署(即使暂不容器化,也应明确运行依赖)
|
||||||
|
|
||||||
|
## 4. 功能需求(高层)
|
||||||
|
|
||||||
|
### 4.1 仓库结构与职责边界
|
||||||
|
|
||||||
|
- `client/`:React Native + Expo 客户端工程
|
||||||
|
- `server/`:FastAPI 后端工程
|
||||||
|
- `infra/`:Docker / k8s / nginx 等基础设施(可后置,但需预留)
|
||||||
|
- `scripts/`:构建/部署脚本(可后置,但需预留)
|
||||||
|
- `.gitea/`:CI/CD 工作流(可后置,但需预留)
|
||||||
|
|
||||||
|
### 4.2 环境区分(dev / prod)
|
||||||
|
|
||||||
|
- **客户端**:使用 `.env.dev` / `.env.prod`,不提交真实 `.env`,提供 `.env.example`
|
||||||
|
- **后端**:使用 `.env.dev` / `.env.prod`,不提交真实 `.env`,提供 `.env.example`
|
||||||
|
- **资源隔离原则**:
|
||||||
|
- dev/pro 必须使用**不同的 MySQL schema**
|
||||||
|
- dev/pro 必须使用**不同的 Redis 访问权限(ACL)与 key 前缀**
|
||||||
|
|
||||||
|
### 4.3 MySQL 命名约定(schema / 表 / 字段)
|
||||||
|
|
||||||
|
- **schema(数据库名)**:
|
||||||
|
- prod:`mindfulness`
|
||||||
|
- dev:`mindfulness_dev`
|
||||||
|
- **字符集**:统一 `utf8mb4`
|
||||||
|
- **命名风格**:统一小写 + 下划线(snake_case)
|
||||||
|
- **约束命名**(建议,便于迁移与排障):
|
||||||
|
- 唯一索引:`uq_<table>_<column>`
|
||||||
|
- 普通索引:`ix_<table>_<column>`
|
||||||
|
- 外键:`fk_<fromtable>_<column>__<totable>`
|
||||||
|
|
||||||
|
### 4.4 Redis 使用范围与隔离约定
|
||||||
|
|
||||||
|
#### 4.4.1 使用范围
|
||||||
|
|
||||||
|
- Redis 主要用于:
|
||||||
|
- Celery broker(任务队列)
|
||||||
|
- (可选)短 TTL 缓存
|
||||||
|
- 约束:
|
||||||
|
- **不允许**把大对象(长文本/图片/base64)直接写入 Redis
|
||||||
|
- 若启用 Celery 结果存储(result backend),必须设置 TTL,避免无限增长
|
||||||
|
- 若生产环境有资源限制(例如 `maxmemory=256MB`),需在文档中明确并按“少用、短 TTL、避免积压”的策略执行
|
||||||
|
|
||||||
|
#### 4.4.2 dev/pro ACL + key 前缀隔离
|
||||||
|
|
||||||
|
Redis 单实例同机部署时,采用 ACL 限制不同环境只能访问自己的 key 前缀:
|
||||||
|
|
||||||
|
- dev:只能访问 `dev:*`
|
||||||
|
- prod:只能访问 `pro:*`
|
||||||
|
|
||||||
|
ACL 规则示例(以 Redis 6+ 为前提):
|
||||||
|
|
||||||
|
```text
|
||||||
|
# Dev 用户,只能访问 dev:* 前缀
|
||||||
|
user dev_user on >devpassword ~dev:* +@all
|
||||||
|
|
||||||
|
# Pro 用户,只能访问 pro:* 前缀
|
||||||
|
user pro_user on >propassword ~pro:* +@all
|
||||||
|
```
|
||||||
|
|
||||||
|
应用侧约定:
|
||||||
|
|
||||||
|
- 所有 Redis key 必须以环境前缀开头(dev 用 `dev:`,prod 用 `pro:`)
|
||||||
|
- Celery 队列名/键名建议也带环境前缀,避免同机多环境互相影响
|
||||||
|
|
||||||
|
## 5. 技术与工程规范(高层)
|
||||||
|
|
||||||
|
### 5.1 文档规范
|
||||||
|
|
||||||
|
- README 必须包含:
|
||||||
|
- 环境准备(版本要求)
|
||||||
|
- 安装依赖与启动命令
|
||||||
|
- dev/prod 配置与 `.env.example` 说明
|
||||||
|
- 数据库/缓存/队列依赖说明
|
||||||
|
- 常见问题与排障建议
|
||||||
|
|
||||||
|
### 5.2 密钥与敏感信息管理
|
||||||
|
|
||||||
|
- 禁止将真实 IP/账号/密码/Token 写入仓库文档与代码
|
||||||
|
- 所有敏感信息通过 `.env.dev/.env.prod` 注入
|
||||||
|
- 仓库仅提供 `.env.example` 展示字段结构
|
||||||
|
|
||||||
|
### 5.3 后端 Python 版本与依赖安装方式
|
||||||
|
|
||||||
|
- **Python 版本基线**:建议 **Python 3.11+**(与 FastAPI / SQLAlchemy 2.x 异步生态兼容性更好)
|
||||||
|
- **依赖管理方式(二选一,团队需统一)**:
|
||||||
|
- **方案 A:`requirements.txt` + `pip`**
|
||||||
|
- 安装:`pip install -r requirements.txt`
|
||||||
|
- 适用:简单直接,适合快速启动与小团队
|
||||||
|
|
||||||
|
|
||||||
|
> 约定:后端 README 需要明确项目采用的方案 A 或方案 B,并给出可复制执行的安装与启动命令。
|
||||||
|
|
||||||
|
## 6. 验收标准
|
||||||
|
|
||||||
|
- 仓库层面:
|
||||||
|
- 目录结构与根 README 清晰,能让新成员理解项目模块划分
|
||||||
|
- 环境隔离层面:
|
||||||
|
- 明确 dev/pro MySQL schema 分别为 `mindfulness_dev` 与 `mindfulness`
|
||||||
|
- 明确 Redis 使用 ACL 限制 dev/pro 前缀访问,并写清 key 前缀约定
|
||||||
|
- 文档可执行层面:
|
||||||
|
- `client/README.md` 与 `server/README.md` 至少能指导完成“本地启动 + 配置注入”的完整流程(若代码未落地,需明确占位与替换点)
|
||||||
|
|
||||||
|
## 7. 风险与注意事项
|
||||||
|
|
||||||
|
- Redis 若设置较小 `maxmemory`,在任务积压或无 TTL 的缓存策略下可能触发内存压力,需要通过限流、缩短 TTL、提高消费能力解决
|
||||||
|
- dev/pro 同机部署时,除 ACL 外还需避免队列名、key 名混用;建议在配置中显式标注环境前缀
|
||||||
|
- 推送依赖外部网络(Expo),需在任务侧加入超时与重试策略,避免偶发失败导致体验不稳定
|
||||||
|
|
||||||
125
spec_kit/Project Bootstrap/tasks.md
Normal file
125
spec_kit/Project Bootstrap/tasks.md
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
# Project Bootstrap(任务清单)
|
||||||
|
|
||||||
|
> 说明:本清单用于把 `plan.md` 拆成“可逐条执行”的任务。执行完成后请把对应项从 `[ ]` 勾选为 `[x]`。
|
||||||
|
|
||||||
|
## 0. 约定与前置
|
||||||
|
|
||||||
|
- [ ] **确认后端依赖管理方式**:使用 `requirements.txt + pip`
|
||||||
|
- [ ] **确认环境**:仅 dev / prod
|
||||||
|
- [ ] **确认 Redis 方案**:单实例 + ACL(`dev:*`/`pro:*`)+ prod `maxmemory=256mb`(硬限制)
|
||||||
|
- [ ] **确认迁移工具**:Alembic
|
||||||
|
- [ ] **确认部署方式**:MySQL/Redis 已部署,后端通过环境变量连接(不做 Docker 起 MySQL/Redis)
|
||||||
|
|
||||||
|
## A. 现有 MySQL/Redis 验证与环境隔离确认
|
||||||
|
|
||||||
|
### A1. MySQL(schema)确认
|
||||||
|
|
||||||
|
- [ ] 确认 dev schema:`mindfulness_dev` 已存在(字符集 `utf8mb4`)
|
||||||
|
- [ ] 确认 prod schema:`mindfulness` 已存在(字符集 `utf8mb4`)
|
||||||
|
|
||||||
|
### A2. Redis(ACL + maxmemory=256MB)确认
|
||||||
|
|
||||||
|
- [ ] 确认 Redis 已启用 ACL,并存在账号:
|
||||||
|
- [ ] `dev_user` 仅允许访问 `~dev:*`
|
||||||
|
- [ ] `pro_user` 仅允许访问 `~pro:*`
|
||||||
|
- [ ] 确认生产环境 Redis 内存限制:
|
||||||
|
- [ ] `maxmemory=256mb` 生效
|
||||||
|
- [ ] `maxmemory-policy=noeviction` 生效(队列优先,不丢任务)
|
||||||
|
|
||||||
|
### A3. Redis ACL 验收(必须可复现)
|
||||||
|
|
||||||
|
- [ ] 使用 `dev_user` 写入 `dev:test=1` 成功
|
||||||
|
- [ ] 使用 `dev_user` 写入 `pro:test=1` 失败(权限不足)
|
||||||
|
- [ ] 使用 `pro_user` 写入 `pro:test=1` 成功
|
||||||
|
- [ ] 使用 `pro_user` 写入 `dev:test=1` 失败(权限不足)
|
||||||
|
|
||||||
|
### A4. 可选:把现有配置“备份到仓库”(不参与部署)
|
||||||
|
|
||||||
|
- [ ] (可选)新建 `infra/redis/users.acl`(把线上 ACL 规则复制备份,注意去掉真实密码)
|
||||||
|
- [ ] (可选)新建 `infra/redis/redis.prod.conf`(把线上关键参数备份:maxmemory/noeviction/aclfile 等)
|
||||||
|
- [ ] (可选)新建 `infra/mysql/init/00-create-schemas.sql`(备份 schema 创建脚本)
|
||||||
|
|
||||||
|
## B. 后端工程骨架(server/)落地
|
||||||
|
|
||||||
|
> 当前 `server/` 目录可能尚未创建代码文件,本阶段目标是“能启动、能连库、能跑迁移、能跑 Celery”。
|
||||||
|
|
||||||
|
### B1. 依赖与环境变量模板
|
||||||
|
|
||||||
|
- [ ] 新增 `server/requirements.txt`
|
||||||
|
- [ ] FastAPI、uvicorn
|
||||||
|
- [ ] SQLAlchemy 2.x(异步)+ MySQL driver(如 `aiomysql`)
|
||||||
|
- [ ] pydantic-settings(pydantic v2)
|
||||||
|
- [ ] alembic
|
||||||
|
- [ ] celery、redis(Python 客户端)
|
||||||
|
- [ ] pytest
|
||||||
|
- [ ] 新增 `server/.env.example`(仅字段结构,不含真实值)
|
||||||
|
- [ ] `APP_ENV=dev|prod`
|
||||||
|
- [ ] `DATABASE_URL=.../mindfulness_dev`(dev)或 `.../mindfulness`(prod)
|
||||||
|
- [ ] `REDIS_URL=redis://<user>:<pass>@<REDIS_HOST>:6379/0`
|
||||||
|
- [ ] `CELERY_BROKER_URL=redis://<user>:<pass>@<REDIS_HOST>:6379/0`
|
||||||
|
- [ ] (默认不写)`CELERY_RESULT_BACKEND`
|
||||||
|
|
||||||
|
### B2. FastAPI 最小入口
|
||||||
|
|
||||||
|
- [ ] 新增 `server/app/main.py`
|
||||||
|
- [ ] 创建 FastAPI 实例 `app`
|
||||||
|
- [ ] 增加健康检查接口 `/healthz`
|
||||||
|
- [ ] 新增 `server/app/core/config.py`
|
||||||
|
- [ ] 使用 `pydantic-settings`
|
||||||
|
- [ ] 根据 `APP_ENV` 加载 `.env.dev` / `.env.prod`
|
||||||
|
|
||||||
|
### B3. SQLAlchemy 与数据库连接
|
||||||
|
|
||||||
|
- [ ] 新增 `server/app/db/`(目录)
|
||||||
|
- [ ] 新增 `server/app/db/session.py`
|
||||||
|
- [ ] 从 `DATABASE_URL` 创建 async engine
|
||||||
|
- [ ] 提供 session 工厂
|
||||||
|
|
||||||
|
### B4. Alembic 初始化
|
||||||
|
|
||||||
|
- [ ] 初始化 Alembic:
|
||||||
|
- [ ] 生成 `server/alembic.ini`
|
||||||
|
- [ ] 生成 `server/alembic/`(env.py / versions/)
|
||||||
|
- [ ] 配置从环境变量读取数据库连接
|
||||||
|
- [ ] 验收:
|
||||||
|
- [ ] 能执行 `alembic revision -m "init"`(即使空迁移也可)
|
||||||
|
- [ ] 能执行 `alembic upgrade head`
|
||||||
|
|
||||||
|
## C. Celery 最小闭环(Redis broker + 队列前缀)
|
||||||
|
|
||||||
|
### C1. Celery 入口与任务示例
|
||||||
|
|
||||||
|
- [ ] 新增 `server/app/worker.py`
|
||||||
|
- [ ] Celery 初始化使用 `CELERY_BROKER_URL`
|
||||||
|
- [ ] 默认 **不启用** result backend(或仅 dev 可选)
|
||||||
|
- [ ] 配置默认队列名带环境前缀(例如 `dev:push` / `pro:push`)
|
||||||
|
- [ ] 新增 1 个示例任务(例如 `server/app/tasks/ping.py`)
|
||||||
|
- [ ] 任务只传小参数(如字符串/ID)
|
||||||
|
|
||||||
|
### C2. Worker/Beat 启动与验收
|
||||||
|
|
||||||
|
- [ ] Worker 验收:
|
||||||
|
- [ ] `celery -A app.worker:celery_app worker -l info` 可启动
|
||||||
|
- [ ] 发送任务后能被消费
|
||||||
|
- [ ] Beat(如需要定时):
|
||||||
|
- [ ] `celery -A app.worker:celery_app beat -l info` 可启动
|
||||||
|
- [ ] 能按周期触发示例任务
|
||||||
|
|
||||||
|
## D. 文档与验收补全
|
||||||
|
|
||||||
|
### D1. 更新 README 与约定一致
|
||||||
|
|
||||||
|
- [ ] 更新 `server/README.md`
|
||||||
|
- [ ] 明确:`requirements.txt + pip` 作为依赖安装方式
|
||||||
|
- [ ] 明确:dev/pro MySQL schema(`mindfulness_dev`/`mindfulness`)
|
||||||
|
- [ ] 明确:Redis ACL + key 前缀、prod `maxmemory=256mb` 硬限制
|
||||||
|
- [ ] 明确:Celery 默认不启用 result backend(或启用需 TTL)
|
||||||
|
- [ ] (可选)更新根 `README.md`
|
||||||
|
- [ ] 追加:基础设施依赖与 dev/pro 隔离约定摘要
|
||||||
|
|
||||||
|
### D2. 最终验收清单(完成后统一勾选)
|
||||||
|
|
||||||
|
- [ ] **环境隔离**:dev/pro MySQL schema 与 Redis ACL 均生效
|
||||||
|
- [ ] **资源约束**:prod Redis `maxmemory=256mb` 生效
|
||||||
|
- [ ] **可运行性**:FastAPI/Celery 可启动并完成最小任务闭环(依赖通过环境变量连接现有 MySQL/Redis)
|
||||||
|
|
||||||
38
spec_kit/overview.md
Normal file
38
spec_kit/overview.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Spec Kit Overview
|
||||||
|
|
||||||
|
本文件用于简短记录当前项目每个 spec 的核心内容摘要,便于快速总览与追踪变更。
|
||||||
|
|
||||||
|
## Client Bootstrap
|
||||||
|
|
||||||
|
- **目标**:初始化正念 APP 客户端工程规范,确保可运行、可切环境、可多语言扩展、目录标准化、可 EAS 打包
|
||||||
|
- **核心范围**:客户端工程骨架与文档(`client/README.md`)、环境变量约定、i18n(CN/EN/ES/PT/TC)、推送/小组件/卡片滑动的一期能力约束
|
||||||
|
- **主要约定**:
|
||||||
|
- 环境:`.env.dev` / `.env.prod`,推荐 `EXPO_PUBLIC_` 前缀
|
||||||
|
- 语言码:`zh-CN/en/es/pt/zh-TW`
|
||||||
|
- 目录:`app/`(可选 expo-router)+ `src/`(features/services/store/utils 等)+ `assets/`
|
||||||
|
- 打包:EAS Build,dev/prod bundle id 约定
|
||||||
|
- **阶段产物**:
|
||||||
|
- `spec_kit/Client Bootstrap/spec.md`
|
||||||
|
- `spec_kit/Client Bootstrap/plan.md`
|
||||||
|
- `spec_kit/Client Bootstrap/tasks.md`
|
||||||
|
- **已完成编码(阶段性)**:
|
||||||
|
- Expo 工程已在 `client/` 初始化,并完成 `pnpm install`
|
||||||
|
- i18n 基座已接入:5 份语言资源 + 设备语言优先/设置可切换/持久化 + 入口初始化
|
||||||
|
|
||||||
|
## Project Bootstrap
|
||||||
|
|
||||||
|
- **目标**:完成项目仓库初始化与工程约定落地,明确 client/server 结构、dev/pro 环境隔离、MySQL/Redis 资源命名与访问边界
|
||||||
|
- **核心范围**:根目录结构与文档、dev/pro 配置策略、MySQL schema 命名(`mindfulness_dev`/`mindfulness`)、Redis ACL + key 前缀隔离(`dev:*`/`pro:*`)
|
||||||
|
- **主要约定**:
|
||||||
|
- MySQL:prod=`mindfulness`,dev=`mindfulness_dev`,统一 `utf8mb4` 与 snake_case
|
||||||
|
- Redis:单实例,通过 ACL 限制不同用户只能访问对应前缀;应用侧强制 key 使用 `dev:`/`pro:` 前缀
|
||||||
|
- 安全:真实 IP/账号/密码/Token 不写入仓库,仅提供 `.env.example` 结构
|
||||||
|
- **阶段产物**:
|
||||||
|
- `spec_kit/Project Bootstrap/spec.md`
|
||||||
|
|
||||||
|
## Onboarding App Shell
|
||||||
|
|
||||||
|
- **目标**:落地首次进入体验(Onboarding 3–5 页可跳过 + Push 设置可跳过)与主应用壳(点赞/讨厌、收藏夹入口、通用设置入口)
|
||||||
|
- **核心范围**:Onboarding 多页流程、Push 引导页、主界面反应操作(Like/Dislike)、收藏夹、设置页(版本、iOS 小组件入口/说明)
|
||||||
|
- **阶段产物**:
|
||||||
|
- `spec_kit/Onboarding App Shell/spec.md`
|
||||||
Reference in New Issue
Block a user