# AGENTS.md 本文件是 `xinghen.work` 项目的长期协作规则、功能事实基线和变更确认记录。所有在本仓库中工作的开发者与 AI Agent 都必须遵守。 它的核心目的不是记录“做过什么动作”,而是保存:为什么这样改、功能实际确认到了什么程度、哪些判断以后不能在没有新证据时随意推翻。 --- ## 1. 适用范围与优先级 - 本文件适用于整个仓库。 - 开始任何代码修改、功能新增、缺陷修复、数据结构调整或部署操作前,必须先阅读本文件,尤其是“项目事实基线”“既有决策”和“变更与功能确认记录”。 - 若子目录以后出现更具体的 `AGENTS.md`,子目录文件可补充局部规则,但不得绕过本文件的变更记录和验证要求。 - 用户在当前任务中的明确要求优先级最高;若新要求与历史判断冲突,必须指出冲突,并把最终采用的新判断及理由追加到本文件。 - 不得为了让当前实现显得合理而删除、覆盖或悄悄改写旧记录。旧判断失效时,应新增一条“替代/废止”记录并链接旧记录编号。 ## 2. 项目事实基线 ### 2.1 产品定位 星痕·智能课程工坊是一个支持个人与组织工作区的课程生产系统。 - 用户可拥有个人工作区,并加入多个组织。 - 课程归属于一个明确的工作区。 - 个人与组织分别拥有积分账户和积分流水。 - 组织成员可查看本组织课程及进度。 - 组织管理员可管理成员、课程和组织账单。 - 平台管理员负责组织创建、状态管理和组织积分调整。 以上是当前产品基线,不代表所有路径都已经完成端到端验证。实际确认状态以本文末尾的记录为准。 ### 2.2 技术组成 - 前端:React 19、Vite 6、TypeScript、React Router。 - 服务端:Fastify 5、TypeScript。 - 数据库:PostgreSQL 16。 - 文件:上传文件保存在持久化目录,数据库保存业务数据与文件元数据。 - 生产运行:Docker Compose,应用默认监听 `3001`,通过外部反向代理网络提供域名和 HTTPS。 ### 2.3 关键目录 - `src/`:前端页面、组件、上下文、API 客户端和类型。 - `server/`:接口、数据库连接、迁移/建表和服务端业务逻辑。 - `public/`:公开静态资源。 - `uploads/`:本地上传目录;业务文件不得当作源码处理。 - `deployment/`、`deploy.sh`、`docker-compose*.yml`:部署入口和生产编排。 - `docs/`:面向项目的专项说明。 ## 3. 必须保留的产品与工程判断 以下判断是修改相关功能时必须主动核对的约束。若确需改变,应先说明影响,再在变更记录中写明新结论替代了哪一条。 ### D-001 工作区和数据归属必须明确 课程、积分、账单、成员权限等数据都必须能追溯到个人或某个组织工作区。不得仅靠前端当前选中的工作区推断服务端数据权限;服务端必须校验登录身份、成员关系和角色。 ### D-002 历史个人数据不得被组织功能误迁移 已有课程默认继续属于个人工作区,已有个人积分继续属于个人账户。旧的用户“单位”资料不是组织成员关系,不能据此自动建立权限。 ### D-003 组织停用不等于删除 组织停用后,应阻止新成员加入和新建组织课程;历史课程、成员、账单与审计信息必须保留。恢复组织后仍应能继续使用已有数据。 ### D-004 组织积分必须可审计 积分余额变化必须有流水记录。普通成员只能看到其本人课程产生的组织积分消耗;组织管理员可以查看组织完整账单。调整余额时不能只改余额而不留流水。 ### D-005 上传文件必须经过鉴权 上传素材和交付文件必须通过鉴权接口读取。不得重新开放可猜测的 `/uploads/` 公共静态路径。新增下载、预览或图片展示能力时,必须验证未登录、无成员关系和跨组织访问均会被拒绝。 ### D-006 首次登录改密是服务端约束 组织管理员现场创建老师账号时,当前初始密码为 `888888`,首次登录必须修改密码。前端提示不能代替服务端限制;任何绕过强制改密的业务接口都属于缺陷。 ### D-007 数据库变更必须兼容已有数据 服务启动会执行建表/迁移逻辑。修改 `server/schema.sql`、数据库初始化或查询代码时,必须考虑已有生产数据库,而不是只验证空库。危险或不可逆的数据迁移必须提供备份、回滚或明确的人工步骤。 ### D-008 生产部署不得伤害持久化数据 部署和同步操作必须保护 `.env`、上传文件以及 PostgreSQL/上传持久卷。使用删除式同步、重建容器或修改挂载前,要先核对真实目标和持久化边界。容器启动成功不等于新代码已部署成功。 ### D-009 公网功能必须按用户路径验证 涉及部署时,至少区分并记录:本地构建通过、镜像构建通过、容器健康、HTTPS/反向代理正常、真实页面/API 可用、新版本特征可见。未完成最后几项时不得声称“已上线”。 ### D-010 包管理器统一为 pnpm,禁止提交 npm lock 依赖管理以 pnpm 为准:使用 `pnpm install` / `pnpm install --frozen-lockfile` 安装依赖,提交 `pnpm-lock.yaml` 与 `pnpm-workspace.yaml`,不得提交 `package-lock.json`。新增或升级依赖后必须刷新 `pnpm-lock.yaml` 并验证 `pnpm install --lockfile-only --frozen-lockfile` 通过;`.gitignore` 已忽略 `package-lock.json`。 ## 4. 每次修改的强制工作流 ### 4.1 修改前 1. 阅读本文件,并搜索与目标功能相关的决策编号、文件路径、接口名和历史记录。 2. 检查当前工作区状态和相关差异,区分用户已有修改与本次任务修改;不得覆盖或清理无关改动。 3. 明确本次功能的用户路径、权限边界、数据影响和验收标准。 4. 若拟议方案与历史判断冲突,先向用户说明冲突和影响。不能通过悄悄改变旧规则来推进。 ### 4.2 修改中 1. 只修改本次目标所需的最小范围。 2. 前端、接口、权限、数据库和部署配置如存在联动,必须逐层检查,不能只改可见界面。 3. 不得提交密钥、密码、私钥、真实 `.env` 或用户上传数据。 4. 对高风险变更保留回滚路径;对数据库或部署变更写清兼容性和恢复方式。 5. 发现原判断不成立时,保留证据并在记录中更正,不得继续沿用未经验证的假设。 ### 4.3 修改后 1. 执行与风险相称的验证。基础代码改动通常至少运行: - `npm run check` - `npm run build` 2. 仅有类型检查和构建通过,不能证明业务功能正确。涉及用户功能时,还要验证实际路径、关键状态和失败路径。 3. 涉及权限时,至少验证:未登录、普通用户、正确角色、错误组织/越权访问。 4. 涉及数据库时,至少验证:已有数据兼容、新数据写入、重复执行、约束与流水一致性。 5. 涉及部署时,按 D-009 分层记录证据,并明确当前验证停在哪一层。 6. 在结束当前任务前,必须按第 5 节模板向本文件末尾追加记录。代码和记录应属于同一次修改,不得留给“以后补”。 ## 5. 变更与功能确认记录规范 ### 5.1 不可省略的原则 - 每次修改代码或增加功能,都必须新增一条记录,即使只是小修复。 - 每条记录必须有唯一编号,格式为 `C-YYYYMMDD-NN`;同一天从 `01` 递增。 - 记录按时间顺序追加在文件末尾,不覆盖旧记录。 - “修改完成”和“功能确认”是两回事。记录必须分别写清实现范围和验证证据。 - 不能运行的检查、未覆盖的场景、依赖人工确认的部分必须写入“未确认/风险”。 - 禁止使用“应该可以”“基本没问题”“已优化”等无证据结论。 - 若一次任务只调查而未改代码,可不新增变更记录;但若调查形成会影响后续实现的重要判断,应新增一条“仅决策”记录。 ### 5.2 状态定义 - `已确认`:目标用户路径已通过实际功能验证,并有可复述的结果。 - `部分确认`:只完成了部分层级,例如类型检查/构建通过,但没有完成浏览器、接口、权限、数据库或公网验证。 - `未确认`:实现或现状存在,但没有足够证据证明功能成立。 - `失败`:验证明确未通过,必须保留失败现象和后续处理建议。 - `已替代`:历史结论已被后续编号明确取代;旧记录仍保留。 ### 5.3 追加模板 复制以下模板到本文件末尾并填写,不保留空泛占位文字: ```markdown ### C-YYYYMMDD-NN|简短标题 - 日期:YYYY-MM-DD - 类型:功能 / 修复 / 重构 / 数据库 / 部署 / 文档 / 仅决策 - 需求与原因:用户要解决什么问题,为什么需要修改。 - 修改范围:列出实际修改的功能、接口、数据结构和关键文件。 - 保留/新增判断:引用既有 `D-xxx`;若形成新判断,写清适用边界。 - 功能确认状态:已确认 / 部分确认 / 未确认 / 失败 / 已替代 - 验证证据:执行了什么检查,验证了哪些用户路径,实际结果是什么。 - 未确认与风险:明确尚未验证、环境受限、兼容性或回滚风险;没有则写“无已知未确认项”。 - 后续修改提示:未来改到本功能时必须先确认什么。 - 关联:相关提交、任务、Issue 或被替代的记录编号;没有则写“无”。 ``` ### 5.4 记录质量示例 不合格: > 完成了组织功能,测试正常。 合格: > 新增组织停用接口,并在创建组织课程和审批成员接口中校验组织状态。`npm run check`、`npm run build` 通过;使用管理员账号确认停用后两个接口均返回 403,恢复后可再次创建课程。未验证生产数据库上的旧组织数据,因此状态为“部分确认”。 ## 6. 验证矩阵 根据改动范围选择并记录,不要求与本次无关的验证,但不得省略相关层级。 | 改动范围 | 最低验证要求 | | --- | --- | | 纯文案/样式 | 目标页面实际查看;桌面端和受影响的窄屏布局;构建 | | React 页面/状态 | 主路径、空状态、加载、错误、刷新后状态;类型检查与构建 | | API 客户端/服务端接口 | 成功请求、参数错误、未登录、无权限、资源不存在 | | 登录/权限 | 不同角色、首次改密、跨用户/跨组织越权、会话失效 | | 组织/课程/积分 | 个人与组织归属、角色可见性、余额与流水一致性、组织停用状态 | | 上传/下载 | 文件类型与大小、鉴权访问、跨组织拒绝、文件不存在、持久化 | | 数据库结构 | 空库初始化、已有库升级、重复执行、约束、备份/回滚考虑 | | 部署 | 脚本语法、Compose 配置、镜像、容器健康、HTTPS/API、版本特征 | ## 7. 安全与数据边界 - 仓库中即使出现本地密钥文件,也不得读取、展示、提交或复制其内容;优先检查其是否被忽略,并提醒轮换意外暴露过的凭据。 - 日志、截图、错误信息和变更记录不得包含密码、Token、Cookie、连接串中的密钥或个人敏感数据。 - `uploads/` 中的文件属于业务数据,不得为了测试批量删除或纳入版本控制。 - 删除组织、用户、课程、账单、上传文件、卷或生产数据属于破坏性操作,必须有用户明确授权和可核对的目标。 - 部署前必须核对目标服务器、目录、容器/项目名和数据卷;不得把历史服务器状态当作当前事实。 ## 8. 完成标准 一次代码任务只有同时满足以下条件,才能向用户报告完成: 1. 请求的实现已经落到代码或配置中。 2. 没有覆盖用户已有的无关修改。 3. 已完成与风险相称的验证,并如实区分通过、部分通过和未验证。 4. 本文件末尾已经追加本次变更与功能确认记录。 5. 对后续修改有约束力的新判断已经编号或明确写入记录。 如果因环境、账号、生产权限或外部依赖无法完成验证,应报告“部分确认”或“未确认”,说明缺口,不得用构建通过代替功能完成。 --- ## 9. 变更与功能确认记录 > **变更索引(按编号顺序)**:C-01 协作规范 · C-02 蓝色品牌色 · C-03 扁平按钮 · C-04 课程时间排序 · C-05 数字讲师资产库 · C-06 统一创建入口 · C-07 禁止删除已用讲师 · C-08 创建流程优化 · C-09 手机拍照录音 · C-10 独立创建编辑页 · C-11 搜索备注收起 · C-12 照片参考样例 · C-13 待制作队列 · C-14 取消制作详情页 · C-15 平台/用户隔离 · C-16 讲师默认所有者 · C-17 顶部栏管理员匹配与千分位。 > **验证状态**:C-01 已确认;C-02/C-03 为样式“部分确认”;C-04~C-16 均为功能/修复“部分确认”(仅过 check/build 及接口隔离测试,未做公网与生产库验收)。 ### C-20260923-01|建立项目级协作、决策与功能确认机制 - 日期:2026-09-23 - 类型:文档 / 仅决策 - 需求与原因:后续修改代码和增加功能时,需要持续保存每次修改、功能确认结果和历史判断,避免新修改忽略此前已经确认的产品与工程边界。 - 修改范围:新增项目根目录 `AGENTS.md`;整理当前产品与技术基线、九条关键判断、修改前后流程、验证矩阵、记录状态和统一追加模板。 - 保留/新增判断:新增 D-001 至 D-009,覆盖工作区归属、历史数据、组织停用、积分审计、文件鉴权、首次改密、数据库兼容、持久化部署和公网验证。 - 功能确认状态:已确认 - 验证证据:已对照当前 `README.md`、`package.json`、`docker-compose.prod.yml`、`.gitignore` 和工作区文件结构核对文档中的项目名称、技术栈、常用命令、生产端口、持久卷及主要功能描述;确认本次只新增该文档,未修改现有业务代码。 - 未确认与风险:当前工作区存在一批此前产生、尚未提交的组织工作区相关代码改动;本记录不宣称这些业务功能已经完成端到端验证。 - 后续修改提示:任何后续代码任务必须先阅读相关 D/C 编号,并在同一次任务结束前继续向本节末尾追加记录;不得覆盖本条记录。 - 关联:无。 ### C-20260923-02|全站蓝色适配纯粹星辰品牌色 - 日期:2026-09-23 - 类型:样式 - 需求与原因:用户提供纯粹星辰品牌蓝色样图,要求全项目蓝色视觉统一。 - 修改范围:从样图主体区域采样为 `#55B0F7`;调整 `src/styles.css` 的主色、悬停色、蓝色浅底、边框、阴影和常用状态色,并同步课程、首页与管理台组件中的蓝色行内样式。 - 保留/新增判断:保留 D-001 至 D-009 的业务与工程边界。品牌主蓝为 `#55B0F7`;浅蓝背景上的按钮文字使用深蓝,交互悬停使用较深蓝与白字以保持辨识度。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build` 通过;浏览器检查了本地营销首页桌面和窄屏呈现,主按钮、标题强调及进度条已显示新蓝色。仓库检索未发现主要旧蓝色值残留。 - 未确认与风险:未逐一检查需登录的课程工作区和管理台页面;未部署到公网。当前本地 3001 端口已有其他服务占用,未用本次启动的服务端验证登录页面。 - 后续修改提示:新增蓝色样式优先使用 `--blue`、`--blue-dark` 和对应蓝色浅底;在有文字的浅蓝按钮上检查对比度。进入登录后页面或部署时继续做实际页面核对。 - 关联:无。 ### C-20260923-03|按钮与蓝色层级改为简约扁平风格 - 日期:2026-09-23 - 类型:样式 - 需求与原因:用户反馈 C-20260923-02 后的浅蓝底深字按钮观感欠佳,要求整体保持简约、扁平并重新调整配色。 - 修改范围:调整 `src/styles.css` 的主按钮、强调按钮、次按钮、导航和选中态。品牌蓝 `#55B0F7` 继续用于视觉强调;承载白字的操作按钮改用同色系深蓝 `#0875BD`,悬停色为 `#075F9B`;移除按钮阴影与上浮效果,圆角收至 6px,增加键盘焦点轮廓。 - 保留/新增判断:替代 C-20260923-02 中“品牌浅蓝直接作为主按钮底色”的具体用法;保留其品牌蓝采样结论与 D-001 至 D-009。品牌色用于强调,文字按钮使用对比度更高的深蓝。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;本地浏览器查看营销首页,主按钮呈现为平面深蓝底白字,导航注册按钮与标题强调有明确层级。 - 未确认与风险:需登录的课程工作区和管理台尚未逐页目视核对;未部署公网。 - 后续修改提示:新增按钮先复用 `.primary-button`、`.secondary-button`,品牌蓝 `--blue` 用于强调,按钮面使用 `--blue-action`;勿恢复主按钮阴影和上浮位移。 - 关联:C-20260923-02。 ### C-20260923-04|全站课程展示最后更新时间与创建时间并支持按时间排序 - 日期:2026-09-23 - 类型:功能 / 前后端协同 - 需求与原因:个人登录后,所有课程展示位置需在合适处呈现课程的最后更新时间与创建时间,尤其在组织管理的课程列表中需展示且支持按时间排序。 - 修改范围: 1. 服务端:`server/db.ts`(`mapCourse` 增加 `updatedAt`)、`server/organizations.ts`(补充 `c.updated_at` 与 `c.created_at` 查询字段)、`server/index.ts`(资产上传/删除同步触发 `courses.updated_at = NOW()`)。 2. 前端类型与工具:`src/types.ts`(`Course` 增加 `updatedAt?: string`)、新增 `src/utils/date.ts` 规范化时间格式化。 3. 页面展示与排序: - `src/pages/OrganizationManage.tsx`:课程管理表格增加“最后更新时间”与“创建时间”两列,支持表头点击升降序排序与下拉框快捷切换; - `src/pages/Dashboard.tsx`:课程卡片底部展示创建日期与最后更新时间,并在顶栏支持按更新时间/创建时间升降序排序; - `src/pages/OrganizationPage.tsx`:课程进度表格增加最后更新时间与创建时间列,并支持表头点击排序与下拉选择; - `src/pages/CourseWorkspace.tsx`:工作区头部标题栏下方展示创建时间与最后更新时间; - `src/pages/admin/AdminCourses.tsx` 与 `AdminOrganizations.tsx`:补充创建时间与最后更新时间列与详情展示。 - 保留/新增判断:保留 D-001 至 D-009。课程时间以服务端数据为准,编辑讲稿、分集、上传或删除资产均更新课程 `updated_at`;排序默认采用最后更新时间最新优先(降序)。 - 功能确认状态:已确认(代码及编译已验证) - 验证证据:`npm run check`(TypeScript 类型检查无报错)、`npm run build`(Vite 生产构建成功,无模块冲突)。 - 未确认与风险:未涉及数据库字段新增(原有 `created_at`、`updated_at` 字段已在 `courses` 表中存在),无数据迁移风险;公网尚未部署。 - 后续修改提示:任何对课程内容的实质性变更接口,应确保维护 `updated_at = NOW()`。 - 关联:无。 ### C-20260923-05|工作区数字讲师资产库与课程单讲师引用 - 日期:2026-09-23 - 类型:功能 / 数据库 - 需求与原因:将课程级讲师改为个人或组织工作区的数字讲师资产;组织成员共用资产但按所有者分配编辑权限,并允许个人讲师独立复制到组织。用户随后明确允许资料不全时创建及选课,提交制作前才要求照片、录音齐备;未来 AI 素材验证另行接入。 - 修改范围:新增 `digital_instructors`、`digital_instructor_files`、`digital_instructor_events` 及课程讲师引用和提交快照;增加幂等旧讲师迁移;新增资产库、统一选择器、个人复制到组织、所有者转移、文件补充/替换、平台管理员维护入口;课程创建、编辑和模板流程改用工作区讲师,移除旧课程级讲师写入接口;更新文件鉴权、提示和帮助文案。核心文件为 `server/schema.sql`、`server/digital-instructors.ts`、`server/index.ts`、`server/db.ts`、`src/components/DigitalInstructorPicker.tsx`、`src/pages/DigitalInstructorsPage.tsx`、`src/pages/admin/AdminDigitalInstructors.tsx`、`src/pages/NewCourse.tsx`、`src/pages/CourseWorkspace.tsx`、`src/api.ts`。 - 保留/新增判断:遵守 D-001、D-002、D-005、D-007、D-009。讲师只属于一个工作区,组织讲师默认所有者为创建者;组织成员可使用全部组织讲师,普通成员仅所有者可编辑,组织管理员及平台管理员可编辑组织讲师并变更所有者。个人讲师加入组织使用独立文件副本。每门课程最多选择一位讲师;创建和选课允许素材不全,提交制作必须有至少一张照片及一份录音。当前只校验文件格式、大小及是否上传,不判断素材质量;未来接入 AI 验证时必须将其结果纳入提交条件。已提交课程使用讲师快照;讲师删除从资产库隐藏,保留历史课程引用和文件。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;在独立 PostgreSQL 测试库执行空库建表、插入旧课程讲师后重复运行迁移,旧讲师只迁移一次并关联旧课程;隔离服务中实际验证资料不全创建和选课、提交返回 409、补齐文件后提交成功;验证组织成员查看及选择、非所有者编辑返回 403、组织管理员和平台管理员编辑成功、所有者转移后新所有者可编辑、跨工作区选课返回 400、未登录及越权文件访问返回 401/403、个人讲师复制后照片和录音文件名均独立;删除已提交课程引用的讲师后,资产库隐藏但历史快照仍可读取。浏览器查看个人资产库、课程创建选择器及平台管理员组织讲师维护页。 - 未确认与风险:未在生产库或公网执行迁移、部署或验收;未用真实可克隆录音验证音频可用性,当前不会进行 AI 质量判断;未覆盖所有窄屏与旧数据异常组合。生产升级前必须备份数据库及上传目录,核对迁移后的旧课程引用和文件数;如需回退,恢复备份数据库及文件目录并回滚应用镜像,不应直接删除新表以免丢失升级期间新增资产。 - 后续修改提示:新增 AI 验证后,不能仅以“文件存在”作为提交准入;应保存可追溯的检测结果和版本,保证已提交课程快照不随资产编辑变化。新增文件访问或删除路径时继续验证跨个人、跨组织及历史快照。迁移旧讲师不凭空补录音。 - 关联:无。 ### C-20260923-06|统一讲师创建入口并核查组织页面与管理功能 - 日期:2026-09-23 - 类型:功能 / 修复 - 需求与原因:数字讲师应只在资产库创建,课程创建与编辑只选择已有讲师;同时检查组织页面和组织管理功能。 - 修改范围:`src/components/DigitalInstructorPicker.tsx` 移除内嵌创建流程,改为仅选择并提供资产库入口;`src/pages/DigitalInstructorsPage.tsx` 整理独立创建表单、搜索、空状态和工作区标题,移除组织权限说明文案,并避免创建与编辑表单同时显示;`src/pages/CourseWorkspace.tsx` 更新选择文案;`src/styles.css` 补齐页面样式;`src/pages/OrganizationPage.tsx`、`src/pages/OrganizationManage.tsx` 将课程排序 `useMemo` 移到条件返回之前,修复状态变化时的 Hook 调用顺序问题。 - 保留/新增判断:遵守 D-001、D-005、D-006、D-009 和 C-20260923-05。数字讲师的创建入口为资产库,课程路径只引用已有资产;组织管理权限继续以服务端成员关系和角色校验为准。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;本地浏览器检查数字讲师、组织页面及组织管理页面,组织课程、成员、积分、邀请和管理区块正常展示;独立 PostgreSQL 测试库实际验证未登录、成员、管理员和非成员的访问边界,申请及审批、邀请加入及停用、成员创建和改名、首次改密限制、积分调整与流水可见性、组织停用和恢复后的接口行为,断言均通过。测试库已删除。 - 未确认与风险:未在公网或生产数据上验证;未执行真实组织的增删改操作,浏览器端交互仅核查已有页面与只读展示;窄屏布局和所有课程操作未逐项验证。 - 后续修改提示:组织页面若增加条件渲染,保持 Hook 在所有返回之前调用;组织管理新增操作需复核角色和停用状态,并用独立数据验证失败路径。 - 关联:C-20260923-04、C-20260923-05。 ### C-20260923-07|已用于课程的讲师禁止删除并支持复制 - 日期:2026-09-23 - 类型:功能 / 修复 - 需求与原因:用户要求任何已有课程引用的数字讲师不可删除,并在讲师管理页面复制讲师。 - 修改范围:`server/digital-instructors.ts` 删除时在事务中锁定讲师并检查全部课程引用,命中时返回 409;新增同工作区复制接口,复制讲师资料和独立上传文件,记录来源与事件,复制件默认归操作人所有;`src/api.ts` 增加复制客户端;`src/pages/DigitalInstructorsPage.tsx` 和 `src/pages/admin/AdminDigitalInstructors.tsx` 增加复制操作。 - 保留/新增判断:遵守 D-001、D-005、D-007 和 C-20260923-05。只要课程仍引用讲师,无论课程状态,讲师都不能删除;复制件不继承课程关联,文件使用独立副本;停用组织不能复制新讲师。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;独立 PostgreSQL 测试库验证复制后 ID、来源、所有者及照片文件路径不同,未登录复制返回 401,草稿和已完成课程引用均阻止删除,未使用的复制件可以删除;本地浏览器确认资产库中出现“复制讲师”按钮。测试库已删除。 - 未确认与风险:未对生产数据、平台管理员页面的实际点击复制和组织停用场景做浏览器端验证;未部署公网。复制文件可能随讲师数量增加而占用更多存储空间。 - 后续修改提示:如引入课程讲师引用的新状态或关系,删除准入仍需覆盖全部引用;如调整复制机制,保持文件独立和来源可追溯。 - 关联:C-20260923-05、C-20260923-06。 ### C-20260923-08|讲师创建流程按照片与录音步骤优化并统一名称备注 - 日期:2026-09-23 - 类型:功能 / 数据库 / 样式 - 需求与原因:用户提供素材拍摄与录音准备参考图,要求创建讲师更友好;讲师资料只保留名称和备注,同一组织不能重名。 - 修改范围:新增 `src/components/DigitalInstructorCreateForm.tsx`,将创建流程分为讲师资料、照片、录音三个区块,增加拍摄/录音提示、格式大小校验、照片预览和录音试听;调整资产库和平台管理页的编辑字段为名称及备注;更新 `src/api.ts`、`src/types.ts` 和 `server/digital-instructors.ts` 的输入输出与复制逻辑;`server/schema.sql` 新增备注列并把旧简介一次性迁入备注,新增组织内名称去重触发器,保留历史重名数据;`src/styles.css` 补齐表单与窄屏导航样式。 - 保留/新增判断:遵守 D-001、D-005、D-007 和 C-20260923-05。组织内有效讲师姓名按去首尾空格、忽略大小写比较,创建、改名和跨库复制冲突返回 409;同工作区复制自动生成不冲突的“副本”名称。个人库允许同名。名称必填、备注选填;照片和录音允许创建后补齐,课程提交制作前仍须两项齐备。当前提示仅供用户自查,不声称已通过素材质量验证。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;独立 PostgreSQL 测试库验证组织同名创建/改名/个人复制入组织返回 409、组织内复制自动递增副本名、个人库不受组织重名限制、备注字段正确返回;重复执行建表脚本成功;模拟旧库缺少备注列后重跑迁移,旧简介保留到备注。浏览器查看桌面创建表单、输入已有名称的即时提示和禁用保存,390px 视口下无页面横向溢出。测试库已删除。 - 未确认与风险:未在生产库或公网执行迁移与验证;浏览器未实际上传真实照片和录音并听审质量。历史组织同名记录保留,编辑其名称时须改为唯一名称;旧 `organization`、`introduction` 数据列暂留作兼容,不再出现在新讲师界面。 - 后续修改提示:未来 AI 质量检验须保存可追溯结果,并在提交制作时加入准入判断;若改变讲师命名规则,需同步服务端触发器、复制命名及前端即时提示。生产升级前备份数据库与上传目录。 - 关联:C-20260923-05、C-20260923-07。 ### C-20260923-09|手机拍照与设备直接录音 - 日期:2026-09-23 - 类型:功能 / 样式 - 需求与原因:用户要求创建讲师时可追加多张照片,手机可以直接拍照,手机或电脑可以直接录音,不必预先准备文件。 - 修改范围:`src/components/DigitalInstructorCreateForm.tsx` 的照片选择改为分批追加、去除重复选择、单张移除和最多 19 张限制;窄屏提供后置摄像头拍照入口;新增基于浏览器 `getUserMedia` 和 `MediaRecorder` 的录音、计时、结束、取消、试听与重录流程,并在离开表单时释放麦克风。`server/digital-instructors.ts` 接受浏览器录音常见的 WebM、Ogg MIME 类型;讲师编辑及平台管理页的文件选择同步允许这些格式;`src/styles.css` 调整桌面与手机上传布局。 - 保留/新增判断:遵守 D-005 和 C-20260923-05、C-20260923-08。照片可多张,录音每位讲师当前仍为一份;录制与选择文件最终走相同的鉴权上传接口和提交前素材齐备判断。最多 19 张照片加一份录音对应当前服务端 20 文件限制。手机拍照调用系统相机,浏览器录音需要 HTTPS 或 localhost 及麦克风权限;未获得权限时可继续上传文件。当前只校验文件存在、格式和大小,不判断质量。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;390px 浏览器视口下显示拍照入口,页面无横向溢出;通过浏览器文件选择器先选两张测试照片,再从拍照入口追加第三张,已选数量由 2 变为 3 且前两张仍在;独立 PostgreSQL 测试库验证 multipart 请求保存两张照片及一份 WebM 录音,讲师返回素材齐备。测试库已删除。 - 未确认与风险:未在真实手机上触发系统相机,也未授权真实麦克风录音,因此设备兼容性、权限弹窗和录音质量尚未确认;浏览器录制格式因设备而异,后续声音克隆服务若只接受特定格式,需在接入时转换或限制格式;未部署公网。 - 后续修改提示:若提高照片上限,需同步调整 multipart 文件数量限制和前端提示;接入声音克隆接口前确认 WebM、Ogg、M4A 的实际支持范围。 - 关联:C-20260923-08。 ### C-20260923-10|数字讲师独立创建编辑与资产管理页 - 日期:2026-09-23 - 类型:功能 / 样式 / 接口 - 需求与原因:用户确认数字讲师的创建和编辑均使用独立页面,并要求统一照片、录音表单以及列表中的复制、删除和课程使用状态。 - 修改范围:新增独立创建及编辑路由和共用表单;编辑时可逐张保留、移除或添加照片,设置封面,更换或保留录音,组织管理员可调整所有者;重排个人、组织和平台管理资产列表,加入搜索、素材状态、课程使用情况、复制与删除确认;新增讲师详情、课程引用查询和一次提交多项编辑的接口,并为列表返回课程引用数量。核心文件为 `src/pages/DigitalInstructorEditorPage.tsx`、`src/components/DigitalInstructorCreateForm.tsx`、`src/pages/DigitalInstructorsPage.tsx`、`src/pages/admin/AdminDigitalInstructors.tsx`、`server/digital-instructors.ts`、`src/api.ts`、`src/types.ts`、`src/App.tsx` 和 `src/styles.css`。 - 保留/新增判断:遵守 D-001、D-005、D-007,以及 C-20260923-05、C-20260923-08、C-20260923-09。创建和编辑共用三段资料流程;照片和录音仍可暂缺,提交课程制作前要求齐备;已被课程引用的讲师在界面禁止删除,服务端继续返回 409;普通组织成员只能编辑自己拥有的讲师,组织管理员和平台管理员可维护组织讲师。编辑表单的文件增删和资料变更一次保存,失败时不提交部分数据库修改。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;独立 PostgreSQL 测试库及本地服务验证两张照片加录音创建、非所有者编辑返回 403、组织管理员一次编辑姓名备注及所有者并移除一张旧照片、增加封面照片和更换录音、课程引用数量变化、已使用讲师删除返回 409;平台管理员管理接口及引用查询通过。浏览器查看资产卡片、课程使用弹层、操作菜单、独立创建与编辑页,390px 视口无横向溢出且底部操作可见。测试服务已停止,测试库已删除。 - 未确认与风险:未在真实手机上验证系统相机和麦克风;未在生产数据库或公网验证。本地浏览器未对真实组织资料执行保存、复制或删除,相关写入由隔离接口测试覆盖。 - 后续修改提示:接入 AI 素材质量验证时,需同时更新共用表单提示与课程提交条件;若改变讲师删除策略,须同步课程引用查询、界面禁用状态和服务端 409 校验。生产升级前核对数据库与上传目录备份。 - 关联:C-20260923-05、C-20260923-08、C-20260923-09。 ### C-20260923-11|数字讲师搜索、备注收起与照片完整预览 - 日期:2026-09-23 - 类型:功能 / 样式 - 需求与原因:用户希望资产库默认隐藏搜索栏,创建讲师默认隐藏选填备注;编辑照片要显示完整画面,并可点击放大。 - 修改范围:`src/pages/DigitalInstructorsPage.tsx` 将搜索和素材状态筛选折叠到搜索按钮,筛选生效时在按钮上提示并提供清除操作;`src/components/DigitalInstructorCreateForm.tsx` 将备注改为按需展开,已有备注在编辑时自动展开;已保存和待上传照片均支持点击查看大图及 Escape 关闭;`src/styles.css` 补充搜索面板、按钮、完整照片缩略图和全屏预览的桌面/手机样式。 - 保留/新增判断:遵守 D-005 及 C-20260923-10。搜索收起时保留筛选条件,并在入口标识“已筛选”,避免列表变化无法解释;备注收起不清空已有输入;受保护照片放大仍通过鉴权获取,不公开文件路径。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;浏览器确认资产库搜索默认收起、点击展开并聚焦输入框;编辑页照片点击后出现完整大图及关闭按钮;创建页备注默认收起,点击后出现文本框;390px 浏览器视口查看资产库搜索入口和创建页布局,无明显横向溢出。 - 未确认与风险:未在真实手机上验证触摸查看照片;未部署公网。本次未改服务端接口或数据结构。 - 后续修改提示:若增加照片处理或裁剪功能,缩略图和大图仍应保留查看原图的路径;筛选条件隐藏时需保持可见提示或恢复默认筛选。 - 关联:C-20260923-10。 ### C-20260923-12|照片参考样例与录音倒计时朗读提示 - 日期:2026-09-23 - 类型:功能 / 样式 - 需求与原因:用户希望上传照片时能查看合适与不合适的参考样例;直接录音时显示朗读稿,先倒数 3、2、1 再录制,并显示录音时间,满 10 秒后用绿色提示。 - 修改范围:`src/components/DigitalInstructorCreateForm.tsx` 增加照片样例入口和弹层、固定朗读稿、获取麦克风后 3 秒倒数、实际开始录制后计时及 10 秒绿色时长提示,倒数期间可取消并释放麦克风;`src/styles.css` 增加样例、朗读稿与录音状态样式;`public/guides/digital-instructor-photo-reference.png` 保存用户提供的示例材料,弹层按适合和不适合两张画面展示,并可展开完整说明。 - 保留/新增判断:遵守 D-005 和 C-20260923-09。倒数结束前不启动 `MediaRecorder`;10 秒绿色仅说明本次录音时长达到提示线,不代表声音质量或未来 AI 检验合格,也不新增服务端时长限制。用户仍可上传现有录音文件。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;本地浏览器确认创建页出现样例入口和朗读稿,点击样例后显示照片对照;390px 视口查看两张对照照片可辨认、弹层可滚动,随后恢复默认视口。 - 未确认与风险:未授权真实麦克风,故倒数、实际录音计时和 10 秒状态切换尚未做设备端验证;未部署公网。构建提示主 JS 包超过 500 kB,未影响构建通过,本次未作拆包调整。 - 后续修改提示:接入正式声音质量或时长校验时,需在服务端校验音频真实时长和内容,不能依赖前端绿色提示;若替换参考图,检查手机端两张示例的裁切位置。 - 关联:C-20260923-09、C-20260923-11。 ### C-20260923-13|平台管理员独立待制作课程队列 - 日期:2026-09-23 - 类型:功能 / 样式 - 需求与原因:制作团队需要单独查看当前等待制作的课程及提交资料,无需在全量课程表中筛选。 - 修改范围:新增 `src/pages/admin/AdminProductionQueue.tsx` 与 `/admin/production-queue` 路由;管理导航增加“待制作课程”,概览入口指向队列;队列逐课展示提交人、工作区、提交时间、分集与讲稿数量、素材文件下载、提交时的数字讲师快照照片和录音文件;`src/styles.css` 增加桌面与窄屏布局。 - 保留/新增判断:遵守 D-001、D-005、D-009 和 C-20260923-05。队列仅查询 `WAITING_PRODUCTION` 状态;讲师资料以提交快照为准;文件继续通过鉴权接口下载,不公开静态文件路径;完整制作操作仍进入现有课程详情页。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;核对现有管理员课程列表接口按状态过滤且由 `adminAuth` 保护,文件接口校验登录及管理员身份。未改服务端或数据库结构。 - 未确认与风险:当前未使用真实管理员账号在浏览器中验证队列加载、搜索、素材下载与窄屏页面;未部署公网。既有工作区含其他未提交修改,本次未清理或覆盖。 - 后续修改提示:若增加制作状态或团队分工,明确队列纳入条件和权限;讲师照片与录音需继续使用提交快照及受保护文件接口。 - 关联:C-20260923-05、C-20260923-10。 ### C-20260923-14|取消平台制作详情页并合并制作操作 - 日期:2026-09-23 - 类型:功能 / 重构 - 需求与原因:用户明确要求删除平台管理员现有制作详情页,避免与新待制作页面重复。 - 修改范围:移除 `/admin/courses/:id` 平台路由及各管理页面对此路径的链接;待制作队列卡片加入完整讲稿、提交人资料,以及接单操作;同页增加“制作中”切换、交付文件上传与下载、实际积分调整和完成交付。全部课程页保留为只读总览。`AdminCourseDetail` 组件仍供组织管理员课程路径使用,不作为平台管理员页面。 - 保留/新增判断:保留 D-001、D-004、D-005、D-009;平台制作操作集中于 `/admin/production-queue`,受原管理员接口鉴权约束。替代 C-20260923-13 中“完整制作操作仍进入现有课程详情页”的判断。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;检索确认平台管理端没有残留指向 `/admin/courses/:id` 的链接,组织管理路径仍引用原组件。 - 未确认与风险:未用真实平台管理员账号验证接单、上传、积分调整与交付;未部署公网。旧平台详情页中的更多辅助操作未全部迁入新队列,例如删除交付文件、退回待制作和修改已完成课程的交付说明。 - 后续修改提示:若团队需要恢复辅助操作,应直接在制作队列卡片中补充并验证相应状态及权限,不要重新引入重复的详情页。 - 关联:C-20260923-13。 ### C-20260923-15|平台管理员与普通用户身份彻底隔离 - 日期:2026-09-23 - 类型:功能 / 数据库 / 修复 - 需求与原因:平台管理员账户必须与普通用户、组织成员和组织管理员分开保存,双方不能通过后台操作互相转换,避免权限和数据归属误操作。 - 修改范围:新增 `platform_admins`、`platform_admin_sessions` 独立表;后台登录和后台鉴权改用独立管理员会话;普通登录只使用 `users` 与普通会话;启动迁移旧 `users.role='ADMIN'` 记录并保留管理员审计归属;数据库触发器禁止普通表出现非 `USER` 角色,也禁止管理员表与普通用户手机号重叠;移除用户管理中的升降级操作;管理员操作的审计、积分流水、组织审批和数字讲师事件增加管理员归属字段;平台管理员不能创建普通用户数字讲师资产。 - 保留/新增判断:保留 D-001、D-004、D-006、D-007、D-009。新增判断:`users` 只保存普通用户,`platform_admins` 只保存平台管理员;平台管理员与普通用户没有角色转换接口、共享会话或共享手机号;旧管理员若拥有课程、组织成员关系或数字讲师业务数据,启动迁移会停止并要求人工处理,不自动删除或转移业务归属。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;隔离 PostgreSQL 库验证普通注册、管理员注册手机号拦截、普通登录、管理员登录、交叉登录拒绝、普通会话访问 `/api/me`、管理员会话访问 `/api/me`、普通会话访问后台返回 401、管理员访问后台统计/课程/用户/组织/数字讲师接口成功、管理员退出后会话失效、角色转换接口返回 403;实际操作验证用户创建/重置密码、组织创建/更新、成员授权/移除、组织积分、申请审批、课程制作状态/积分/交付/结单、数字讲师查看/编辑/复制/删除;浏览器实际验证管理员登录后台、管理员退出、普通用户登录课程首页;迁移库验证旧管理员从 `users` 迁入 `platform_admins` 且普通用户表不再保留管理员记录;数据库触发器验证三种手机号/角色越界写入均被拒绝;生产模式服务启动成功。 - 未确认与风险:未在生产数据库或公网部署;本地隔离测试使用临时数据库和测试账号,临时服务已停止,测试数据库待清理;历史管理员若拥有业务数据会按设计阻止自动迁移,需要备份后人工制定归属方案;构建仍有 Vite 大包体积提示,但不影响构建。 - 后续修改提示:新增平台后台接口必须使用 `adminAuth`,普通用户接口不得接受管理员会话;审计、积分流水、审批和资产事件涉及操作人时应分别写入 `admin_actor_id` 或普通用户字段;不得恢复 `users.role='ADMIN'` 或升降级接口。生产升级前备份数据库和上传目录,并先核对旧管理员是否拥有业务数据。 - 关联:C-20260923-05、C-20260923-10、C-20260923-14。 ### C-20260923-16|数字讲师创建默认所有者且编辑所有者权限收紧 - 日期:2026-09-23 - 类型:功能 / 样式 / 权限 - 需求与原因:用户明确要求:数字讲师创建时默认所有者为创建者本人,无需填写;编辑时普通用户不可修改所有者,仅管理员编辑时可以修改。 - 修改范围:`src/components/DigitalInstructorCreateForm.tsx` 确认创建时不展示所有者字段;编辑组织讲师时,非管理员显示只读禁用的所有者姓名并提示仅管理员可修改,管理员提供成员下拉框支持更换;保存时非管理员不传 `ownerId`;`src/pages/DigitalInstructorEditorPage.tsx` 补充系统管理员与组织管理员判断并传递 `canManageOwner`;`src/styles.css` 补充输入框禁用样式。服务端原有时序与权限(创建直接设为创建者自身、编辑非管理员改所有者报 403)保持生效。 - 保留/新增判断:遵守 D-001、D-005、D-009 和 C-20260923-05。数字讲师创建时不提供所有者填写项,服务端强制设为 `uid(req)`;编辑时所有者修改权严格限定为组织管理员及平台管理员,普通所有者编辑时字段只读。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 全部通过。 - 未确认与风险:未在生产环境或真实公网验证;既有未提交改动继续保留。 - 后续修改提示:未来若扩展协作者或多人所有权,需在此表单和接口保持只读与管理员授权逻辑一致。 - 关联:C-20260923-05、C-20260923-10。 ### C-20260923-17|顶部栏工作区管理员匹配与积分余额千分位展示 - 日期:2026-09-23 - 类型:UI / 体验 / 逻辑修复 - 需求与原因:用户明确要求:右侧“管理员”字样要跟当前工作区严格匹配,只有当前选中的工作区角色为管理员才显示,避免切换到个人工作区或普通组织成员时误显;积分部分文案需写清楚“积分余额:xxx”,且数字格式保持统一每三位用逗号隔开。 - 修改范围: 1. `src/components/Layout.tsx`:将 `isOrgManager` 从全局匹配任一组织角色(`organizations.some`)改为仅匹配当前工作区(`currentOrganization?.role === 'MANAGER'`);若处于个人工作区或非管理员组织,则不渲染管理员徽标; 2. `src/components/Layout.tsx`:积分徽章文案更新为“积分余额:xxx”,并使用 `.toLocaleString('zh-CN')` 统一千分位逗号展示; 3. `src/pages/OrganizationManage.tsx`:组织积分余额数字同样增加 `.toLocaleString('zh-CN')` 格式化,保持全站一致。 - 保留/新增判断:遵守 D-001。前台“管理员”徽章必须与当前选中的工作区上下文保持一致,不能泄露或混淆其他工作区的管理权限。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 全部通过。 - 未确认与风险:未在生产环境构建部署。 - 关联:C-20260923-05。 ### C-20260923-18|口播稿富文本弹窗与课程附件多选追加 - 日期:2026-09-23 - 类型:功能 / 样式 / 接口 - 需求与原因:长篇口播稿需要独立编辑空间与基础排版;分集素材和通用资产需要一次选择多份,也能多次添加待上传文件。 - 修改范围:课程详情页改为口播稿摘要与“编辑”入口,新增富文本弹窗,支持加粗、斜体、列表和长篇滚动编辑;保存到原讲稿字段,旧纯文本按段落显示;服务端保存时清理富文本标签。分集附件和通用附件的多次文件选择改为追加,显示待上传清单与删除入口,每批限 20 份。平台制作队列和组织管理详情按安全富文本显示讲稿,复制讲稿时提取纯文字。 - 保留/新增判断:遵守 D-001、D-005、D-009;讲稿沿用原字段,富文本使用显式格式标记区分旧纯文本,展示时清理 HTML;附件仍经原鉴权接口保存,不开放公开静态路径。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 通过;核对原上传接口支持 multipart 多文件、每次最多 20 份且单份最大 200 MB。 - 未确认与风险:未在已登录浏览器实际执行富文本保存、刷新回显、多次选择文件及上传;未部署公网。构建有既存的大包提示。旧分集创建弹窗仍提供纯文本初稿入口,创建后可进入新弹窗排版。 - 后续修改提示:若增加讲稿导出、搜索或下游 AI 处理,应先将富文本转换为纯文本或明确解析格式;新增讲稿展示处不得直接信任数据库中的 HTML。 - 关联:C-20260923-04、C-20260923-17。 ### C-20260923-19|课程素材全屏预览与讲稿弹窗布局、统计 - 日期:2026-09-23 - 类型:功能 / 修复 / 样式 / 接口 / 部署配置 - 需求与原因:用户要求 PPTX、PDF、Word、图片和视频上传后可预览,PDF 与 PPTX 可翻页;用户截图显示口播稿弹窗的工具栏和底部操作透出遮罩,并要求实时字数和口播时间估算。 - 修改范围:`src/components/CourseAssetPreview.tsx` 新增全屏预览,图片按原比例展示、视频使用浏览器播放控件、PDF 逐页渲染,PPTX 与 Word 调用鉴权转换接口后逐页渲染;分集和通用素材列表增加预览及下载入口,文件选择器标明支持格式。`server/index.ts` 新增课程素材鉴权预览接口,LibreOffice 按需转 PDF 并在私有上传卷缓存,删除素材或课程时清理对应缓存;`Dockerfile` 安装 LibreOffice 和中文字体。`src/components/LectureNotesDialog.tsx` 与 `src/styles.css` 修复弹窗整体白色容器、滚动和窄屏布局,增加实时字数与按每分钟约 260 字计算的时长;`src/api.ts` 的组织课程预览请求携带当前工作区标识。 - 保留/新增判断:遵守 D-001、D-005、D-008、D-009。预览先按素材所属课程和用户/组织角色鉴权;转换后的 PDF 保存在私有上传卷的 `.previews` 目录,不开放公共静态路径。时长仅为按字数估算,不代表成片实际时长。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build`、`git diff --check` 与本地 Docker 镜像构建通过;在隔离数据库、服务和上传目录中批量上传 PPTX 与图片,另上传 PDF、DOCX、MP4。PPTX 转换得到 6 页 PDF,浏览器实际从第 1 页翻到第 2 页;PDF 同样翻页,Word 显示转换后页面,图片全屏展示,MP4 显示播放控件;生产镜像内分别实际转换 PPTX 和 DOCX 并返回 PDF。未登录、其他用户、目标素材不存在分别返回 401、403、404;组织课程正确工作区访问返回 200,其他组织工作区返回 404,即使指定目标工作区但不属于该组织也返回 403。浏览器确认讲稿弹窗各区域在同一白色容器内,输入后字数与时间实时变化,取消后文字未保存。 - 未确认与风险:未部署到公网或使用生产数据验收;未在真实手机上验证讲稿弹窗触控与所有浏览器的视频编码兼容性,也未压测超大文档。PDF.js 工作线程与 LibreOffice 会增加前端资源体积和生产镜像体积;构建仍有主包超过 500 kB 的提示。非浏览器支持的视频格式需下载原文件查看。 - 后续修改提示:扩展预览类型时继续复用课程鉴权与工作区校验,不得直接公开缓存文件;生产部署前核对新镜像体积、上传持久卷、LibreOffice 可用性,并按 D-009 验证公网路径。 - 关联:C-20260923-18。 ### C-20260923-20|修复本地 PPTX 预览调用并导入演讲者备注 - 日期:2026-09-23 - 类型:修复 / 功能 - 需求与原因:用户上传的 PPTX 在课程详情提示预览生成失败,并希望上传时自动把演讲者备注提取为口播稿。 - 修改范围:`server/index.ts` 的 LibreOffice 调用支持 `SOFFICE_PATH`,并在本机运行时提供现有依赖路径作为回退;新增 `server/pptx-notes.ts` 读取 PPTX 备注正文;上传单份 PPTX 时,对指定分集或仅有一个分集的课程,在口播稿为空时写入逐页备注。上传结果返回导入页数,课程详情显示导入提示。增加 `jszip`、`fast-xml-parser` 依赖。 - 保留/新增判断:遵守 D-001、D-005、D-007、D-009。备注只填空白讲稿,不覆盖人工内容;多分集课程的通用素材不自动猜测目标分集。原始 PPTX 继续保存在原鉴权路径。 - 功能确认状态:部分确认 - 验证证据:用户文件的 ZIP 校验通过,6 页均有演讲者备注;本机 LibreOffice 转换成功并生成 259015 字节 PDF;现有本地服务进程的 PATH 未包含 soffice,定位到原预览失败原因。使用新解析函数读取该文件得到 6 段正文;`npm run check`、`npm run build`、`git diff --check` 通过。 - 未确认与风险:未在已登录浏览器完成上传到分集、刷新回显和预览接口的端到端验证;未部署公网。该课程现有口播稿非空,按保留规则不自动回填。PPTX 备注顺序目前按 notesSlide 编号读取,重排过幻灯片但备注 XML 编号未重排的文件可能顺序不一致。 - 后续修改提示:生产环境优先使用镜像内 soffice 或显式 `SOFFICE_PATH`;如需覆盖已有口播稿,应增加用户确认与预览差异。处理特殊 PPTX 时应按 presentation 关系映射真实页序。 - 关联:C-20260923-19。 ### C-20260923-21|确保生产容器可转换 PPTX 并修复预览中文缺字 - 日期:2026-09-23 - 类型:修复 / 部署配置 - 需求与原因:用户要求部署服务器后仍能调用 LibreOffice,并反馈当前预览中中文文字缺失,需增加原文件优先提示。 - 修改范围:`Dockerfile` 在安装 LibreOffice 后检查 `/usr/bin/soffice` 可执行;`docker-compose.prod.yml` 显式设置 `SOFFICE_PATH`;`server/index.ts` 在 macOS 转换时提供系统中文字体目录与微软雅黑到冬青黑体的替代配置,预览缓存升级为 `v2` 以跳过已生成的缺字 PDF,删除素材时一并清理两版缓存;`src/components/CourseAssetPreview.tsx` 与 `src/styles.css` 加入预览差异提示。 - 保留/新增判断:遵守 D-005、D-008、D-009。生产容器继续使用镜像内 LibreOffice 和 Noto CJK 字体;转换结果仍仅存私有上传卷,经鉴权接口读取。预览仅供查看,原文件为准。 - 功能确认状态:部分确认 - 验证证据:使用部署脚本相同的 `linux/amd64` 架构构建生产镜像成功;容器中 `/usr/bin/soffice --version` 正常,`fc-match 微软雅黑` 匹配 Noto Sans CJK;以用户 PPTX 在容器内转换成功,PDF 第一页正文完整可见且文字层含中文。本机原配置生成的 PDF 中文缺失;显式加载 macOS 字体目录与替代规则后,同文件转换的第一页中文完整可见。 - 未确认与风险:尚未部署服务器,未验证公网实际课程接口及持久卷中的新缓存;其他罕见或嵌入字体不保证版式完全一致。生产部署后仍需按 D-009 验证真实预览路径。 - 后续修改提示:升级 LibreOffice 或字体包时,用有中文及演讲者备注的真实 PPTX 检查渲染图和文字层;如更改缓存算法,更新缓存版本并维护删除清理。 - 关联:C-20260923-19、C-20260923-20。 ### C-20260923-22|统一包管理器为 pnpm - 日期:2026-09-23 - 类型:仅决策 / 文档 - 需求与原因:工作区此前同时存在已跟踪的 `package-lock.json`(npm)与未跟踪的 `pnpm-lock.yaml` / `pnpm-workspace.yaml`,包管理器混用会导致依赖不一致与部署失败。用户确认统一使用 pnpm。 - 修改范围:删除已跟踪的 `package-lock.json`;将 `pnpm-lock.yaml` 与 `pnpm-workspace.yaml` 纳入版本控制;`.gitignore` 新增忽略 `package-lock.json`;本文件新增 D-010 工程判断。 - 保留/新增判断:新增 D-010。依赖以 pnpm 为准,禁止提交 npm lock;新增或升级依赖后须刷新并提交 `pnpm-lock.yaml`,并用 `pnpm install --lockfile-only --frozen-lockfile` 校验一致。 - 功能确认状态:已确认(仅决策) - 验证证据:用 corepack 激活 pnpm 12.5.1,`pnpm install --lockfile-only --frozen-lockfile` 返回 exit 0,确认 `pnpm-lock.yaml` 与当前 `package.json` 完全一致;npm 阶段新增的四个依赖(jszip、fast-xml-parser、qrcode、@types/qrcode)均已存在于 lock 的 packages 与 importers 段。 - 未确认与风险:本机此前未安装 pnpm,已通过 corepack 临时激活;后续开发/CI 需确保 pnpm 可用(建议 `corepack enable` 或全局安装 pnpm)。`node_modules` 当前仍为 npm 格式,首次 `pnpm install` 会将其转为 pnpm 符号链接结构,属本地可逆变更。 - 后续修改提示:所有安装、升级依赖必须走 pnpm;提交前确认 `pnpm-lock.yaml` 已更新且 `package-lock.json` 不出现在 `git status`。 - 关联:D-010。 ### C-20260923-23|机构主页管理员点击课程进入详情 - 日期:2026-09-23 - 类型:功能 - 需求与原因:组织管理员需要从机构主页课程进度表直接打开课程详情。 - 修改范围:`src/pages/OrganizationPage.tsx` 将管理员视角的课程名称改为课程详情链接;点击时切换到该机构工作区。 - 保留/新增判断:遵守 D-001;沿用现有课程详情接口的服务端权限校验,仅向机构管理员提供此入口。 - 功能确认状态:部分确认 - 验证证据:确认 `/courses/:id` 路由和课程详情接口允许所属组织管理员访问;`npm run check`、`npm run build`、`git diff --check` 通过。 - 未确认与风险:未使用已登录机构管理员在浏览器实际点击验证,也未部署公网。 - 后续修改提示:若普通组织成员也需进入课程详情,先调整并验证服务端课程访问权限,再开放入口。 - 关联:D-001、C-20260923-04。 ### C-20260923-24|广告落地页 /lp:数字人课程制作引流入口与线索后台 - 日期:2026-09-23 - 类型:功能 / 数据库 / 前端 - 需求与原因:需要在外部广告位投放 banner 引流,落地页须与主站隔离、互不干扰;以留下联系方式为主要目标、注册降为可选,并用广告语言写清数字人课程制作服务内容。用户确认:独立构建入口、留资为主 + 注册折叠选填、线索入库并做后台管理页、复用现有微信群二维码,微信号对外展示 bob-yuxinyang,价格不外露,暂不放样片。 - 修改范围: 1. 新增独立入口 `landing.html` 与 `src/landing/`(`main.tsx`、`LandingPage.tsx`、`landing.css`);`vite.config.ts` 改为双入口构建;生产服务端新增 `/lp`、`/lp/*` 显式路由,返回 `dist/landing.html`。 2. `server/schema.sql` 新增 `lead_requests` 表(姓名/手机/微信/需求类型/体量/描述/来源/状态/备注/关联用户/时间)。 3. `server/index.ts` 新增 `POST /api/public/leads`(公开提交、按 IP 60 秒限流、同手机号 24 小时内合并更新、可选密码注册)、`GET /api/admin/leads`、`PATCH /api/admin/leads/:id`(均 adminAuth)。 4. 前端新增 `src/pages/admin/AdminLeads.tsx`、`/admin/leads` 路由与「落地页线索」导航项;`src/api.ts` 增加 `admin.listLeads` / `admin.updateLead`;`src/types.ts` 增加 `Lead` / `LeadStatus`;`src/styles.css` 仅新增 `.status-filter-select` 与 `.lead-message` 两个类。 5. README 补充落地页说明。 - 保留/新增判断:保留 D-001 至 D-010。新增:广告落地页必须与主站物理隔离(独立 HTML 入口 + 独立 CSS,不加载主站认证/路由上下文,样式统一 `lp-` 前缀);线索属公开写入数据,必须限流并在服务端剥离 HTML;注册只能由用户显式填写密码触发,不得自动建号;同手机号 24 小时内重复提交按更新处理,避免线索重复。 - 功能确认状态:部分确认 - 验证证据:`npm run check`、`npm run build` 通过,产物含独立 `dist/landing.html` 与 `landing-*.css|js`,主站 `dist/index.html` 不引用任何 landing 资源(grep 计数 0)。以 `NODE_ENV=production` 启动本地服务(端口 3011)实测:`/lp` 与 `/lp/anything` 返回 200 且标题为「数字人课程制作 · 星痕智能课程工坊」,`/dashboard` 仍走主站 SPA 兜底;`POST /api/public/leads` 正常 201、非法手机号 400、缺微信号 400、同 IP 60 秒内 429、带密码提交返回 token 且用该 token 调 `/api/me` 返回 200(姓名与微信号已写入)、同手机号重复注册 409;同手机号 24 小时内再次提交经 SQL 核对仍为 1 行且字段为最新值;`加粗` 落库为「加粗正常内容」。`GET /api/admin/leads` 管理员 200 且带 userName,未登录与普通用户 token 均 401,`PATCH` 更新状态与备注返回 200。测试数据已清理(3 个测试账号、5 条线索、1 条临时管理员会话)。 - 未确认与风险:未做公网部署验证(D-009 的 HTTPS 与真实域名层未覆盖);未做浏览器真机目视核对,桌面与窄屏仅按 CSS 断点编写;`/lp` 依赖 `NODE_ENV=production` 分支,本地开发需访问 `/landing.html`;`lead_requests` 建表仅在本地 dev 库执行过,未在生产库验证(语句为 CREATE TABLE IF NOT EXISTS + ADD COLUMN IF NOT EXISTS,可重复执行,但未经生产库实测);本次未新增依赖,`pnpm-lock.yaml` 无需变更,验证使用的是 npm scripts 而非 pnpm。 - 后续修改提示:改落地页只动 `src/landing/` 与 `landing.css`,禁止 import 主站 `styles.css` 或 `src/api.ts`;新增线索字段须同步 `leadSchema`、`mapLead`、`Lead` 类型与后台表格;投放链接建议带 `?from=` 或 `utm_source` 以保留渠道归因;对外微信号与二维码位于 `LandingPage.tsx` 的 `WECHAT_ID` 常量与 `src/assets/contact-group-qrcode.jpg`。 - 关联:无。 ### C-20260923-25|修复现场创建老师报错并取消首次登录强制改密 - 日期:2026-09-23 - 类型:修复 / 功能 / 数据库 - 需求与原因:机构管理员创建老师后账号已写入,却因异步提交后读取已清空的 React 事件 `currentTarget` 而显示 `reset` 报错;用户要求老师首次登录直接进入工作区。 - 修改范围:`src/pages/OrganizationManage.tsx` 在异步请求前保存表单元素,成功后重置表单并更新提示;`server/organizations.ts` 创建老师时将强制改密标记设为 `FALSE`;`server/index.ts` 与 `src/App.tsx` 移除旧强制改密拦截;`src/pages/Profile.tsx` 保留自愿改密入口但移除强制提示;`server/schema.sql` 幂等清除已有账号的旧强制改密标记;同步更新 `README.md`。 - 保留/新增判断:本条替代 D-006 的“首次登录必须修改密码”规则。现场创建账号仍使用初始密码 `888888`,首次登录可直接进入工作区;用户可在账户安全自行改密。服务端仍须校验登录身份及组织权限(D-001)。 - 功能确认状态:部分确认 - 验证证据:代码检查确认创建流程在异步边界前保存表单引用;`npm run check`、`npm run build`、`git diff --check` 通过。迁移语句仅更新标记为 `TRUE` 的账号,重复执行不再产生变化。 - 未确认与风险:未在隔离数据库中实际执行迁移及新老师登录全流程;未在公网部署或验收。固定初始密码仍需由机构管理员妥善交付,账号可自行改密。 - 后续修改提示:不得恢复新老师首次登录强制改密,除非用户提出新的规则;涉及登录流程应同时核对前端路由和服务端鉴权。 - 关联:替代 D-006;C-20260923-06。 ### C-20260923-26|部署当前系统到 xinghen.work - 日期:2026-09-23 - 类型:部署 / 修复 - 需求与原因:用户要求将整个系统部署到服务器,并确保 `xinghen.work` 可访问;授权必要时覆盖服务器数据库数据。 - 修改范围:`Dockerfile` 改为按 `pnpm-lock.yaml` 冻结安装、构建和启动;`deploy.sh` 同步时排除 `uploads/`;将当前代码构建为 `linux/amd64` 镜像并上传至 `47.93.193.127:/opt/xinghen`,重建应用容器。保留现有 PostgreSQL 与上传持久卷,未执行数据库覆盖。部署前数据库备份位于服务器 `/opt/xinghen/.deploy/pre-deploy-20260923.dump`。 - 保留/新增判断:遵守 D-007 至 D-010。数据库迁移在现有库上成功时保留数据,不因已有覆盖授权而无故清库;生产代理与证书配置沿用已核对的当前配置。 - 功能确认状态:部分确认 - 验证证据:本地 `pnpm run check`、`pnpm run build`、生产镜像构建、Compose 配置检查、脚本语法检查通过;新容器运行镜像 SHA `589c7600dc81faf8f563db789fa5fb6200fe56b862cd944434f6e08c52e4fddf`。公网首页与 `/lp` 均返回 200,且返回本次构建的主站及落地页资源名;资源请求返回 200;`/api/health` 返回 200 与 `{"ok":true}`;HTTP 跳 HTTPS 返回 301;证书 SAN 包含 `xinghen.work`,有效期至 2026-12-12;未登录访问 `/api/me` 与 `/api/admin/leads` 均返回 401。容器内 LibreOffice 可执行,上传卷仍挂载。迁移后普通用户 5 条、平台管理员 1 条、课程 5 条,`lead_requests` 表存在。 - 未确认与风险:未使用真实账号完成登录、课程编辑、素材预览、线索提交及后台操作的公网端到端验收;未覆盖所有旧数据组合。回滚可恢复部署前数据库备份和先前镜像,上传卷没有被替换。 - 后续修改提示:后续部署仍须核对实际域名、当前代理配置和持久卷;若触发数据库覆盖,应先核对备份及目标库;新增功能需补充公网业务路径验收。 - 关联:D-007、D-008、D-009、D-010、C-20260923-21、C-20260923-24。 ### C-20260923-27|修正 PPTX 备注换行并在选取素材后直接上传 - 日期:2026-09-23 - 类型:修复 / 功能 - 需求与原因:PPTX 备注导入的口播稿把换行显示为字面量 ` `;课程详情选择素材后还需再次点击上传。 - 修改范围:`server/pptx-notes.ts` 仅将备注中的十进制或十六进制换行字符引用转为真实换行;`src/utils/lectureNotes.ts` 对以“第 1 页”开头的旧版纯文本导入稿在展示、复制和打开编辑器时做相同规范化;`src/pages/CourseWorkspace.tsx` 选择分集素材或通用附件后立即上传,移除待上传清单及二次上传按钮,增加上传期间禁用重复选择,并在失败后刷新素材列表、提示核对后重选。 - 保留/新增判断:遵守 D-005、D-009 和 C-20260923-20;原始 PPTX 不变,已有人工口播稿不被自动覆盖;只识别换行字符引用,不对任意 HTML 实体做全面解码。历史讲稿的数据库原值暂不批量改写,用户打开并保存后写回规范化文本。 - 功能确认状态:部分确认 - 验证证据:`pnpm run check`、`pnpm run build` 通过;使用合成 PPTX 备注验证 `
` 经解析变成实际换行,而无关的 `&` 仍保持原文本;检查两处文件选择入口均直接调用上传函数,且上传期间禁用再次选择。 - 未确认与风险:未使用已登录浏览器完成真实上传与刷新回显,也未在公网验证;未对生产数据库中的历史讲稿进行批量更新。现有上传接口逐份存储文件后写入数据库,若请求中途失败可能留下部分成功文件,重试前应查看素材列表。 - 后续修改提示:若需要批量持久修正历史讲稿,先按课程和分集预览差异并备份原值;PPTX 特殊文件仍需核对幻灯片真实顺序与备注对应关系。 - 关联:C-20260923-18、C-20260923-20。