soia-team/soia-open-pkm-vault-skills · Archived

soia-pkm-clip-wechat-account

批量归档用户自己管理的微信?

First seen Jul 25, 2026

Installation

$ npx skills add soia-team/soia-open-pkm-vault-skills --skill soia-pkm-clip-wechat-account

Summary

批量归档用户自己管理的微信公众号已发文章到 Obsidian vault。支持官方 API、公众号后台接口、登录态 Cookie 三条路线,并按 url 去重。Triggers:「同步我的公众号」「批量拉取我公众号历史文章」「批量归档我的公众号」「导入公众号已发文章」「clip 我的公众号」

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from soia-team/soia-open-pkm-vault-skills · top by installs.

npx skills add soia-team/soia-open-pkm-vault-skills

Browse all from soia-team/soia-open-pkm-vault-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 5
License LICENSE
Default branch main
Open issues 0
Status Archived

Skill metadata

Parsed from SKILL.md frontmatter.

Version2.0.4

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 25,453 B
  • docs SUMMARY.md 384 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 4 installs

SKILL.md

soia-pkm-clip-wechat-account

clip 家族的公众号批量成员:和 soia-pkm-clip-wechat-article(单篇、贴 URL 归档)不同,这个 skill 面向"把我自己公众号的历史文章一次性拉进 vault"的批量场景。只用于归档你自己管理的公众号——不是通用的公众号爬虫。

客户可读说明

这个技能可以做什么

批量归档用户自己管理的微信公众号已发文章到 Obsidian vault。支持官方 API、公众号后台接口、登录态 Cookie 三条路线,并按 url 去重

客户想要 技能会做 客户能看到
完成本技能覆盖的工作 读取用户请求、必要上下文和本技能正文流程,执行最小可靠步骤 客户会看到 Obsidian/vault 文件变更、终端日志、生成产物路径和最终回执。
缺少依赖、权限、配置或 key 停止需要外部状态的动作,明确指出缺什么 安装命令、申请地址、配置路径或需要客户确认的问题
执行完成 汇总成功、跳过、失败、文件变更和验证结果 一段可复制进工单/日志的完成回执

客户如何使用

  1. 用自然语言说明目标,并提供必要输入:文件、URL、repo、workspace、proposal、vault 或平台账号状态。
  2. 能 dry-run 或预览的动作先给预览;涉及删除、覆盖、发送、发布、写远端状态时先征求客户确认。

依赖与安装

安装(推荐:装整个领域插件,一次装好本仓全部技能):

claude plugin marketplace add soia-team/soia-open-skills
claude plugin install soia-pkm-vault@soia

只要这一个技能时,可用 npx 路线。注意技能会落进共享真源 ~/.agents/skills;若同时装了插件,同一技能会出现两份索引且各自漂移,建议二选一:

npx skills add soia-team/soia-open-pkm-vault-skills -g -a '*' -s soia-pkm-clip-wechat-account -y

配置约定:

~/.config/soia-skills/soia-pkm-clip-wechat-account/config.yml
SOIA_PKM_CLIP_WECHAT_ACCOUNT_CONFIG_FILE=<custom-config-path>
# 兼容别名:$SOIA_PKM_CLIP_GZH_CONFIG_FILE(以及原有 $SOIA_PKM_CLIP_GZH_ENV_FILE)
  • 如果本技能不需要私有配置,可以不创建 config.yml
  • 如果需要 API key、cookie、session、provider home 或本机路径,只能放进私有 config.yml、进程环境或 provider 自己的登录态里,不能写进仓库、vault 正文或日志。
  • 第三方 skill 只能声明依赖和安装方式,不直接修改第三方 skill 文件。

WorkBuddy 的装载单位是角色化专家而不是插件,npx skills add -a '*' 覆盖不到它,需要单独安装,见 docs/install/workbuddy.md

日志与完成回执

批量抓取只代表 captured;落盘后按生命周期管理技能逐对象完成 organized → MOC/导航 → map → Base,不适用阶段显式写 not_applicable,不能以批量成功数代替闭环证据。

每次执行都要让客户看见过程和结果。最低回执格式:

完成:<一句话说明本次完成了什么>。

