| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804 |
- # -*- coding: utf-8 -*-
- """根据 doc/文档/QiWe开放平台文档 重写 企微客户服务-功能实现说明.md"""
- from __future__ import annotations
- import json
- import re
- from pathlib import Path
- ROOT = Path(__file__).resolve().parents[1]
- OUT = ROOT / "doc" / "企微客户服务-功能实现说明.md"
- SRC = ROOT / "doc" / "企微客户服务-功能实现说明.md"
- QIWE_README = ROOT / "doc" / "文档" / "QiWe开放平台文档" / "README.md"
- DOC_BASE = "./文档/QiWe开放平台文档"
- COMMON_HEADERS = """| Header | 必填 | 说明 |
- |--------|:----:|------|
- | `Content-Type` | 是 | `application/json` |
- | `X-QIWEI-TOKEN` | 是 | 控制台申请的租户 Token,写入 `QIWEI_TOKEN` |"""
- DOAPI = """| 项 | 值 |
- |----|-----|
- | URL | `POST {QIWEI_BASE_URL}/api/qw/doApi`(默认 `http://manager.qiweapi.com/qiwe/api/qw/doApi`) |
- | Body 结构 | `{ "method": "<路径>", "params": { ... } }` |"""
- def compact_json(text: str) -> str:
- return re.sub(r"\s+", " ", text.strip())
- def build_io_table(
- method: str | None,
- req_example: str | None,
- resp_example: str | None,
- req: list[tuple[str, str, str]],
- resp: list[tuple[str, str, str]],
- passive: bool = False,
- ) -> str:
- if passive and req_example and not req_example.strip().startswith("{"):
- inp = req_example.strip()
- elif passive:
- inp = "无(QiWe POST → callbackUrl)"
- elif req_example:
- inp = compact_json(req_example)
- elif method and method not in ("【占位】", "见官方页"):
- m = method.split()[0]
- keys = ", ".join(f'"{r[0]}": ""' for r in req[:8])
- inp = compact_json(f'{{"method": "{m}", "params": {{ {keys} }}}}')
- else:
- inp = "—"
- if resp_example:
- out = compact_json(resp_example)
- elif resp:
- data_keys = ", ".join(f'"{r[0]}": ""' for r in resp[:8])
- out = compact_json(f'{{"code": 0, "data": {{ {data_keys} }}, "msg": "成功"}}')
- else:
- out = "—"
- # 单元格内用行内代码,避免表格嵌套代码块无法渲染
- inp_cell = inp if inp == "—" else f"`{inp}`"
- out_cell = out if out == "—" else f"`{out}`"
- return "\n".join(
- [
- "| 输入 | 输出 |",
- "|------|------|",
- f"| {inp_cell} | {out_cell} |",
- "",
- ]
- )
- def api_block(
- api_id: str,
- title: str,
- method: str | None,
- official: str,
- local_md: str | None,
- desc: str,
- req: list[tuple[str, str, str]],
- resp: list[tuple[str, str, str]],
- req_example: str | None = None,
- resp_example: str | None = None,
- extra: str = "",
- passive: bool = False,
- ) -> str:
- local_link = f"[{Path(local_md).stem}]({DOC_BASE}/md/{local_md})" if local_md else "—"
- method_line = f"`{method}`" if method else "被动推送"
- off_link = (
- f"[官方]({official})"
- if official.startswith("http")
- else "—"
- )
- parts = [
- f'<a id="{api_id.lower()}"></a>',
- "",
- f"### {api_id} {title}",
- "",
- f"| method | 官方 | 本地 |",
- f"|--------|------|------|",
- f"| {method_line} | {off_link} | {local_link} |",
- "",
- ]
- if desc.strip():
- parts += [desc.strip(), ""]
- parts.append(build_io_table(method, req_example, resp_example, req, resp, passive))
- if extra:
- parts += [extra, ""]
- return "\n".join(parts)
- APIS: list[dict] = []
- def add(**kw):
- APIS.append(kw)
- add(
- api_id="API-01",
- title="租户 Token / 快速开始",
- method=None,
- official="https://doc.qiweapi.com/doc-7562288",
- local_md=None,
- desc="> 在 [QiWe 控制台](http://manager.qiweapi.com/login) 申请 API Key;无单独 method。",
- req=[],
- resp=[],
- req_example='Header: X-QIWEI-TOKEN',
- resp_example='{"code": 0, "msg": "success"}',
- passive=True,
- )
- add(
- api_id="API-02",
- title="创建设备与扫码登录(四步)",
- method="/client/createClient 等",
- official="https://doc.qiweapi.com/api-344613850",
- local_md="创建设备(步骤1).md",
- desc="""人员账号纳入系统需完成登录闭环(亦可控制台在线登录直接拿 `guid`):
- | 步骤 | 接口文档 | method(以官方为准) |
- |:----:|----------|---------------------|
- | 1 | [创建设备](https://doc.qiweapi.com/api-344613850) | `/client/createClient` |
- | 2 | [二维码-获取](https://doc.qiweapi.com/api-344613856) | 见官方页 |
- | 3 | [二维码状态-检测](https://doc.qiweapi.com/api-344613857) | 见官方页 |
- | 4 | [二维码-code验证](https://doc.qiweapi.com/api-344613858) | 见官方页 |
- ⚠️ 创建设备后 5 分钟内未登录,实例会被清理。""",
- req=[
- ("deviceName", "string", "设备名称(步骤1 必填)"),
- ("deviceType", "integer", "0=ipad(推荐), 2=windows 等"),
- ("clientVersion", "string", "客户端版本,一般可空"),
- ("areaCode", "integer", "地区代理 ID,与登录地一致"),
- ("proxyUrl", "string", "可选 socks5 代理"),
- ("aid", "string", "可选本地 Aid 代理"),
- ],
- resp=[("guid", "string", "设备 ID,后续所有 params.guid")],
- req_example="""{
- "method": "/client/createClient",
- "params": {
- "deviceName": "店长-ipad",
- "deviceType": 0,
- "clientVersion": "",
- "areaCode": 320000,
- "proxyUrl": ""
- }
- }""",
- resp_example='{"code": 0, "data": {"guid": "a3318ad6-xxxx"}, "msg": "成功"}',
- )
- add(
- api_id="API-03",
- title="设置回调地址",
- method="/client/setCallback",
- official="https://doc.qiweapi.com/api-354411522",
- local_md="设置回调地址.md",
- desc="按 **Token** 配置回调;一个 Token 下所有账号共用。推送体含 `guid` 区分账号。",
- req=[
- ("callbackUrl", "string", "本方公网 URL,如 `https://{域名}/api/qiwei/webhook`"),
- ("authType", "string", "如 `Authorization`"),
- ("authSecret", "string", "回调鉴权密钥,可空"),
- ],
- resp=[],
- req_example="""{
- "method": "/client/setCallback",
- "params": {
- "callbackUrl": "https://your.domain/api/qiwei/webhook",
- "authType": "Authorization",
- "authSecret": ""
- }
- }""",
- resp_example='{"code": 0, "msg": "成功"}',
- )
- add(
- api_id="API-04",
- title="Webhook 回调结构(被动)",
- method=None,
- official="https://doc.qiweapi.com/doc-7331304",
- local_md="回调结构说明.md",
- desc="""QiWe **POST** 至 `callbackUrl`;`data[]` 含 `cmd`(15000 普通消息)、`guid`、`msgType`(13=链接)、`fromRoomId`、`msgData`。详见 [回调结构说明](%s/md/回调结构说明.md)。本项目:`cmd=15000` + `msgType=13` → 解析 `msgData.linkUrl` 发现沟通记录表。""" % DOC_BASE,
- req=[],
- resp=[
- ("data[].cmd", "integer", "回调类型"),
- ("data[].guid", "string", "账号"),
- ("data[].msgType", "integer", "消息类型"),
- ("data[].fromRoomId", "string", "群 ID"),
- ("data[].msgData", "object", "消息内容"),
- ],
- passive=True,
- resp_example="""{
- "code": 0,
- "data": [{
- "cmd": 15000,
- "guid": "xxx",
- "msgType": 13,
- "fromRoomId": "10791082xxxx",
- "msgData": {
- "title": "沟通记录",
- "linkUrl": "https://doc.weixin.qq.com/doc/xxx?docid=YYY"
- },
- "timestamp": 1708324990
- }],
- "msg": "成功"
- }""",
- )
- add(
- api_id="API-05",
- title="用户状态(在线检测)",
- method="/login/checkLogin",
- official="https://doc.qiweapi.com/api-347221662",
- local_md="用户状态.md",
- desc="查询指定 `guid` 是否在线,批量任务前应调用。",
- req=[("guid", "string", "设备 ID")],
- resp=[
- ("userOnlineStatus", "integer", "-1 需扫码;0 可免扫码;1 已扫码待确认;2 在线;4 取消;10 待验证码"),
- ("userId", "string", "企微用户 ID"),
- ("nickname", "string", "昵称"),
- ("corpName", "string", "企业名称"),
- ("lastActiveTime", "integer", "最后活跃时间"),
- ],
- req_example='{"method": "/login/checkLogin", "params": {"guid": "{{guid}}"}}',
- resp_example="""{
- "code": 0,
- "data": {
- "userOnlineStatus": 2,
- "userId": "1688852****",
- "nickname": "店长A",
- "corpName": "某某公司"
- },
- "msg": "成功"
- }""",
- )
- add(
- api_id="API-06",
- title="同步历史消息分页",
- method="/msg/syncMsg",
- official="https://doc.qiweapi.com/api-344613926",
- local_md="同步历史消息分页.md",
- desc="补拉历史群聊;`msgSeq` 递增分页直至 `hasMore=0`。",
- req=[
- ("guid", "string", "设备 ID"),
- ("msgSeq", "integer", "游标,首次 0,下次用上次返回的 seq"),
- ("limit", "integer", "每页条数,如 10~100"),
- ],
- resp=[
- ("hasMore", "integer", "是否还有下一页"),
- ("travelSyncKey", "integer", "同步游标"),
- ("syncMsgList[]", "array", "消息列表,含 fromRoomId、msgType、msgData、seq 等"),
- ],
- req_example='{"method": "/msg/syncMsg", "params": {"guid": "{{guid}}", "msgSeq": 0, "limit": 50}}',
- resp_example="""{
- "code": 0,
- "data": {
- "hasMore": 1,
- "travelSyncKey": 922174,
- "syncMsgList": [{
- "fromRoomId": 1023,
- "msgType": 13,
- "msgData": {"linkUrl": "https://doc.weixin.qq.com/..."},
- "seq": 9221964,
- "timestamp": 1708324990
- }]
- },
- "msg": "成功"
- }""",
- )
- add(
- api_id="API-07",
- title="群分页",
- method="/room/getRoomList",
- official="https://doc.qiweapi.com/api-344613881",
- local_md="群分页.md",
- desc="仅查**本人创建**的群;查全部群需结合 [会话分页](https://doc.qiweapi.com/api-344613938)(`sessionType=1` 为群 id)。",
- req=[
- ("guid", "string", "设备 ID"),
- ("nextStartIndex", "integer", "分页游标,首次 0"),
- ],
- resp=[
- ("hasMore", "integer", "是否有下一页"),
- ("nextStartIndex", "integer", "下次请求传入"),
- ("roomCount", "integer", "本页数量"),
- ("roomList[].roomId", "string", "群 ID"),
- ("roomList[].roomName", "string", "群名称"),
- ("roomList[].roomMemberCount", "integer", "成员数"),
- ],
- req_example='{"method": "/room/getRoomList", "params": {"guid": "{{guid}}", "nextStartIndex": 0}}',
- )
- add(
- api_id="API-08",
- title="群详情-批量",
- method="/room/batchGetRoomDetail",
- official="https://doc.qiweapi.com/api-344613882",
- local_md="群详情-批量.md",
- desc="先 API-07 拿 `roomId`,再批量查详情;成员**显示名**需再调 API-18。",
- req=[
- ("guid", "string", "设备 ID"),
- ("roomIdList", "string[]", "群 ID 列表"),
- ],
- resp=[
- ("roomList[].roomId", "string", "群 ID"),
- ("roomList[].roomName", "string", "群名称"),
- ("roomList[].roomAnnouncement", "string", "群公告(合规可匹配文档链接)"),
- ("roomList[].memberList[]", "array", "成员列表 userId、joinTime 等"),
- ],
- req_example='{"method": "/room/batchGetRoomDetail", "params": {"guid": "{{guid}}", "roomIdList": ["10802031057945400"]}}',
- )
- add(
- api_id="API-09",
- title="群成员变动查询",
- method="见官方页",
- official="https://doc.qiweapi.com/api-437674162",
- local_md="群成员变动查询.md",
- desc="按群 + 时间窗查询进退群记录(method 以 [官方页](https://doc.qiweapi.com/api-437674162) 为准)。",
- req=[("guid", "string", "设备 ID"), ("roomId", "string", "群 ID"), ("startTime/endTime", "integer", "时间窗(以官方为准)")],
- resp=[("memberEvents[]", "array", "进退群事件列表(字段以官方为准)")],
- )
- add(
- api_id="API-10",
- title="创建群",
- method="/room/createRoom",
- official="https://doc.qiweapi.com/api-344613883",
- local_md="创建群.md",
- desc="新建外部客户群并返回 `roomId`。",
- req=[
- ("guid", "string", "设备 ID"),
- ("isOuterRoom", "integer", "1=外部群"),
- ("memberList", "string[]", "初始成员 userId 列表"),
- ],
- resp=[
- ("roomId", "string", "新群 ID"),
- ("roomCreatetime", "integer", "创建时间"),
- ("memberList", "string[]", "成员列表"),
- ],
- req_example="""{
- "method": "/room/createRoom",
- "params": {
- "guid": "{{guid}}",
- "isOuterRoom": 1,
- "memberList": ["168885****57534"]
- }
- }""",
- )
- add(
- api_id="API-11",
- title="修改群公告",
- method="/room/modifyRoomNotice",
- official="https://doc.qiweapi.com/api-344613890",
- local_md="修改群公告.md",
- desc="将沟通记录文档链接写入群公告。",
- req=[
- ("guid", "string", "设备 ID"),
- ("roomId", "string", "群 ID"),
- ("notice", "string", "公告正文(可含文档 URL)"),
- ],
- resp=[("code", "integer", "0=成功")],
- req_example='{"method": "/room/modifyRoomNotice", "params": {"guid": "{{guid}}", "roomId": "108144***", "notice": "沟通记录:https://doc.weixin.qq.com/..."}}',
- )
- add(
- api_id="API-12",
- title="群消息置顶-列表",
- method="/msg/roomTopMessageList",
- official="https://doc.qiweapi.com/api-344613920",
- local_md="群消息置顶-列表.md",
- desc="⚠️ **仅群主**可置顶;用于合规检查文档是否已置顶。",
- req=[("guid", "string", "设备 ID"), ("roomId", "string", "群 ID")],
- resp=[
- ("list[].msgUniqueIdentifier", "string", "消息唯一标识"),
- ("list[].msgType", "integer", "消息类型"),
- ("list[].msgData", "object", "消息体"),
- ("list[].senderId", "string", "发送人"),
- ],
- req_example='{"method": "/msg/roomTopMessageList", "params": {"guid": "{{guid}}", "roomId": "1088541******6"}}',
- )
- add(
- api_id="API-13",
- title="群消息置顶-添加",
- method="/msg/roomTopMessageSet",
- official="https://doc.qiweapi.com/api-344613921",
- local_md="群消息置顶-添加.md",
- desc="⚠️ PoC:须传原消息的 msgId、msgSenderId、msgTimestamp、msgType、msgData。",
- req=[
- ("guid", "string", "设备 ID"),
- ("roomId", "string", "群 ID"),
- ("msgId", "string", "消息 id"),
- ("msgSenderId", "string", "发送人 userId"),
- ("msgTimestamp", "integer", "发送时间戳"),
- ("msgType", "integer", "消息类型"),
- ("msgData", "object", "如 `{ \"content\": \"...\" }`"),
- ],
- resp=[("code", "integer", "0=成功")],
- req_example="""{
- "method": "/msg/roomTopMessageSet",
- "params": {
- "guid": "{{guid}}",
- "roomId": "10965*****579",
- "msgId": "CIGABBDd*****",
- "msgSenderId": "16888****804",
- "msgTimestamp": 1752224990,
- "msgType": 0,
- "msgData": {"content": "沟通记录表链接"}
- }
- }""",
- )
- add(
- api_id="API-14",
- title="发送纯文本消息",
- method="/msg/sendText",
- official="https://doc.qiweapi.com/api-344613906",
- local_md="发送纯文本消息.md",
- desc="整改通知、预警、待办提醒等推送到用户或群。",
- req=[
- ("guid", "string", "设备 ID"),
- ("content", "string", "文本内容"),
- ("toId", "string", "用户 userId 或群 roomId"),
- ("isNoNeedRead", "boolean", "可选,是否无需已读"),
- ],
- resp=[
- ("isSendSuccess", "integer", "是否发送成功"),
- ("msgServerId", "integer", "消息服务端 ID"),
- ("msgUniqueIdentifier", "string", "消息唯一标识"),
- ("seq", "integer", "序号"),
- ],
- req_example='{"method": "/msg/sendText", "params": {"guid": "{{guid}}", "content": "请更新沟通记录表", "toId": "168****768657", "isNoNeedRead": true}}',
- )
- add(
- api_id="API-15",
- title="群发消息",
- method="/msg/sendGroupMsg",
- official="https://doc.qiweapi.com/api-344613923",
- local_md="群发消息.md",
- desc="每天对每个客户/群仅可群发一次;`sendType`:0=外部联系人,1=外部群。",
- req=[
- ("guid", "string", "设备 ID"),
- ("sendType", "integer", "0 联系人 / 1 群"),
- ("toIdList", "string[]", "接收方 ID 列表"),
- ("msgList[]", "array", "消息列表,type:0 文本、13 链接、14 图片等"),
- ],
- resp=[("groupMsgId", "integer", "群发任务 ID,供 API-16 查询")],
- req_example="""{
- "method": "/msg/sendGroupMsg",
- "params": {
- "guid": "{{guid}}",
- "sendType": 1,
- "toIdList": ["10791082****"],
- "msgList": [{"type": 0, "msgData": {"content": "本周运营内容"}}]
- }
- }""",
- )
- add(
- api_id="API-16",
- title="群发消息-状态查询",
- method="/msg/sendGroupMsgStatus",
- official="https://doc.qiweapi.com/api-344613924",
- local_md="群发消息-状态查询.md",
- desc="根据 `groupMsgId` 轮询发送进度。",
- req=[
- ("guid", "string", "设备 ID"),
- ("groupMsgId", "string", "群发任务 ID"),
- ("endDetailId", "integer", "分页游标"),
- ],
- resp=[
- ("hasSend", "boolean", "是否已发送"),
- ("isEnd", "boolean", "是否结束"),
- ("total", "integer", "总数"),
- ("customerList[]", "array", "各接收方状态"),
- ],
- req_example='{"method": "/msg/sendGroupMsgStatus", "params": {"guid": "{{guid}}", "groupMsgId": "115258331353230686", "endDetailId": 2}}',
- )
- add(
- api_id="API-17",
- title="外部联系人分页",
- method="/contact/getWxContactList",
- official="https://doc.qiweapi.com/api-344613869",
- local_md="外部联系人分页.md",
- desc="分页拉外部联系人;拿到 `userId` 后再调 API-18 查详情。建议落库后靠回调增量更新。",
- req=[
- ("guid", "string", "设备 ID"),
- ("currentSeq", "integer", "游标,首次 0"),
- ("limit", "integer", "每页条数"),
- ("bizType", "integer", "1=联系人变动;2=好友申请"),
- ],
- resp=[
- ("hasMore", "boolean", "是否有下一页"),
- ("currentSeq", "integer", "下次请求游标"),
- ("contactList[].userId", "string", "用户 ID"),
- ("contactList[].nickname", "string", "昵称"),
- ("contactList[].remark", "string", "备注"),
- ],
- req_example='{"method": "/contact/getWxContactList", "params": {"guid": "{{guid}}", "currentSeq": 0, "limit": 50, "bizType": 1}}',
- )
- add(
- api_id="API-18",
- title="联系人详情-批量",
- method="/contact/batchGetUserinfo",
- official="https://doc.qiweapi.com/api-344613868",
- local_md="联系人详情-批量.md",
- desc="批量查联系人详情(含群成员真实姓名场景)。",
- req=[
- ("guid", "string", "设备 ID"),
- ("userIdList", "string[]", "用户 ID 列表"),
- ],
- resp=[
- ("contactList[].userId", "string", "用户 ID"),
- ("contactList[].nickname", "string", "昵称"),
- ("contactList[].mobile", "string", "手机号"),
- ("contactList[].avatarUrl", "string", "头像"),
- ],
- req_example='{"method": "/contact/batchGetUserinfo", "params": {"guid": "{{guid}}", "userIdList": ["168*****5548"]}}',
- )
- add(
- api_id="API-19",
- title="客户标签-增删",
- method="/label/contactEditLabel",
- official="https://doc.qiweapi.com/api-344613937",
- local_md="客户标签-增删.md",
- desc="`opType`:1=增加,2=删除;`labelIdList`/`labelSuperIdList`/`labelOwnerList` 须一一对应。",
- req=[
- ("guid", "string", "设备 ID"),
- ("opType", "integer", "1 增 / 2 删"),
- ("paramList[].userId", "string", "客户 userId"),
- ("paramList[].labelIdList", "string[]", "标签 ID"),
- ],
- resp=[("data", "array", "操作结果")],
- req_example="""{
- "method": "/label/contactEditLabel",
- "params": {
- "guid": "{{guid}}",
- "opType": 1,
- "paramList": [{
- "userId": "78813023**",
- "labelIdList": ["1407374973784***"],
- "labelSuperIdList": ["1407375223060***"],
- "labelOwnerList": ["168885236**"]
- }]
- }
- }""",
- )
- add(
- api_id="API-20",
- title="添加群成员好友",
- method="/contact/addRoomContact",
- official="https://doc.qiweapi.com/api-425758709",
- local_md="添加群成员好友.md",
- desc="⚠️ PoC:从群内发起加好友,须合规确认。",
- req=[
- ("guid", "string", "设备 ID"),
- ("roomId", "string", "群 ID"),
- ("userId", "string", "目标成员 userId"),
- ("verifyText", "string", "验证语"),
- ],
- resp=[("code", "integer", "0=成功")],
- req_example='{"method": "/contact/addRoomContact", "params": {"guid": "{{guid}}", "roomId": "1079271***", "userId": "168885***", "verifyText": "您好"}}',
- )
- add(
- api_id="API-21",
- title="企微文件下载",
- method="/cloud/wxWorkDownload",
- official="https://doc.qiweapi.com/api-344613901",
- local_md="企微文件下载.md",
- desc="⚠️ 候选 PoC:从消息里的 fileId/fileAeskey 下载;返回临时 `cloudUrl`(7–15 天清理)。**不能替代「读在线文档正文」**。",
- req=[
- ("guid", "string", "设备 ID"),
- ("fileId", "string", "文件 ID"),
- ("fileAeskey", "string", "AES 密钥"),
- ("fileSize", "integer", "文件大小"),
- ("fileType", "integer", "1 大图 / 2 小图 / 4 视频 / 5 文件语音等"),
- ],
- resp=[("cloudUrl", "string", "临时下载地址")],
- req_example='{"method": "/cloud/wxWorkDownload", "params": {"guid": "{{guid}}", "fileId": "...", "fileAeskey": "...", "fileSize": 32768, "fileType": 5}}',
- )
- add(
- api_id="API-22",
- title="读在线文档正文",
- method="【占位】",
- official="—",
- local_md=None,
- desc="**QiWe 开放平台当前无「按 docid 读取企微在线文档表格正文」的专用接口。** 合规读表须 PoC API-21 或等待官方能力;禁止编造 method。",
- req=[],
- resp=[],
- )
- def render_section_2() -> str:
- lines = [
- "## 二、QiWe 官方接口速查(含入参/出参)",
- "",
- "> 文档来源:[QiWe 开放平台文档索引](%s/README.md)(本地最新爬取,2026)。 " % DOC_BASE,
- "> 各接口仅保留 **输入 / 输出** 一张表(报文示例);公共 Header 见 §2.0。",
- "",
- "### 2.0 统一调用方式",
- "",
- COMMON_HEADERS,
- "",
- DOAPI,
- "",
- "### 2.1 接口索引",
- "",
- "| 编号 | 接口 | method | 官方 | 本地 md |",
- "|:----:|------|--------|------|---------|",
- ]
- for a in APIS:
- local = f"[{a['local_md']}]({DOC_BASE}/md/{a['local_md']})" if a.get("local_md") else "—"
- off = a["official"]
- if off.startswith("http"):
- off_cell = f"[在线]({off})"
- else:
- off_cell = "—"
- m = a.get("method") or "被动"
- lines.append(f"| {a['api_id']} | {a['title']} | `{m}` | {off_cell} | {local} |")
- lines.append("")
- lines.append("### 2.2 各接口详细说明")
- lines.append("")
- for a in APIS:
- lines.append(
- api_block(
- a["api_id"],
- a["title"],
- a.get("method"),
- a["official"],
- a.get("local_md"),
- a["desc"],
- a.get("req", []),
- a.get("resp", []),
- a.get("req_example"),
- a.get("resp_example"),
- a.get("extra", ""),
- passive=a.get("passive", False),
- )
- )
- return "\n".join(lines)
- def api_ref(ids: list[str]) -> str:
- links = []
- for api_id in ids:
- a = next(x for x in APIS if x["api_id"] == api_id)
- links.append(f"[{api_id} {a['title']}](#{api_id.lower()})")
- return "、".join(links) + "(详见 §二)"
- def enhance_feature_section(text: str) -> str:
- """在功能小节中补充接口文档链接与入参出参指引"""
- text = re.sub(r"\n\*\*接口(文档与)?入参/出参[^\*]*\*\*[^\n]+\n", "\n", text)
- text = re.sub(
- r"(\*\*接口入参/出参表:\*\*[^\n]+\n)(?:\1)+",
- r"\1",
- text,
- )
- def repl_official(m):
- api = m.group(1)
- a = next((x for x in APIS if x["api_id"] == api), None)
- if not a:
- return m.group(0)
- block = f"\n\n**接口文档:** {api_ref([api])}\n"
- if a.get("method") and a["method"] not in ("【占位】", "见官方页", None):
- block += f"\n**method:** `{a['method']}`\n"
- if a.get("req"):
- block += "\n**主要入参:** " + "、".join(f"`{r[0]}`" for r in a["req"][:6])
- if len(a["req"]) > 6:
- block += " …"
- block += "\n"
- if a.get("resp"):
- block += "**主要出参:** " + "、".join(f"`{r[0]}`" for r in a["resp"][:6])
- if len(a["resp"]) > 6:
- block += " …"
- block += "\n"
- return m.group(0) + block
- # 在「需实现的 QiWe 官方接口」段落后注入
- text = re.sub(
- r"(\*\*需实现的 QiWe 官方接口[::]\*\*[^\n]*\n)",
- lambda m: m.group(1) + inject_api_refs(m.group(1)),
- text,
- )
- text = text.replace(
- "[callback-structure.md](./qiweapi-scrape/callback-structure.md)",
- f"[回调结构说明]({DOC_BASE}/md/回调结构说明.md)",
- )
- text = text.replace("sync 页需重爬", f"[同步历史消息分页]({DOC_BASE}/md/同步历史消息分页.md)")
- text = text.replace("(爬取 md 需重爬)", f"(见 [{DOC_BASE}/md/同步历史消息分页.md]({DOC_BASE}/md/同步历史消息分页.md))")
- text = re.sub(
- r"\| 编号 \| 接口 \| 爬取文档 \|\n\|:----:\|------\|----------\|\n(?:\| API-\d+[^\n]+\n)+",
- lambda m: m.group(0).replace("爬取文档", "文档").replace("见官方链接", "见 §二"),
- text,
- )
- text = text.replace(
- "[platform-intro.md](../output/qiweapi-test/platform-intro.md)",
- f"[QiWe 开放平台文档索引]({DOC_BASE}/README.md)",
- )
- return text
- def inject_api_refs(line: str) -> str:
- ids = re.findall(r"API-\d+", line)
- if not ids:
- return ""
- return f"\n**接口入参/出参表:** {api_ref(ids)}\n"
- def main():
- old = SRC.read_text(encoding="utf-8")
- # 保留 §三 目录表 + §四~十四 模块正文
- m = re.search(r"(## 三、功能目录总表[\s\S]*)", old)
- tail = m.group(1) if m else ""
- tail = enhance_feature_section(tail)
- header = """# 企微客户服务 — 功能实现说明(开发用)
- > **文档定位:** 在 [功能清单](./企微客户服务-功能清单.md) **同一套功能条目**基础上,为开发人员补充:**每条功能的详细实现流程**、**QiWe 官方接口入参/出参**(对照 [QiWe 开放平台文档](./文档/QiWe开放平台文档/README.md))。
- > **表格用法:** [§三 功能目录总表](#三功能目录总表) 当**目录**,点击「详述」跳到对应功能点。
- > **接口速查:** [§二 QiWe 官方接口](#二qiwe-官方接口速查含入参出参) 含可点击的 method、params、响应字段。
- > **仅官方接口汇总:** [官方接口按模块统计](./企微客户服务-官方接口按模块统计.md)
- > **更新:** 2026-05-19
- ---
- ## 一、阅读说明
- | 标记 | 含义 |
- |------|------|
- | **QiWe 官方** | `POST {QIWEI_BASE_URL}/api/qw/doApi`,Header `X-QIWEI-TOKEN`,body `{ "method", "params" }` |
- | **本方** | 自研服务 / DB / 定时任务(路径为建议名) |
- | **【占位】** | 官方文档未提供专用接口(如读在线文档正文),**禁止编造 method** |
- | **本地文档** | [./文档/QiWe开放平台文档/](./文档/QiWe开放平台文档/README.md) 内 md,可离线查阅;与 [doc.qiweapi.com](https://doc.qiweapi.com/) 同步 |
- | **⚠️ PoC** | 须联调验证(置顶、加好友、文件下载等) |
- **统一请求示例:**
- ```json
- {
- "method": "/msg/syncMsg",
- "params": {
- "guid": "设备ID-来自登录",
- "msgSeq": 0,
- "limit": 50
- }
- }
- ```
- **统一响应外壳:**
- ```json
- {
- "code": 0,
- "data": { },
- "msg": "成功"
- }
- ```
- ---
- """
- section1_end = render_section_2()
- out = header + section1_end + "\n---\n\n" + tail
- out = re.sub(
- r"\*\*维护:\*\*.*",
- "**维护:** 官方接口变更时同步更新 [§二](#二qiwe-官方接口速查含入参出参) 与 [QiWe 开放平台文档](./文档/QiWe开放平台文档/README.md);新增功能先改 §三 目录,再补对应模块小节。",
- out,
- )
- OUT.write_text(out, encoding="utf-8")
- print("wrote", OUT, "lines", len(out.splitlines()))
- if __name__ == "__main__":
- main()
|