# DocSystem(MxsDoc)HTTP 接口文档 目标系统:MxsDoc / DocSystem V2.02.71 地址:`http://172.1.4.31:8100/DocSystem` 前端入口:`/DocSystem/web/index.html` → `projects.html` → `project.html?vid=<仓库ID>` 本文档由前端 JS 逆向 + 逐接口实测得出,用于支撑 `scripts/docsys.py`。 --- ## 通用约定 - 全部接口为 `POST`,表单编码 `application/x-www-form-urlencoded; charset=UTF-8` (仅 `uploadDoc.do` 为 `multipart/form-data`)。 - 必须带请求头 `X-Requested-With: XMLHttpRequest`。 - 会话靠 Cookie 维持:`JSESSIONID`、`dstoken`(= base64(密码))、`dsuser`(= 用户名)。 `rememberMe=1` 时服务端下发 `dstoken`/`dsuser` 用于自动登录。 - 响应统一 JSON:`{"status":"ok"|"fail","data":...,"msgInfo":"...","msgData":...}`。 少数接口(如 `getDocContent.do`)返回纯文本。 - 节点类型:`type=1` 文件,`type=2` 目录。 - 时间字段为毫秒时间戳。 ## 鉴权 ### POST `User/login.do` | 参数 | 说明 | |---|---| | `userName` | 邮箱或手机号 | | `pwd` | 密码经 base64 编码后的字符串(标准 base64,非 URL-safe) | | `rememberMe` | `1` 记住登录,`0` 不记住 | 成功返回 `data` 为用户对象(`id`/`name`/`realName`/`role`/…)。 ### POST `User/logout.do` 无参数。 ### POST `User/getLoginUser.do` 无参数,返回当前登录用户。可用于探测会话是否有效。 --- ## 仓库 ### POST `Repos/getReposList.do` 无参数。返回 `data` 为仓库数组: `{id, name, info, type, verCtrl, path, owner, ...}` 实测可见仓库: | id | 名称 | 说明 | |---|---|---| | 1 | 意见与反馈 | 当前账号可新增/编辑,不可删除/下载 | | **2** | **研发管理配置库** | **CLI 默认仓库**(`DEFAULT_REPO_NAME`),基本只读,部分目录可下载 | | 5 | 常用工具 | 只读 | | 6 | 临时仓库 | 无访问权限 | > **约定**:`scripts/docsys.py` 中若省略仓库参数,默认操作 id=2「研发管理配置库」。 > 判定规则:第一个位置参数以 `/` 或 `./` 开头则视为路径(仓库取默认),否则视为仓库 id/名称。 ### POST `Repos/getDocSysConfig.do` 无参数,返回系统配置(如 `defaultReposStorePath`)。 --- ## 目录列举 ### POST `Repos/getSubDocList.do` | 参数 | 说明 | |---|---| | `vid` | 仓库 id | | `docId` | 目录 docId(**实测服务端不使用**,仅随请求透传) | | `path` | 目录路径,仓库根为 `""`,其余以 `/` 结尾 | | `name` | 目录名。服务端取 `path + name` 作为目标目录 | | `sort` | 排序方式,`0` 名称 | | `needLockState` | `1` | | `shareId` | 可传空 | **关键结论**:目标目录 = `path + name`。传 `path="a/"`、`name=""` 与 `path=""`、`name="a"` 等价;但传 `path="a/"` 且 `name="a"` 会变成 `a/a` 从而返回空。 返回 `data` 为节点数组,节点字段: `docId, pid, path, name, type, level, size, checkSum, createTime, latestEditTime, latestEditorName, creatorName, localRootPath, reposPath, ...` 其中 `path` 是**节点所在目录**的路径,节点完整路径 = `path + name`。 --- ## 权限 ### POST `Repos/getUserDocAuth.do` | 参数 | 说明 | |---|---| | `docId` | 目录 docId,仓库根为 `0` | | `reposId` | 仓库 id | | `path` | 目录自身路径 | | `name` | 目录名 | 返回 `data`:`{access, addEn, editEn, deleteEn, downloadEn, isAdmin, uploadSize, ...}` 实测(账号 `qian.huang@sunyard.com`,仓库 1): ``` access=1 addEn=1 editEn=1 deleteEn=0 downloadEn=0 isAdmin=0 ``` 注意:**rename / move 在服务端按"删除 + 新增"鉴权**,因此即使 `addEn=1`, `deleteEn=0` 时重命名和移动同样会被拒绝,报错为「您没有该目录的删除权限」。 ### 权限是逐目录授予的,不是仓库级 同一个仓库里不同目录的权限可以完全不同。实测: | 仓库 / 目录 | access | addEn | editEn | deleteEn | downloadEn | |---|---|---|---|---|---| | 2 研发管理配置库 `/`(根) | 1 | 0 | 0 | 0 | 0 | | 2 `/Sunyard/Docs/开发流程` | 1 | 0 | 0 | 0 | **1** | | 2 `/Sunyard/Dev/产品/系统研发/工具` | 1 | **1** | **1** | **1** | **1** | | 1 意见与反馈 `/`(根) | 1 | 1 | 1 | 0 | 0 | 所以**不能凭仓库名推断权限**,写操作前务必先查 `getUserDocAuth`。 --- ## 写操作 ### POST `Doc/addDoc.do`(新建文件 / 目录) | 参数 | 说明 | |---|---| | `reposId` | 仓库 id | | `type` | `1` 文件,`2` 目录 | | `pid` | 父目录 docId,仓库根为 `0` | | `path` | **父目录完整路径 + `/`**,仓库根为 `""` | | `name` | 新节点名 | | `content` | 文本文件内容;建目录传空 | 返回 `data` 为新节点,`msgData` 为 `isNewNode`。 > ⚠️ **实测:`content` 被服务端忽略。** 即使传了内容,建出来也是 0 字节空文件 > (响应 `size=0`,随后 `getDocContent` 读不到任何内容)。 > 要创建带内容的文件必须改用 `Doc/uploadDoc.do`(CLI 的 `touch` 已自动走这条路)。 ### POST `Doc/renameDoc.do` | 参数 | 说明 | |---|---| | `reposId` | 仓库 id | | `docId` | 节点 docId | | `pid` | 父目录 docId | | `type` | 节点类型 | | `level` | 节点层级 | | `path` | 节点**自身所在目录**路径 | | `name` | 原名 | | `dstName` | 新名 | ### POST `Doc/deleteDoc.do` | 参数 | 说明 | |---|---| | `reposId` | 仓库 id | | `docId` | 节点 docId | | `pid` | 父目录 docId | | `path` | 节点自身所在目录路径 | | `name` | 节点名 | 删除目录时为递归删除。 ### POST `Doc/moveDoc.do` | 参数 | 说明 | |---|---| | `reposId` | 仓库 id | | `docId` | 节点 docId | | `type` | 节点类型 | | `srcLevel` | 源层级 | | `dstLevel` | 目标层级(父目录 level + 1;移到仓库根为 `0`) | | `srcPid` | 源父目录 docId | | `dstPid` | 目标父目录 docId,仓库根为 `0` | | `srcPath` | 源目录路径 | | `srcName` | 源名 | | `dstPath` | **目标父目录完整路径 + `/`**,仓库根为 `""` | | `dstName` | 目标名(可与源名不同,即改名 + 移动) | ### POST `Doc/copyDoc.do` 参数同 `moveDoc`。 --- ## 上传 / 下载 ### POST `Doc/uploadDoc.do`(multipart/form-data) | 字段 | 说明 | |---|---| | `reposId` | 仓库 id | | `path` | **目标目录完整路径 + `/`** | | `name` | 远端文件名 | | `size` | 文件字节数 | | `checkSum` | 文件 MD5 | | `uploadFile` | 文件内容(文件域) | | `commitMsg` | 提交说明,可空 | | `dirPath` | 文件夹上传标记,可空 | 大文件走分片:`chunkNum` / `cutSize` / `chunkIndex` / `chunkSize` / `chunkHash` / `combineDisabled=1`,分片全部传完再调 `Doc/combineChunks.do`。 ### POST `Doc/downloadDocPrepare.do` | 参数 | 说明 | |---|---| | `reposId`, `docId`, `pid`, `path`, `name` | 目标节点 | | `downloadType` | `1` 单文件 | | `downloadList` | 可空 | 返回 `data`:`{path, name, targetPath, targetName}`,`msgData` 为 `deleteFlag`。 若 `msgData == 5` 表示目录正在打包,用 `data.id` 轮询 `Doc/queryDownloadPrepareTask.do`(参数 `taskId`),每 2 秒一次,直到 `msgData == 6`。 ### GET `Doc/downloadDoc.do?...` 查询串:`vid, path, name, targetPath, targetName, deleteFlag, encryptEn=1`(值需 URL 编码)。 > 实测:直接调 `Doc/getDocFileLink.do` 拿到的链接指向 `tmp/History/rdata//...` > 历史目录,该文件通常不存在,会返回 500。**必须走 `downloadDocPrepare` 流程。** --- ## 读取 / 搜索 ### POST `Doc/searchDoc.do` | 参数 | 说明 | |---|---| | `searchWord` | 关键词 | | `sort` | `0` | | `reposId` | 仓库 id | | `path` | 限定目录范围,全库传 `""` | 返回 `data` 为命中项,`hitType`:`1` 文件名命中,`3` 正文命中; `content` 为正文的 base64 编码;`dataEx` 为命中总数。 ### POST `Doc/getDocContent.do` | 参数 | 说明 | |---|---| | `reposId`, `docId`, `pid`, `path`, `name` | 目标文件 | 返回**纯文本**,响应以 `ok` 开头,后面才是文件正文,解析时须去掉前两个字符。 ### POST `Doc/getDoc.do` | 参数 | 说明 | |---|---| | `reposId`, `docId`, `pid`, `path`, `name` | 目标节点 | | `docType` | `0` 不读取正文和备注 | 返回节点元信息。 --- ## 已探明但未使用的接口 - `Doc/lockDoc.do` / `Doc/unlockDoc.do` — 文件锁定 - `Doc/executeDoc.do`、`Doc/refreshDoc.do` - `Doc/checkChunkUploaded.do`、`Doc/combineChunks.do`、`Doc/getMaxThreadCount.do` — 分片上传 - `Doc/getDocFileLink.do`、`Doc/getZipDocFileLink.do` — 预览/下载链接(下载场景不可靠,见上) - `Bussiness/getDocOfficeLink.do`、`Bussiness/getDocCadLink.do` — 在线预览 - `Bussiness/addDocShare.do`、`Doc/getDocShare.do` — 分享 - `Doc/querySystemLog.do` — 操作日志 - `Repos/getReposInitMenu.do` — 目录树初始化菜单