日志摘要:
- started: <检查到的输入/配置/依赖,不打印秘密值>
- processed: <数量或范围>
- created/updated: <数量或路径>
- skipped/failed: <数量和原因>

文件变化:
- <绝对路径或“未改动文件”>

验证:
- <运行过的检查、命令或人工核对点>

问题与下一步:
- <缺 key / 缺依赖 / 需要客户确认 / 建议下一条命令;没有则写“无”>

交付顺序:批量归档必须先把文章全部落盘,再统一输出上面的回执,不得反过来;author--account-name 兜底、content_complete: false 等不确定字段要在回执里显式标注“未核实”,不编造。

三条路线怎么选

路 A · 官方 API 路 C · 公众号后台接口 路 B · 登录态 Cookie
覆盖范围 只有通过草稿箱「发布」发出的文章 全部历史,含手动群发的老文 全部历史,含手动群发的老文
账号要求 需已认证服务号/订阅号 任意公众号,只要你能登录后台 任意公众号,只要你能登录后台
凭据 AppID/AppSecret,长期有效 + IP 白名单 token/Cookie,几小时过期 key/passticket/appmsgtoken,几小时~几天过期
凭据抓取难度 一次性配置(后台生成) 抓 1 对值(token+Cookie) 抓 3 个票据 + Cookie,容易漏抓/配错对
官方程度 developers.weixin.qq.com 有文档 非官方,社区逆向(源码级核对) 非官方,社区逆向,未见于官方文档
脚本 scripts/fetch_api.py scripts/fetch_mp.py scripts/fetch_cookie.py

建议:先跑路 A 探路(凭据搭起来一次成本低,长期可复用于增量同步;多数个人未认证号会直接卡在权限门槛,见下)。要读全部历史(含手动群发的老文),优先用路 C——凭据只需抓一对 token/Cookie,比路 B 要配对三个票据更不容易出错,是本 skill 里读自己号历史文章的推荐路线。路 B 保留作为路 C 失效时的备选(例如某天两个接口都改版)。三条路都只在你自己能登录/管理的账号上跑;跑过的账号落地时会按 url 自动去重(互相跳过已归档的文章)。

路 C 内部又分两条子路径,脚本按你传不传 --name/--fakeid 自动选:不传(默认)走 appmsgpublish,读你自己当前登录的号;传了 --name/--fakeid 才走 searchbiz+appmsg,读指定/别人的号——原因见下方路 C 小节。

执行前确认与授权

确认策略

正式抓正文/写文件前,先用 --dry-run --limit 3 之类的小样本探路,向客户报告本次预计处理的公众号、篇数量级与时间范围;客户确认范围无误后再放开全量抓取。显式调用本 skill、传了 --name/--limit 等参数,都只是推荐输入,不构成跳过这次确认的理由——唯一跳过条件是客户当前这句话明确说"直接抓/不用确认",跳过后要在回执里说明本次沿用的范围假设。

使用前确认(consent)

路 B / 路 C 走的是公众号后台非官方接口,存在 ToS 风险。首次使用本 skill,或本节声明版本变化时,必须先展示以下声明并取得客户显式同意才能继续:

本技能部分路线(路 B / 路 C)使用公众号后台非官方接口,非微信官方文档收录,存在 ToS 风险;仅限归档你自己管理的账号,不得用于抓取他人公众号。(声明版本 v1)

  • 客户同意后,把 {accepted: true, acceptedAt: <ISO 时间戳>, disclaimerVersion: "v1"} 写入私有配置目录下的 consent.json(与 config.yml 同级:~/.config/soia-skills/soia-pkm-clip-wechat-account/consent.json)。
  • 已存在且 disclaimerVersion 匹配当前版本(v1)→ 跳过展示,直接继续,仅在日志里提一句"已确认使用条款(\<acceptedAt\>)"。
  • 客户拒绝 → 停止,不执行任何抓取动作。
  • 路 A(官方 API)走的是官方文档收录的接口,不受此 consent 约束。

路 A · 官方 API

接口依据(2026-07 核对 developers.weixin.qq.com + 微信开放社区,字段名已写进脚本注释)

