85 lines
7.5 KiB
Markdown
85 lines
7.5 KiB
Markdown
# 教学案例展示
|
||
|
||
## 功能与入口
|
||
|
||
- 管理中心 → 教学案例展示 → 新建 / 编辑。填写标题、学员化名、经历课时(节)、练习背景、变化与收获。
|
||
- 每个案例支持 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 合成和真机界面验收。
|