Files
mp-pilates/docs/teaching-cases.md
2026-09-18 18:26:47 +08:00

85 lines
7.5 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–9 组图片:每组可以是练习前 / 后两张照片,也可以是一张已拼好的对比图;共最多 18 张原图。第一组为封面,可前后调整顺序。每张 JPG / PNG / WebP 不超过 10 MB。
- 双图在同一画面并排展示,保留完整比例。手动滑动及左右按钮切换,提供张数、文字说明和点击放大;不自动轮播。
- 编辑页可预览,并可把任意一组生成一张带标题、化名、课时和前后标签的对比图,通过图片预览保存。生成动作只在本地进行,不自动发布。
- 管理员可保存草稿、发布、保存并下架、撤回授权并下架。排序数字越大越靠前,同序按创建时间和 ID 倒序。
- 未登录访客:首页品牌区域之后,第一个内容模块展示最多 3 个已发布案例;无案例则不占位。登录用户仍优先显示快捷入口和即将开始的预约,随后展示案例。
- 更多案例分页浏览;详情无需登录,支持分享及跳转至首页课程与体验区。下架案例的旧分享链接返回不可用状态。
## 数据与权限
新增 `TeachingCase` 和 `TeachingCaseAsset` 两张独立表。不关联或引用私密 `ProgressPhoto`,管理员不能利用成长档案授权直接公开照片。
公开接口:
- `GET /api/teaching-cases?page=1&limit=3`
- `GET /api/teaching-cases/:id`
管理接口均通过 JWT 及 ADMIN 角色校验:
- `GET/POST /api/admin/teaching-cases`
- `GET/PUT /api/admin/teaching-cases/:id`
- `POST /api/admin/teaching-cases/:id/withdraw`
- `POST /api/admin/teaching-cases/assets/credentials`
- `POST /api/admin/teaching-cases/assets/:id/complete`
公开查询同时限制 published 与 consentConfirmed;白名单输出案例内容,不输出授权备注、授权时间及编辑版本。公开响应使用 no-store。分页最大 20 条。
图片流程为申请资产 → 上传私有 COS 对象 → HEAD 校验 → 标记完成 → 保存案例。对象固定在 `teaching-cases/` 前缀下,保存时只接受资产表中已完成的 ID,不接受外部 URL、私密成长档案对象或未完成上传。
编辑版本通过条件更新递增;旧编辑页不能覆盖较新的修改或授权撤回。撤回操作同时取消发布、清除有效授权状态并增加版本;授权备注保留为管理记录,重新发布需再次确认。授权备注在请求日志中脱敏。
## 授权与内容约定
发布前管理员确认已取得学员单独的公开宣传授权,并记录日期、方式与凭证位置。这是线下授权记录,不是小程序替学员签署电子授权。照片、文字或用途变化后,应确认原授权覆盖新的内容。仅填写化名,不写身份证、诊断等无关信息;建议上传前遮挡面部和身份信息。系统不会自动识别面部、打码或移除 EXIF。
案例展示真实个人经历,不保证相同课时获得相同效果。照片建议保持相同视角、距离和光线,避免夸大变化。
## COS 与缓存边界
沿用 `COS_BUCKET`、`COS_REGION`、`COS_SECRET_ID`、`COS_SECRET_KEY`,不新增环境变量。服务凭证须能上传私有对象并执行 HEAD / GET。确认桶策略没有给 teaching-cases/ 前缀额外开放匿名读取权限;对象 private ACL 不能抵消更宽泛的桶策略。
- 上传签名有效 5 分钟;展示链接有效 15 分钟,重新进入页面或图片加载失败后刷新可获取新链接。
- 下架立即阻止新查询,但已经签发的链接可能继续有效最多 15 分钟,已被下载的图片不能远程撤回。紧急移除需同时处理对应 COS 对象和任何独立缓存。
- 微信合法域名需包含桶域名的 uploadFile / downloadFile;现有 API 域名继续用于 request。合成已有照片需要从 COS 下载,必须真机核对。
- 中断上传及未保存编辑可能留下未引用资产。清理需先导出案例全部 frames 中的资产引用,核对后仅删除长期未引用资产,建议至少保留 7 天。不要给整个前缀配置自动过期规则,否则会删除有效案例图片。
## 发布与回退
按项目既有发布流程部署后端及小程序,发布前备份数据库并核对目标环境:
1. 在后端环境执行 `prisma migrate deploy`,应用 `20260918000000_teaching_cases`,再生成客户端、部署后端。
2. 验证匿名列表为空时正常返回、管理员权限、真实图片上传和读取。
3. 发布小程序新版本,完成下方真机验收。
迁移仅增表,不修改原有业务数据。应用回退时保留两表及 COS 对象,避免丢失案例和授权记录;旧前端不使用新入口。不要自动执行 DROP TABLE 或批量删除照片。
## 验证
已完成共享包和后端构建、Prisma schema 校验、前端 vue-tsc、微信小程序生产构建、差异空白检查。新增服务/存储/HTTP 测试覆盖权限、草稿隔离、授权条件、上传完成性、多组图、分页参数及并发编辑。全量回归的普通测试在沙箱通过,HTTP 测试需要本机回环监听权限并已单独通过;身份在 HTTP 测试中模拟,角色守卫及 DTO 校验为真实实现。
仓库 `pnpm lint` 当前无法执行:server 声明了 eslint 命令,但未安装 eslint(command not found);本次未修改依赖来扩大范围。
真机 / 实际存储验收仍需完成:
- 游客有 / 无案例、登录切换及首页刷新;已登录预约模块优先级。
- 多组双图与合成图、长标题、小屏幕、滑动/按钮翻页与全屏预览。
- 上传取消、断网重试、超大图、缺少半组照片、保存时重复点击。
- 真机 Canvas 合成图正确、前后图不裁切、图片预览可保存。
- 草稿不公开、授权撤回、旧分享链接失效、旧编辑页并发冲突。
- 同一页面停留超过 15 分钟后图片刷新;确认 COS 前缀匿名访问被拒绝。
设计参考:[移动端轮播可用性](https://www.nngroup.com/articles/mobile-carousels/);[个人信息保护法](https://www.npc.gov.cn/npc/c2/c30834/202108/t20210820_313088.html)。
## 2026-09-18 生产发布记录
- 部署前发现生产已上线商城、当前工作区落后一个提交;已快进同步 `95b9f65` 并恢复教学案例改动,保留商城模型、接口及前端入口。没有提交或推送教学案例改动。
- 合并后共享包/后端构建、前端类型检查通过;29 个测试套件、455 项测试全部通过。
- 服务器本地备份:`/usr/local/backups/mp-pilates/2026-09-18T10-20-50-357Z-teaching-cases/`。包含 `database.sql.gz` 与 `backend.tar.gz`,已检查压缩包完整性;权限限制为服务器管理员访问。
- 使用 `pnpm deploy:server` 完成上传。部署脚本补充排除整个 logs 目录,避免上传本地日志轮转索引覆盖生产索引;生产 .env 保持不变。
- 生产数据库 `db_mp_focus` 于北京时间 18:22:18 应用 `20260918000000_teaching_cases`,新增两张表可查询,暂为 0 条记录;商城现有商品记录保留。
- 服务器本地及公网 HTTPS:health 200、teaching-cases 200(空列表)、未登录 admin/teaching-cases 401;不存在的案例 404。商城 products 接口 200。
- PM2 服务 `mp-pilates-server` online,部署后检查无异常重启。
- 本次完成生产数据库迁移及后端发布;未发布微信小程序,未替代真实照片上传、Canvas 合成和真机界面验收。