同事会平台 · 标准 API 文档v1.0渲染样式 v1.0
中文 (中国) ▾
管理后台 / 文档服务 / 接口文档

首次调用 API #

同事会平台开放 API 兼容 HTTP REST 风格的多端接入,5 类端(小程序 / 支付宝 / 鸿蒙 / App / 电脑网页)通过统一 base URL 访问。 公开端点无需鉴权,限流 60/60s/IP;管理端点需后台 Bearer Token。

1. Base URL

所有公开端点共享同一基址,按环境区分。

PARAMVALUE
base_url REQUIRED
string
生产:https://www.xiaoniao.online
测试:https://staging.xiaoniao.online
api_prefix REQUIRED
string
所有平台 API 前缀:/api/v1(亦支持 /api 双前缀)
content_type REQUIRED
string
所有请求 / 响应统一 JSON:application/json; charset=utf-8
version string
本文档对应 API 版本:v1.0(基线稳定)

1.1 三行代码(任选一种)

curl python javascript
# 公开端点:GET 平台基础信息
curl -sS https://www.xiaoniao.online/api/v1/platform/settings

# 期望响应:HTTP 200 + 标准信封
import requests
res = requests.get('https://www.xiaoniao.online/api/v1/platform/settings')
data = res.json()['data']
print(data['name'], data['supportEmail'])
// 浏览器 / Node 18+
const { data } = await fetch('https://www.xiaoniao.online/api/v1/platform/settings').then(r => r.json());
console.log(data.name, data.supportEmail);

2. 鉴权说明 #

公开端点:本页所列 5 个端点均无需任何 token,Authorization 头不传亦可。
管理端点(未列入本页):需后台 Bearer Token。
1. 后台登录 POST /api/v1/admin/login → 拿 access_token
2. Authorization: Bearer <token> 携带
3. 鉴权声明由服务端统一校验(Access Token 有效期内有效,2 小时自动过期)
4. 完整管理端 API 见 管理后台 → 平台 API 库

3. 平台基础信息 #

平台级元数据(名称 / 客服 / 时区 / 版权 / SEO 等 9 字段)的统一读取入口。多端共用,任意客户端首次启动应缓存 5-10 分钟。

GET /api/v1/platform/settings PUBLIC · 无需鉴权

3.1 响应字段(9 字段 + 审计列)

FIELDTYPEDESCRIPTION
name REQUIRED
string平台名称(全局唯一)
shortName
string | null平台简称(顶部 logo 旁文字)
supportEmail
string | null客服邮箱(公开给所有端)
supportPhone
string | null客服电话
defaultLanguage
enum: zh-CN / zh-TW / en新用户默认语言
defaultTimezone
string (IANA)默认时区,默认 Asia/Shanghai
copyright
string | null版权信息(关于页脚)
seoKeywords
string | nullSEO 关键字(逗号分隔)
brandColorToken
string平台主色(设计令牌),默认 --sky-500
updatedBy
string | null最近更新人(仅管理端写入)
updatedAt
iso8601最近更新时间

3.2 多端调用示例

curl python javascript 微信小程序 支付宝 鸿蒙 ArkTS
curl -sS https://www.xiaoniao.online/api/v1/platform/settings
import requests
res = requests.get('https://www.xiaoniao.online/api/v1/platform/settings', timeout=5)
data = res.json()['data']
print(f"平台:{data['name']}, 客服:{data['supportEmail']}")
// 浏览器 / Node 18+ SSR
const r = await fetch('https://www.xiaoniao.online/api/v1/platform/settings');
const { data } = await r.json();
wx.request({
  url: 'https://www.xiaoniao.online/api/v1/platform/settings',
  method: 'GET',
  success: (r) => { if (r.data.code === 0) wx.setStorageSync('pf', r.data.data); }
});
my.request({
  url: 'https://www.xiaoniao.online/api/v1/platform/settings',
  method: 'GET',
  success: (r) => { if (r.data.code === 0) my.setStorageSync({ key: 'pf', data: r.data.data }); }
});
import { http } from '@kit.NetworkKit';
const req = http.createHttp();
const resp = await req.request('https://www.xiaoniao.online/api/v1/platform/settings', { method: http.RequestMethod.GET });
const body = JSON.parse(resp.result as string);
req.destroy();

