diff --git a/.gitignore b/.gitignore index e13eabe..2112e6e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ android/ .trae/ hx_page.html git/gitea.txt + +# ZCode 工具链运行产物(plans / rollout 缓存) +.zcode/ diff --git a/NEXT-STEPS.md b/NEXT-STEPS.md new file mode 100644 index 0000000..e127dd1 --- /dev/null +++ b/NEXT-STEPS.md @@ -0,0 +1,412 @@ +# 下一步计划 · 文章闭环收尾 + 工具链打通 + +> 交接对象:下一个接手的模型 / 工程师。 +> 本文档分两部分:**A. 本轮实际做完的事**(含验证证据),**B. 还没做的清单**(含具体做法)。 +> 前置计划:`.zcode/plans/plan-sess_cd5437b2-dd23-478b-bfec-f4cce1ee4b10.md`(架构决策与 7 步实施步骤,已全部落地)。 + +--- + +## A. 本轮完成清单(本次会话实测,非推断) + +### A1. 文章编辑→存储→渲染闭环(上一轮遗留,本轮核对确认已落地) + +| # | 内容 | 落点 | 核对方式 | +|---|---|---|---| +| 1 | 数据适配层 `normalizeCmsList` / `cmsLstToDelta` / `createVoteId` / `getArticleText` / `getArticleImages` / `hasRenderableContent` | `uniCloud-alipay/cloudfunctions/uni-cms-articles/content-adapter.js`(170 行,含空块过滤、媒体字符串归一、投票最少 2 项) | 已读全文 + `check-article-flow.js` 通过 | +| 2 | 云对象 add/update 契约:mobile 缺 content 时服务端生成兼容 Delta;update 用 `valueOrExisting` 合并缺省字段;发布强制 `hasRenderableContent`;vote_id 自动生成 | `uni-cms-articles/index.obj.js` | 已读全文 | +| 3 | schema:`content` 从 `required` 移除(现为 `['user_id','title']`);read 权限收敛为 `doc.article_status == 1 \|\| (auth.uid != null && ('admin' in auth.role \|\| doc.user_id == auth.uid))` | `uni-cms-articles.schema.json` | 程序化解析确认 | +| 4 | 审核覆盖:`content \|\| cmsLst` 双通道;mobile 不依赖 content 返回正文;`content.ops` 缺失时降级不抛异常 | `uni-cms-articles.schema.ext.js` | 已读 `afterRead` 全文 | +| 5 | 移动编辑器:首块文字可加载(`editText.vue:97` 无 `if (index && ...)` 假值陷阱,`:137` 用 `Number.isInteger`);`{...oldItem}` 保留媒体字段;小标题写回;封面统一字符串数组 | `pages2/editCms/*.vue` | 逐处 grep 确认 | +| 6 | 详情渲染:mobile 分支输出 text/title;`render-article-detail/*` 兼容 `data.value` 与 `data.attributes.src`;列表递归 children;移除 Vue2 `this.$set`;`wx.` 已用 `#ifdef MP-WEIXIN` 包裹;`100wv/60hv` 全部清零 | `uni_modules/uni-cms-article/**` | grep 确认 0 命中 | +| 7 | 媒体删除鉴权:admin 放行;普通作者必须带 `article_id` 且文件确实被该文章引用 | `ext-storage-co/index.obj.js:65-82` | 已读实现 | + +### A2. 本轮新修的问题 + +**① `ext-storage-co.downloadFile()` 审计告警 = 误报,已从根上修掉** + +真因:`tools/audit-project.js` 的 `WRITE_METHOD_RE` 不剥离 `//` 注释, +把文件里**被注释掉的** `async downloadFile()` 当成活方法,报「未校验登录态」。 + +修法(已落地):新增 `blankLineComments()`,用**等长空格**替换行注释—— +保持字节偏移不变,所以 `lineAt()` 行号和 `src.slice()` 切片仍然正确。 +`sectionCloudAuth` 内所有正则改跑 `scanned`,`lineAt` 仍传原始 `src`。 + +> ⚠️ 不要改成直接 `src.replace(...)` 后再切片——偏移会错位,行号全乱。 + +**② 新增 `tools/check-cloud-methods.js`——云对象方法存在性校验** + +补的是一层**原本完全没人管的静态盲区**:`audit-project.js` 只校验 +`importObject` 引用的「云函数文件」是否存在,**不校验方法名**。 +方法名写错在静态审计里 0 报错,只在运行时炸 `xxx is not a function`。 + +- 用 `acorn` 解析 `module.exports` 对象字面量取顶层方法名 +- 支持两种导出形态:对象字面量 / `module.exports = require('./functions')`(递归解析 require 链,深度上限 3) +- 同时发现随插件分发的云对象(`uni-id-co` / `uni-pay-co` / `uni-captcha-co` / `uni-media-library-co`) +- 4 组内联样本自测:对象字面量 / 默认对象参数 / 注释里的假方法 / 字符串里的花括号 +- **实测结果:18 个云对象、94 处调用点,0 错误** + +被它拦下的真实问题(均已确认无碍): +- `article_info` 缺 `getInfo` / `isSelectVote` / `getCmsArticleLog` → 实际都有,是我第一版正则提取器漏了 +- `uni-media-library-co` 缺 6 个方法 → 该云对象是 `module.exports = require('./functions')` 转出,加了 require 链解析后正常 + +**③ 新增 `tools/check-android-contract.js`——安卓端 × 云对象契约校验** + +`android/` 是独立 Kotlin 工程,通过 URL 化 HTTP 网关调云对象,同样有方法名漂移风险。 + +- 从 Kotlin 源码抽 `callChecked/callRaw("云对象","方法")` 调用对(70 个文件、44 处调用点、去重 43 个) +- **做了反例测试**:故意把 `getPublishedArticles` 改成 `getPublishedArticlesXX` → 脚本正确报错;还原后恢复 0 错误 + +**④ `package.json` 声明了此前隐式依赖的 `acorn`** + +`check-cloud-methods.js` 用了 `acorn`,但它是**未声明的传递依赖**—— +一次 `npm install` 就会把它剪掉,脚本随即 `Cannot find module 'acorn'`。 +现已写入 `devDependencies`(`^8.11.3`)并补了 npm scripts。 + +**⑤ 补上安卓端依赖的 `getPublishedArticles`(真实功能缺口)** + +`android/README.md` 要求部署该方法,`ArticleRepository.kt:34` 优先调用它, +**但云函数里根本不存在**——安卓首页/搜索永远只能回退到「仅本人文章」,游客拿到空列表。 + +已实现(`uni-cms-articles/index.obj.js`): +- 公开、无需登录;`article_status = 1` 过滤 +- 支持 `offset` / `limit`(上限 100)、`category_id`、`keyword` +- **keyword 做了正则转义**(`escapeRegExp`),避免用户输入被当正则执行 +- **投影只取列表字段,刻意不取 `content`**:因为 `schema.ext` 的 `afterRead` + 在读取 `content` 字段时会顺带给 `view_count` 加 1,列表页不该把「出现在首页」 + 算成一次阅读 +- 排序 `publish_date desc`,附带 `total` + +**⑥ 修掉 H5 构建的硬阻塞:`u-parse` 引用已被移除的 `