服务:http://121.43.55.7:10081/dms(DMS_HOST 可覆盖)
整理时间:2026-09-16
📍 本文引用的 artifacts / tools / RUNBOOK 都不在本仓库。 这些文件属于另一个项目
F:\yysk\AI_zhaoshang\DMS_Data_Migration\(本文就是从它的harness/docs/复制过来的)。本文里的链接已改为绝对路径指向那边; 若那边目录挪了位置,链接会失效 —— 记得回来改。 权威来源:服务端自带 OpenAPIdms_openapi.json(GET /dms/v3/api-docs,实时抓取,比 Apifox 新)可信度标注:本文每个接口都标了来源。
- ✅ 实测 —— 本项目真实调用过,参数与响应已验证
- 📄 文档 —— 仅见于 SKILL.md / Apifox / OpenAPI,本项目尚未调用过,用前先小样本验证
除登录外所有接口都要在 header 带 token:
token: {access_token}
AuthInterceptor 会拦截 /content/**、/model/**、/column/**、/task/**,
内部回调 OAuth 校验:
POST {oauth}/api/user/getUserByToken
Header: token
Body: serviceId=2, strUrl={当前请求URI}
OAuth serviceId 固定传 2(DMS 专用)。
token 通过环境变量传递,不要写进任何文件:
export DMS_TOKEN='...'
| 场景 | Content-Type |
|---|---|
| 多数接口 | application/x-www-form-urlencoded |
| 文件上传 | multipart/form-data |
⚠️
model/column的写接口有个陷阱:OpenAPI 声明它们是 query 参数, 但把大 payload(如 41 字段的fieldList)放 query string 会触发 HTTP 400。 一律用 form body。详见 §5.3。
{ "code": 200, "content": ..., "message": "成功" }
| code | 含义 |
|---|---|
200 |
成功 |
201 |
无权限 |
202 |
数据不存在(栏目为空) |
205 |
无数据 / 模型不存在 |
212 |
无效 token |
-1 |
参数错误 |
214 |
数据错误 |
-1/214/205的具体触发条件见 §5.4 —— 这几个是实测踩出来的,文档里没有。
GET /dms/v3/api-docs 共 82 个端点。按业务分组:
| 分组 | 主要端点 |
|---|---|
| 栏目 | /column/getColumnList、/column/getColumnById、/column/addColumn、/column/update、/column/delColumn、/column/selectChild |
| 模型 | /model/addModel、/model/getModelById、/model/getModelByName、/model/getModelListByPage、/model/updateFieldList、/model/createModelByOldModel |
| 内容 | /content/addContent、/content/updateContent、/content/selectContentList、/content/selectContentById、/content/delContentById、/content/importJSON |
| 字段库 | /param/selectAll、/param/addParam |
| 单/多选框 | /select/addSelects、/select/getSelectByType |
| 类别 | /category/addCategorys、/category/selectByType |
| 任务 | /task/addDataSync、/task/run、/task/taskLog |
| 权限 | /permission/getColumnByPermission |
| 文件 | /file/uploadFile |
这 8 个是本次建栏目真实调用并验证过的。
GET /dms/v3/api-docs ✅服务端自带的 OpenAPI 规范。排查接口问题的第一站,比 Apifox 权威。
curl -s "http://${DMS_HOST}/dms/v3/api-docs" -H "token: ${DMS_TOKEN}" -o dms_openapi.json
返回 118KB JSON,含 82 个端点的参数定义。已存档
dms_openapi.json。
POST /column/getColumnList ✅获取分级栏目树(无参数)。
curl -s -X POST "http://${DMS_HOST}/dms/column/getColumnList" -H "token: ${DMS_TOKEN}"
返回 content 为顶级栏目数组,每个节点:
| 字段 | 说明 |
|---|---|
id |
栏目 id |
title |
栏目名 |
type |
0 分级栏目 / 1 内容栏目 |
tag |
权限标签,同步 OAuth permissionName |
parentId |
父栏目 id |
level / state |
层级 / 状态 |
columnList |
子栏目数组(递归) |
实测返回 28 个顶级栏目。树是嵌套的,找子栏目要递归
columnList, 不要只看顶层。
POST /model/getModelById ✅curl -s "http://${DMS_HOST}/dms/model/getModelById?modelId=1839" -H "token: ${DMS_TOKEN}"
⚠️ 参数走 query string(不是 form body),
modelId必须带。
content 字段:
| 字段 | 说明 |
|---|---|
modelName |
模型名,物理表为 column_{modelName} |
modelAlias |
中文别名 |
type |
1 基础模型 / 2 栏目模型 |
fieldList |
JSON 字符串,见 §5.1 |
searchField / sortField |
实测为 "[]" |
pageSize |
实测为 20 |
authorId / authorName |
创建者 |
POST /model/getModelByName ✅curl -s "http://${DMS_HOST}/dms/model/getModelByName?modelName=yszs_page_view_model" -H "token: ${DMS_TOKEN}"
参数走 query string。用于幂等检查:建模型前先查名字是否存在。
POST /model/getModelListByPage ✅curl -s "http://${DMS_HOST}/dms/model/getModelListByPage?type=2" -H "token: ${DMS_TOKEN}"
唯一参数是 type。type=1 基础模型,type=2 栏目模型。
⚠️ 不传
type会返回code=205 无数据(不是空列表)。 同名的/model/getModelList无论怎么传都返回 205 —— 用getModelListByPage。
POST /param/selectAll ✅查询所有初始字段(无参数)。返回 28 个基础字段模板:
| id | name | type | alias |
|---|---|---|---|
| 1 | summary | text | 摘要 |
| 2 | content | text | 正文内容 |
| 3 | date_time | timestamp | 时间 |
| 4 | text | text | 文本内容 |
| 5 | int_num | integer | 整数类型 |
| 6 | float_num | double | 浮点数类型 |
| 7 | boolean | boolean | 布尔类型 |
| 8 | check | check | 单选框 |
| 9 | multiple_check | mcheck | 多选框 |
| 11 | select | type | 类别 |
| 12 | multiple_select | mtype | 多级类别 |
| 20 | files | file | 文件 |
| 21 | picture | varchar | 图片 |
| … |
这个表的 name 就是 fieldList 里 frontType 该填的值。 见 §5.2。
PUT /model/addModel ✅创建基础模型。
参数(form body):
| 参数 | 值 | 说明 |
|---|---|---|
modelName |
字符串 | 模型名,必须唯一 |
modelAlias |
字符串 | 中文别名 |
fieldList |
JSON 字符串 | 见 §5.1 |
searchField |
"[]" |
字面量空数组,不是字段名 |
sortField |
"[]" |
同上 |
type |
1 |
基础模型 |
pageSize |
20 |
|
authorId |
整数 | 必传,否则 code=-1 |
authorName |
字符串 | 必传,否则 code=-1 |
authorId/authorName 从 token 的 JWT payload 里取(userId/username)。
POST /model/updateProperty ✅ — 更新模型改模型字段(含别名)用这个。参数与 addModel 相同,外加 id(模型 id):
| 参数 | 说明 |
|---|---|
id |
模型 id(必传) |
modelName / modelAlias / fieldList / searchField / sortField / type / pageSize |
与 addModel 同 |
authorId / authorName |
必传 |
⚠️ 端点选择有坑(实测):
| 端点 | 结果 |
|---|---|
POST /model/updateProperty |
✅ code=200 |
POST /model/updateFields |
❌ HTTP 400 |
POST /model/updateFieldList |
❌ HTTP 500 |
名字最贴切的 updateFieldList(摘要「修改FieldList内容字段」)反而不能用。
工具:fix_field_alias.py
PUT /column/addColumn ✅创建栏目。要求 modelId 指向的模型已存在,否则 code=205 模型不存在。
参数(form body):
| 参数 | 值 | 说明 |
|---|---|---|
type |
0/1 |
0 分级栏目(仅目录,无内容表)/1 内容栏目 |
parentId |
整数 | 父栏目 id;-1 表示无父级 |
tag |
字符串 | 全局唯一,同步 OAuth permissionName |
title |
字符串 | 栏目名 |
content |
字符串 | 说明 |
state |
0 |
0 开启 / 1 关闭 / 2 销毁 |
level |
0 |
开放 |
modelId |
整数 | 基础模型 id |
modelName |
字符串 | 模型名 |
authorId / authorName |
必传 |
副作用:
c_ 前缀column_{栏目模型名}Column.tag → permissionName)幂等做法:先用 getModelByName 查模型是否存在,存在则跳过整个流程。
参考实现 create_dms_columns.py。
⚠️ 以下均未实测。 参数来自
SKILL.md/ Apifox,OpenAPI 未描述(这些接口用@RequestBody,服务端规范抓不到)。首次使用前请先在测试栏目验证。
POST /content/selectContentList 📄 — 列表查询(最常用)columnId=123&page=0&pageSize=20&search=[...]&orderBy=[...]&states=3
返回 content.data(数组)+ content.count(总数)。
count是行数核对(校验标准 C1)的基准值。 ⚠️states过滤会影响 count —— 核对时不要带states,否则会误判"少了数据"。
search 格式:
[{"field":"c_title","searchType":2,"content":{"value":"政策"}}]
| searchType | 含义 |
|---|---|
| 1 | 精确 |
| 2 | 模糊 |
| 3 | 区间(start/end) |
| 4 | in 数组 |
| 5 | between |
orderBy 格式:
[{"field":"c_created_at","orderByType":2}]
orderByType:1 升序,2 降序。
states(发布状态):
0 草稿 · 1 待审 · 2 审完 · 3 发布 · 4 销毁 · 5 退回
多个用逗号:states=2,3。指定 3 则为发布态。
POST /content/selectContentListInfo 📄 — 综合查询(详情)同 selectContentList 参数,额外做字典解析(把 check/type 等字段的码值翻译成文本)。
排查详情问题时用它。
POST /content/selectContentById 📄 — 单条查询columnId=123&contentId={uuid}
contentId是 UUID 字符串,不是数字 id。
POST /content/addContent 📄 — 新增| 参数 | 说明 |
|---|---|
columnId |
栏目 id |
modelId |
栏目模型 id(type=2 的那个) |
content |
JSON 字符串,字段用 c_ 前缀 |
curl -s -X POST "http://${DMS_HOST}/dms/content/addContent" \
-H "token: ${DMS_TOKEN}" \
-d 'columnId=1885' \
-d 'modelId=2030' \
-d 'content={"c_id":1,"c_business_code":"abc"}'
POST /content/updateContent 📄 — 修改参数同 addContent,content 里必须带 id(记录 uuid)。
DELETE /content/delContentById 📄 — 删除服务端 OpenAPI 确认存在(
DELETE /content/delContentById,摘要「删除内容」), 但参数未文档化,Apifox 也未收录。 在生产栏目用之前,先在测试栏目确认它对columnId/contentId的确切要求。
POST /content/importJSON 📄 — JSON 批量导入服务端 OpenAPI 确认存在(POST/PUT/GET/DELETE 都注册了), 但参数未文档化。大批量迁移可能比逐条
addContent快得多,值得优先验证。
POST /content/importBeautifiedExcel 📄 — Excel 导入multipart/form-data:file、columnId、parseArray、titleParam、contentParam。
POST /file/uploadFile 📄 — 附件上传multipart/form-data:columnId、contentId、paraName、type、file。
type:0 图 · 1 音 · 2 视 · 3 文件。
这几条是本次踩出来的,SKILL.md 与 Apifox 都没有。详见
DMS_MAPPING.md §5。
fieldList 格式外层是 dict,值是转义后的 JSON 字符串(fastjson 紧凑输出,无空格):
{
"credit_code": "{\"name\":\"credit_code\",\"alias\":\"统一社会信用代码\",\"must\":true,\"index\":0,\"sequence\":0,\"customType\":\"\",\"describe\":\"\",\"defaultValue\":\"\",\"type\":\"text\",\"frontType\":\"varchar\",\"searchType\":\"1\",\"showParam\":\"name,alias,desc,type,front_type,must,default_value\"}"
}
字段属性:
| 属性 | 要求 |
|---|---|
name |
不加 c_ 前缀 —— DMS 克隆时才加。自己加会变成 c_c_xxx |
index |
必须有,从 0 起 |
sequence |
从 0 起 |
type |
DMS 类型(text/integer/double/boolean/timestamp/file…) |
frontType |
不是 "content" —— 取 §3.6 基础字段表的 name |
id |
新格式不需要 |
must |
是否必填 |
| PG 类型 | type |
frontType |
状态 |
|---|---|---|---|
character varying / character |
text |
varchar |
✅ 实测 |
text |
text |
text |
✅ 实测 |
jsonb / json |
text |
text |
✅ 实测(DMS 无原生 JSON,存 text 后不可用 search 检索内部) |
bigint / integer / smallint |
integer |
int_num |
✅ 实测 |
numeric / double precision |
double |
float_num |
✅ 实测 |
boolean |
boolean |
boolean |
✅ 实测 |
| (epoch 秒换算后) | timestamp |
date_time |
✅ 实测 |
| — | check / mcheck |
单/多选 | 📄 未用到,未验证 |
| — | type / mtype |
类别 | 📄 未用到,未验证 |
| — | file |
files |
📄 未用到,未验证 |
| — | geometry |
点/线/面 | 📄 未用到,未验证 |
customType/defaultValue传空字符串""。
OpenAPI 把 addModel/addColumn 的参数都声明为 query,但:
| 方式 | 结果 |
|---|---|
| query string | 41 字段的 fieldList → HTTP 400(URL 过长) |
form body(-d) |
✅ 正常 |
结论:大 payload 一律用 form body,忽略 OpenAPI 的 in: query。
| code | 实测触发条件 |
|---|---|
-1 参数错误 |
addModel / addColumn 缺 authorId/authorName |
205 模型不存在 |
addColumn 时模型还没建 |
214 数据错误 |
① frontType 填了 "content" 而非 param 名② 缺 index③ fieldList 的 JSON 带空格(非紧凑) |
| HTTP 400 | fieldList 放进了 query string |
PUT /model/addModel 建基础模型 (type=1)
↓
POST /model/getModelByName 拿 modelId(幂等检查也用它)
↓
PUT /column/addColumn 建内容栏目 (type=1),DMS 自动克隆模型 → type=2 + c_ 前缀
不可颠倒 —— addColumn 要求模型已存在。
export DMS_HOST=121.43.55.7:10081
export DMS_TOKEN='...'
# 接口规范(排障第一站)
curl -s "http://${DMS_HOST}/dms/v3/api-docs" -H "token: ${DMS_TOKEN}"
# 栏目树
curl -s -X POST "http://${DMS_HOST}/dms/column/getColumnList" -H "token: ${DMS_TOKEN}"
# 模型
curl -s "http://${DMS_HOST}/dms/model/getModelById?modelId=2030" -H "token: ${DMS_TOKEN}"
curl -s "http://${DMS_HOST}/dms/model/getModelListByPage?type=2" -H "token: ${DMS_TOKEN}"
# 基础字段库(frontType 的取值来源)
curl -s -X POST "http://${DMS_HOST}/dms/param/selectAll" -H "token: ${DMS_TOKEN}"
# 核对行数(C1 校验)
curl -s -X POST "http://${DMS_HOST}/dms/content/selectContentList" \
-H "token: ${DMS_TOKEN}" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "columnId=1885&page=0&pageSize=1"
| 文档 | 内容 |
|---|---|
dms_openapi.json |
服务端 OpenAPI 全量(82 端点),最权威 |
SKILL.md |
DMS 领域模型与常用流程 |
apifox-dms.md |
Apifox 干净版 |
DMS_MAPPING.md |
本项目表→栏目映射 + 踩坑记录 |
RUNBOOK.md |
操作手册 |
POST /file/getFiles ✅用途:列服务器上某个目录的内容。查「后端把文件存哪了」时用它 —— 这些路径不是 URL,用浏览器/curl 直接访问一律 404(实测)。
path(form body)curl -s -X POST "http://121.43.55.7:10081/dms/file/getFiles" -H "token: ${DMS_TOKEN}" --data-urlencode "path=../../../../data/dms/dms_upload/yszs"
⚠️
path是相对 DMS 进程工作目录(Tomcat 的 bin)的,不是文件系统绝对路径。 传绝对路径(/data/...)不会报错,但会返回「当前文件不是文件夹,无子文件」—— 因为那是相对拼接后并不存在的路径。这是最容易踩的一点。
| 想要的位置 | 写成 |
|---|---|
| Tomcat bin(工作目录) | . |
Tomcat 根(有 webapps*、conf、logs、uploads…) |
.. |
/usr/local |
../.. |
/(根) |
../../../.. |
/data |
../../../../data |
/data/dms/dms_upload |
../../../../data/dms/dms_upload |
/data/dms/dms_upload/yszs |
../../../../data/dms/dms_upload/yszs |
{"code":200,"message":"成功!","content":["../webapps_dms","../logs", ...]}
| 情况 | 返回 |
|---|---|
| 目录有内容 | content 是数组,元素是「带前缀的相对路径」 |
| 目录为空 | 没有 content 字段(只有 code/message)—— 别把「字段缺失」当成出错 |
| 传的是文件路径 | content: ["当前文件不是文件夹,无子文件"](说明它只能列目录,读不了文件内容) |
参数名写错(dir/directory/filePath…) |
HTTP 500 |
/data/dms/
├── dms_upload/ ← DMS 上传根目录
│ ├── photo/ 7027 项
│ ├── file/ 6324 项
│ ├── video/ 3 项
│ ├── other/ 1 项
│ ├── yszs/ **空**(营商助手专用,新建未写入)
│ └── *.json 7 个 uuid 命名的 json
└── down_files/
📌 结论:
/data/dms/dms_upload/yszs是DMS 服务器上的真实目录, 目前没有数据;它既不是 URL,也不能通过getFiles读文件内容(只能列目录)。