Kimai 2.71 常用命令与运维实战

本文逐条实测 Kimai 2.71.0 的命令行工具链与 JSON API:19 条 `kimai:` 命令全家桶(用户管理、安装升级、缓存重载、发票、导出、邮件、插件)、API 令牌创建与 Bearer 认证、核心端点(客户/项目/活动/时间表/费率)的 CRUD 全流程、备份恢复,以及 6 个真实排障案例(含 2.71.0 移除 API 密码后的认证迁移与 `X-AUTH-TOKEN` 失效问题)。配置体系见 [《Kimai 2.71 配置参数详解》](https://www.quzuo.net/document/rUgrmnGbREdD),部署过程见 [《Kimai 2.71 Docker Compose 部署实战》](https://www.quzuo.net/document/3CNq42eP7Z4D)。

KimaiKimai工时管理REST API运维自动化2026/10/27

Kimai 2.71 常用命令与运维实战

本文逐条实测 Kimai 2.71.0 的命令行工具链与 JSON API:19 条 kimai: 命令全家桶(用户管理、安装升级、缓存重载、发票、导出、邮件、插件)、API 令牌创建与 Bearer 认证、核心端点(客户/项目/活动/时间表/费率)的 CRUD 全流程、备份恢复,以及 6 个真实排障案例(含 2.71.0 移除 API 密码后的认证迁移与 X-AUTH-TOKEN 失效问题)。配置体系见 《Kimai 2.71 配置参数详解》,部署过程见 《Kimai 2.71 Docker Compose 部署实战》。

一、命令行工具总览

1.1 入口与执行环境

Kimai 的命令行入口是 Symfony Console:安装目录下的 bin/console。裸机部署直接在安装目录执行;官方镜像部署则在应用侧进入安装目录 /opt/kimai/ 执行(部署篇的编排下等价写法为 docker compose exec kimai bin/console <命令>)。本文所有命令均在 /opt/kimai/ 目录下以 bin/console <命令> 形式实测,输出为真实回显。

1.2 kimai 命令命名空间全景

bin/console list kimai

实测回显(2.71.0 共 19 条专用命令):

Available commands for the "kimai" namespace:
  kimai:export:create       Create exports
  kimai:install             [kimai:update] Install and update Kimai
  kimai:invoice:create      Create invoices
  kimai:mail:test           Send a test email using MAILER_URL and MAILER_FROM
  kimai:plugins             Manage Kimai plugins
  kimai:reload              Reload Kimai caches
  kimai:reset:dev           Resets the "development" environment
  kimai:reset:locales       Regenerate the locale definition file
  kimai:reset:test          Resets the "test" environment
  kimai:timesheet:stop-all  Stop all running timesheets immediately
  kimai:translations        Automated translation adjustments
  kimai:update              Install and update Kimai
  kimai:user:activate       Activate a user
  kimai:user:create         Create a new user
  kimai:user:deactivate     Deactivate a user
  kimai:user:demote         Demote a user by removing a role
  kimai:user:list           List all users
  kimai:user:password       Change the password of a user
  kimai:user:promote        Promotes a user by adding a role
  kimai:version             Show version information

除 kimai: 专用命令外,底层 Symfony 与 Doctrine 的通用命令在运维中同样高频:about(环境信息)、cache:clear(缓存)、doctrine:migrations:status(迁移状态)、dbal:run-sql(直连执行 SQL)、debug:config(查看生效配置)、debug:dotenv(查看环境变量)、debug:router(路由表)。命令按需选择:日常管理集中在二、三节,发票导出在四节,维护在五节,API 集成在六节。

二、版本、安装与缓存命令

2.1 kimai:version

bin/console kimai:version

实测回显:

Kimai 2.71.0 by Kevin Papst.

2.2 about:运行环境一屏总览

bin/console about

实测回显(节选,完整输出含 Kernel/PHP/Environment 三大段):

 -------------------- ---------------------------------
  Symfony
 -------------------- ---------------------------------
  Version              6.4.48
  Long-Term Support    Yes
  End of maintenance   11/2026 (in +34 days)
  End of life          11/2027 (in +399 days)
 -------------------- ---------------------------------

  PHP
  Version              8.3.36
  Architecture         64 bits
  Timezone             UTC (2026-10-27T15:29:19+00:00)
  OPcache              true
  APCu                 false
  Xdebug               false

排障价值:确认实际运行的 PHP 版本(官方要求 8.2–8.5,本环境 8.3.36)、OPcache 是否启用、Symfony 版本是否为 6.4 LTS。

2.3 kimai:install / kimai:update(幂等)

kimai:install 与 kimai:update 互为别名(同一命令),负责建库、执行 Doctrine 迁移、重建缓存。在已安装环境重跑(-n 免交互):

bin/console kimai:install -n

实测回显(已略去 deprecation 日志行):

 [OK] Cache for the "prod" environment (debug=false) was successfully cleared.


 // Warming up the cache for the prod environment with debug false

 [OK] Cache for the "prod" environment (debug=false) was successfully warmed.


 [OK] Successfully installed Kimai version 2.71.0 🎉

结论:安装与升级是同一条幂等命令——重复执行安全无害(迁移链已到头则跳过),这正是部署篇重启时序中每次启动都重跑它的原因。配合迁移状态查询:

bin/console doctrine:migrations:status | head -6

实测回显:

 +----------------------+----------------------------------------+
 | Configuration                                                            |
 +----------------------+----------------------------------------+
 | Storage              | Doctrine\\Migrations\\Metadata\\Storage\\TableMetadataStorage
 | Current Version      | 2026-10-25 12:00:00 (DoctrineMigrations\\Version20261025120000)
 | Previous Version     | 2026-10-19 16:27:27 (DoctrineMigrations\\Version20261019162727)

2.4 kimai:reload(改配置后必跑)

bin/console kimai:reload

实测回显:

 // Warming up the cache for the prod environment with debug false

 [OK] Cache for the "prod" environment (debug=false) was successfully warmed.


 [OK] Kimai config was reloaded

与裸 cache:clear 的区别:kimai:reload 在清缓存之余同步重建翻译/locale 与应用配置缓存,是配置篇 3.2 节覆盖机制的生效步骤。它不需要 cache:clear 配合,单独一次即可。

2.5 kimai:reset:locales / kimai:translations

bin/console kimai:reset:locales

实测回显(重新生成 locale 定义文件,节选尾部):

 * HH 'h' mm

输出为逐条 locale 规则的再生过程,无 [OK] 结束语属正常。kimai:translations 用于开发态翻译整理,kimai:reset:dev/kimai:reset:test 仅重置开发/测试环境(生产环境执行无害但无意义)。

三、用户管理命令

3.1 kimai:user:list

bin/console kimai:user:list

实测回显(当前环境已建两个用户):

 ---------- ------------------- ----------------------------- -------- ---------------
  Username   Email               Roles                         Active   Authenticator
 ---------- ------------------- ----------------------------- -------- ---------------
  admin      admin@example.com   ROLE_SUPER_ADMIN, ROLE_USER   Yes      kimai
  kevin      kevin@example.com   ROLE_TEAMLEAD, ROLE_USER      Yes      kimai
 ---------- ------------------- ----------------------------- -------- ---------------

Authenticator 列显示账号的认证来源:kimai(内部口令)/ldap/saml——排查「能否用命令重置密码」时先看这列(外部认证账号改本地密码无意义)。

3.2 kimai:user:create

bin/console kimai:user:create <username> <email> [<role>] [<password>]

实测(创建普通用户并回查):

bin/console kimai:user:create kevin kevin@example.com ROLE_USER q1w2e3r4
 [OK] Success! Created user: kevin

重复创建的报错回显(用户名唯一性约束):

bin/console kimai:user:create admin admin2@example.com ROLE_USER q1w2e3r4
 [ERROR] username: The username is already used.

参数要点:role 支持逗号分隔多角色(如 ROLE_USER,ROLE_ADMIN);省略 password 时交互式询问;--request-password 强制用户首登改密;--ignore-existing 让幂等脚本化建号在用户已存在时退出码为 0(Ansible 类工具友好)。

3.3 角色升降与密码:promote / demote / password

bin/console kimai:user:promote kevin ROLE_TEAMLEAD
 [OK] Role "ROLE_TEAMLEAD" has been added to user "kevin".
bin/console kimai:user:demote kevin ROLE_TEAMLEAD
bin/console kimai:user:password kevin q1w2e3r4
 [OK] Role "ROLE_TEAMLEAD" has been removed from user "kevin".
 [OK] Changed password for user "kevin".

角色语义与权限矩阵的对应关系见配置篇 6.1 节(maps 段)。

3.4 停用与恢复:deactivate / activate

bin/console kimai:user:deactivate kevin
bin/console kimai:user:activate kevin
 [OK] User "kevin" has been deactivated.
 [OK] User "kevin" has been activated.

停用只是禁登录,数据保留;kimai:user:list 的 Active 列相应翻转。注意 2.71.0 没有 user:delete 命令——这是刻意设计(时间记录的外键完整性),离职人员一律停用而非删除。

四、发票与导出命令

4.1 kimai:invoice:create:命令行开发票

bin/console kimai:invoice:create --help

实测回显(参数全集):

Description:
  Create invoices

Usage:
  kimai:invoice:create [options]

Options:
      --user=USER            The user to be used for generating the invoices
      --start[=START]        Start date (format: 2020-01-01, default: start of the month)
      --end[=END]            End date (format: 2020-01-31, default: end of the month)
      --timezone[=TIMEZONE]  Timezone for start and end date query (fallback: users timezone)
      --customer[=CUSTOMER]  Comma separated list of customer IDs
      --project[=PROJECT]    Comma separated list of project IDs
      --by-customer          If set, one invoice for each active customer in the given timerange is created
      --by-project           If set, one invoice for each active project in the given timerange is created
      --set-exported         [DEPRECATED] this flag has no meaning any more: invoiced items are always exported
      --template[=TEMPLATE]  Invoice template
      --search[=SEARCH]      Search term to filter invoice entries
      --exported[=EXPORTED]  Exported filter for invoice entries (possible values: exported, all), by default only "not exported" items are fetched
      --preview[=PREVIEW]    Absolute path for a rendered preview of the invoice, which will neither be saved nor the items be marked as exported.
      --preview-unique       Adds a unique part to the filename of the generated invoice preview file, so there is no chance that they get overwritten on same project name.

成功实测(客户 2 本月已有两条未开票记录,小时费率与固定费率各一条):

bin/console kimai:invoice:create --user admin --customer 2 --template '标准服务发票'

回显(伴随 3 条 twig 弃用 INFO 日志,见 8.6 节):

+----+---------------+----------+---- Created 1 invoice(s) -------------------------------------+
| ID | Customer      | Total    | Filename                                                      |
+----+---------------+----------+---------------------------------------------------------------+
| 2  | Beta 咨询公司 | 1725 CNY | /opt/kimai/var/data/invoices/2026-002-Beta_zi_xun_gong_si.pdf |
+----+---------------+----------+---------------------------------------------------------------+

金额 1725 = 525(1.5h × 350 小时费率)+ 1200(固定费率),与 API 侧费率计算精确一致;PDF 落盘 /opt/kimai/var/data/invoices/(部署篇 6.2 节的备份目录)。开票条目随即被标记 exported=1(--set-exported 因成默认而弃用)——这是理解 8.3 节排障的前提。

4.2 kimai:export:create:时间表导出

bin/console kimai:export:create --username admin --start 2026-10-01 --end 2026-10-31 --customer 2 --template 'csv'
 [OK] Saved export to: /tmp/20261027-Beta_zi_xun_gong_si.csv

导出内容实测(首部数据行,列头与数据对应):

Date,From,To,Duration,Currency,Price,"Internal price","Hourly price","Fixed price",Name,User,E-mail,"Staff number",Customer,Project,Activity,Description,Billable,Tags,Type,category,Account,"Project number",VAT-ID,"Order number"
2026-10-27,16:00,17:00,1:00,CNY,350,280,350,0,admin,admin,admin@example.com,,"Beta 咨询公司","移动端 App 开发",需求梳理,导出实测记录,1,,timesheet,work,0003,0003,,

两个必须注意的点:

  • --template 必填。漏掉时的报错:

     [ERROR] You must pass the "template" option
    
  • 产物落在系统 /tmp(非 var/data)——命令行导出是一次性交付物,执行后应立即取走;对运行中实例内的文件而言它随服务重建而消失。需要长期归档请走 Web 端导出或自行拷贝。

--username 决定导出用的权限与十进制设置;--set-exported 可在导出同时把条目标记为已导出(配合审计口径)。

五、维护命令

5.1 kimai:timesheet:stop-all

bin/console kimai:timesheet:stop-all
 [OK] Stopped 0 timesheet records.

用途:停机维护/大量补录前的状态收敛(把所有用户运行中的计时一键停止)。数字为实际停止条数,当前环境无运行中记录故为 0。

5.2 kimai:mail:test

bin/console kimai:mail:test admin@example.com
echo "exit=$?"

实测回显(默认空传输器下静默成功):

exit=0

「成功」只代表走完邮件管道;真实 SMTP 验证语义见配置篇 8.2 节。

5.3 kimai:plugins

bin/console kimai:plugins
 [WARNING] No plugins installed

插件管理入口:装好的插件经 var/plugins 目录持久化(部署篇编排已挂卷),升级 Kimai 后插件缓存同样由 kimai:reload 重建。官方商店插件(费用/任务管理/电子发票等)为付费 Bundle,安装方式为下载解压到 var/plugins/ 后执行 kimai:reload。

六、JSON API 实战

6.1 认证:创建访问令牌与 Bearer 头

2.71.0 的 API 认证流程(旧版「API 密码」特性已在 2.71.0 移除):

  1. Web 界面以目标用户登录,进入「个人配置 → API」页(路由 /profile/<用户名>/api-token);
  2. 「Create」表单填令牌名称(可选有效期)提交;
  3. 创建结果页展示一次明文令牌(24 位十六进制),此后仅显掩码(页面参数 hide-token=1),令牌落库于 kimai2_access_token 表(明文存储,可追溯 last_usage)。

请求时使用 Authorization: Bearer <令牌> 头。最小验证:

curl -s -H "Authorization: Bearer 0b57c60f251f97fdf38ec141c" http://127.0.0.1:8136/api/ping
{"message":"pong"}

API 端点面(bin/console debug:router | grep /api/ 实测统计):100 条路由 = 36 GET + 22 POST + 19 PATCH + 23 DELETE,读写全覆盖。Swagger 文档在 http://<主机>:8136/api/doc。

6.2 只读端点:version / users / customers / config

TOK='0b57c60f251f97fdf38ec141c'
curl -s -H "Authorization: Bearer $TOK" http://127.0.0.1:8136/api/version
{"version":"2.71.0","versionId":27100,"copyright":"Kimai 2.71.0 by Kevin Papst."}
curl -s -H "Authorization: Bearer $TOK" http://127.0.0.1:8136/api/users
[{"apiToken":false,"locale":"zh_CN","timezone":"Asia/Shanghai","language":"zh_CN","initials":"AD","color-safe":"#E91E63","id":1,"alias":null,"title":null,"avatar":null,"username":"admin","email":"admin@example.com","accountNumber":null,"enabled":true,"systemAccount":false,"color":null},{"apiToken":false,"locale":"en","timezone":"UTC","language":"en","initials":"KE","color-safe":"#0074D9","id":2,"username":"kevin","email":"kevin@example.com",...}]
curl -s -H "Authorization: Bearer $TOK" http://127.0.0.1:8136/api/config/timesheet
{"trackingMode":"default","defaultBeginTime":"now","activeEntriesHardLimit":1,"isAllowFutureTimes":true,"isAllowOverlapping":true}

时间跟踪配置经 API 只读暴露(trackingMode/hardLimit 与配置篇 4 节 YAML 值一一对应),便于外部系统探测实例行为。同族端点还有 /api/config/colors(28 色板)、/api/tags、/api/teams。

6.3 写入端点:客户 → 项目 → 活动三层创建

三层结构存在客户→项目的外键依赖,必须按序创建:

# 1. 建客户
curl -s -X POST -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
  -d '{"name":"Beta 咨询公司","country":"CN","currency":"CNY","timezone":"Asia/Shanghai","comment":"文档实测创建"}' \
  http://127.0.0.1:8136/api/customers
{"color-safe":"#a972c9","id":2,"name":"Beta 咨询公司","number":"0003",...,"currency":"CNY","timezone":"Asia/Shanghai",...}
# 2. 建项目(customer 指向客户 id,金额预算直接给数值)
curl -s -X POST -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
  -d '{"name":"移动端 App 开发","customer":2,"budget":50000,"timeBudget":100000}' \
  http://127.0.0.1:8136/api/projects
{"parentTitle":"Beta 咨询公司","customer":2,"id":2,"name":"移动端 App 开发","number":"0003","budget":50000.0,"timeBudget":360000000,...}

⚠️ 注意 timeBudget 请求与回显的单位差异:本例请求传 timeBudget: 100000(按小时口径),回显为 360000000——即 100000 小时 × 3600 = 3.6 亿秒,API 存储/回显单位是秒而请求口径按小时折算。集成前先用一笔已知值核对单位语义(不同版本端点对单位处理存在差异),避免预算口径事故。

# 3. 建活动(project 指向项目 id)
curl -s -X POST -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
  -d '{"name":"需求梳理","project":2}' http://127.0.0.1:8136/api/activities
{"parentTitle":"移动端 App 开发","project":2,"id":2,"name":"需求梳理","number":"0003",...}

6.4 时间表端点:开始 / 查询 / 停止

# 开始计时(不传时间 = 从现在开始)
curl -s -X POST -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
  -d '{"project":2,"activity":2,"description":"D1 文档 API 实测计时"}' \
  http://127.0.0.1:8136/api/timesheets
{"activity":2,"project":2,"user":1,"tags":[],"id":2,"begin":"2026-10-27T23:22:00+0800","end":null,"duration":0,"break":0,"description":"D1 文档 API 实测计时","rate":0.0,"internalRate":0.0,"fixedRate":null,"hourlyRate":null,"exported":false,"billable":true,"metaFields":[]}
# 运行中记录查询(含嵌套的客户/项目/活动对象)
curl -s -H "Authorization: Bearer $TOK" http://127.0.0.1:8136/api/timesheets/active | head -c 300
[{"tags":[],"id":2,"begin":"2026-10-27T23:22:00+0800","end":null,"duration":0,...,"description":"D1 文档 API 实测计时",...}]
# 停止计时——PATCH 方法(不是 POST,见 8.2 排障)
curl -s -X PATCH -H "Authorization: Bearer $TOK" http://127.0.0.1:8136/api/timesheets/2/stop
{"id":2,"begin":"2026-10-27T23:22:00+0800","end":"2026-10-27T23:23:00+0800","duration":60,...,"rate":0.0,"hourlyRate":0.0,...}

补录形态(起止时间显式给出)与固定费率:

curl -s -X POST -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
  -d '{"project":2,"activity":2,"begin":"2026-10-27T10:00:00","end":"2026-10-27T12:30:00","fixedRate":1200,"description":"固定费率实测"}' \
  http://127.0.0.1:8136/api/timesheets
{"id":3,"begin":"2026-10-27T10:00:00+0800","end":"2026-10-27T12:30:00+0800","duration":9000,"rate":1200.0,"fixedRate":1200.0,...}

集合查询(列表响应为精简序列化,无 fixedRate/hourlyRate 字段,消费脚本别按详情响应的字段集解析):

curl -s -H "Authorization: Bearer $TOK" 'http://127.0.0.1:8136/api/timesheets?size=10'
id=2 2026-10-27T23:22:00+0800 -> 2026-10-27T23:23:00+0800 dur=60s rate=0.0 desc=D1 文档 API 实测计时
id=1 2026-10-27T09:00:00+0000 -> 2026-10-27T11:42:00+0000 dur=9720s rate=0.0 desc=首页视觉稿迭代
id=4 2026-10-27T14:00:00+0800 -> 2026-10-27T15:30:00+0800 dur=5400s rate=525.0 desc=活动费率验证
id=3 2026-10-27T10:00:00+0800 -> 2026-10-27T12:30:00+0800 dur=9000s rate=1200.0 desc=固定费率实测

6.5 费率子资源

# 活动级小时费率(isFixed=false)/ 固定费率传 "fixed": true
curl -s -X POST -H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
  -d '{"rate":350,"internalRate":280}' http://127.0.0.1:8136/api/activities/2/rates
{"id":1,"user":null,"rate":350.0,"internalRate":280.0,"isFixed":false}

后续记录的折算联动(1.5h × 350 = 525;内部 1.5 × 280 = 420):

{"id":4,"duration":5400,"rate":525.0,"internalRate":420.0,"hourlyRate":350.0,...}

同族子资源:/api/projects/{id}/rates、/api/customers/{id}/rates(客户级兜底费率);读端点 GET .../rates、删端点 DELETE .../rates/{rateId}。三级费率优先级见配置篇 5.1 节。

七、备份与恢复

7.1 备份集 = 数据库 + var/data + var/plugins

# 数据库全量(业务数据全在这里)
mysqldump -ukimaiuser -p'q1w2e3r4' --single-transaction kimai | gzip > /opt/backup/kimai-$(date +%F).sql.gz
# 文件资产(发票 PDF、上传模板、logo、插件)
tar -czf /opt/backup/kimai-files-$(date +%F).tgz -C /opt/kimai var/data var/plugins

实测(库 35 张表,压缩产物 9227 字节):

-rw-r--r-- 1 root root 9227 Oct 27 23:25 /opt/backup/kimai-2026-10-27.sql.gz

--single-transaction 保证 InnoDB 一致性快照不锁表。MySQL 数据目录整拷(部署篇 bind mount 目录)只作应急手段,跨大版本不保证兼容,以逻辑 dump 为准。

7.2 恢复流程

  1. 按部署篇起一个空库实例(库名/用户名与备份一致);
  2. gunzip -c kimai-2026-10-27.sql.gz | mysql -ukimaiuser -p kimai;
  3. 解开 var/data、var/plugins 到安装目录对应位置;
  4. 启动应用——启动时序自动重跑 kimai:install,迁移链已到头则直接进入服务状态(部署篇 3.3 节日志可对照)。

八、排障实录

8.1 案例:老文档的 X-AUTH-TOKEN 头返回 401

现象:按 2.x 早期文档用 X-AUTH-TOKEN: <令牌> 调 API:

curl -s -H "X-AUTH-TOKEN: 0b57c60f251f97fdf38ec141c" http://127.0.0.1:8136/api/ping
{"code":401,"message":"Unauthorized"}

根因:2.71.0 移除了 API 密码特性,旧头随之失效;有效的认证头是 Authorization: Bearer。

对照实测:

$ curl -s -H "Authorization: Bearer 0b57c60f251f97fdf38ec141c" http://127.0.0.1:8136/api/ping
{"message":"pong"}

处置:所有集成改用 Authorization: Bearer;Basic Auth(-u 用户:密码)同样被拒(401)——用户名口令不是 API 凭据,必须经访问令牌。

8.2 案例:停止计时返回 405 Method Not Allowed

现象:按 REST 直觉用 POST /api/timesheets/{id}/stop:

{"code":405,"message":"Method Not Allowed"}

根因:该端点路由注册为 PATCH(stop_timesheet PATCH /api/timesheets/{id}/stop,debug:router 可查)。

处置:改用 curl -X PATCH 后正常返回停止后的记录详情(6.4 节回显)。同族 restart 端点同为 PATCH。规律:Kimai API 的「状态迁移类」操作(stop/restart)用 PATCH,资源创建用 POST——集成前用 debug:router 或 Swagger 核对方法。

8.3 案例:发票命令报 No invoice was generated

现象:

bin/console kimai:invoice:create --user admin --customer 1 --template '标准服务发票'
 [WARNING] No invoice was generated

根因:发票只捞取**本月未导出(exported=0)**的条目;客户 1 本月唯一一条记录已在先前开票时被标记 exported=1(4.1 节机制),本月无未开票条目。数据库侧核对:

SELECT id,exported FROM kimai2_timesheet;
id   exported
1    1
2    0
3    0
4    0

处置:换目标客户(customer 2 有未导出条目)立即成功(4.1 节回显);或按 --exported=all 重新开票全量条目(重复开票场景需自行控制业务语义)。同样机制下,kimai:export:create 报 No entries found, skipping 亦是「条目已被发票锁定」。

8.4 案例:导出产物落在 /tmp 且随服务重建消失

现象:kimai:export:create 成功后找不到文件。

根因:CLI 导出路径是系统临时目录(8.1 节实测 /tmp/20261027-Beta_zi_xun_gong_si.csv),不在 var/data 持久化范围。

处置:命令执行后立即取走文件;需要持久化交付物走 Web 导出界面(落 var/data)或脚本自行搬运。

8.5 案例:API 客户端解析列表响应报 KeyError

现象:按「详情响应」的字段集解析 /api/timesheets 列表,取 fixedRate 抛 KeyError。

根因:集合端点(列表)与单项端点(详情)的序列化深度不同——列表为精简形态(无 fixedRate/hourlyRate/user 嵌套等),详情才全量(6.4 节两种回显对照可见)。

处置:消费脚本按端点分别建模;需要完整字段时对列表里的 id 逐个取详情,或直接用 /api/timesheets?size=N 的分页参数控制批量。

8.6 案例:发票命令刷屏 deprecation 日志

现象:kimai:invoice:create/kimai:install 输出前滚出多条 deprecation.INFO: User Deprecated: Since twig/twig 3.29... JSON 日志。

根因:2.71.0 依赖树中 Symfony/Twig 上游的弃用提示(经 monolog 以 INFO 级打印),不影响功能结果——发票正常生成、安装正常完成。

处置:过滤 grep -v deprecation 查看有效输出;日志治理见 monolog 配置(config/packages/monolog.yaml),不必升级行为变更也不应作为报错处理。

8.7 排障速查表

现象关键回显定位处置
API 401{"code":401,"message":"Unauthorized"}头不对/令牌无效/令牌过期改 Authorization: Bearer;Web 端重建令牌
API 405{"code":405,"message":"Method Not Allowed"}stop/restart 用了 POST改 PATCH
API 404{"code":404,"message":"Not Found"}端点不存在(如 /api/config 根路径)debug:router 核对路由名
发票空生成No invoice was generated条目已 exported换客户/时段或 --exported=all
导出报错You must pass the "template" option漏 --template补模板名
改密无效Authenticator 列非 kimaiLDAP/SAML 账号到上游身份源改密
升级后异常迁移版本落后未跑安装命令kimai:install -n

专栏四篇至此构成完整闭环:认知选型(《Kimai 2.71 介绍与选型指南》)→ 部署(《Kimai 2.71 Docker Compose 部署实战》)→ 配置(《Kimai 2.71 配置参数详解》)→ 本篇命令与运维。