Files
doodle-store/README.md
T

114 lines
8.9 KiB
Markdown

# Doodle Store
独立于 `doodle-mini` 的 pnpm Monorepo,用于维护统一业务后端、后续管理后台和用户 Web 网站。小程序源码与 CloudBase 兼容函数仍在 `doodle-mini`,两个项目通过 HTTPS API 协作。
## 当前范围
当前已完成基础工程、本地数据库、小程序登录鉴权及分类服务:
- `apps/server`:可运行的 NestJS 服务,已接入日志、中间件、Swagger、微信静默登录、JWT 与真实账户检查,提供本人摘要、分类查询/管理、题型列表/详情与组合筛选和数据库就绪接口。
- `packages/contracts`:公开响应、登录请求/结果、本人摘要、分类与安全错误类型,不包含数据库模型或私有实现。
- `packages/asset-sync`:本地云存储备份同步七牛的独立 Node.js 工具,支持预演、校验、重试,头像与其他资源共用公开空间。
- `apps/admin`、`apps/web`:仅预留目录与说明,尚未选择或安装前端框架。
- 本地 PostgreSQL 18.6 Docker 开发环境已启动,13 张表及初始配置已创建,并于 2026-10-15 与最新 SQL 复核一致;Prisma 与微信登录已接入,七牛、其他业务及生产部署待实施。
## 本地使用
先按 [本地数据库说明](infra/README.md) 配置根 `.env`,执行 `pnpm infra:up`。默认数据库地址为 `127.0.0.1:5434`,开发库为 `doodle_dev`。图片等资源直接使用七牛,本地仅启动 PostgreSQL。生产与按需测试的安排见 [v3.5 开发与部署环境方案](docs/v3.5/开发与部署环境方案.md)。
使用 Node.js **24.20.0** 与 pnpm **11.24.0**。如使用 fnm,可在本目录执行 `fnm use --install-if-missing`。
```bash
pnpm install --frozen-lockfile
pnpm build
pnpm dev
```
默认地址为 `http://127.0.0.1:8800/api/v1/health`。该接口只证明服务进程可以处理 HTTP 请求,不代表数据库、文件存储或业务系统已经可用。
`pnpm dev` 在服务监听成功后显示完整 Swagger 地址(默认 `http://127.0.0.1:8800/api-docs`),并使用系统默认浏览器自动打开一次。热更新不会重复打开;关闭 Swagger 时不打开。链接按实际 HOST/PORT 生成,建议使用输出的 IP 地址,避免 `localhost` 解析到其他项目。
可在 `apps/server/.env` 中配置 `PORT`、`HOST`,变量示例见 `apps/server/.env.example`。未配置时使用安全的本地默认值。不要提交真实 `.env` 或密钥。
JWT_SECRET 必须配置为至少 32 字节的独立密钥,本机开发密钥已生成。Swagger 默认开发开启:`http://127.0.0.1:8800/api-docs`,生产未显式配置时关闭;其他设置与账户查询接入边界见 [Server 基础设置](docs/v3.5/Server基础设置.md)。
微信小程序配置在 `apps/server/.env` 中填写 `WECHAT_MINI_APP_ID` 和 `WECHAT_MINI_APP_SECRET`。两项均为启动必填;DATABASE_URL 同样必须配置。真实 AppSecret 仅由服务端持有,不提交仓库。
```bash
pnpm check # 格式、代码检查、类型检查、构建与测试
pnpm test:db # 独立临时数据库中的登录/鉴权/分类测试,结束后清理
pnpm build # 按工作区依赖顺序构建
pnpm start # 运行已构建的服务端
pnpm format # 格式化源码与配置
```
## 目录
```text
apps/
server/ NestJS 服务端
admin/ 后续管理后台,当前仅占位
web/ 后续用户网站,当前仅占位
packages/
asset-sync/ 云存储备份同步七牛的独立脚本
contracts/ 可公开的接口类型
deploy/ 后续部署说明,当前未部署
infra/ 本地 PostgreSQL Docker 配置
docs/ 工程初始化与接入边界说明
```
每个实际应用与包声明自己的依赖。工作区内部使用 `workspace:*`,按应用独立构建、发布;不要求后台、网站与 API 同时上线。暂不引入 Turborepo、Nx、Redis、队列或微服务。
数据库模型、密钥和未来私有生成/绘制逻辑只能由服务端持有,不能放进公共契约包供前端导入。小程序不直接依赖本仓库源码或本地工作区包。
登录请求、恢复错误码、数据库基线及联调边界见 [服务端登录接入说明](docs/v3.5/服务端登录接入说明.md)。
分类公开/管理 API、本地导入工具及本批 6 条分类的导入结果见 [分类接口与数据导入](docs/v3.5/分类接口与数据导入.md)。
51 条旧题型数据已导入本地库,预览文件仍待迁移;导入工具、异常年级和计数对账见 [题型数据导入说明](docs/v3.5/题型数据导入说明.md)。
题型查询支持分类、审核年级、年龄段、Tag、最新/热门、运营标记和分页,见 [题型查询接口说明](docs/v3.5/题型查询接口说明.md)。
现有功能迁移的权威方案见 [docs/云服务端迁移方案.md](docs/云服务端迁移方案.md),小程序产品规划与历史设计仍保留在 `doodle-mini`。本仓库的初始化说明见 [docs/初始化说明.md](docs/初始化说明.md)。
## 云存储备份同步七牛
独立脚本项目位于 [packages/asset-sync](packages/asset-sync/README.md),配置入口为该目录的 `.env`,已填写本地 cloud_backup 源目录。`pnpm assets:sync` 默认只预演;填写七牛 AK/SK 与 bucket 后,使用 `pnpm assets:sync --apply --only assets/previews` 同步题型预览。支持保留相对路径、同内容跳过、冲突保护、有限重试和报告;头像与其他资源共用 QINIU_BUCKET 公开空间。该工具不修改业务数据库或自动替换旧 cloud 引用。
### 服务端资源地址配置
`apps/server/.env` 与 asset-sync 使用同一组公开配置;服务端无需配置七牛 AK/SK:
```dotenv
QINIU_PUBLIC_DOMAIN=https://cdn.joeyone.cn
QINIU_KEY_PREFIX=doodle-assets/
QINIU_BUCKET=doodle-cn
```
地址规则为「域名 / 对象 key」。字体等固定资源由 `publicResourceUrl(config, 'assets/fonts/ToneOZ-Pinyin-Regular.ttf')` 补上前缀,得到 `https://cdn.joeyone.cn/doodle-assets/assets/fonts/ToneOZ-Pinyin-Regular.ttf`。支持空前缀;域名仅接受 HTTPS origin,不包含目录。修改配置后重启 server。
数据库 `assets.object_key` 存完整 key,例如 `doodle-assets/assets/previews/math/example.jpg`,返回预览时直接拼域名,不再次添加前缀。`QINIU_BUCKET` 是对象所属空间,不拼进 URL,也不会自动修改资产记录。图片上传成功后仍需根据同步报告回填资产的 key、bucket、摘要等字段并标记 ready;pending 资产继续返回空预览,配置域名不会自动宣布迁移完成。
资源上传后,可将成功同步报告映射回本地 `assets`,保留原资源 ID 与业务表外键:
```bash
# 预演:重新核对本地文件与七牛对象,仅允许本机 doodle_dev
pnpm assets:map --report /绝对路径/成功同步报告.json
# 核对通过后,整批事务写入并记录迁移审计
pnpm assets:map --report /绝对路径/成功同步报告.json --apply
```
当前工具处理已有 cloud 来源的题型预览:根据原文件相对路径匹配,回填 bucket、完整 objectKey、MIME、大小和 SHA-256,设置 ready。重复执行同一映射会跳过,已就绪但映射不同的资产会拒绝覆盖;有缺失或核对失败则整批不写入。不创建用户头像记录、不猜测头像归属。
业务表(例如 worksheets.preview_asset_id、users.avatar_asset_id)仅保存资源 ID;assets 保存文件定位和元数据,不保存七牛完整访问 URL。固定字体未作为业务记录入库,小程序通过 `miniprogram/config/assets.ts` 统一配置域名和前缀;接口提供的图片地址直接使用,不在小程序重复拼接。
### 微信登录上游失败排查
`503 / AUTH_UPSTREAM_UNAVAILABLE` 表示服务端兑换微信 code 未成功。查看同一 requestId 的 `wechat_exchange_failed` 日志:只记录失败阶段、HTTP 状态和微信数值错误码,不输出 AppSecret、code、完整请求 URL 或微信 errmsg。
若 `wechat_errcode=40164`,且有 `egress_ip`,表示微信拒绝当前服务器公网出口 IP。登录对应 AppID 的微信公众平台,在服务端接口调用的 IP 白名单中添加该出口 IP(与代码上传白名单分别核对),保存后在小程序重新触发登录。换网络或代理出口后需重新核对 IP;上线时使用实际服务器的固定出口 IP。仅修改本站后端 `.env` 无法解除微信侧白名单限制。
### 小程序业务接口迁移
除微信预拉取外,用户资料、头像、收藏、下载记录、年龄推荐、动态首页兜底和各管理页已迁至本站 API;详见 [小程序云接口迁移清单](docs/v3.5/小程序云接口迁移清单.md)。上传凭据仅由服务端持有,本地已沿用 asset-sync 的七牛配置。`20261019000000_member_assets` 已在本地应用;云端历史用户及关联记录仍需单独导入。管理功能要求数据库当前角色为 admin,不自动授权开发者账户。