Leantime 3.12.0 常用 API 与运维命令详解

Leantime 3.12.0 常用 API 与运维命令详解:/api/jsonrpc 方法命名与 @api 白名单、三种认证与 CLI 签发 Bearer Token、项目/任务/评论/工时/搜索链路实测、错误码表与备份恢复运维命令。

LeantimeLeantime项目管理PHPMySQL自动化运维2026/10/28

Leantime 3.12.0 常用 API 与运维命令详解

目标:把 Leantime 3.12.0 的两个「自动化入口」讲透——/api/jsonrpc 的 JSON-RPC 2.0 接口怎么认证、怎么调、错误码怎么读,以及 php bin/leantime 与数据库备份恢复等日常运维命令。本文全部请求、响应与回显于 2026-10-28 在 Ubuntu 22.04.5 LTS + Leantime 3.12.0 + PHP 8.3.36 + MariaDB 10.11.19 的服务上实测照抄。

一、环境信息

项目值
操作系统Ubuntu 22.04.5 LTS
Leantime3.12.0
PHP8.3.36
数据库MariaDB 10.11.19(库名 leantime,用户 leantime)
应用目录/var/www/leantime
接口地址http://192.168.1.71:8141/api/jsonrpc
演示账号admin@example.com / Admin@q1w2e3r4

二、接口形态与命名规则

Leantime 只暴露一个 API 端点:/api/jsonrpc。它不是 REST,而是 JSON-RPC 2.0,所有业务能力都通过「方法名 + 参数对象」调用。

2.1 方法名格式

leantime.rpc.{module}.{service}.{method}

例如 leantime.rpc.Tickets.getTicket。四段式写法(leantime.rpc.Tickets.getTicket)也可以,此时 module 与 service 同名。解析规则是大小写敏感的:Tickets 不能写成 tickets。

进入分发前有三道校验,任一不过都返回 -32601:

  1. 服务类是否存在(Domain\{Module}\Services\{Service}、插件同名路径);
  2. 方法是否存在;
  3. 方法是否带 @api 注释——没有 @api 的方法一律不可远程调用。

实测一个没有 @api 的方法:

curl -s -X POST http://192.168.1.71:8141/api/jsonrpc \
  -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" \
  -d '{"jsonrpc":"2.0","method":"leantime.rpc.Tickets.prepareTicketDates","params":{},"id":23}'
{"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found","data":"Method is not available via API: prepareTicketDates"},"id":23}