3.3 响应示例 实时同步中…

jsonHTTP 200 · 数据实时取自 /api/v1/platform/settings(失败时展示静态示例)
{
  "code": 0,
  "message": "success",
  "data": {
    "id": 1,
    "name": "同事会 · 多企业版",
    "shortName": "同事会·多企业版",
    "supportEmail": "support@xiaoniao.online",
    "supportPhone": "400-800-2026",
    "defaultLanguage": "zh-CN",
    "defaultTimezone": "Asia/Shanghai",
    "copyright": "© 2026 同事会平台",
    "seoKeywords": null,
    "brandColorToken": "--sky-500",
    "updatedBy": "admin",
    "updatedAt": "2026-08-21T17:11:47",
    "createdAt": "2026-08-21T16:59:19"
  }
}

4. 生成小程序码 #

「关于同事会」弹窗内显示,扫码直接进入小程序首页。基于微信 getUnlimitedQRCode,失败降级到普通 QR 码。

GET /api/v1/system/miniprogram-qrcode PUBLIC · 限流 10/60s

响应字段

FIELDTYPEDESCRIPTION
qrType
stringwxacode(微信小程序码) 或 qrcode(降级普通 QR)
qrPngBase64
string (data URL)base64 编码 PNG,可直接 <img src="..."> 渲染
hint
stringUI 提示文案
curl终端直接跑
curl -sS https://www.xiaoniao.online/api/v1/system/miniprogram-qrcode \
  | python -m json.tool | head -5

5. 创建扫码登录 ticket #

电脑端生成二维码 → 微信/支付宝扫一扫 → 跳小程序授权 → 回调 ticket → 电脑端每 2s 轮询 scan-status 检测登录态。

GET /api/v1/system/scan-ticket PUBLIC · 限流 30/60s
使用流程:电脑端 GET /scan-ticket → 拿 ticket + 二维码 PNG → 用户扫码 → 小程序识别 ticket 并发起 scan-confirm 写入状态 → 电脑端 GET /scan-status/{ticket} 查到 status=confirmed 即跳后台。

6. 轮询扫码状态 #

GET /api/v1/system/scan-status/{ticket} PUBLIC

响应

jsonHTTP 200
{
  "status": "pending",         // pending / confirmed / expired
  "token": "eyJ...",             // confirmed 后才有
  "user": { "id": 34, "name": "秦剑" }
}

7. 健康检查 #

GET /health PUBLIC
bash无 /api 前缀
curl -sS https://www.xiaoniao.online/health
# → {"status":"ok","service":"tongshihui-python","version":"1.0.0"}

8. 错误码 #

HTTPcode含义处理建议
2000成功data
200非 0业务错误(如平台名称为空)toast message,不重试
401未携带/无效 token(管理端点)重新登录
403权限不足联系 superadmin
429限流命中退避 60s 后重试
500服务器内部错误5min 内自动恢复

9. 限流 #

端点维度阈值超出行为
公开 GET /api/v1/platform/settings客户端 IP60 / 60sHTTP 429
小程序码 GET /api/v1/system/miniprogram-qrcode客户端 IP10 / 60sHTTP 429
扫码 ticket GET /api/v1/system/scan-ticket客户端 IP30 / 60sHTTP 429
扫码状态 GET /api/v1/system/scan-status/{ticket}反向代理层兜底
健康检查 GET /health反向代理层兜底

10. 铁律边界 #

多端禁止直连数据库。所有 5 端访问基础信息必须且只能走本页列出的 HTTP API:
  • ❌ 在小程序里写 mysqldb.connect(...).query("SELECT * FROM platform_settings")
  • ❌ App 通过内网绕过 API 网关直连生产 MySQL
  • ❌ 把数据库连接串写进客户端代码
  • ❌ 用任何 ORM 直连 platform_settings
合规做法:客户端 → HTTPS → API 网关 → 后端 FastAPI → MySQL。所有请求统一走 /api/v1/... 前缀,便于网关白名单、限流、审计。