接口 方法 请求 响应关键字段 依据
获取 access_token GET /cgi-bin/token?granttype=clientcredential&appid=&secret= accesstoken, expiresin 官方文档(已取到完整请求/响应示例)
获取成功发布列表 POST /cgi-bin/freepublish/batchget?accesstoken=,body {offset,count,nocontent} totalcount,itemcount,item[].articleid,item[].updatetime,item[].content.newsitem[].{title,author,digest,content,contentsourceurl,thumbmediaid,showcoverpic,needopencomment,onlyfanscancomment,url,is_deleted} 官方文档(抓取时未能取到完整示例正文,字段名已与微信开放社区多个独立帖子交叉核对一致——建议先用 --dry-run --limit 3 实测校准一次
获取永久素材列表 POST /cgi-bin/material/batchgetmaterial?accesstoken=,body {type:"news",offset,count}(count 官方标注 1-20) totalcount,itemcount,item[].mediaid,item[].updatetime,item[].content.newsitem[].{title,thumbmediaid,showcoverpic,author,digest,content,url,contentsource_url} 官方文档(已取到完整请求/响应示例)

⚠️ 覆盖范围限制(务必先读)

  • freepublish/batchget 只返回通过草稿箱「发布」动作发出的文章。只走过"群发"但没点过"发布"的内容、以及草稿箱功能上线前的旧版图文消息,官方目前没有任何 API 能拿到——这不是本 skill 的实现缺陷,是微信开放社区多个帖子交叉确认过的平台限制。
  • material/batchget_material(type=news)返回的是永久图文素材;据社区反馈,一篇文章一旦经草稿箱正式「发布」,可能就从这个素材列表里消失。两个接口是互补关系,取并集也不等于"全部已发布历史"。
  • 「发布能力」相关接口文档注明:自 2025-07 起,个人主体账号、企业主体未认证账号、以及不支持认证的账号,这些接口的调用权限会被回收。多数个人订阅号大概率整条路线都跑不通,需要已认证服务号/订阅号(同 soia-pkm-publish-wechat-draftdraft/add 一样的账号门槛)。
  • 结论:路 A 更适合"抽查/核对/增量同步已发布内容",不要指望它是"全量备份"。要读全部历史(含手动群发的老文)优先用路 C,路 B 作为备选。

凭据与限制

  • WECHATAPPID / WECHATAPPSECRET:获取路径、IP 白名单配置同 soia-pkm-publish-wechat-draft SKILL.md「如何获取并配置微信公众号 AppID / AppSecret」一节,不重复贴——两个 skill 用同一套公众号开发凭据。
  • 私有配置自动探测:$SOIAPKMCLIPWECHATACCOUNTCONFIGFILE(兼容别名 $SOIAPKMCLIPGZHCONFIGFILE,以及原有 $SOIAPKMCLIPGZHENVFILE)> ~/.config/soia-skills/soia-pkm-clip-wechat-account/config.yml。配置文件使用 YAML env: 映射,示例见 assets/config.example.yml;秘钥不进 vault、不进这个开源 skill 仓库。

用法

python3 scripts/fetch_api.py [--out <vault内相对目录>] [--limit N] [--dry-run] \
    [--vault <path>] [--account-name <公众号显示名>] [--force] [--page-size 20]
  • --dry-run:只拉列表打印,不写文件——首次跑强烈建议先 dry-run,核对 totalcount/itemcount 和拿到的篇数是否符合预期(尤其 freepublish/batchget 字段未 100% 核实到官方示例正文)。
  • --account-name:两个接口都不返回公众号昵称,给文件名/来源信息用;不传则文件名里用"公众号"占位。
  • --force:按 url 已归档的文章默认跳过,加这个才会覆盖重写。

路 C · 公众号后台接口(推荐:读自己号全部历史)

复用 mp.weixin.qq.com 后台(作者登录后台,非读者端 profileext)的接口,和路 B 一样能读全部历史(含手动群发的老文),但凭据只需抓一对 token/Cookie,比路 B 要配对三个票据(key/passticket/appmsg_token)更不容易出错。

路 C 内部有两条子路径,scripts/fetch_mp.py 按你是否传 --name/--fakeid 自动切换:

默认(不传 --name/--fakeid --name--fakeid
接口 appmsgpublish(作者端「发表记录」) searchbiz(名→fakeid)+ appmsg?action=list_ex(fakeid→列表)
读的是谁的号 你自己当前登录的这个账号,不需要指定名字/fakeid 指定名称/fakeid 对应的账号(可以是别的、你也管理的号)
适用场景 最常见场景:就是想备份自己号 你以某账号登录后台,但要读的是另一个你也管理、且知道名称/fakeid 的号
验证状态 已实测:2026-07-09 真实账号跑通,ret=0total_count=152 源码级核对,未见官方文档,标记「待用户实测校准」

为什么默认路径不用 searchbizsearchbiz 是按名字模糊搜索公众号列表,设计给"找到别人的号"用;用自己公众号的名字去搜,经常搜不到自己(返回空列表),不适合当"我已经登录、我就是这个号"的默认路径。appmsgpublish 直接读的是当前登录态对应账号的发表记录,不存在这个问题。

接口依据(源码级核对 + appmsgpublish 已实测)

接口 方法 请求 响应关键字段 依据
appmsgpublish(默认,读自己号) GET /cgi-bin/appmsgpublish,params sub=list&begin=<0,20,40,...>&count=20&type=1011&freepublishtype=1&subaction=listex&token=&lang=zhCN&f=json&ajax=1 baseresp.retpublishpageJSON 字符串json.loads 后为 {totalcount, publishlist[]});每条 publishlist[].publishinfo又是一层 JSON 字符串),json.loads 后为 {appmsgex[]},每条 appmsgex[]title/link/update_time/digest cv-cat/WechatOAApis — utils/wxutils.py 源码级核对,并于 2026-07-09 由用户在真实账号上实测验证通过
searchbiz(名 → fakeid) GET /cgi-bin/searchbiz,params action=searchbiz&begin=0&count=5&query=<公众号名>&token=&lang=zhCN&f=json&ajax=1 baseresp.retlist[].{alias,fakeid,nickname,roundheadimg,servicetype} wnma3mz/wechatarticlesspider — ArticlesUrls.py::officialinfo()cv-cat/WechatOAApis — utils/wxutils.py::getfakeidparams() 两个独立仓库参数完全一致
appmsg(fakeid → 文章列表) GET /cgi-bin/appmsg,params action=listex&begin=<0,5,10,...>&count=5&fakeid=<fakeid>&type=9&query=&token=&lang=zhCN&f=json&ajax=1 baseresp.retappmsgcntappmsglist[].{aid,appmsgid,cover,digest,itemidx,link,title,updatetime}不含 author 字段 wnma3mz/wechatarticlesspider — ArticlesUrls.py::__getarticlesdata()