data 里明确写了「is not available via API」,与「方法不存在」区分:(不存在的写 Method doesn't exist: xxx)。排障时看这一句就能判断是拼错还是没开放。

2.2 传输约束

  • 只支持 POST 与 GET;PATCH/DELETE 返回 -32600,提示只支持 POST/GET;
  • jsonrpc 字段必须存在且等于 "2.0",否则 -32600;
  • 请求体是 JSON 对象即单次调用,是 JSON 数组即批量调用;
  • params 可以是对象,也可以是「JSON 字符串」——服务端会二次解码,方便老客户端传字符串。

三、认证:三种方式

方式适用场景传递方式
会话 Cookie前端页面自身调用浏览器 session
API Key脚本/第三方集成请求头 x-api-key: <key>
Bearer Token自动化任务、CI请求头 Authorization: Bearer <token>

三者都走同一个认证中间件,认证通过后按该用户身份执行;API Key 在数据库里是一条 source='api' 的用户记录,所以它的权限就是创建时选的角色。

3.1 无凭据的响应

curl -s -i -X POST http://192.168.1.71:8141/api/jsonrpc \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"leantime.rpc.Api.Config.getSystemInfo","params":{},"id":1}'

实测:

HTTP/1.1 401 Unauthorized
{"error":"Unauthorized"}

注意 401 的响应体不是 JSON-RPC 错误对象,客户端要按 HTTP 状态码先分支。

3.2 用 CLI 签发 Bearer Token(实测最省事)

3.12.0 自带 auth:create-bearer-token,不需要任何插件:

cd /var/www/leantime
php bin/leantime auth:create-bearer-token --email=admin@example.com --name=doc-d5 --quiet-output

实测回显(仅输出 token 本体,便于 $(...) 捕获):

NFs0u8wB685EglWBLO4TpSczYMsSR1ICEs9a1umu

随后即可调用:

curl -s -X POST http://192.168.1.71:8141/api/jsonrpc \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer NFs0u8wB685EglWBLO4TpSczYMsSR1ICEs9a1umu' \
  -d '{"jsonrpc":"2.0","method":"leantime.rpc.Api.Config.getSystemInfo","params":{},"id":1}'
{"jsonrpc":"2.0","result":{"version":"3.12.0","enabledPlugins":[]},"id":1}

--name 会记在 token 行上,用于审计「这个 token 是给谁用的」。token 一旦泄露就是管理员权限,用完应到数据库删除对应行或换发。

3.3 认证失败也会限流

用错误密钥连续请求,超过 LEAN_RATELIMIT_AUTH(默认 20,实例里被调成 2)后返回:

HTTP/1.1 429 Too Many Requests
{"error":"Too many failed authentication attempts. Try again later."}

窗口按「IP + 1 分钟」滚动,期间换成正确 token 也会被拒。自动化脚本必须在首次 401 就停止重试,而不是循环刷新。

四、系统与能力探测

方法参数用途
Api.Config.getSystemInfo无版本号 + 已启用插件,最适合做健康检查
Users.getAllactiveOnly(可选)拉用户清单,核对权限与账号状态
Users.getUserByEmailemail按邮箱定位用户 ID
Projects.getAllProjects无全部项目(不含权限过滤,管理员视角)

Users.getAll 实测(截取前两条):

{"jsonrpc":"2.0","result":[
 {"id":1,"lastname":"","role":"50","status":"A","username":"admin@example.com","twoFAEnabled":0,"jobTitle":"DevOps Engineer","firstname":"admin"},
 {"id":3,"lastname":"Lee","role":"30","status":"A","username":"ops@example.com","twoFAEnabled":0,"clientName":"quzuo","firstname":"Ops"}
],"id":3}

role 是数字字符串:50 为所有者、40 管理员、30 经理、20 成员、10 只读。判断「谁能改配置」时看这个字段,不要看 jobTitle。

五、项目与任务链路

5.1 项目

{"jsonrpc":"2.0","method":"leantime.rpc.Projects.getAllProjects","params":{},"id":2}
{"jsonrpc":"2.0","result":[
 {"id":1,"name":"My Project","clientId":null,"hourBudget":"0","type":"project","modified":"2026-10-27 15:28:12"},
 {"id":2,"name":"API 演示项目","clientId":1,"clientName":"quzuo","type":"project","modified":"2026-10-27 16:19:21"}
],"id":2}

单项目用 Projects.getProject(参数 id),返回体里多出 details、psettings(restricted 表示仅项目成员可见)、isFavorite。

5.2 建任务:addTicket 返回新 ID

{"jsonrpc":"2.0","method":"leantime.rpc.Tickets.addTicket","params":{"values":{
  "headline":"D5 实测任务:JSON-RPC 创建","description":"由 API 命令篇实测创建",
  "projectId":2,"type":"task","status":3,"userId":1,"editorId":1,"priority":3}},"id":5}

实测回显:

{"jsonrpc":"2.0","result":12,"id":5}

result 直接是新任务 ID(标量,不是数组)。status 取值来自状态标签接口,3 通常对应 New。

5.3 读任务:getAll 与 getTicket

按项目 + 未完成筛选:

{"jsonrpc":"2.0","method":"leantime.rpc.Tickets.getAll","params":{"searchCriteria":{"projectId":2,"status":"not_done"}},"id":4}

返回是任务对象数组,关键字段:id、headline、projectId、status、priority、editorId、milestoneid、dateToFinish、sprint。注意 dateToFinish 在未设置时是 0000-00-00 00:00:00,脚本里必须判空而不是直接格式化。

单条用 Tickets.getTicket(参数 id),返回体包含 projectName、clientName、authorFirstname 等联表字段,适合做详情页。

5.4 改任务:patchTicket

{"jsonrpc":"2.0","method":"leantime.rpc.Tickets.patchTicket",
 "params":{"id":12,"values":{"headline":"D5 实测任务:已改名","status":0}},"id":7}
{"jsonrpc":"2.0","result":true,"id":7}

patchTicket 是部分更新:只传要改的字段,未传的保持原值。再次 getTicket 验证,headline 已变为 D5 实测任务:已改名。若需要整对象覆盖用 updateTicket,两者对必填字段的严格要求不同,跨版本升级时优先用 patchTicket。

5.5 状态标签

{"jsonrpc":"2.0","method":"leantime.rpc.Tickets.getStatusLabels","params":{"projectId":2},"id":62}
{"jsonrpc":"2.0","result":{
 "3":{"name":"New","class":"label-info","statusType":"NEW","kanbanCol":true,"sortKey":1},
 "1":{"name":"Blocked","class":"label-important","statusType":"INPROGRESS","kanbanCol":true,"sortKey":2},
 "4":{"name":"In Progress","class":"label-warning","statusType":"INPROGRESS"}
},"id":62}

statusType 只有三档(NEW/INPROGRESS/DONE),看板的列与「完成率」统计都按它归类,自定义状态名不会改变统计口径。

六、协作数据:评论、工时、迭代、标签

6.1 评论

{"jsonrpc":"2.0","method":"leantime.rpc.Comments.addComment",
 "params":{"values":{"text":"API 实测评论:链路打通"},"module":"ticket","entityId":12},"id":8}
{"jsonrpc":"2.0","result":true,"id":8}

读取:

{"jsonrpc":"2.0","method":"leantime.rpc.Comments.getComments","params":{"module":"ticket","entityId":12},"id":9}
{"jsonrpc":"2.0","result":[{"id":1,"text":"API 实测评论:链路打通","date":"2026-10-27 17:01:13","moduleId":12,"userId":1,"commentParent":0,"firstname":"admin","lastname":""}],"id":9}

module 支持 ticket、project、article、idea、{type}canvasitem,复数形式(tickets)也认。评论对象不存在时抛 NotFoundException,返回 -32001 一类业务错误码。

6.2 工时

{"jsonrpc":"2.0","method":"leantime.rpc.Timesheets.logTime",
 "params":{"ticketId":12,"params":{"hours":1.5,"kind":"GENERAL","date":"2026-10-28","description":"API 登记工时"}},"id":10}
{"jsonrpc":"2.0","result":true,"id":10}

汇总校验:

{"jsonrpc":"2.0","method":"leantime.rpc.Timesheets.getSumLoggedHoursForTicket","params":{"ticketId":12},"id":61}
{"jsonrpc":"2.0","result":1.5,"id":61}

logTime 是累加语义(同名日期多次调用会叠加),要覆盖某天工时用 upsertTime。kind 必须是 Timesheets.getLoggableHourTypes 返回的可记账类型之一,传未定义值会被拒绝。

6.3 迭代与标签

{"jsonrpc":"2.0","method":"leantime.rpc.Sprints.getAllSprints","params":{"projectId":2},"id":12}
{"jsonrpc":"2.0","result":[],"id":12}

该演示项目未建迭代,返回空数组——这是正常值,不要按「接口失败」处理。

{"jsonrpc":"2.0","method":"leantime.rpc.Tags.getTags","params":{"projectId":2,"term":"api"},"id":13}
{"jsonrpc":"2.0","result":[],"id":13}

Tags.getTags 是前缀检索(term 是输入中的词干),用于做标签自动补全;项目里没有匹配标签时同样返回空数组。

七、全局搜索

{"jsonrpc":"2.0","method":"leantime.rpc.Search.quickSearch","params":{"term":"API","perType":2},"id":14}

实测(节选):

{"jsonrpc":"2.0","result":{
 "tickets":[
  {"type":"tickets","id":10,"title":"API 创建任务示例","snippet":"通过 JSON-RPC 创建","projectName":"My Project","modalPath":"/tickets/showTicket/10"},
  {"type":"tickets","id":12,"title":"D5 实测任务:已改名","snippet":"由 API 命令篇实测创建","projectName":"API 演示项目","modalPath":"/tickets/showTicket/12"}
 ],
 "projects":[{"type":"projects","id":2,"title":"API 演示项目","snippet":"由 JSON-RPC 创建"}]
},"id":14}

返回按类型分组,perType 控制每类条数。modalPath 是前端可直接打开的详情路径,做「搜索跳转」时用它比拼接 URL 可靠。需要分页与过滤时换用 Search.search(参数 term、type、limit、offset、filters)。

八、批量调用与 GET 调用

8.1 批量

请求体为数组时,服务端逐个执行并按顺序返回数组,单个子请求失败不影响其他子请求:

[
 {"jsonrpc":"2.0","method":"leantime.rpc.Api.Config.getSystemInfo","params":{},"id":31},
 {"jsonrpc":"2.0","method":"leantime.rpc.Projects.getAllProjects","params":{},"id":32}
]

实测回显(完整):

[
 {"jsonrpc":"2.0","result":{"version":"3.12.0","enabledPlugins":[]},"id":31},
 {"jsonrpc":"2.0","result":[{"id":1,"name":"My Project","clientId":null,"hourBudget":"0","dollarBudget":0,"state":null,"menuType":"","type":"project","modified":"2026-10-27 15:28:12","clientName":null,"start":null,"end":null,"status":null},{"id":2,"name":"API 演示项目","clientId":1,"hourBudget":"0","dollarBudget":0,"state":null,"menuType":"","type":"project","modified":"2026-10-27 16:19:21","clientName":"quzuo","start":null,"end":null,"status":null}],"id":32}
]

批量请求体仍是一次 HTTP 请求,所以只占一个限流配额——这是把大量只读查询打包时最划算的做法。

8.2 GET

GET 把参数放在查询串里,params 必须是 base64(JSON):

PARAMS=$(printf '{"id": 2}' | base64 | tr -d '\n')
curl -s -G http://192.168.1.71:8141/api/jsonrpc \
  -H "Authorization: Bearer $TOKEN" \
  --data-urlencode 'method=leantime.rpc.Projects.getProject' \
  --data-urlencode "params=$PARAMS" \
  --data-urlencode 'jsonrpc=2.0' \
  --data-urlencode 'id=42'

实测回显:

{"jsonrpc":"2.0","result":{"id":2,"name":"API 演示项目","clientId":1,"details":"由 JSON-RPC 创建","psettings":"restricted","clientName":"quzuo","isFavorite":0},"id":"42"}

注意返回的 id 变成了字符串 "42"——GET 模式下所有查询串参数都是字符串,客户端做 id 比对时要宽松处理。GET 适合浏览器直接打开的只读查询,写操作一律用 POST。

九、错误码与排查表

Leantime 的 JSON-RPC 错误全部以 HTTP 200 返回,错误信息在 error 对象里;只有认证与限流走 HTTP 401/429:

现象codedata 示例处置
协议版本不对-32600Leantime only supports JSON-RPC version 2.0补 "jsonrpc":"2.0"
方法不存在-32601Method doesn't exist: notAMethod核对方法名大小写与拼写
方法未开放-32601Method is not available via API: xxx找带 @api 的等价方法
缺必填参数-32602Required Parameter Missing: id按方法签名补参数
参数类型无法转换-32602Could not cast parameter: xxx传字符串/数字要匹配声明类型
业务异常-32001 / 自定义消息来自异常本身读 message,多为权限或资源不存在
插件未启用-32004Plugin 'X' is required but not enabled.到后台启用插件
未认证HTTP 401{"error":"Unauthorized"}补 x-api-key 或 Bearer
限流HTTP 429Retry-After: n按头退避,勿盲目重试
无响应体(通知)——未带 id 的请求按规范不返回响应体

实测四条错误回显:

{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid Request","data":"Leantime only supports JSON-RPC version 2.0"},"id":21}
{"jsonrpc":"2.0","error":{"code":-32601,"message":"Method not found","data":"Method doesn't exist: notAMethod"},"id":22}
{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params","data":"Required Parameter Missing: id"},"id":24}

排障顺序:先看 HTTP 状态(401/429 属于传输层),再看 error.code(协议层),最后读 error.data(业务层,最具体)。data 里出现 Required Parameter Missing 就直接补参数,不要怀疑权限。

十、日常运维命令

10.1 服务与日志

systemctl status leantime --no-pager
journalctl -u leantime -n 20 --no-pager

应用日志按 LEAN_LOG_CHANNELS 决定去向;实测实例设为 stderr,因此 journalctl 里能直接看到访问日志与限流记录:

127.0.0.1 -  27/Oct/2026:17:01:24 +0000 "POST /index.php" 200
127.0.0.1 -  27/Oct/2026:17:02:23 +0000 "GET /index.php" 429

10.2 应用 CLI:php bin/leantime

CLI 是 Laravel 11.45.1 的命令行外壳,运维常用的四组:

cd /var/www/leantime

# 列全部命令
php bin/leantime list

# 建用户(邮箱 + 密码必填)
php bin/leantime user:add --email=dev2@example.com --password='Dev@q1w2e3r4'

# 改数据库设置(站点名、可信 URL 等)
php bin/leantime setting:save --key=system.trustedAppUrl --value=https://pm.example.com

# 签发/清理认证凭据
php bin/leantime auth:create-bearer-token --email=admin@example.com --name=ci-bot --quiet-output
php bin/leantime auth:clear-resets

实测 setting:save 回显:

 [OK] Saved Successfully

这些命令都在应用根目录执行;用 sudo -u www-data 运行可避免生成的缓存文件属主变成 root(属主错乱会导致网页 500)。

10.3 数据库备份与恢复

mysqldump -uleantime -pq1w2e3r4 --single-transaction --quick leantime \
  > /opt/backup/leantime-$(date +%F-%H%M).sql
ls -lh /opt/backup/ | tail -2
gzip -c /opt/backup/leantime-2026-10-28-0105.sql | wc -c

实测:初始数据量下备份文件 64 KB,gzip 后 11 269 字节。

-rw-r--r-- 1 root root 64K Oct 28 01:05 leantime-2026-10-28-0105.sql
11269

恢复:

mysql -uleantime -pq1w2e3r4 leantime < /opt/backup/leantime-2026-10-28-0105.sql
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.1.71:8141/auth/login

恢复后要再探一次登录页(期望 200)。--single-transaction 保证 InnoDB 表一致性快照,不要漏掉它;漏了在写入高峰期备份会得到跨表不一致的数据。备份文件里含用户密码哈希与 zp_settings,按敏感数据处理。

10.4 健康检查与例行巡检

# 1. 版本与插件状态(需要 token)
curl -s -X POST http://192.168.1.71:8141/api/jsonrpc \
  -H 'Content-Type: application/json' -H "Authorization: Bearer $TOKEN" \
  -d '{"jsonrpc":"2.0","method":"leantime.rpc.Api.Config.getSystemInfo","params":{},"id":1}'

# 2. 登录页可达
curl -s -o /dev/null -w '%{http_code} %{time_total}s\n' http://192.168.1.71:8141/auth/login

# 3. 可写目录
ls -ld /var/www/leantime/userfiles /var/www/leantime/storage/logs

三项分别覆盖「应用逻辑」「Web 链路」「磁盘权限」,是每日巡检的最小集合。若第 1 项返回 429 而第 2 项 200,问题在限流阈值而不是服务宕机。

十一、参考链接