# K93 短剧类型与内容类型支持多选 ## 目标 - 后台短剧基础信息中的「类型」「内容类型」改为至少选一项的多选。 - 后台列表筛选、列表展示支持多分类与多内容类型。 - App 分类分区和内容类型 feed 使用多标签字段做整词匹配。 - 单值字段继续保留,兼容创作者中心、旧接口和现有 App 卡片。 ## 分支 - FruitServer:`feat/k93-drama-multi-tag`,从 `origin/develop` 创建。 - ReelShortWeb:`feat/k93-drama-multi-tag-ui`,从 `origin/develop` 创建。 ## A. FruitServer ### A1 数据库 - 新增 `sql/migrate-k93-drama-multi-tag.sql`: - `drama.drama_types VARCHAR(32) NOT NULL DEFAULT ''` - `drama.category_ids VARCHAR(255) NOT NULL DEFAULT ''` - 从现有 `drama_type / category_id` 幂等回填。 - 同步 `sql/schema.sql` 的 drama 建表字段。 - 只提交迁移文件,不执行数据库。 ### A2 实体与 VO - `Drama`:增加 `dramaTypes/categoryIds` 逗号字符串。 - `DramaSaveReqVO`: - 新增 `List dramaTypes`、`List categoryIds`,列表优先。 - 保留 `dramaType/categoryId` 单值兜底。 - `DramaPageReqVO`:增加 `List categoryIds`,保留 `categoryId`。 - `DramaRespVO`:增加 `dramaTypes/categoryIds/categoryNames`,旧单值字段语义不变。 ### A3 保存规则 - 内容类型列表:normalize、去重、保持提交顺序;主值写第一个,完整列表写 `drama_types`。 - 分类列表:过滤 null/非正数、去重、保持提交顺序;主值写第一个或 0,完整列表写 `category_ids`。 - 列表为空时回退单值;单值也没有时保留当前值。 - 创建时内容类型最终为空则默认 LIVE。 - 非法内容类型继续返回 BAD_REQUEST。 ### A4 查询规则 统一使用整词匹配: ```sql (',' || column || ',') LIKE '%,value,%' ``` - 后台分页: - `dramaTypes` 非空:`drama_types` token 匹配。 - `categoryIds` 非空:`category_ids` token 匹配。 - `categoryIds` 为空:回退 `category_id` 等值。 - App `listPublishedFeed`:`drama_types` token 匹配。 - App feed 游标:marker 的解析后 `dramaTypes` 与任一筛选类型相交才允许继续。 - App `listPublished(categoryId)`:`category_ids` token 匹配,使多分类剧出现在每个分类分区。 ### A5 响应装配 - 解析 `drama_types/category_ids`,空值回退单值。 - `categoryNames` 按 categoryIds 顺序映射分类名。 - 分类名缺失时回退 `genre`。 - 现有 `dramaType/categoryId/category` 保持旧语义。 ### A6 文档与测试 - 更新 `docs/api-phase1.md` 的后台保存/分页字段、App feed 和分类说明。 - 增加服务测试覆盖: - 多类型/多分类去重、顺序与主值写回。 - 单值兜底行为不变。 - 后台分页 token 条件。 - feed 多标签 marker 可被 ANIME/LIVE 命中。 - App 分类分区 token 条件。 - 响应多值解析和分类名顺序。 - 门禁:`mvn test` 全模块通过。 ## B. ReelShortWeb ### B1 类型 - `DramaItem` 增加 `dramaTypes/categoryIds/categoryNames`。 - `DramaSaveData` 增加 `dramaTypes/categoryIds`。 - `DramaPageParams` 增加 `categoryIds`。 ### B2 表单 - `form.categoryIds: number[]`,默认空数组。 - `form.dramaTypes: string[]`,创建默认 `['LIVE']`。 - 两个下拉均使用 multiple、collapse-tags、collapse-tags-tooltip、filterable。 - 校验至少 1 项。 - 编辑时优先数组,缺失回退单值。 - 提交数组;`genre` 使用所选分类名以 ` / ` 拼接。 ### B3 筛选与表格 - 分类筛选改为 `categoryIds: number[]`。 - 内容类型筛选改为 `dramaTypes: string[]`。 - 查询传数组,重置为空数组。 - 类型列显示 `categoryNames.join(' / ')`,回退 `genre`。 - 内容类型列逐个渲染标签,缺失时回退 `dramaType`。 ### B4 门禁 - `pnpm ts:check` - `pnpm build:dev` - 对修改文件执行定向 ESLint ## 风险与说明 1. 新代码依赖迁移后的 `drama_types/category_ids` 列,发布顺序必须先执行迁移再启动新版本。 2. 逗号列仅存储受控内容类型和数字 id,不允许用户输入;查询值均来自校验后的数组。 3. 主值继续保留,降低旧客户端、创作者中心和旧接口的兼容风险。 4. 本次不改 specs 与 Flutter 客户端,不执行数据库迁移,不操作真机。 ## Git - FruitServer:`feat(content): 短剧类型与内容类型支持多选` - ReelShortWeb:`feat(admin): 短剧类型与内容类型多选` - 两仓库均 `--no-ff` 合回 `develop` 并 push。 - 作者:`Zhou Weidong <502333560@qq.com>`;禁止 `git add -A`。