appmsgpublishappmsg?action=listex 不是同一个接口、响应结构和分页步长都不同——应该是 mp 后台新旧两版前端各自调用的接口,脚本里是两套独立实现(listownaccount() vs fetcharticle_list()),不能混用。

⚠️ 限制与风险(务必先读)

  • 非官方接口:和路 B 一样没有官方文档背书,随时可能被调整。appmsgpublish 虽然已实测通过,同样不是官方文档收录的接口。
  • 仅用于归档你自己管理的公众号--name 命中后会打印 nickname/fakeid 并提示确认,不是你自己的号就 Ctrl+C 中断;默认路径读的就是当前登录账号本身,不存在认错号的问题。
  • token/Cookie 会过期:社区报告通常几小时失效,无刷新机制,过期后 base_resp.ret 会返回非 0(脚本已按下表给出针对性提示,两条子路径共用同一套提示,社区逆向交叉核对,未见官方文档,仍属「待用户实测校准」):
ret 含义 处理
200003 invalid session token/cookie 已过期,重新登录后台抓包替换
200013 freq control 触发限流,社区报告通常需要等待较长时间,别立刻重跑
200040 invalid csrf token token 和 Cookie 疑似不是同一次登录会话抓的
  • 两条子路径列表接口都不返回 author 字段:落地时 author--account-name / WECHATACCOUNTNAME 兜底,不保证等于文章真实署名作者,需要精确作者请人工核对原文页。
  • 限流风险:脚本默认每页/每篇请求间隔 --sleep 3 秒;社区报告即使 5 秒/页的节流,抓到近千篇量级仍可能触发 freq control,批量抓取整年历史建议配合 --limit 分批跑。