11. OAuth2 扫码授权登录(外部接入应用) #

外部系统(网页 / H5)可复用同事会主系统的登录认证能力,让用户用同事会小程序扫码完成授权登录,拿到仅限本应用域有效的 Bearer Token。类比微信一键授权,但 token 做了域隔离 + scope 最小化授权

谁该用:需在自有网页 / 系统中嵌入「同事会账号登录」的第三方应用(如代收款 YXTSS)。应用须先在业务研发中心 → 外部接入应用登记并经 superadmin 审核通过(status=published),否则所有 OAuth 端点返回 403
安全红线:① 跨域 token 仅限当前应用域(client_id 绑定),不能跨应用互用;② client_secret 真实值仅存服务器 .env,DB 仅存掩码;③ CORS 严禁 allow_origins=["*"],外部域名须登记进 allowed_origins 审核通过。

11.1 端到端流程

流程
外部网页 GET /oauth/scan-ticket → 渲染二维码 → 用户微信扫码 → 小程序 POST /oauth/scan-confirm → 外部网页轮询 GET /oauth/scan-status 拿 token → 调主系统 API(Bearer)

11.2 端点清单(公开,无需后台登录态)

GET/api/v1/oauth/scan-ticket?client_id={module_key}PUBLIC · 限流 30/60s
GET/api/v1/oauth/scan-info/{ticket}PUBLIC
POST/api/v1/oauth/scan-confirm/{ticket}PUBLIC · 限流 20/60s
GET/api/v1/oauth/scan-status/{ticket}PUBLIC
GET/api/v1/oauth/resource/{client_id}?scope=参考 / 样板
POST/api/v1/oauth/exchange跨域兑换 · 限流 10/60s

11.3 获取登录二维码

bash未登记 client_id → 403
curl -sS "https://www.xiaoniao.online/api/v1/oauth/scan-ticket?client_id=oauth_yxtss_cashier"

响应字段

FIELDTYPEDESCRIPTION
ticket
string扫码凭据(24 位 hex),绑定 client_id,5 分钟过期
qrType
stringwxacode(微信小程序码,太阳码)或 h5_webpage_fallback(降级普通二维码)
qrPngBase64
string (data URL)二维码 PNG,直接 <img src> 渲染
appName
string应用名称(小程序授权页展示)
scopes
string[]应用登记的授权范围(如 workbench:view);由后台编辑应用勾选 scopes 保存时同步写入 module_auth(subject_type=oauth_client、只读授权),是 token 获得 scope 的唯一来源;未勾选则 token scope=[],调受保护接口返回 403
expireSeconds
int二维码有效期(300s)

11.4 小程序扫码确认(回调主系统)

jsonPOST /api/v1/oauth/scan-confirm/{ticket}
{
  "openId": "oKn9p3...",        // 小程序登录态 open_id
  "client_id": "oauth_yxtss_cashier"  // 须与 ticket 绑定一致,否则 400
}
主系统校验 ticket + client_id 绑定 + 用户已注册(未注册微信用户返回 403),随后签发域隔离 token(携带 client_id + iss + scope)。

11.5 轮询拿 token(一次性消费)

bashconfirmed 后返回 token,立即清空防止重放
curl -sS "https://www.xiaoniao.online/api/v1/oauth/scan-status/{ticket}"
jsonHTTP 200 · status=confirmed
{
  "status": "confirmed",
  "token": "eyJ...",            // Bearer Token,2h 过期
  "userId": 34,
  "clientId": "oauth_yxtss_cashier"
}

11.6 Token 结构(域隔离 + scope)

jsondecode 后的 payload(签名由主系统校验)
{
  "sub": "user:34",
  "user_id": 34,
  "open_id": "oKn9p3...",
  "client_id": "oauth_yxtss_cashier",   // 域隔离核心:token 仅限此应用
  "iss": "https://www.xiaoniao.online", // 签发方
  "aud": "admin",
  "scope": ["workbench:view"],      // 授权范围,由 module_auth 推导(后台勾选 scopes 保存即写入授权记录)
  "role": "admin", "admin_role": "superadmin",
  "jti": "...", "exp": 1234567890
}
声明含义校验
client_id
绑定的外部应用请求方 client_id 必须一致,否则 401
iss
签发方必须为 https://www.xiaoniao.online,否则 401
scope
授权范围{module_key}:view|operate,缺所需 scope → 403

