From 48c45ada8946b3d0d761d6ea9594ea594a86d048 Mon Sep 17 00:00:00 2001 From: R524809 Date: Mon, 19 Oct 2026 17:48:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=83=A8=E7=BD=B2=E5=87=86=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 + deploy/.env.example | 5 + deploy/README.md | 128 ++++++++++++++++++++++- deploy/compose.database.yaml | 56 ++++++++++ deploy/postgres/10-create-roles.sh | 22 ++++ deploy/postgres/backup.sh | 22 ++++ docs/v3.5/开发交接文档.md | 157 +++++++++++++++++++++++++++++ 7 files changed, 387 insertions(+), 5 deletions(-) create mode 100644 deploy/.env.example create mode 100644 deploy/compose.database.yaml create mode 100755 deploy/postgres/10-create-roles.sh create mode 100755 deploy/postgres/backup.sh create mode 100644 docs/v3.5/开发交接文档.md diff --git a/README.md b/README.md index ea3b14a..cf700a6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Doodle Store +当前进度、旧线上服务保留约束、验证结果及下一步任务见 [开发交接文档](docs/v3.5/开发交接文档.md)(2026-10-19)。 + 独立于 `doodle-mini` 的 pnpm Monorepo,用于维护统一业务后端、后续管理后台和用户 Web 网站。小程序源码与 CloudBase 兼容函数仍在 `doodle-mini`,两个项目通过 HTTPS API 协作。 ## 当前范围 diff --git a/deploy/.env.example b/deploy/.env.example new file mode 100644 index 0000000..b821e13 --- /dev/null +++ b/deploy/.env.example @@ -0,0 +1,5 @@ +# 仅在云服务器保存真实 .env,权限 600;三个密码分别生成,不提交仓库。 +# 建议使用 secrets.token_hex(32) 或 openssl rand -hex 32,便于后续组成连接串。 +POSTGRES_ADMIN_PASSWORD= +DOODLE_MIGRATOR_PASSWORD= +DOODLE_APP_PASSWORD= diff --git a/deploy/README.md b/deploy/README.md index 1abcd31..290499f 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,9 +1,127 @@ -# 部署预留目录 +# Doodle 云端部署准备 -本次未向云服务器部署服务或启动生产数据库,也未修改云服务器。 +更新时间:2026-10-19。本文件记录部署方案与待确认条件。已在 1Panel 申请根域及泛域名证书;尚未创建 Doodle 云端应用、应用域名 DNS 记录或镜像仓库,也未改动旧线上云开发链路。 -本地数据库配置位于 [infra](../infra/README.md),与云服务器部署分开维护。云端首期只常驻 PostgreSQL 生产库,真实联调按需创建同实例独立测试库;图片等资源直接使用七牛。详见 [v3.5 开发与部署环境方案](../docs/v3.5/开发与部署环境方案.md)。 +## 已确认条件 -后续加入 Docker Compose、Nginx、数据库备份与恢复配置,按应用独立部署;数据库不开放公网端口,真实环境变量不提交仓库。 +- 云平台:腾讯云;主机名 `VM-16-14-ubuntu`。 +- 系统:Ubuntu 24.04,内核 `6.8.0-71-generic`,架构 x86_64;应用镜像目标为 `linux/amd64`。 +- 1Panel:2.3.2。 +- 用户提供的地址 `10.0.16.14` 是内网地址;公网域名还需确认实际公网 IP。 +- CPU、内存、磁盘余量、现有网站、OpenResty 网络模式及端口占用尚未核查。历史文档中的“2 核 4 GB”不能视为本轮已确认。 -在增加数据库、admin、SSR Web 或渲染进程前检查现有 2 核 4 GB 服务器资源,不在生产长期运行整套开发模式。 +## 环境边界 + +当前采用本地开发 + 单套云端业务环境;未来确有需要时再启用独立 staging,不预先常驻第二套云端系统。 + +| 小程序包 | 接口选择 | +| -------- | ---------------------------------------------------------- | +| develop | 默认 local,可在 Debug 页切换 local / staging / production | +| trial | 固定 production,与正式版使用同一套后端和数据库 | +| release | 固定 production | + +小程序配置位于 `doodle-mini/miniprogram/config/api.ts`。按用户要求,当前三个环境地址暂时都填写本地接口,客户端会话/缓存隔离不代表服务端数据已隔离;云端验收就绪后再单独替换相应地址。 + +现有已发布小程序继续使用原云开发。保留全部旧云函数、预拉取模式、地址、生成/发布与定时链路。开发版跳过旧线上预拉取,体验/正式包保留原有预拉取行为;本次不关闭旧服务。 + +## 建议部署结构 + +采用“1Panel 管理入口,项目维护应用编排”的组合方式: + +```text +小程序 / Web + → HTTPS 域名 + → 现有 1Panel OpenResty(统一管理 80/443 和证书) + → Doodle API 容器(容器内 8800) + → 独立 PostgreSQL 容器(持久化数据卷,不发布公网端口) + +预览图、字体与头像 → 现有七牛资源域名 +``` + +API、PostgreSQL 的 Docker Compose、镜像版本、环境变量示例和备份流程保存在项目仓库;1Panel 可用于观察容器状态和管理入口,不要求将应用制作成 1Panel 商店应用,也不额外部署一套 Node.js 运行环境。 + +1Panel 与项目脚本不能同时维护同一份容器编排。既有服务不为本项目被替换或重装,也不另启 Nginx/Caddy 抢占 80/443。 + +反向代理的上游地址要依据实际网络模式确定:若 OpenResty 使用 host 网络,可访问宿主机回环地址;若使用 bridge 网络,容器内的 `127.0.0.1` 是 OpenResty 自身,应使用经过核查的共享 Docker 网络连接 API 服务。数据库只与 API 在内部网络通信。应用信任代理的地址范围也据此配置,不直接信任任意来源的转发头。 + +## 域名选择 + +以下为建议名称,尚未创建 DNS: + +| 用途 | 建议域名 | +| -------- | ----------------------- | +| 未来 Web | `doodle.joeyone.cn` | +| API | `doodle-api.joeyone.cn` | + +`api.doodle.joeyone.cn` 同样可以使用,名称层级不会使接口功能或 HTTPS 安全性更强。当前推荐 `doodle-api.joeyone.cn`,因为它与 Web 域名处于同一层级,也可被 `*.joeyone.cn` 覆盖(仅在已有这种证书时适用);`api.doodle.joeyone.cn` 不受该通配符覆盖,需要精确域名证书或 `*.doodle.joeyone.cn`。 + +用户已选择统一管理根域及一级子域名证书;2026-10-19 已通过 1Panel 签发 `joeyone.cn` + `*.joeyone.cn`,可以覆盖表中的 Web/API 域名。将来换独立品牌域名时,可以在过渡期间让新旧域名同时路由到同一 API,不立即强制重定向旧小程序的 POST/上传请求;待旧客户端退出后再安排旧域名退役。 + +## 证书与自动续签 + +用户已选择由 1Panel 统一管理 `joeyone.cn` + `*.joeyone.cn` 的泛域名证书,复用现有 ACME 与腾讯云 DNS 账户;不接入 Atlas 的 acme.sh/systemd/七牛部署任务。 + +2026-10-19 面板现场核验结果: + +| 项目 | 结果 | +| ---------------- | ------------------------------- | +| 证书 ID | `11` | +| 主域名 | `joeyone.cn` | +| 其他域名 | `*.joeyone.cn` | +| 颁发机构 | Let's Encrypt | +| 验证方式 | DNS 账户自动验证,腾讯云DNS认证 | +| 密钥算法 | EC 256 | +| 状态 | 正常 | +| 面板显示过期时间 | `2027-01-17 16:15:52` | +| 自动续签 | 已开启,刷新后确认仍开启 | +| 备注 | joeyone.cn 泛域名共享证书 | + +日志确认两个域名均验证成功、DNS-01 临时验证记录已执行清理,并于 2026-10-19 17:14:27(面板日志)返回申请成功。以上确认的是当前签发与续签开关设置,不代表未来的自动续签任务已经执行验收。 + +后续为 `doodle-api.joeyone.cn` 或 `doodle.joeyone.cn` 建站时,可在 HTTPS 设置中选择同一张证书。证书覆盖域名不替代 A/CNAME 解析、网站绑定和反向代理配置;实际网站证书部署完成后仍需从客户端核验。 + +泛域名使用 DNS 自动验证,HTTP 验证不能签发泛域名。DNS 账户权限和凭据持续有效才可自动续签,任务失败应有通知。此次未读取、导出或复制 DNS 密钥及证书私钥。 + +`*.joeyone.cn` 不覆盖 `api.doodle.joeyone.cn` 或 `img.atlas.joeyone.cn`。七牛和 Atlas 现有证书继续独立维护,1Panel 的续签不会自动更新七牛绑定。 + +参考:[1Panel 申请证书](https://docs.fit2cloud.com/1panel/user_manual/websites/certificate-create/)、[1Panel 续签证书](https://docs.fit2cloud.com/1panel/user_manual/websites/certificate-renew/)、[Let's Encrypt 验证类型](https://letsencrypt.org/docs/challenge-types/)。 + +## 镜像构建与交付 + +建议使用多阶段 Dockerfile:构建阶段安装锁定依赖、构建 contracts 和 NestJS,并生成与 Linux 运行环境匹配的 Prisma 客户端;运行阶段包含 Node.js、构建产物和运行依赖,生产启动编译后的服务。 + +不从 macOS 复制 `node_modules`,不把 `.env`、数据库备份、微信或七牛密钥打进镜像。数据库数据保存在独立卷中,不随应用镜像更新。数据库迁移作为独立发布步骤执行,不在每次应用重启时隐式执行。 + +镜像要与服务器架构匹配。服务器已确认 x86_64,因此本机构建(包括 Apple Silicon Mac)时明确选择 `linux/amd64`。版本使用发行号或 Git 提交标识,保留上一可运行版本,不只依赖 `latest`。 + +有两种交付方式,均可使用同一份镜像和 Compose: + +### 镜像仓库 + +```text +本机或 CI 构建镜像 → push 私有镜像仓库 → 云服务器登录仓库 → pull 指定版本 → Compose 更新 API +``` + +优先核查腾讯云容器镜像服务个人版在现有账号/地域的可用性与限制,不为初期部署预先购买企业版。镜像仓库是分发镜像的存储服务,不是运行 API 的服务器;应用仍在现有 Ubuntu 主机上运行。 + +参考:[腾讯云个人版推送与拉取](https://cloud.tencent.com/document/product/1141/63910)。镜像地址和登录方式以账号实际控制台为准,不能把示例 namespace 当成已经创建的仓库。 + +### 首次离线交付 + +如暂不配置镜像仓库,可以本地构建后 `docker image save` 导出,使用 SSH/SCP 上传,再在云端 `docker image load` 导入,由 Compose 启动。无需将整个源码目录上传到服务器,也不要求在云端执行依赖安装与编译。 + +这适合首次部署或仓库网络暂不可用的情况;后续频繁发布可改为 push/pull,不改变应用内部结构。 + +参考:[Docker 镜像导出](https://docs.docker.com/reference/cli/docker/image/save/)、[跨平台构建](https://docs.docker.com/build/building/multi-platform/)。 + +## 后续配置准备顺序 + +1. 核查公网 IP、CPU/内存/磁盘、Docker/Compose、80/443 占用与 OpenResty 网络模式。 +2. 准备 Dockerfile、`.dockerignore`、云端 Compose、运行时环境变量示例和迁移入口。 +3. 准备数据库持久化、备份和恢复流程;备份副本需要保存到服务器之外。 +4. 核对基础镜像及应用镜像从服务器的拉取能力,再确定仓库分发或首次离线交付。 +5. 确认 API 域名并配置 DNS、1Panel 反向代理,绑定已签发的证书 ID 11,验收 HTTPS 与续签部署链路。 +6. 初始化单套云端数据库、执行迁移、受控导入内容;不整库复制开发用户和临时数据。 +7. 完成微信服务端出口 IP 与合法域名配置,再更新小程序 production 接口地址供体验版验收。 + +当前仅完成 1Panel 泛域名证书申请,未部署 Doodle 云端应用或数据库。公网入口与云端数据库验收完成前,不将开发机的 `127.0.0.1` 地址当作可用的体验版接口。 diff --git a/deploy/compose.database.yaml b/deploy/compose.database.yaml new file mode 100644 index 0000000..ed9410a --- /dev/null +++ b/deploy/compose.database.yaml @@ -0,0 +1,56 @@ +name: doodle-prod + +services: + postgres: + image: postgres:18.6-alpine + container_name: doodle-postgres + restart: unless-stopped + environment: + POSTGRES_USER: postgres + POSTGRES_DB: postgres + POSTGRES_PASSWORD: ${POSTGRES_ADMIN_PASSWORD:?请配置独立的数据库管理密码} + DOODLE_MIGRATOR_PASSWORD: ${DOODLE_MIGRATOR_PASSWORD:?请配置迁移账号密码} + DOODLE_APP_PASSWORD: ${DOODLE_APP_PASSWORD:?请配置应用账号密码} + POSTGRES_INITDB_ARGS: '--encoding=UTF8 --locale=C --auth-host=scram-sha-256' + TZ: UTC + PGTZ: UTC + command: + - postgres + - -c + - timezone=UTC + - -c + - max_connections=50 + - -c + - shared_buffers=128MB + - -c + - work_mem=4MB + # PostgreSQL 18 持久化父目录;不发布宿主机端口。 + volumes: + - postgres-data:/var/lib/postgresql + - ./postgres/10-create-roles.sh:/docker-entrypoint-initdb.d/10-create-roles.sh:ro + networks: + - database + mem_limit: 512m + cpus: 1.0 + pids_limit: 128 + shm_size: 128m + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U postgres -d doodle_prod'] + interval: 10s + timeout: 5s + retries: 12 + start_period: 15s + logging: + driver: json-file + options: + max-size: '10m' + max-file: '3' + +volumes: + postgres-data: + name: doodle-prod-postgres-data + +networks: + database: + name: doodle-prod-db + internal: true diff --git a/deploy/postgres/10-create-roles.sh b/deploy/postgres/10-create-roles.sh new file mode 100755 index 0000000..6422210 --- /dev/null +++ b/deploy/postgres/10-create-roles.sh @@ -0,0 +1,22 @@ +#!/bin/sh +set -eu + +# 只在官方镜像首次初始化空数据卷时执行。psql 引用变量,密码不进入 SQL 字面拼接或输出。 +: "${DOODLE_MIGRATOR_PASSWORD:?缺少迁移账号密码}" +: "${DOODLE_APP_PASSWORD:?缺少应用账号密码}" +psql --username "$POSTGRES_USER" --dbname postgres --set ON_ERROR_STOP=1 <<'SQL' +\getenv migration_password DOODLE_MIGRATOR_PASSWORD +\getenv application_password DOODLE_APP_PASSWORD +CREATE ROLE doodle_migrator LOGIN PASSWORD :'migration_password' NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION; +CREATE ROLE doodle_app LOGIN PASSWORD :'application_password' NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION; +CREATE DATABASE doodle_prod OWNER doodle_migrator ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0; +REVOKE ALL ON DATABASE doodle_prod FROM PUBLIC; +GRANT CONNECT ON DATABASE doodle_prod TO doodle_migrator, doodle_app; +\connect doodle_prod +REVOKE ALL ON SCHEMA public FROM PUBLIC; +GRANT USAGE ON SCHEMA public TO doodle_app; +-- 迁移由 doodle_migrator 执行:后续创建的表自动赋予应用 DML 权限,不授予建表或删表权限。 +ALTER DEFAULT PRIVILEGES FOR ROLE doodle_migrator IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO doodle_app; +ALTER DEFAULT PRIVILEGES FOR ROLE doodle_migrator IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO doodle_app; +REVOKE ALL ON DATABASE postgres FROM PUBLIC; +SQL diff --git a/deploy/postgres/backup.sh b/deploy/postgres/backup.sh new file mode 100755 index 0000000..86a073c --- /dev/null +++ b/deploy/postgres/backup.sh @@ -0,0 +1,22 @@ +#!/bin/sh +set -eu +umask 077 + +# 使用容器本地 socket 备份,无须在命令行或日志中传密码;只生成新文件,不自动清理旧备份。 +deploy_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +backup_dir="$deploy_dir/backups" +mkdir -p "$backup_dir" +backup_file="$backup_dir/doodle_prod_$(date -u +%Y%m%dT%H%M%SZ).dump" +if [ -e "$backup_file" ] || [ -e "$backup_file.partial" ]; then + echo '备份文件已存在,请稍后重试' >&2 + exit 1 +fi +trap 'rm -f "$backup_file.partial"' EXIT HUP INT TERM +docker compose --env-file "$deploy_dir/.env" -f "$deploy_dir/compose.database.yaml" \ + exec -T postgres pg_dump -U postgres -d doodle_prod -Fc > "$backup_file.partial" +# 从容器内部校验归档目录,确保可被 pg_restore 识别后才发布完整文件名。 +docker compose --env-file "$deploy_dir/.env" -f "$deploy_dir/compose.database.yaml" \ + exec -T postgres pg_restore --list < "$backup_file.partial" > /dev/null +mv "$backup_file.partial" "$backup_file" +trap - EXIT HUP INT TERM +printf '%s\n' "$backup_file" diff --git a/docs/v3.5/开发交接文档.md b/docs/v3.5/开发交接文档.md new file mode 100644 index 0000000..562c159 --- /dev/null +++ b/docs/v3.5/开发交接文档.md @@ -0,0 +1,157 @@ +# Doodle v3.5 开发交接文档 + +更新时间:2026-10-19。本次整理当前进度和后续任务,未执行云端部署。验证结果来自此前开发记录,本次文档更新未重新运行全套测试。 + +## 1. 当前阶段与保留约束 + +本地数据库、主要业务后端、小程序开发代码的接口迁移及七牛接入已完成。下一阶段先准备部署工程,再部署云端测试 API 和独立测试数据库,供小程序体验版联调。 + +必须区分:本地开发中的小程序已改用新后端;当前已发布的线上版仍使用旧云开发。用户明确要求: + +- 保留 `wx.getBackgroundFetchData`、现有预拉取模式和地址。 +- 保留旧云端预拉取数据生成、发布和定时刷新链路。 +- 保留全部旧云函数源码、云开发配置和线上服务,继续服务当前线上版。 +- 未另行明确安排前,不停用旧任务、不改变旧接口契约、不删除旧云数据,也不迁移预拉取模式。 +- 头像与其他资源使用同一个七牛公开空间。 +- 七牛 AK/SK、微信 AppSecret、JWT 密钥及数据库密码只保存在服务端配置,不提交、不写入文档、不下发小程序。 + +历史文档中的“停止旧任务”“私有头像”“七牛尚未接入”等过时表述,不应直接作为当前操作依据。旧线上链路持续保留的要求优先。 + +## 2. 工作区与本地环境 + +| 项目 | 路径或配置 | +| ------------------- | -------------------------------------------------------- | +| 后端仓库 | `/Users/joey-xd/sites/doodle/doodle-store` | +| 小程序仓库 | `/Users/joey-xd/sites/doodle/doodle-mini` | +| 云存储备份 | `/Users/joey-xd/sites/doodle/doodle-assets/cloud_backup` | +| 后端应用 | `apps/server`,NestJS + Prisma + PostgreSQL | +| 公共契约 / 资源工具 | `packages/contracts` / `packages/asset-sync` | +| 后续管理后台 / Web | `apps/admin` / `apps/web`,当前仅占位 | +| 运行时 | Node.js 24.20.0、pnpm 11.24.0 | +| 本地数据库 | Docker PostgreSQL 18.6,`127.0.0.1:5434`,`doodle_dev` | +| 本地 API | `http://127.0.0.1:8800/api/v1` | +| Swagger | `http://127.0.0.1:8800/api-docs` | + +配置入口分别为根 `.env`、`apps/server/.env`、`packages/asset-sync/.env`,真实文件均须保持 Git 忽略。启动前先检查进程状态,避免重复启动。 + +两个仓库有大量尚未提交的开发改动。继续前检查 Git 状态,不覆盖或回滚已有修改;服务端还有用户安装的技能文件,不为通过检查而擅自修改或删除。 + +## 3. 已完成内容 + +### 数据库与内容 + +- 数据库设计、SQL、Prisma 已落地;本地已应用基线、分类发布状态、`20261019000000_member_assets` 三个迁移。 +- 本地已导入 6 条分类和 51 条题型,保留源 ID、原始计数和展示种子。 +- 已实现分类公开查询、管理员新增/修改/删除,以及 worksheets 列表、详情、分类、年级、年龄、Tag、最新/热门查询。 +- 热门按下载量加展示种子计算;年龄段使用完整覆盖匹配,例如 3~8 岁题型可以命中 5~6 岁。参数及兼容行为以当前 DTO 和 Swagger 为准。 +- 分类与题型发布状态、分类祖先可见性约束已实现;未就绪的预览图返回空值。 + +### 登录、个人业务与管理 + +- `wx.login` 获取 code,新后端向微信兑换并签发本站 JWT;AppSecret 仅服务端持有。 +- 已实现启动/回前台静默登录、到期前续登、并发合并、业务 401 一次恢复重试,以及账户切换、删除和禁用时的处理。 +- 已实现资料修改、头像上传及绑定、清除资料、注销账户、本人统计、收藏操作和分页、下载记录新增和分页。 +- 收藏和下载计数包含事务及并发幂等;下载通过 `exportId` 去重,小程序保存图片成功后上报。 +- 已实现管理员题型发布、批量修改/预演、状态调整、首页配置读取/重建及每日快照刷新开关。 +- 管理员权限根据数据库当前角色校验,不自动授权开发者或第一个用户。 +- 新后端首页刷新任务与旧云端任务分开,现有管理开关不会关闭旧云任务。 + +完整路由及权限对照见 [小程序云接口迁移清单](./小程序云接口迁移清单.md)。 + +### 七牛与资源 + +- `packages/asset-sync` 支持配置备份目录、AK/SK、预演、上传、同内容跳过、冲突保护、重试及报告。 +- 用户已上传资源;此前核验的成功同步报告包含 62 个文件。51 个题型预览资产已回填本地库为 ready,原资产 ID 与业务外键保持不变。 +- 首页、分类等预览和字体已改用七牛;已知旧预拉取/缓存预览地址会转换为七牛地址。 +- 业务表保存资源 ID;`assets` 保存完整对象 key、bucket、摘要和状态等;响应 URL 根据配置生成。 +- 服务端头像及管理员预览上传已实现,限制 PNG/JPEG、2 MiB,校验图片尺寸、幂等标识和远端文件。 +- 头像替换、清除和账户注销会排队清理资源,包含失败重试与慢上传交错处理。 + +公开配置为 `QINIU_PUBLIC_DOMAIN=https://cdn.joeyone.cn`、`QINIU_KEY_PREFIX=doodle-assets/`、`QINIU_BUCKET=doodle-cn`。示例字体为 `https://cdn.joeyone.cn/doodle-assets/assets/fonts/ToneOZ-Pinyin-Regular.ttf`。 + +现有资产映射工具仅允许本机 `doodle_dev`,云端内容初始化需另行准备流程,不能直接更改目标库运行本地工具。 + +### 小程序与旧线上链路 + +- 最近静态扫描覆盖 707 个代码及配置文件,未发现运行代码中的直接 `wx.cloud` 调用。用户业务、内容查询和管理页均已切换新后端。 +- 首页顺序为 L1 微信预拉取 → L2 本地缓存 → L3 新 `/page-config`;L1/L2 命中后后台请求新后端刷新。 +- 旧预拉取与新后端数据源尚未统一,首屏可能短暂展示旧内容,随后刷新。保留现状,后续调整需单独安排。 +- `wx.uploadFile` 用于新后端 multipart 上传,是普通网络 API。 +- 配置入口为 `doodle-mini/miniprogram/config/api.ts`。develop 可切 local/online;trial/release 当前共用未填写的 `onlineBaseUrl`,不会回退本地。 +- 17 个旧云函数全部保留,仍服务当前线上版;实际云端部署和触发器状态未通过控制台核验,本次不调整。 + +## 4. 验证记录与未解决事项 + +此前验证结果: + +- 后端类型检查、相关业务文件 ESLint/格式检查及两个仓库的 `git diff --check` 通过。 +- 后端常规测试通过;隔离数据库回归覆盖 6 组文件、49 项测试,结束后清理测试库。 +- 小程序本次迁移相关测试 28 项通过,1 项本地服务集成测试跳过。 +- 本地 HTTP 已验证 health、page-config、年龄推荐和 Swagger 参数/路由。health 成功仅证明 HTTP 进程可响应。 +- 上传测试使用替身,未由测试向真实七牛上传或删除文件;真实微信 code 和真机上传仍需验收。 + +待处理事项及限制: + +- 小程序完整 TypeScript 检查仍有 115 个既有错误,既有 ESLint 配置也有兼容问题,不能声称全项目检查通过。 +- 根 `pnpm check` 曾被用户新安装的第三方技能 Markdown 格式问题阻塞;业务代码已单独检查,不擅自更改这些技能文件。 +- 登录曾返回 `503 / AUTH_UPSTREAM_UNAVAILABLE`,诊断得到微信错误 `40164`,为服务端出口 IP 白名单拒绝。云部署需配置实际服务器出口 IP;尚无后续真实 code 成功验收记录。 +- 旧云端用户、收藏及下载历史未迁移,新旧账户关联和正式切换流程待制定。 +- 云端 API 域名未确定;后端和数据库尚未部署到云服务器。 +- 上传限流当前为单实例每人每小时 30 个新标识,未实现多实例共享配额。 +- 小程序 4 个 `worksheet:patch:*` 命令引用不存在的旧脚本;部分文案、类型和文档仍有 Cloud 命名。可单独清理,不涉及删除旧云函数。 + +## 5. 下一步任务:云端体验版联调 + +下列任务尚未执行。先准备可审查的部署配置与说明,再结合实际服务器条件部署;本阶段不切换当前线上版。 + +### A. 部署工程与环境配置 + +- [ ] 确认服务器地址、系统、已有服务、端口、资源和部署方式;历史记录为 2 核 4 GB,执行前重新核实。 +- [ ] 确定测试 API HTTPS 域名、DNS 和证书。 +- [ ] 编写构建/发布、反向代理、进程管理、日志及健康检查配置;内部 API 使用 8800,对外使用 HTTPS,云端运行构建产物,不长期运行 `pnpm dev`。 +- [ ] 数据库仅内部访问;配置明确的代理信任范围、服务自启动及重启策略。 +- [ ] 小程序环境拆为 local / staging / production,体验版用 staging,未来正式版用 production,避免共用地址误连库。 +- [ ] 使用独立 `doodle_staging` 库和角色,不连接本地开发库或未来生产库;可复用 PostgreSQL 实例,无须常驻两套完整系统。 +- [ ] 测试与正式环境分开配置 JWT 密钥、数据库凭据、Swagger 访问策略;微信配置与实际小程序一致。 +- [ ] 明确测试上传和清理范围,避免删除现有资源;对象前缀不能替代 AK/SK 权限隔离。 +- [ ] 更新过时的部署环境方案,明确旧云函数和预拉取链路持续保留。 + +### B. 云端数据与运维 + +- [ ] 使用 `prisma migrate deploy` 执行迁移,不使用开发期重置命令初始化云端业务库。 +- [ ] 准备受控内容初始化流程,迁入分类、题型、关联资产及必要配置;核对 ID、发布状态、计数、分类关联和预览,不整库复制测试用户及临时记录。 +- [ ] 配置数据库持久化、异机备份、恢复与迁移失败处理,并完成一次恢复验证。 +- [ ] 保留上一可运行后端版本,明确应用回滚与数据库迁移的兼容边界。 + +### C. 微信与体验版验收 + +- [ ] 配置相应 request / uploadFile 等合法域名,以及微信服务端接口 IP 白名单中的实际服务器出口 IP。 +- [ ] 真机验证静默登录和到期恢复、查询、资料/头像、收藏、下载、管理员权限及内容下架。 +- [ ] 验证预拉取仍用原模式/地址,L3 和后台刷新使用新后端,新管理操作不修改旧云数据或任务。 +- [ ] 验证重复请求、异常恢复、资源上传/清理、服务重启和数据库恢复。 + +体验版验收通过后,再单独制定正式发布、历史用户迁移及新旧版本并行兼容方案。正式切换不是本轮部署默认动作,旧线上云服务继续保留。 + +## 6. 继续工作入口 + +- [小程序云接口迁移清单](./小程序云接口迁移清单.md):最新接口与权限。 +- [数据库设计文档](./数据库设计文档.md):结合 Prisma schema 和迁移核对结构。 +- [用户鉴权与登录设计](./用户鉴权与登录设计.md)、[服务端登录接入说明](./服务端登录接入说明.md)。 +- [题型查询接口说明](./题型查询接口说明.md)、[内容发布与可见性](./内容发布与可见性.md)。 +- [资源同步工具](../../packages/asset-sync/README.md)。 +- [部署目录](../../deploy/README.md)、[开发与部署环境方案](./开发与部署环境方案.md):部分历史内容待下一阶段更新。 + +后端常用验证,在 `doodle-store` 根目录执行: + +```bash +pnpm typecheck +pnpm test +pnpm test:db +pnpm build +``` + +小程序针对本次迁移的回归,在 `doodle-mini` 根目录执行: + +```bash +node --import tsx --test tests/auth-api.test.mjs tests/category-api.test.mjs tests/preview-assets.test.mjs +```