凭据获取

登录 mp.weixin.qq.com 后台,随便打开一篇文章编辑页或文章列表页,F12 打开浏览器开发者工具「网络」面板,从地址栏 URL 里复制 token 参数、从请求头复制完整 Cookie 字符串。按 assets/config.example.yml 填进私有 config.ymlenv.WECHATMPTOKEN / env.WECHATMPCOOKIE

⚠️ 注意 token 要取地址栏 URL 里的那个(数字串),不是「网络」面板里某个请求参数名叫 appmsg_token 的那个票据——两者是不同的票据,appmsgpublish 认的是地址栏 token

用法

# 默认:不传 --name/--fakeid,读你自己当前登录的账号(appmsgpublish,推荐)
python3 scripts/fetch_mp.py [--out <目录>] [--limit N] [--dry-run] \
    [--vault <path>] [--account-name <显示名>] [--force] [--sleep 3] [--page-size 20]

# 传 --name 或 --fakeid:读指定/别人的号(searchbiz + appmsg?action=list_ex)
python3 scripts/fetch_mp.py --name <公众号名> [--out <目录>] [--limit N] [--dry-run] \
    [--vault <path>] [--account-name <显示名>] [--force] [--sleep 3] [--page-size 5]
  • 不传 --name/--fakeid(默认,推荐):读你自己当前登录的账号,走 appmsgpublish--page-size 默认 20。
  • --name--fakeid(二选一):走 searchbiz+appmsg?action=list_ex--page-size 默认 5;--name 会先跑 searchbiz 搜索、取第一个匹配结果;已知 fakeid 时直接传 --fakeid 跳过搜索这一步更稳。
  • --dry-run:只翻页拉列表打印标题+链接,不抓正文、不写文件——比路 B 的 dry-run 更快(路 B 的 dry-run 仍会抓完全部正文),首次跑建议先 dry-run 核对篇数和标题是否符合预期。
  • --page-size 不传时按走的子路径自动取默认值(自己号 20 / 指定号 5),两个都是已验证/社区实测的稳定值,别调大。
  • 每条列表项拿到 link 后,脚本直接 GET 文章公开页面解析 #js_content(和路 B / soia-pkm-clip-wechat-article 单篇归档同一套正文抓取思路,不需要额外 cookie)。

路 B · 登录态 Cookie

接口依据(非官方,社区逆向,交叉核对)

GET https://mp.weixin.qq.com/mp/profile_ext
    ?action=getmsg&__biz=<biz>&f=json&offset=<offset>&count=10&is_ok=1
    &scene=124&uin=<uin,通常固定777>&key=<key>&pass_ticket=<pass_ticket>
    &wxtoken=&appmsg_token=<appmsg_token>&x5=0
Header: Cookie: <登录态 Cookie>

响应:{"ret":0,"errmsg":"ok","msg_count":N,"can_msg_continue":0|1,
       "general_msg_list":"<JSON 字符串,需二次 json.loads>","next_offset":N,...}
general_msg_list 解析后:
  {"list":[{"comm_msg_info":{"datetime":<unix ts>},
            "app_msg_ext_info":{"title","author","content_url","is_multi",
              "multi_app_msg_item_list":[同字段的合集子项]}}]}

依据来源:多篇独立技术博客(博客园/CSDN/知乎,2019-2024)对同一接口的逆向记录交叉核对,字段名彼此一致,但均非官方文档,developers.weixin.qq.com 未收录此接口。脚本已按此实现并写进代码注释,仍标记为「待用户实测校准」——第一次跑务必 --dry-run --limit 3

提示:给用户的原始需求里附了一个参考实现链接(zjp1997720/wechat-article-search);实测那个项目实际调用的是搜狗微信搜索(weixin.sogou.com),并没有用 cookie 抓 profileext 的技术路线,和"读全部历史"的目标不匹配,所以路 B 没有照抄它,改成上面这套被多方独立复现过的 profileext 方案。

