契约锁电子签管理专家
一句话定位:把契约锁的「查、核、发、排」四件事收敛到一个入口——先判环境,再选通道,最后按 SOP 执行。
契约锁存在三套互不相通的通道,用错通道是本领域最高频的失败原因。本技能的第一职责就是先判定你在哪套环境,再决定用什么方式操作。
一、铁律(每次会话先过一遍)
- 先判环境,再动手。 SaaS 与私有化的接口域名、路径前缀、认证方式完全不同,混用必然 404 或登录失效。判定方法见第二节,不要凭地址猜测,要用指纹验证。
- 写操作必须显式确认。 发起、撤回、作废、签署、盖章、删除、权限变更,都属于会产生法律效力或不可逆的操作。执行前必须展示「对象 + 动作 + 影响范围」并取得用户明确同意。
- 契约锁部分接口
send默认为true。 只想建草稿时必须显式传send:false,否则会在确认前就把文件发出去。这是本领域最危险的一个默认值。 - 凭据不落盘、不回显。
appSecret、Cookie、访问 token 只在本次调用流程中使用;报告与回复中一律掩码(如sk_****abcd)。本技能脚本把登录态存在本机~/.qys-master/,不写入技能目录。 - 只读优先。 查询类先走只读接口把事实摆清楚,再谈变更。给结论前先确认数据来源(哪个接口、哪个环境、什么时间取的)。
- 不确定就查文档、不要猜。 私有化各版本接口路径不同;OpenAPI 字段以 MCP 返回的当次文档为准。拿不到权威依据时,如实说明「当前无法确认」,不编造路径、字段或版本号。
二、第 0 步:环境识别(决策树)
用户给的地址
├─ 含 cloud.qiyuesuo.com / *.qiyuesuo.com(且非企业自有域名)
│ → 通道 A:SaaS 云平台(接口在 cloudapi.qiyuesuo.com)
├─ 企业自有域名(如 qys.company.com / 内网 IP:端口)
│ → 通道 B:私有化控制台会话(网页同款 /api/* 接口)
└─ 要对接系统、写代码、调开放平台
→ 通道 C:私有化 OpenAPI(MCP 取文档 + x-qys-* 签名)
指纹验证(不要只看地址长相,建议实测一次):
| 探针 | SaaS 表现 | 私有化表现 |
|---|---|---|
GET <host>/api/user |
落到 SPA / 404(SaaS 没有 /api 前缀) |
返回 JSON(含 result.organization) |
GET cloudapi.qiyuesuo.com/user |
200 + JSON | 不适用 |
| 页面是否有「多企业切换」下拉 | 有,且后端接口为 category/switch?companyId= |
单主体,无切换 |
三通道对照:
| A · SaaS 云平台 | B · 私有化控制台 | C · 私有化 OpenAPI | |
|---|---|---|---|
| 接口域名 | cloudapi.qiyuesuo.com |
企业自有域名 | 企业自有域名 |
| 路径前缀 | 无(/company/list) |
/api(/api/user) |
各接口自带(/contract/createbycategory) |
| 认证 | 浏览器 Cookie | 浏览器 Cookie + 安全头 | x-qys-accesstoken / x-qys-signature / x-qys-timestamp |
| 身份 | 一个账号可管多家企业 | 单个法人主体 | 开放平台应用 |
| 典型用途 | 多企业证书/认证核查 | 合同、待办、用印流程 | 系统集成、批量发起 |
| 本技能支持 | qys-saas.mjs |
qys-console.mjs |
MCP 取文档 + 手工构造请求 |
> ⚠️ SaaS 的页面域名不承载 REST。在 cloud.qiyuesuo.com 上请求 /company/list 会落到前端路由(返回 HTML)。
> 只要看到「HTTP 200 但 body 是 <!doctype html>」,就是路径不存在,不是没权限、也不是登录失效。
三、通道 A:SaaS 云平台(多企业场景)
3.1 认证
优先复用已有登录态(本技能自动读取 ~/.qys-master 与 ~/.qys-cli 两处档案):
cd <技能目录>/scripts
node qys-saas.mjs whoami --profile cloud # 先确认身份与通道
无登录态时二选一:node qys-login.mjs --channel saas --profile cloud(弹浏览器),
或用 --cookie "粘贴 Cookie"、环境变量 QYS_COOKIE 直接传。不要索取用户密码。
3.2 核心接口
| 用途 | 接口 | 关键字段 |
|---|---|---|
| 名下企业列表 | GET /company/list |
id、name、auth、authLevel、tenantType |
| 企业基本信息 | GET /company/get?companyId= |
legalPerson、registerNo、auth、esealCount |
| 数字证书 | GET /cert/cloud-platform/info?tenantId= |
caType、CN、StartTime、EndTime、Expired、SN、IssuerDN |
| 当前账号 | GET /user |
result.user.name、result.employee.id |
两个易踩的参数错误:
- 证书接口的参数名是
tenantId(不是companyId);传companyId会返回400 BAD_REQUEST。 - 无证书不是报错:接口返回
code:0且没有result字段——这与页面「基本信息」里整行「数字证书」不存在完全对应。 - ❌ 不要用
/companyauth/record/list?companyId=做逐企业核查:该接口忽略入参,永远只返回会话主主体的认证记录,会得出「所有企业都是同一个法人」的错误结论。
3.3 多企业数字证书批量核查(标准流程)
# 一次跑完:逐企业拉取企业信息 + 数字证书,按基准判定,并出 JSON 与 HTML 报告
node qys-saas.mjs cert-audit --profile cloud \
--expect-ca 陕西CA --expect-start 2026-07-28 --expect-end 2028-07-28 \
--out ./cert-audit.json --html ./cert-audit.html
脚本行为:串行遍历(默认间隔 200ms,--delay 可调)→ 每家企业输出一行进度 → 汇总
总数 / 有证书 / 符合基准 / 有证书但不符 / 无证书 → 生成明细 JSON + 商务风 HTML 报告。
判定口径(务必向用户说清楚):
- 「符合基准」= CA 名称一致 且 起始日不早于基准日 且 到期日一致(按日期部分比较,忽略时分秒);
- 「有证书但不符」必须给出具体差在哪(CA 名 / 起始日 / 到期日),不能只说「不符」;
- 「无证书」≠「未实名认证」,两者是独立状态,报告中要分开表述。
3.4 交叉验证(重要,别省)
接口结论必须与页面核对一次,避免参数传错导致的「全量无证书」假象:
- 在浏览器打开
https://cloud.qiyuesuo.com/company/info,读「基本信息」区的法定代表人 / 统一社会信用代码 / 数字证书三行; - 再切换到一家「被判为无证书」的企业(顶部企业下拉),确认该企业确实没有「数字证书」这一行;
- 两边一致才算结论成立。企业切换的实质是后端
category/switch?companyId=,前端下拉是 header 内的el-select。
四、通道 B:私有化控制台(单主体运维)
4.1 认证与身份
node qys-console.mjs whoami --base https://qys.example.com --profile prod
必须分清三个 ID,混用会直接报错:
| 用途 | 取值 | 常见错误 |
|---|---|---|
发起合同 tenantId |
organization.displayCompanyId(法人公司 ID) |
用 organization.id → 报 tenantId 无效 |
签署方 orgId |
organization.id(内部组织 ID) |
与上一行混用 |
| 企业章节点操作人 | employee.id(员工 ID) |
用 user.id → 节点无操作人 |
4.2 只读查询
node qys-console.mjs categories # 当前员工可用的用印流程
node qys-console.mjs category-config --category <ID> # 流程预设体检(发起前必做)
node qys-console.mjs contracts --status SIGNING # 合同列表
node qys-console.mjs contract <合同ID> # 合同详情(含签署方节点)
node qys-console.mjs todo # 我的待办
- 合同列表行不含状态字段(只有主题/ID/类型),要看状态必须
contract <ID>。 - 待办里
id是待办 ID,businessId才是合同 ID;拿id去查详情必报「合同不存在」。 - 状态枚举与跳转链接见
references/04-查询与状态枚举.md,不要凭字面猜「待签署/待填写」这类词(产品里没有这些文案)。
4.3 安全头(自建请求时必须带)
合同类接口校验防重放,缺头返回 449 / INVALID_NONCE:
req-token = uuidv4() + '-' + uuidv1()
nonce = sha256(req-token + 'XMLHttpRequest' + timestamp) # hex
headers : X-Requested-With: XMLHttpRequest, I18N-Language: zh_CN, timestamp(ms), req-token, nonce
本技能的 lib/core.mjs 已实现 securityHeaders(),自写脚本时直接复用,不要手抄。
五、发起合同(六步 SOP,不可跳步)
> 完整命令、字段表与报错对照见 references/02-合同发起全链路.md 与 references/03-签署方节点构造.md。
> 以下只给骨架与不可省略的判断点。
| 步 | 动作 | 关键判断 |
|---|---|---|
| 1 | 查用印流程 categories |
查不到时先重查一次(防旧列表),仍无则如实告知「列表里没有该流程」并排查:环境是否对、流程是否启用、发起范围是否含该账号 |
| 2 | 体检流程预设 category/config/convert |
必须带 --max-output 99999999。响应常达数十万字符,截断会导致把「带 3 份强制模板」的流程误判成干净 |
| 3 | 准备文件 / 确认模板 | 模板文档 ID 每次改配置都会重新生成,必须实时拉取,不能缓存 |
| 4 | 建草稿(不发起) | 按预设对齐节点;默认只存草稿,send 必须显式控制 |
| 5 | 核对草稿 | contract detail 逐项核对节点数量/顺序/形态;把发起页 /launch/contract/<ID> 给用户自查 |
| 6 | 发起 | 用户明确同意后再执行;发起后给详情页 /detail/<ID> |
顺序红线:环境 → 选流程 → 体检预设 → 才问签署方。在体检出预设之前,不要先问「几方签署」——
预设决定了该问「怎么补全」还是「几方签署」,顺序错了等于白问。
预设体检看什么:
| 字段 | 含义 |
|---|---|
documents[] |
流程自带模板文档,可当正文用;allowRemoveTemplate:false 时删不掉、会强制进合同 |
signatories[] / fillSignatories[] |
预设签署节点 / 拟定方节点(两个并列数组,只传前者会丢环节) |
subjectStatus / snStatus |
RULE = 主题/编号按规则生成,硬塞自定义值不生效 |
六、通道 C:OpenAPI 集成
接口的唯一权威来源是 MCP 返回的当次文档,本地经验只作执行细节补充。完整规则见 references/05-OpenAPI集成.md。
- 连接器
qiyuesuo-openapi-mcp,传输类型必须streamableHttp(写成sse会握手失败、工具列表加载不出来); - 检索用短业务词/英文路径片段(
合同、contract、seal),长中文词组容易命中为空; - 每次返回末尾的
{"doc_version":"..."}是实际生效版本的唯一凭据,必须核对并向用户报告; - 签名:
x-qys-signature = MD5(trim(appToken) + trim(appSecret) + trim(timestamp)),timestamp为 13 位毫秒且与服务端误差 ≤15 分钟,头名以当次文档为准; - 真实调用前必须给出脱敏请求预览并取得确认;MCP 只提供文档检索,没有执行工具。
高频坑(详见参考文件):
- 长 ID(
categoryId、documentId)一律传字符串,JS number 超 2^53 会丢精度,表现为「文件不存在」; - 非
PERSONAL的签署方必须显式传actions,否则目标服务端空指针,对外只报笼统的code=100; tenantType只能传COMPANY/PERSONAL,INNER_COMPANY是响应里的类型,写进请求会触发同样的 NPE。
七、能力总览(按用户意图路由)
| 用户说 | 走哪条通道 | 做什么 |
|---|---|---|
| 「检查所有企业的数字证书 / 有没有陕西CA」 | A | cert-audit + 页面交叉验证 + HTML 报告 |
| 「这家企业的法人是谁 / 认证了没有」 | A | company/get,或 companies 批量看 |
| 「我有哪些待办 / 这份合同到哪一步了」 | B | todo → 取 businessId → contract <ID> |
| 「帮我发一份合同」 | B / C | 走第五节的六步 SOP,先体检预设再问签署方 |
| 「这个流程为什么带出一份模板文件」 | B | category-config 看 documents[] 与 allowRemoveTemplate |
| 「接口报 449 / INVALID_NONCE」 | B | 缺安全头,用 securityHeaders() 补齐 |
| 「发起合同报 code=1000000」 | B | 企业章节点缺「印章归属」五键,见 references/03 |
| 「对接开放平台 / 查接口文档 / 签名怎么算」 | C | 先 MCP 取当次文档,再构造请求 |
| 「环境是 SaaS 还是私有化」 | — | 第二节指纹验证 |
八、输出规范
查询类:一句汇总(环境 + 总数 + 关键分布)+ Markdown 表格。表格列按业务定,不要自创「分布统计 + 最近 N 条 + 完整清单文件」的组合。
核查类(如证书核查):必须给
- 汇总数字(总数 / 命中 / 不符 / 缺失);
- 逐条明细(企业名、法人、证书 CA 与有效期、结论);
- 判定口径与交叉验证方式(否则结论不可复核);
- 处置建议(哪些需要人工介入、为什么前端改不了)。
报告文件:仅当用户要求文件时生成;默认交付表格。HTML 报告用 cert-audit --html 直接产出,单文件、无外部资源、可打印为 PDF。
禁止:把真实 appSecret、Cookie、访问 token 写进报告;把「无证书」直接说成「未认证」。
九、坑位速查(Top 12)
| 现象 | 根因 | 处理 |
|---|---|---|
SaaS 上 /api/... 全 404 |
用错了通道 | SaaS 走 cloudapi.qiyuesuo.com 且无 /api 前缀 |
| 证书批量核查「全军覆没」 | 参数用了 companyId |
证书接口参数是 tenantId |
| 所有企业法人相同 | 用了 companyauth/record/list |
该接口忽略入参,改用 company/get?companyId= |
HTTP 200 但返回 HTML |
路径在本版本/本环境不存在(SPA 兜底) | 不是权限问题;换路径或按版本适配 |
449 / INVALID_NONCE |
缺 req-token / nonce / timestamp | 用 securityHeaders() |
| 发起后页面显示「当前节点无操作人」 | 我方企业章操作人给了 COMPANY |
必须 EMPLOYEE + employee.id,后端自动派生 COMPANY |
code=1000000 签章节点必须指定组织 |
缺印章归属五键 | counterType / sealOwnerCompany / sealOwnerCompanyType / sigTenantId / signatoryTenantType 同时给 |
code=3003002 文档绑定失败 |
同一 documentId 被第二份合同复用 | 一个文档只能挂一份合同,重新上传(这个错说明节点结构已通过校验) |
HTTP 405 空 body |
把接口返回的对象整包回传 | 只挑需要的字段重新构造 |
| 预设体检说「没有预设」但页面有模板 | api 输出被截断 |
加 --max-output 99999999 |
tenantId 无效 |
传了 organization.id |
改传 displayCompanyId |
Git Bash 下 node fetch 报 unknown scheme |
MSYS 把 /path 改写成 C:/Program Files/Git/... |
加 MSYS_NO_PATHCONV=1,或用本技能脚本(已内置还原) |
十、参考文件
| 文件 | 内容 |
|---|---|
references/01-通道与环境识别.md |
三通道判定细节、登录态管理、凭据存放位置 |
references/02-合同发起全链路.md |
六步 SOP 完整命令、预设体检字段表、草稿核对项 |
references/03-签署方节点构造.md |
三种节点形态、印章归属五键、完整 JSON、报错对照、实测矩阵 |
references/04-查询与状态枚举.md |
待办/合同状态全量枚举、跳转链接表、易混概念 |
references/05-OpenAPI集成.md |
MCP 连接与版本机制、签名算法、请求构造、已知行为与坑 |
references/06-SaaS多企业核查.md |
本次实测沉淀:SaaS 接口清单、批量核查实现、交叉验证方法 |
scripts/ |
qys-saas.mjs / qys-console.mjs / qys-login.mjs(用法见 scripts/README.md) |