11.7 跨域 token 兑换

跨域跳转(应用 X → 应用 Y):应用 Y 拒收域 X 的 token(401),前端回主系统 POST /oauth/exchange 兑换域 Y 的 token。

bash源 token 走 Authorization,或 body.token
curl -sS -X POST https://www.xiaoniao.online/api/v1/oauth/exchange \
  -H "Authorization: Bearer <source_token>" \
  -H "Content-Type: application/json" \
  -d '{"target_client_id":"yxtss_report","scope":"report:view"}'
主系统校验源 token 有效(client_id 非空 + iss 正确)→ 目标应用已审核通过 → 推导目标 scope → 返回域 Y token。频限 10/60s/IP。

12. 跨域访问(CORS) #

外部应用网页用 fetch 调主系统 API 时受浏览器同源策略约束,须通过 CORS 放行。

策略
allow_origins
静态白名单(xiaoniao.online / ai.yuexinghome.com)+ 动态:已审核通过(published)的 oauth 应用 allowed_origins,后台 60s 刷新缓存
allow_credentials
true(响应 Access-Control-Allow-Credentials: true
allow_methods / allow_headers
* / *
*
严禁 allow_origins=["*"](与 credentials 组合是致命漏洞)
接入步骤:① 在外部接入应用登记 allowed_origins(如 https://cashier.yuexinghome.com);② 审核通过(published);③ 后台 60s 内自动合并进 CORS;④ 未登记域名请求无 CORS 头 → 浏览器拦截。
排查:跨域报 CORS blocked 先确认域名已登记且 status=published,并等待 ≤60s 缓存刷新;仍不行联系 superadmin。

13. 外部系统对接指南(代收款 YXTSS 示例) #

  1. 登记应用:业务研发中心 → 外部接入应用 → 新建,填 module_key=oauth_yxtss_cashierallowed_origins=[https://cashier.yuexinghome.com]redirect_uris(当前未启用——内网部署不支持服务端主动回调,采用前端轮询模型)、scopes=[workbench:view]
  2. 审核:superadmin 点「批准」→ status=publishedclient_secret 真实值服务端写入 .env,DB 仅存掩码。
  3. 展示登录二维码:前端 GET /oauth/scan-ticket?client_id=oauth_yxtss_cashier → 渲染 qrPngBase64
  4. 轮询 token:每 2s GET /oauth/scan-status/{ticket}status=confirmedtoken(一次性)。
  5. 调用 APIAuthorization: Bearer <token> 调主系统业务接口;验证器自动做 client_id + iss + scope 校验。
  6. 跨域跳转:跳另一应用前先 POST /oauth/exchange 兑换目标域 token。
javascript最小前端样例
// 1) 拿二维码
const { data } = await fetch(`/api/v1/oauth/scan-ticket?client_id=oauth_yxtss_cashier`).then(r => r.json());
document.querySelector('#qr').src = data.qrPngBase64;
// 2) 轮询
const t = setInterval(async () => {
  const s = await fetch(`/api/v1/oauth/scan-status/${data.ticket}`).then(r => r.json());
  if (s.data?.status === 'confirmed') {
    clearInterval(t);
    localStorage.setItem('oauth_token', s.data.token);
  }
}, 2000);

14. 变更记录 #

版本日期概要
v1.02026-08-21首版发布。5 个公开端点 + 9 字段基础信息 API + 渲染样式 V1.0
v1.12026-08-25新增 OAuth2 扫码授权登录(6 端点 + token 域隔离 / scope)、跨域访问(CORS)动态白名单、外部系统对接指南(代收款 YXTSS 示例)三章节;与后端 P2–P5 实现对齐

同事会平台 · 标准 API 文档 v1.0 · 渲染样式 V1.0 · 2026-08-21
配套规范:平台设计令牌 · 治理标识:system_constants.doc_api_standard.render_version=v1.0