Mayan EDMS 常用命令与运维实战

Mayan EDMS 4.12.2 运维实战:管理命令全景(21 组 44 条实测清单)、REST API Token 认证实测(文档上传/OCR/全文检索/抽屉/标签/事件)、数据库与应用卷备份、队列观测与排障实录。

Mayan EDMSMayan EDMS2026/10/28

Mayan EDMS 常用命令与运维实战

目标:盘点 Mayan EDMS 4.12.2 的日常运维入口——管理命令行(21 组 44 条命令全清单)、REST API(Token 认证 + 文档上传/OCR/检索/归档全链路 curl 实测)、备份恢复与故障排查。所有命令与回显均在 4.12.2 实测环境逐条执行(部署过程见 《Mayan EDMS Docker Compose 部署实战》)。

一、环境信息

  • Mayan EDMS:4.12.2(官方 s4.12 镜像,all_in_one 形态,2026-10-28 实测)
  • 管理员:admin / q1w2e3r4(首启自动创建,AUTOADMIN_* 变量预设)
  • 已初始化对象:文档类型 Contract(id 2)、抽屉 2026 Contracts(id 1)、标签 ocr-verified(id 1)
  • 实测文档:QZ-2026-0428 Purchase Contract(id 3,英文 OCR)、QZ-2026-0429 中文采购合同(id 4,中文 OCR)
  • 服务入口形态:官方 Compose 部署下,管理命令经应用服务控制台执行 entrypoint.sh run_command <命令>;run_command 会先走一遍入口脚本初始化段(依赖探活、uid/gid、字体缓存)再执行目标命令

二、管理命令全景(21 组 44 条实测清单)

entrypoint.sh run_command help 实测输出(Mayan EDMS 4.12.2):

分组命令用途
appearanceappearance_prepare_static静态资源准备
authchangepassword、createsuperuser改密 / 建管理员
authentication_otpauthentication_otp_disable/_initialize/_status双因素 OTP 管理
authtokendrf_create_token创建/获取 REST API Token
autoadminautoadmin_create手动触发自动管理员创建
axesaxes_list_attempts、axes_reset(_failure_logs/_ip/_ip_username/_logs/_username)登录限流(防爆破)查看与解锁
commoncommon_generate_random_secret_key、common_initial_setup、common_perform_upgrade随机密钥生成 / 初始化 / 升级迁移
contenttypesremove_stale_contenttypes清理失效内容类型
databasesmakemigrations生成迁移(开发用)
dependenciesdependencies_check/_check_version/_generate_requirements/_install/_show_version/_uninstall依赖核对与管理
django(核心)check、compilemessages、createcachetable、dbshell、diffsettings、dumpdata、flush、inspectdb、loaddata、makemessages、migrate、optimizemigration、sendtestemail、shell、showmigrations、sqlflush、sqlmigrate、sqlsequencereset、squashmigrations、startapp、startproject、test、testserverDjango 标准命令
documentationdocumentation_features_generate生成功能文档
drf_spectacularspectacular、(rest_framework)generateschema导出 OpenAPI Schema
dynamic_searchsearch_index_objects、search_initialize、search_reindex、search_status、search_upgrade全文检索索引管理
file_cachingfile_caching_purge_cache清预览缓存
lock_managerlock_manager_purge_locks清孤儿锁
mayan_statisticsstatistics_purge清统计数据
mirroringmirroring_mount_cabinet、mirroring_mount_index抽屉/索引 FUSE 镜像挂载
permissionspermissions_purge清权限残留
platformsplatforms_template平台模板
sessionsclearsessions清过期会话
smart_settingssettings_revert、settings_save、settings_show运行期设置查看/保存/回退
staticfilescollectstatic、findstatic、runserver静态资源与开发服务器
storagestorage_process、storage_shard_files存储处理与分片
task_managertask_manager_purge_periodic_tasks清理周期任务残留

版本差异提醒:老版本文档里的 showsettings 在 4.12.2 已不存在(实测报 Unknown command,提示改用 diffsettings);设置查看以 settings_show 为准。

三、关键管理命令实测

3.1 drf_create_token:创建 API Token

entrypoint.sh run_command drf_create_token admin

实测回显(末行):

5626fcf3f8966638b9228234974f89902efeda4d

对同一用户重复执行返回同一个 Token(幂等)。此后所有 API 调用带请求头 Authorization: Token <值>。

3.2 settings_show:查看生效设置

entrypoint.sh run_command settings_show

实测摘录:

OCR_AUTO_OCR: true
OCR_BACKEND: mayan.apps.ocr.backends.tesseract.Tesseract
OCR_BACKEND_ARGUMENTS:

AUTOADMIN_EMAIL: autoadmin@example.com
AUTOADMIN_PASSWORD: q1w2e3r4
AUTOADMIN_USERNAME: admin

CELERY_BROKER_URL: amqp://mayan:mayanrabbitpass@rabbitmq:5672/mayan

注意:输出含明文密码,勿外贴。参数含义见 《Mayan EDMS 配置参数详解》。

3.3 search_status:检索索引状态

entrypoint.sh run_command search_status

实测回显(文档入库后):

Document type            2
Document version         2
Document version page    2
...
User                     1

每类对象一行计数。文档上传后若检索无结果,先看这里计数是否已增长(索引是异步更新的)。

3.4 dependencies_check:依赖完整性核对

entrypoint.sh run_command dependencies_check

实测尾部回显:

* sh            Class: Python     | Version: ==2.2.6   | App: Common             | Check: True
* signature_pad Class: JavaScript | Version: =4.2.0    | App: Signature captures | Check: True
* whitenoise    Class: Python     | Version: ==6.12.0  | App: Platforms          | Check: True

升级后排查依赖错乱的第一条命令,逐项给出 Check: True/False。

3.5 common_generate_random_secret_key:生成随机密钥

entrypoint.sh run_command common_generate_random_secret_key

实测回显:

qj_-++8(v&(4i^armj0$@gxn)^q40=ys0p&v(l3b=%n*l50f@b

可直接用于生产 SECRET_KEY 等密钥场景。

3.6 数据库直查(psql)

数据库控制台内直查对象计数,验证数据落库:

psql -U mayan mayan -c "SELECT 'documents' t, count(*) FROM documents_document UNION ALL SELECT 'versions', count(*) FROM documents_documentversion UNION ALL SELECT 'files', count(*) FROM documents_documentfile UNION ALL SELECT 'cabinets', count(*) FROM cabinets_cabinet UNION ALL SELECT 'tags', count(*) FROM tags_tag;"

实测回显:

     t     | count
-----------+-------
 documents |     2
 versions  |     2
 files     |     2
 cabinets  |     1
 tags      |     1
(5 rows)

四、REST API 实战(curl 全链路)

API 根:/api/v4/(带 Token 访问返回 46 个端点分组)。下文 $TOKEN 即 §3.1 创建的 Token 值,$BASE=http://127.0.0.1:8140。

4.1 鉴权验证

curl -s -H "Authorization: Token $TOKEN" $BASE/api/v4/document_types/

实测回显节选(默认类型存在):

{
    "count": 2,
    "results": [
        {"id": 1, "label": "Default", "trash_time_period": 30, ...},
        {"id": 2, "label": "Contract", "filename_generator_backend": "uuid", ...}
    ]
}

无 Token 或 Token 错误返回 401。

4.2 建文档类型与抽屉

curl -s -X POST -H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
  -d '{"label":"Contract","trash_time_period":30,"trash_time_unit":"days"}' \
  $BASE/api/v4/document_types/

curl -s -X POST -H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
  -d '{"label":"2026 Contracts","parent":null}' \
  $BASE/api/v4/cabinets/

实测:两者均返回 201 与完整序列化对象(抽屉返回 full_path: "2026 Contracts")。

4.3 创建文档记录 + 上传文件(两步异步)

第一步:POST /documents/ 只建记录,不收文件。 文档记录(实测返回 id=3):

curl -s -X POST -H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
  -d '{"label":"QZ-2026-0428 Purchase Contract","document_type_id":2,"description":"E2E OCR test with DejaVu font"}' \
  $BASE/api/v4/documents/

第二步:向文档上传实际文件。 端点 POST /documents/{id}/files/,字段是 file_new(multipart),返回 202(后台受理):

curl -s -w '\nHTTP %{http_code}\n' -X POST -H "Authorization: Token $TOKEN" \
  -F "file_new=@ocr-test2.png" -F "action_name=replace" -F "comment=clean font OCR test" \
  $BASE/api/v4/documents/3/files/

实测回显:

HTTP 202

action_name=replace 表示新文件成为新的活动版本。上传后管线异步推进:文件落库 → 版本创建 → 页面拆分 → 预览图 → OCR → 索引(实测每步间隔 12~25 秒)。

4.4 读取版本页面与 OCR 结果

# 活动版本页面列表
curl -s -H "Authorization: Token $TOKEN" $BASE/api/v4/documents/3/versions/4/pages/

# 页面 OCR 内容(页面 id 由上一步返回)
curl -s -H "Authorization: Token $TOKEN" $BASE/api/v4/documents/3/versions/4/pages/4/ocr/

实测 OCR 回显:

{
    "content": "Mayan EDMS OCR Test 2026\nPurchase Order Amount: 12345 USD\nContract Reference: QZ-2026-0428\n\nVendor: Quzuo Trading Co.\n"
}

与测试图原文逐行一致(Tesseract 5.5.0,英文 100% 命中)。

4.5 预览图渲染与文件下载

# 页面预览图
curl -s -o /dev/null -w 'image HTTP %{http_code} type=%{content_type} size=%{size_download}\n' \
  -H "Authorization: Token $TOKEN" $BASE/api/v4/documents/3/versions/4/pages/4/image/

# 原文件下载(注意用文件 id,不是文档 id)
curl -s -o /dev/null -w 'download HTTP %{http_code} type=%{content_type} size=%{size_download}\n' \
  -H "Authorization: Token $TOKEN" $BASE/api/v4/documents/3/files/4/download/

实测回显:

image HTTP 200 type=image/jpeg size=38118
download HTTP 200 type=image/png size=33338

下载字节数 33338 与上传原图完全一致(无损存取)。版本导出为异步动作:POST /documents/3/versions/4/export/ 返回 202,产物经导出队列落盘后再取。

4.6 挂抽屉与打标签

# 挂抽屉:面向抽屉的添加动作,字段名是 document(不是 document_id)
curl -s -w 'HTTP %{http_code}\n' -X POST -H "Authorization: Token $TOKEN" \
  -H "Content-Type: application/json" -d '{"document": 3}' \
  $BASE/api/v4/cabinets/1/documents/add/

# 打标签:先建标签,再挂到文档,字段名是 tag(不是 tag_id)
curl -s -X POST -H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
  -d '{"label":"ocr-verified","color":"#2E86DE"}' $BASE/api/v4/tags/
curl -s -w 'HTTP %{http_code}\n' -X POST -H "Authorization: Token $TOKEN" \
  -H "Content-Type: application/json" -d '{"tag": 1}' \
  $BASE/api/v4/documents/3/tags/attach/

实测:挂抽屉返回 200;文档侧复核 GET /documents/3/cabinets/ 返回 count: 1 [('2026 Contracts', '2026 Contracts')],GET /documents/3/tags/ 返回 [('ocr-verified', '#2E86DE')]。

4.7 全文检索(中英实测)

检索端点形态是 GET /api/v4/search/<检索模型>/results 或 ?q=,检索模型名必须是 documents.documentsearchresult:

curl -s -H "Authorization: Token $TOKEN" \
  "$BASE/api/v4/search/documents.documentsearchresult/?q=QZ-2026-0428"

实测回显节选:

{
    "count": 1,
    "results": [
        {
            "label": "QZ-2026-0428 Purchase Contract",
            "document_type": {"id": 2, "label": "Contract", ...},
            "file_latest": {"checksum": "02c622fb...", "comment": "clean font OCR test", ...}
        }
    ]
}

中文检索同样命中(中文文档 language 设为 chi_sim 并重新识别后):

curl -s -H "Authorization: Token $TOKEN" \
  "$BASE/api/v4/search/documents.documentsearchresult/?q=%E6%9B%B2%E4%BD%9C%E8%B4%B8%E6%98%93"

实测:count: 1,命中「QZ-2026-0429 中文采购合同」。

4.8 事件流与回收站

# 事件流(实测 46 条,含文档创建/版本提交/OCR 完成)
curl -s -H "Authorization: Token $TOKEN" "$BASE/api/v4/events/?page_size=3"

# 移入回收站与彻底清除(均异步 202)
curl -s -w 'HTTP %{http_code}\n' -X DELETE -H "Authorization: Token $TOKEN" $BASE/api/v4/documents/1/
curl -s -w 'HTTP %{http_code}\n' -X DELETE -H "Authorization: Token $TOKEN" $BASE/api/v4/trashed_documents/1/

实测回显:

HTTP 202
HTTP 202

4.9 API 字段名速查(踩坑总结)

想做的事正确写法错误写法(实测后果)
上传文件POST /documents/{id}/files/,字段 file_new,返回 202 异步POST /documents/ 带 file 字段不报错但不建文件
挂抽屉POST /cabinets/{id}/documents/add/,body {"document": 3}{"cabinet_id": 1} 打到 405;{"document_id": 3} 打到 400
打标签POST /documents/{id}/tags/attach/,body {"tag": 1}{"tag_id": 1} 打到 400 "tag":["This field is required."]
全文检索/search/documents.documentsearchresult/?q=.../search/documents.search/... 打到 404 且应用日志堆栈报错
文档语言PATCH /documents/{id}/ body {"language":"chi_sim"}语言包装了但文档 language 仍是 eng 时 OCR 中文乱码

五、备份与恢复

5.1 备份对象盘点

默认部署共 5 个数据卷(docker volume ls 实测,卷名前缀即项目名):

卷容量内容
mayan-edms-412-stack_app828 KB文档文件存储 document_storage/、config.yml、预览缓存
mayan-edms-412-stack_postgres72.1 MB全部元数据
mayan-edms-412-stack_rabbitmq1.0 MB队列数据(可重建,价值低)
mayan-edms-412-stack_redis4 KB锁/结果/事件流(可重建,价值低)
mayan-edms-412-stack_postgres-initdb—初始化脚本挂载点(空)

必须备份的是前两个卷:文档二进制在 app 卷,结构化元数据在数据库卷,两者需同一时间点成对备份才能完整恢复。

5.2 备份实测

# ① 元数据库逻辑备份
docker compose exec postgresql pg_dump -U mayan mayan | gzip > mayan-db-20261028.sql.gz

# ② 应用数据卷打包(文档二进制 + config.yml)
docker run --rm -v mayan-edms-412-stack_app:/src:ro \
  -v /opt/data/quzuo/tmp/mayan-edms/backup-drill:/backup alpine \
  tar czf /backup/mayan-app-20261028.tgz -C /src .

实测产物:

mayan-db-20261028.sql.gz    58541 字节(zcat 首行:-- PostgreSQL database dump)
mayan-app-20261028.tgz     307046 字节

官方编排还内置了 postgresql-backup profile(pg_dump | gzip > /backups/$(date +%Y-%m-%d_%H-%M).sql.gz 写入专用备份卷),可按需启用做周期备份。

5.3 恢复要点

  1. 以同版本编排重建空栈(up 不启动应用),或先 down 清掉旧实例。
  2. 恢复数据库:gunzip -c mayan-db-*.sql.gz | psql -U mayan mayan(空库导入)。
  3. 恢复应用卷:解包 tar 回 app 卷(覆盖 document_storage/ 与 config.yml)。
  4. 启动后执行 common_perform_upgrade 让迁移对齐版本,再核对 search_status 计数与文档下载完整性。

六、队列与进程观测

6.1 任务队列深度

docker compose exec rabbitmq rabbitmqctl list_queues -p mayan name messages

实测回显(空闲态,全部 0):

sources            0
file_metadata      0
documents_periodic 0
documents_file_slow 0
documents_file     0
default            0
workflows          0
converter          0
documents_exports  0
indexing_slow      0
search_slow        0
documents_slow     0
signatures_slow    0
events_fast        0
...(共 20+ 队列)

上传后持续不为 0 的队列即瓶颈点(对照六类 worker 的队列分工,见配置参数详解篇 §5.2)。

6.2 进程与日志

docker compose exec app supervisorctl status   # 8 个进程 RUNNING 为健康态
docker compose logs app | grep -E 'ERROR|Traceback' | tail   # 应用错误日志

实测:正常运维期日志仅出现少量中间件捕获的异常记录(对应 API 误用请求),worker 无异常。

七、排障实录汇编

7.1 检索端点模型名写错 → 404 + 日志堆栈

误用 /api/v4/search/documents.search/?q=... 返回 404 页面,应用日志出现 KeyError: 'documents.search' 堆栈(错误中间件捕获)。正确模型名用 GET /api/v4/search_models/ 查询,或直接用 documents.documentsearchresult。

7.2 API 字段名语义差异

挂抽屉/打标签的请求体字段不带 _id 后缀({"document": 3}、{"tag": 1}),带后缀会得到 400 校验错误;批量动作端点与单文档端点混用也会 404。第一次打不中时先看 400 回显里的字段提示。

7.3 上传后"文档没文件"

POST /documents/ 只建记录;文件必须走 POST /documents/{id}/files/(202 异步)。等待期间反复查询同一端点会得到 files count=0 的假象,管线完成后才出现文件与版本。

7.4 中文 OCR 乱码(两层根因)

①镜像默认语言包仅 eng/osd,需 MAYAN_APT_INSTALLS=tesseract-ocr-chi-sim 补包;②补包后仍乱码,因 OCR 语言取自文档 language 属性(默认 eng),PATCH 为 chi_sim 后重新提交 OCR 即全对。判定顺序:tesseract --list-langs → 文档 language → 重新提交识别。

7.5 删除文件连带版本页面消失

删除文档文件记录会级联清掉由它派生的版本页面(版本记录仍在、pages 归零、OCR 内容随页面消失)。清理重复文件前先核对 file_latest 与版本归属。

八、参考链接