旧版服务端

This commit is contained in:
tianmo committed 2026-09-29 13:39:36 +08:00
1 parent 3429c8b402
commit 25bbb5f9c8
971 files changed
+87141 -40

No files matched your search

+31 -40
View File
@@ -40,7 +40,7 @@
| 编号 | 决策 | 理由 | 影响 |
|---|---|---|---|
| ADR-001 | 放弃口令码,权益由订单驱动 | 支付上线后口令码仅剩线下分发用途,维护成本高于价值 | 删除 `课程口令码`、`课程口令码读取信息`;老用户走 legacy 权益迁移 |
| ADR-001 | 放弃口令码,权益由订单驱动 | 支付上线后口令码仅剩线下分发用途,维护成本高于价值 | `课程口令码` 表整体作废;已兑换用户按其 `用户已购课程` 记录标记为已拥有;`课程口令码读取信息` 升级为 `courses` |
| ADR-002 | 数据端保留 MySQL 5.7,不升级 | 用户明确要求 | Prisma `provider = mysql`;见 4.1 约束清单 |
| ADR-003 | 服务端与管理后台使用独立域名 | 用户要求 | Nginx 分站点;后端仍为同一 Node 进程 |
| ADR-004 | 小程序前端不重写,只重写 JS 层 | 保证「外观不变」 | 复用全部 `.wxml` / `.wxss` / `static/` |
@@ -131,7 +131,7 @@
> 1. 为迁移专用账号授予 `CREATE` / `DROP` 权限(推荐,本机开发用)。
> 2. 在本地生成迁移 SQL(`prisma migrate diff`),生产端仅执行 `prisma migrate deploy`。
### 4.2 表清单与映射:20 张 → 16 张
### 4.2 表清单与映射:20 张 → 15 张
| 旧表 | 新表 | 处理 |
|---|---|---|
@@ -146,7 +146,7 @@
| `网页列表` (2) | `articles` | 迁移 |
| `课程页面Tips` (2) | `course_tips` | 迁移 |
| `cloudconfig` (13) | `settings` | 迁移,数字 ID 改字符串 key |
| `课程口令码` (5) | `legacy_redeem_codes` | **保留原样**,见 9.4 |
| `课程口令码` (5) | — | 废弃(口令码作废,见 9.4) |
| `网页数据` | — | 废弃 |
| `课程页面Tips2` | — | 废弃 |
| `首页课程列表2` | — | 废弃 |
@@ -229,7 +229,7 @@
| id | BIGINT UNSIGNED | PK | |
| user_id | BIGINT UNSIGNED | NOT NULL | → users.id |
| course_id | BIGINT UNSIGNED | NOT NULL | → courses.id |
| source | TINYINT | NOT NULL | 1 订单 / 2 老数据 / 3 后台发放 / 4 旧口令码 |
| source | TINYINT | NOT NULL | 1 订单 / 2 老数据(历史已购)/ 3 后台发放 |
| ref_id | BIGINT UNSIGNED | NULL | 订单 id 或操作人 id |
| granted_at | DATETIME | NOT NULL | |
| expire_at | DATETIME | NULL | NULL 表示永久 |
@@ -297,19 +297,6 @@
| status | TINYINT | |
| last_login_at | DATETIME | |
#### legacy_redeem_codes(旧口令码留存)
原样保留 5 条记录,用于凭证备查与人工核销,不参与任何自动流程。
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT UNSIGNED PK | |
| code | VARCHAR(64) UNIQUE | 原口令码 |
| course_id | BIGINT UNSIGNED | → courses.id |
| remaining_uses | INT | 原剩余有效次数 |
| expires_at | DATETIME NULL | 原口令结束日期 |
| note | VARCHAR(255) | 备注 |
#### settings
数字 ID 迁移为字符串 key,附映射关系(见 9.3)。
@@ -441,7 +428,6 @@ apps/api/src/
| GET | `/admin/v1/orders` | 订单列表 |
| POST | `/admin/v1/orders/:id/refund` | 发起退款(Android) |
| GET/POST/DELETE | `/admin/v1/entitlements` | 权益发放 / 回收 |
| POST | `/admin/v1/legacy-codes/redeem` | 旧口令码人工核销 |
### 5.4 统一契约
@@ -607,9 +593,8 @@ signature = HMAC_SHA256(session_key, post_body)
4. **课时管理**:按课程列出课时,编辑标题/章节/排序。
5. **权益管理**:手动发放、回收、按用户查权益(**替代原发码功能**)。
6. **订单管理**:列表、详情、状态、发起退款(Android)。
7. **旧口令码核销**:输入口令码 + 选择用户 → 扣减次数 → 发放权益。
8. **内容管理**:Banner、首页新课长图、首页试听入口、文章、课程页文案。
9. **系统配置**:`settings` 表全部键值。
7. **内容管理**:Banner、首页新课长图、首页试听入口、文章、课程页文案。
8. **系统配置**:`settings` 表全部键值。
---
@@ -713,7 +698,7 @@ VideoDetailsPage.wxml
| `网页列表` | `articles` | `网页id → legacy_page_id`(图片路径依赖) |
| `课程页面Tips` | `course_tips` | |
| `cloudconfig` | `settings` | 数字 ID → 字符串 key,见 9.3 |
| `课程口令码` | `legacy_redeem_codes` | 原样保留,见 9.4 |
| `课程口令码` | — | 废弃(不迁移,见 9.4) |
### 9.3 settings 键映射
@@ -733,11 +718,27 @@ VideoDetailsPage.wxml
| 11 | 我是有底线的! | `home.footer_text` | 首页触底提示 |
| 12 | 长按识别二维码 | `profile.qrcode_caption` | 图片标题 |
### 9.4 旧口令码处理(待确认)
### 9.4 旧口令码处理:整体作废,已兑换用户直接标记为已拥有
**现状**:`课程口令码` 表中有 5 条记录,剩余有效次数合计 18 次。
**已确认的处理方式**:口令码机制整体废弃,`课程口令码` 表不迁入新库。
| 口令码 | 课程 | 剩余次数 |
**已兑换用户如何处理**:历史上任何用户使用课程码兑换成功,服务端都会写入一条 `用户已购课程` 记录。因此**「谁用过课程码」这件事已经完整落在 `用户已购课程` 表中**,共 48 条,无需从口令码反推。
迁移时逐条转为 `entitlements`:
| 源字段 | 目标字段 | 取值 |
|---|---|---|
| OpenID | user_id | 按 openid 匹配 `users` |
| 课程id | course_id | 按 code 匹配 `courses` |
| (固定) | source | `2`(老数据) |
| 购课日期 | granted_at | 原值(转 UTC) |
| 课程介绍 | note | 原值(可选) |
**用户侧表现**:这些用户升级后打开「知识」页,看到的已购课程与升级前完全一致,无需任何操作,也不经过支付。这就是「不走支付、直接标记为已拥有」。
**作废部分**:`课程口令码` 表中 5 条记录、合计 18 次**未使用**的剩余次数,随机制一并作废,不迁移、不补偿。此项为明确决策,非疏漏。
| 口令码 | 课程 | 未使用次数(作废) |
|---|---|---|
| 202601 | 7年级数学同步视频课(北师大) | 3 |
| 202602 | 8年级数学同步视频课(北师大) | 3 |
@@ -745,15 +746,7 @@ VideoDetailsPage.wxml
| 202604 | 梁老师中考数学一轮满分冲刺 | 5 |
| 202605 | 梁老师中考数学二轮满分冲刺 | 4 |
**技术事实**:口令码是「次数池」,**表结构中不含用户字段**,即无法从数据推导出这 18 次分别属于谁。因此**无法自动转换成「某用户已购买某课程」的记录**——缺少用户维度。
**本方案的处理(无损、可兜底)**:
1. 5 条记录原样迁入 `legacy_redeem_codes`,作为凭证留存。
2. 管理后台提供「旧口令码核销」功能:输入口令码 + 选择用户 → 扣减 `remaining_uses` → 写入 `entitlements(source = legacy_code)`。
3. 当持有旧码的用户来访时,运营在后台一键发放权益,业务闭环完整。
> ❓ **需要确认**:若本意是「这 18 次已经卖给具体学生、且手上有对应名单」,应**直接按名单批量生成** `entitlements(source = legacy)` 记录,而不是走核销流程。请提供名单或确认按上述核销方案执行。
> 若后续有人反馈持有未使用的旧码,用管理后台的「权益手动发放」补发即可,无需为它保留专门的核销功能与数据表。
### 9.5 迁移步骤
@@ -806,7 +799,7 @@ VideoDetailsPage.wxml
| R3 | `courses.code` 被改写 | 全部视频资源失效 | 迁移脚本禁止改写;加校验断言 | 1 |
| R4 | 封面 / 图片文件名被改写 | 图片 404 | 迁移脚本原值透传;加校验断言 | 1 |
| R5 | 时间字段未做 UTC 转换 | 订单时间偏移 8 小时 | 迁移时显式转换 + 抽样核对 | 1 |
| R6 | 18 次旧口令码归属不明 | 用户投诉 | 保留原表 + 后台核销入口;或按名单批量发放 | 1 / 5 |
| R6 | 18 次未使用的旧口令码随机制作废 | 持码人无法兑换 | 已确认为接受项;若有人反馈,用后台「权益手动发放」补发 | 1 |
| R7 | `VideoDetailsPage` 路径变更 | 已分发的入口失效 | 路径原样保留 | 8 |
| R8 | `sessionKey` 泄漏 | 用户态签名可被伪造 | 加密存储;日志脱敏 | 2 |
| R9 | 发货推送丢失 | 用户付款未开通 | 双通道:推送 + 定时查单 | 6 |
@@ -820,10 +813,9 @@ VideoDetailsPage.wxml
| 编号 | 事项 | 现状 | 需要 |
|---|---|---|---|
| Q1 | 旧口令码的 18 次剩余次数 | 无用户维度,无法自动转成「用户已购买」 | 确认按 9.4 核销方案执行,或提供持有者名单 |
| Q2 | `VideoDetailsPage` 的实际访问入口 | 代码中无跳转引用,但可能经小程序码 / 搜索直达 | 确认是否对外分发过;若通过小程序码分发,路径必须保留 |
| Q3 | 头像资源数量 | `cloudconfig` 记录为 50,`Images/Headicons/` 实际 51 个文件 | 确认有效范围 |
| Q4 | 三大内容块是否全保留 | 默认全部保留 | 确认无内容需要下线 |
| Q1 | `VideoDetailsPage` 的实际访问入口 | 代码中无跳转引用,但可能经小程序码 / 搜索直达 | 确认是否对外分发过;若通过小程序码分发,路径必须保留 |
| Q2 | 头像资源数量 | `cloudconfig` 记录为 50,`Images/Headicons/` 实际 51 个文件 | 确认有效范围 |
| Q3 | 三大内容块是否全保留 | 默认全部保留 | 确认无内容需要下线 |
---
@@ -869,4 +861,3 @@ yelaymaths/
| `6ec33e18e8ab…` | 9年级数学同步视频课(北师大) |
| `25caa811c67c…` | 梁老师中考数学一轮满分冲刺(视频+讲义) |
| `77d091c27378…` | 梁老师中考数学二轮满分冲刺(视频+讲义) |