⚠️ 限制与风险(务必先读)

  • 非官方接口:没有官方文档背书,微信随时可能调整实现让它失效;出问题没有官方支持渠道。
  • 仅用于归档你自己管理的公众号:多篇参考资料都提示这类逆向调用"建议仅用于学习研究目的",抓取他人公众号历史文章可能违反微信服务条款。这个脚本设计为你自己账号的自助备份,不要用来抓别人的号。
  • 票据会过期key/passticket/appmsgtoken 是会话票据,社区报告从几小时到几天不等;没有刷新机制,过期后报错就得重新登录抓包替换。
  • 有限流风险:脚本默认每次请求间隔 --sleep 1.5 秒,别调太快;批量抓取整年历史建议分批(配合 --limit)跑,别一次性拉几百篇。

凭据获取

登录 mp.weixin.qq.com 后台,打开该公众号任意一篇历史文章(或 profile_ext?action=home),在浏览器开发者工具「网络」面板里找同域请求,从请求 URL 里复制 __biz/key/passticket/appmsgtoken,从请求头复制完整 Cookie 字符串。按 assets/config.example.yml 填进私有 config.yml

用法

python3 scripts/fetch_cookie.py --biz <__biz> [--out <目录>] [--limit N] [--dry-run] \
    [--vault <path>] [--account-name <显示名>] [--force] [--sleep 1.5]
  • 列表接口每页 10 条(社区实测上限),脚本自动按 canmsgcontinue/next_offset 翻页。
  • 每条列表项拿到 contenturl 后,脚本直接 GET 文章公开页面解析 #jscontent(和 soia-pkm-clip-wechat-article 单篇归档同一套正文抓取思路,不需要额外 cookie)。
  • --dry-run 依然会翻完整个列表、抓完全部正文再打印(不写文件),量大时耗时较长——想快速核对列表本身,先看 stderr 里逐页打印的 拿到 N 条,累计 M

落地规范(三条路共用)

  • 中转区,不是终点:落到 vault 的收件箱层,不是 soia-pkm-organize-article-moc 归位后的最终位置——organize 上线前先囤在这里,之后随"作品库重构"一起归位到 40/50 区。
  • 默认输出目录:Inbox/gzh-articles/<年>/(vault 内相对路径,代码里的通用默认值)。这个 vault 建议设为你自己的中转区,例如 <vault-inbox-dir>/gzh-articles/——用 --out <vault-relative-dir> 或私有配置里的 env.OBSIDIANGZHOUT 覆盖(默认值刻意不硬编码具体中文路径,遵守本仓库 [SKILLSPEC.md](../../SKILLSPEC.md) 的"no hardcoded personal paths"规则)。
  • 文件名:<出版日期>-公众号-<账号显示名>-<标题>.md,同名冲突时按 url 特征加短后缀。
  • frontmatter:tags:[公众号原创, 待归位]source: 公众号自有urltitleauthorpublishedatcapturedatroute: api|cookie|mp-backendcontent_completetopics: []topics 是本 skill 相对用户原始字段清单多加的一项,方便后续 organize 直接补分类,无内容时留空数组,不影响其他字段)。
  • 正文:HTML → Markdown 走 stdlib html.parser(零第三方依赖,和这个仓库其它脚本一致),不保证 100% 还原排版,复杂内联样式/公众号专属组件会被拍平成纯文本+图片链接。content_complete: false 表示正文抓取失败或为空,需要人工核对原文链接。
  • 幂等:三个脚本都按 urlout_dir 下递归查找是否已归档,已存在则默认跳过(--force 才覆盖),三条路交替跑不会重复落地同一篇。

归档后

按 clip 家族惯例,AI 后续可以:

  1. 逐篇补「## 摘要」(30-80 字)。
  2. 判断/回填 frontmatter topics
  3. 等作品库重构落定后,交给 organizeInbox/gzh-articles 批量归位。

闭环位置

★clip-gzh(批量收) → organize(归位/分类) → distill(收藏→观点) → compose(观点→文) → publish(发)

soia-pkm-clip-wechat-article(单篇归档)互补:clip-wechat 处理"看到一篇转发链接,随手存一篇";clip-gzh 处理"批量迁入我自己公众号的历史存量"。两者落地规范不完全一致(clip-gzh 多了 route/content_complete 字段),暂不合并,等作品库重构时一并评估是否收敛成一套。