init: 信创文件安全传输系统内外网本地 Docker 部署
包含 s-file 后端、React 前端、MeshSafe 安全检测对接,以及内外网两套 compose/nginx/配置(仅前端 28180/28280 对外发布,其余端口不暴露)。 说明:deploy/config/application-iw.yml 与 application-ow.yml 按维护者 明确要求原样入库,其中含本机 Docker 用的库口令、RabbitMQ 口令与 JWT 密钥;上生产前需替换。
This commit is contained in:
171
backend/docs/接口文档-文件安全检测.md
Normal file
171
backend/docs/接口文档-文件安全检测.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# 前端接口文档 —— 文件安全检测(MeshSafe 对接)
|
||||
|
||||
> 版本:v1.0(2026-11-01)
|
||||
> 适用后端:s-file(alita-bjy-uygur)
|
||||
> Swagger 分组:「文件安全检测」
|
||||
|
||||
## 功能说明
|
||||
|
||||
对接独立文件安全检测服务 **MeshSafe**:文件上传(网盘上传)与跨网传输接收解包后,自动提交 MeshSafe 检测,检测结论回写到本系统并标记在文件上。
|
||||
|
||||
核心规则:
|
||||
|
||||
- **检测不通过(误报可复核)**:文件**只能查看/下载/预览**,**禁止分享与跨网传输**;管理员可人工复核修改检测结果(需填写复核说明),复核通过后即可正常分享/传输;
|
||||
- **文件夹部分文件不通过**:分享/传输弹窗内提示未通过的文件清单,用户确认后**仅分享/传输检测通过的内容**(请求带 `onlyPassed=true`,后端排除不通过/检测中的文件);不确认则整体拦截;
|
||||
- **检测中**:禁止分享与传输(检测通常秒级完成,稍后重试即可);
|
||||
- **检测失败**:默认不阻断业务(仅标记"检测失败",可重新检测);`meshsafe.fail-open=false` 时检测失败同样禁止分享/传输;
|
||||
- **未检测**:不阻断(历史文件/开关未开启期间上传的文件);
|
||||
- 同一文件允许多条检测记录(重新检测、人工复核均新增记录),**列表与校验均取最新一条**;
|
||||
- 人工复核会尽力回写 MeshSafe 的人工复核接口(失败仅告警,不影响本地生效)。
|
||||
|
||||
检测状态(`securityStatus`)取值:
|
||||
|
||||
| 值 | 含义 | 能否分享/传输 |
|
||||
|---|---|---|
|
||||
| 空 | 未检测 | 能 |
|
||||
| 1 | 检测中 | 否 |
|
||||
| 2 | 通过 | 能 |
|
||||
| 3 | 不通过 | 否 |
|
||||
| 4 | 检测失败 | 默认能(fail-open=false 时否) |
|
||||
|
||||
## 通用约定
|
||||
|
||||
与既有接口一致:登录态调用;统一返回 `AlitaResult<T>`(`code="200"` 且 `success=true` 成功;业务失败 `success=false`,`msg` 为提示文案)。
|
||||
|
||||
**总开关**:`meshsafe.enabled=false` 时检测功能整体关闭——上传/解包不送检、分享/传输不拦截、预检返回空、复核与重新检测接口报"文件安全检测未开启";前端通过 `GET /file_security/enabled` 获取开关状态,关闭时列表隐藏"安全检测"列。
|
||||
|
||||
## 文件列表新增字段
|
||||
|
||||
`GET /user_file/page`、`GET /user_file/list`、`GET /user_file/record/page_by_phone` 等返回的文件记录新增以下字段(前端据此展示与禁用操作):
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| securityStatus | String | 安全检测状态:1检测中/2通过/3不通过/4检测失败,null 为未检测 |
|
||||
| securityAction | String | MeshSafe 处置动作:pass/manual_review/reject |
|
||||
| securityReason | String | 检测结论说明(可直接展示给用户,人工复核时为复核说明) |
|
||||
| securityReviewComment | String | 人工复核说明(人工改判时有值) |
|
||||
|
||||
---
|
||||
|
||||
## 1. 查询文件最新检测结果
|
||||
|
||||
- **URL**:`GET /file_security/detail`
|
||||
- **权限**:所有登录用户
|
||||
- **参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| fileCode | String | 是 | 文件编码 |
|
||||
|
||||
- **返回**:`AlitaResult<FileSecurityDTO>`,无检测记录时 `data` 为 null
|
||||
|
||||
**FileSecurityDTO 字段**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| fileCode | String | 文件编码 |
|
||||
| taskId | String | MeshSafe 检测任务 ID |
|
||||
| scanStatus | String | 检测状态:1检测中/2通过/3不通过/4检测失败 |
|
||||
| action | String | MeshSafe 处置动作:pass/manual_review/reject |
|
||||
| reason | String | 检测结论说明 |
|
||||
| resultJson | String | 检测完整结果 JSON(原始响应) |
|
||||
| reviewComment | String | 人工复核说明(人工改判时有值) |
|
||||
| createTime | String | 记录时间 yyyy-MM-dd HH:mm:ss |
|
||||
|
||||
## 2. 人工复核修改检测结果
|
||||
|
||||
- **URL**:`POST /file_security/review`
|
||||
- **权限**:平台超管(全部)/ 机构管理员(仅本机构用户的文件)
|
||||
- **Content-Type**:`application/json`
|
||||
- **参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| fileCode | String | 是 | 文件编码 |
|
||||
| decision | String | 是 | 复核结论:`pass` 通过 / `reject` 不通过 |
|
||||
| comment | String | 是 | 复核说明(审计要求,必填),例如"经人工查验为误报" |
|
||||
|
||||
- **返回**:`AlitaResult<FileSecurityDTO>`(本次复核生成的最新检测记录)
|
||||
- **行为**:新增一条检测记录留痕并立即生效;若该文件此前有 MeshSafe 任务 ID,会尽力回写 MeshSafe 人工复核接口(失败仅告警)
|
||||
|
||||
**错误提示**:无检测记录、复核结论不合法、无操作权限、非本机构文件时返回 `success=false` 并附 `msg`。
|
||||
|
||||
## 3. 分享/传输预检
|
||||
|
||||
- **URL**:`GET /file_security/share_check`
|
||||
- **权限**:所有登录用户(校验本人文件)
|
||||
- **参数**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| userFileId | Long | 是 | 用户文件ID(普通文件/文件夹) |
|
||||
|
||||
- **返回**:`AlitaResult<FileSecurityShareCheckDTO>`
|
||||
|
||||
**FileSecurityShareCheckDTO 字段**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| fileType | String | 1文件/2文件夹 |
|
||||
| totalFileCount | Integer | 目录内文件总数(不含目录) |
|
||||
| blockedCount | Integer | 检测不通过数量 |
|
||||
| scanningCount | Integer | 检测中数量 |
|
||||
| failedCount | Integer | 检测失败数量 |
|
||||
| blockedFileNames | List<String> | 检测不通过文件名 |
|
||||
| scanningFileNames | List<String> | 检测中文件名 |
|
||||
| failedFileNames | List<String> | 检测失败文件名 |
|
||||
| excludeFileIds | List<Long> | 确认跳过时需排除的用户文件ID |
|
||||
|
||||
**前端用法**:文件夹分享/传输前调用,`excludeFileIds` 非空时弹窗展示文件清单,用户确认后分享/传输请求带 `onlyPassed=true`。
|
||||
|
||||
## 4. 分享/传输的 onlyPassed 参数
|
||||
|
||||
`POST /share_user_file/create`、`POST /transfer_user_file/create` 请求体新增可选字段:
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| onlyPassed | Boolean | 文件夹内存在检测未通过文件且用户已确认跳过时传 `true`,仅复制/传输检测通过的内容;缺省或 `false` 时维持整体拦截 |
|
||||
|
||||
规则:单文件检测不通过 → 无论该参数一律拦截;文件夹 + `onlyPassed=true` → 排除不通过/检测中文件;文件夹内全部被拦截 → 报错「文件夹内没有检测通过的文件,无法分享/传输」。
|
||||
|
||||
## 5. 重新提交安全检测
|
||||
|
||||
- **URL**:`POST /file_security/rescan`
|
||||
- **权限**:平台超管(全部)/ 机构管理员(仅本机构用户的文件)
|
||||
- **参数**(query):
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| fileCode | String | 是 | 文件编码 |
|
||||
|
||||
- **返回**:`AlitaResult<Boolean>`,受理成功返回 `true`(异步执行,结果通过列表/详情查询获取)
|
||||
- **行为**:新增一条"检测中"记录并异步提交 MeshSafe;该文件正在检测中时拒绝重复提交
|
||||
|
||||
---
|
||||
|
||||
## 后端对接配置(运维)
|
||||
|
||||
```yaml
|
||||
meshsafe:
|
||||
enabled: true # 总开关,false 时完全不送检、不校验
|
||||
base-url: https://meshsafe.internal
|
||||
client-id: s-file # MeshSafe 分配的调用方标识
|
||||
secret: "***" # MeshSafe 分配的密钥(请勿泄露)
|
||||
channel: default # 业务通道标识(仅用于 MeshSafe 日志追溯)
|
||||
timeout-seconds: 120 # 单次请求超时
|
||||
fail-open: true # 检测服务异常时是否放行(true仅标记检测失败)
|
||||
poll-timeout-minutes: 60 # 检测中记录超时时长,超时标记检测失败
|
||||
review-sync-to-meshsafe: true # 人工复核是否回写 MeshSafe
|
||||
poll:
|
||||
cron: "0 */30 * * * ?" # 轮询未完成检测任务的定时任务(默认30秒)
|
||||
```
|
||||
|
||||
数据表:`x_file_security`(见 `db/upgrade-20261101-meshsafe-file-security.sql`)。
|
||||
|
||||
送检触发点:
|
||||
|
||||
1. **网盘上传**:`POST /normal_user_file/create` 落库后异步送检;
|
||||
2. **跨网传输接收**:传输解包定时任务创建文件记录后逐个异步送检(保护"外传内"方向);
|
||||
3. **手动重新检测**:上述 `/file_security/rescan`。
|
||||
|
||||
分享/传输入口强校验:`POST /share_user_file/create`、`POST /transfer_user_file/create` 均校验文件及子文件的最新检测状态,不通过/检测中直接拒绝,`msg` 会带上具体文件名与原因。
|
||||
181
backend/docs/接口文档-机构上传配置.md
Normal file
181
backend/docs/接口文档-机构上传配置.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# 前端接口文档 —— 机构上传配置(文件上传类型/大小限制)
|
||||
|
||||
> 版本:v1.0(2026-09-14)
|
||||
> 适用后端:s-file(alita-bjy-uygur)
|
||||
> Swagger 分组:「机构上传配置」
|
||||
|
||||
## 功能说明
|
||||
|
||||
机构管理员可以为本机构配置网盘上传策略:
|
||||
|
||||
- **上传总开关**:关闭后本机构用户不能上传任何文件到网盘;
|
||||
- **类型白名单**:可限制只允许上传指定后缀的文件(如 `doc、docx、pdf、jpg`);
|
||||
- **单文件大小上限**:单位 MB,超过即拒绝。
|
||||
|
||||
规则约定:
|
||||
|
||||
- 配置**按机构隔离**,作用于当前登录机构,管理的是本机构用户的**网盘上传**(`normal_user_file/create` 落库时强制校验);
|
||||
- **未配置过的机构 = 默认策略**:允许上传、允许全部类型、不限大小(对现有用户完全兼容);
|
||||
- 个人用户(无登录机构)不受机构配置限制;
|
||||
- 后缀不区分大小写,配置时可带可不带前导点(`PDF`、`.pdf`、`pdf` 等价),服务端统一转小写存储;
|
||||
- 文件夹创建不受类型/大小校验影响;
|
||||
- 文件**传输(发送/接收)与分享不受此配置影响**,该配置只管上传。
|
||||
|
||||
## 通用约定
|
||||
|
||||
与既有接口一致:登录态调用;统一返回 `AlitaResult<T>`(`code="200"` 且 `success=true` 成功;业务失败 `success=false`,`msg` 为提示文案);时间格式 `yyyy-MM-dd HH:mm:ss`。
|
||||
|
||||
---
|
||||
|
||||
## 1. 查询当前机构的上传配置
|
||||
|
||||
- **URL**:`GET /upload_config/info`
|
||||
- **权限**:所有登录用户(前端可在上传前读取并据此过滤可选文件类型)
|
||||
- **参数**:无(按当前登录机构查询)
|
||||
- **返回**:`AlitaResult<UploadConfigDTO>`
|
||||
|
||||
**UploadConfigDTO 字段**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| orgId | Long | 机构 id |
|
||||
| uploadEnabled | Boolean | 是否允许上传 |
|
||||
| allowAllType | Boolean | 是否允许全部类型 |
|
||||
| allowedSuffixes | List<String> | 允许的后缀(小写、不含点),`allowAllType=false` 时有值,否则为 null |
|
||||
| maxFileSize | Long | 单文件大小上限(MB),null 或 0 表示不限制 |
|
||||
| configured | Boolean | **机构是否已配置过**。`false` 表示当前是默认策略(允许上传全部类型、不限大小),前端此时可不限制文件选择器 |
|
||||
|
||||
**返回示例(已配置)**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "200",
|
||||
"success": true,
|
||||
"data": {
|
||||
"orgId": 12,
|
||||
"uploadEnabled": true,
|
||||
"allowAllType": false,
|
||||
"allowedSuffixes": ["doc", "docx", "pdf", "jpg", "png"],
|
||||
"maxFileSize": 200,
|
||||
"configured": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**返回示例(未配置过)**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "200",
|
||||
"success": true,
|
||||
"data": {
|
||||
"orgId": 12,
|
||||
"uploadEnabled": true,
|
||||
"allowAllType": true,
|
||||
"allowedSuffixes": null,
|
||||
"maxFileSize": null,
|
||||
"configured": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 保存机构的上传配置
|
||||
|
||||
Upsert:该机构无配置则新建,有则更新。
|
||||
|
||||
- **URL**:`POST /upload_config/modify`
|
||||
- **权限**:**机构管理员** 或 平台超管(其他用户返回「当前用户无操作权限」)
|
||||
- **Body**(application/json)
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| uploadEnabled | Boolean | 是 | 是否允许上传 |
|
||||
| allowAllType | Boolean | 是 | 是否允许全部类型 |
|
||||
| allowedSuffixes | List<String> | 条件必填 | `allowAllType=false` 时必填且不能为空;后缀可带可不带点,自动小写去重 |
|
||||
| maxFileSize | Long | 否 | 单文件上限(MB),null 或 0 不限制;传负数报错 |
|
||||
|
||||
**校验失败提示**
|
||||
|
||||
| 场景 | msg |
|
||||
|---|---|
|
||||
| 非机构管理员/超管 | 当前用户无操作权限 |
|
||||
| 白名单为空 | 未允许全部类型时,允许的文件后缀不能为空 |
|
||||
| maxFileSize ≤ 0 | 单文件大小上限必须大于0 |
|
||||
| 并发修改 | 上传配置正在修改,请稍后重试 |
|
||||
|
||||
**请求示例(只允许文档和图片,单个最大 200MB)**
|
||||
|
||||
```json
|
||||
POST /upload_config/modify
|
||||
{
|
||||
"uploadEnabled": true,
|
||||
"allowAllType": false,
|
||||
"allowedSuffixes": ["doc", "docx", "xls", "xlsx", "pdf", "jpg", "png"],
|
||||
"maxFileSize": 200
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例(禁止本机构上传)**
|
||||
|
||||
```json
|
||||
POST /upload_config/modify
|
||||
{
|
||||
"uploadEnabled": false,
|
||||
"allowAllType": true
|
||||
}
|
||||
```
|
||||
|
||||
**返回示例**
|
||||
|
||||
```json
|
||||
{ "code": "200", "success": true, "data": true }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 上传预检(可选,提升体验)
|
||||
|
||||
按文件名/大小对当前机构配置做一次预检,用于**选择文件后、真正上传前**给出友好提示,避免白传一遍物理文件才被拒绝。
|
||||
|
||||
- **URL**:`GET /upload_config/check`
|
||||
- **权限**:所有登录用户
|
||||
- **参数**(URL query)
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| fileName | String | 是 | 完整文件名(含后缀) |
|
||||
| fileSize | Long | 否 | 文件大小(字节),不传则跳过大小校验 |
|
||||
|
||||
- **返回**:通过时 `{"code":"200","success":true,"data":true}`;不通过时 `success=false`,`msg` 说明原因(与真实上传被拦截时的文案一致)。
|
||||
|
||||
**不通过返回示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "700",
|
||||
"success": false,
|
||||
"msg": "仅允许上传后缀为 doc、docx、pdf、jpg、png 的文件",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 上传被拦截时的服务端行为
|
||||
|
||||
用户上传最终在网盘创建记录接口(`POST /normal_user_file/create`)被强制校验,不通过时接口返回 `success=false`,`msg` 为以下之一:
|
||||
|
||||
| 场景 | msg |
|
||||
|---|---|
|
||||
| 机构关闭上传 | 当前机构已禁止上传文件,请联系机构管理员 |
|
||||
| 后缀不在白名单 | 仅允许上传后缀为 doc、docx、pdf 的文件(动态列出) |
|
||||
| 机构白名单为空(异常配置) | 当前机构未配置可上传的文件类型,请联系机构管理员 |
|
||||
| 超过大小上限 | 上传文件过大,单文件最大不得超过200MB(动态数值) |
|
||||
|
||||
**前端建议**:
|
||||
|
||||
1. 上传页加载时调 `GET /upload_config/info`:`uploadEnabled=false` 时直接隐藏/禁用上传入口;`allowAllType=false` 时设置文件选择器的 accept 过滤;
|
||||
2. 选择文件后可先调 `GET /upload_config/check` 预检(可选);
|
||||
3. 真实上传两步(`/file/upload` → `/normal_user_file/create`)中第二步失败时直接 toast `msg`。
|
||||
498
backend/docs/接口文档-用户管理与记录查询.md
Normal file
498
backend/docs/接口文档-用户管理与记录查询.md
Normal file
@@ -0,0 +1,498 @@
|
||||
# 前端接口文档 —— 用户管理增强 & 机构账号查询 & 按手机号查询文件记录
|
||||
|
||||
> 版本:v1.2(2026-10-27,新增 1.4 修改用户姓名、1.5 批量创建机构用户)
|
||||
> 适用后端:s-file(alita-bjy-uygur)
|
||||
> Swagger 分组:「用户」「用户文件」
|
||||
|
||||
---
|
||||
|
||||
## 0. 通用约定
|
||||
|
||||
### 0.1 请求与认证
|
||||
|
||||
- 所有接口均需**登录后调用**,认证 token 按现有方式携带(header 或 cookie,与系统其他接口一致)。
|
||||
- 新增接口均要求**平台管理员(超管)身份**(`uc_user.supeiror = true`),非管理员调用统一返回失败,提示「当前用户无操作权限」。
|
||||
|
||||
### 0.2 统一返回结构 `AlitaResult<T>`
|
||||
|
||||
```json
|
||||
{
|
||||
"requestId": "xxx",
|
||||
"code": "200",
|
||||
"success": true,
|
||||
"msg": null,
|
||||
"data": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| code | `"200"` 成功;业务失败时 success=false,msg 为错误提示(如「不能禁用当前登录账号」) |
|
||||
| success | 是否成功 |
|
||||
| data | 业务数据,失败时为 null |
|
||||
|
||||
### 0.3 分页请求与返回
|
||||
|
||||
分页查询接口通过 **URL query 参数** 传递分页信息,返回结构 `Page<T>`:
|
||||
|
||||
```
|
||||
pageNo 第几页,默认 1
|
||||
pageSize 页宽,默认 20,上限 1000
|
||||
needPage 是否分页,默认 true(无需传)
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"pageNo": 1,
|
||||
"pageSize": 20,
|
||||
"pageCount": 5,
|
||||
"totalCount": 88,
|
||||
"entityList": [ ... ]
|
||||
}
|
||||
```
|
||||
|
||||
### 0.4 通用枚举(前后端共用)
|
||||
|
||||
**用户状态 `status`**
|
||||
|
||||
| 值 | 含义 |
|
||||
|---|---|
|
||||
| 1 | 待激活 |
|
||||
| 2 | 已激活 |
|
||||
| 3 | 已注销 |
|
||||
|
||||
**是否启用 `enabled`**:`true` 启用,`false` 禁用。
|
||||
|
||||
**文件状态 `fileStatus`**
|
||||
|
||||
| 值 | 含义 |
|
||||
|---|---|
|
||||
| 1 | 正常 |
|
||||
| 2 | 失效 |
|
||||
| 11 | 解析中 |
|
||||
| 12 | 待审核 |
|
||||
| 13 | 传输中 |
|
||||
| 14 | 失败 |
|
||||
|
||||
**记录类型 `recordType`**(本文档 3.4 节专用)
|
||||
|
||||
| 值 | 含义 |
|
||||
|---|---|
|
||||
| 1 | 上传记录 |
|
||||
| 2 | 发送记录(本端发起文件传输) |
|
||||
| 3 | 接收记录(本端接收文件传输) |
|
||||
|
||||
> 发送/接收与部署侧有关,但**前端无需关心**:传入 `recordType` 后,后端按部署配置(内网/外网)自动映射到对应的 `root_type`;返回列表中每条记录也已回填 `recordType` / `recordTypeDesc`。
|
||||
|
||||
---
|
||||
|
||||
## 1. 用户管理:启用 / 禁用 / 注销
|
||||
|
||||
### 1.1 启用用户
|
||||
|
||||
将被禁用的账号重新启用(幂等:已启用的账号重复调用仍返回成功)。
|
||||
|
||||
- **URL**:`POST /user/enable`
|
||||
- **权限**:平台管理员
|
||||
- **Body**(application/json)
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| userId | Long | 是 | 目标用户 id |
|
||||
|
||||
- **返回**:`AlitaResult<Boolean>`,data 为 `true`
|
||||
- **失败场景**:用户不存在;账号已注销(提示「已注销账号不可启用」);非管理员
|
||||
|
||||
**请求示例**
|
||||
|
||||
```json
|
||||
POST /user/enable
|
||||
{ "userId": 10086 }
|
||||
```
|
||||
|
||||
**返回示例**
|
||||
|
||||
```json
|
||||
{ "code": "200", "success": true, "data": true }
|
||||
```
|
||||
|
||||
### 1.2 禁用用户
|
||||
|
||||
禁用账号,禁用后该用户无法登录/使用系统(后端登录态校验会实时读取启用状态,被禁用用户下次请求即被拒绝)。幂等:已禁用的账号重复调用仍返回成功。
|
||||
|
||||
- **URL**:`POST /user/disable`
|
||||
- **权限**:平台管理员
|
||||
- **Body**(application/json)
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| userId | Long | 是 | 目标用户 id |
|
||||
|
||||
- **返回**:`AlitaResult<Boolean>`,data 为 `true`
|
||||
- **失败场景**:
|
||||
- 用户不存在
|
||||
- `userId` 为当前登录人(提示「不能禁用当前登录账号」)
|
||||
- 目标是超级管理员(提示「不能禁用超级管理员账号」)
|
||||
- 目标已注销(提示「已注销账号无需禁用」)
|
||||
|
||||
**请求示例**
|
||||
|
||||
```json
|
||||
POST /user/disable
|
||||
{ "userId": 10086 }
|
||||
```
|
||||
|
||||
### 1.3 注销用户(管理员直接注销)
|
||||
|
||||
管理员将账号直接置为**已注销**(status=3)并同步禁用(enabled=false),**不进入注销申请审批流程**。注销后账号不可恢复、不可登录。幂等:已注销账号重复调用返回成功。
|
||||
|
||||
- **URL**:`POST /user/admin_cancel`
|
||||
- **权限**:平台管理员
|
||||
- **Body**(application/json)
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| userId | Long | 是 | 目标用户 id |
|
||||
|
||||
- **返回**:`AlitaResult<Void>`,data 为 null
|
||||
- **失败场景**:
|
||||
- 用户不存在
|
||||
- `userId` 为当前登录人(提示「不能注销当前登录账号」)
|
||||
- 目标是超级管理员(提示「不能注销超级管理员账号」)
|
||||
- 并发重复操作(提示「该用户正在注销,请勿重复操作」)
|
||||
|
||||
**请求示例**
|
||||
|
||||
```json
|
||||
POST /user/admin_cancel
|
||||
{ "userId": 10086 }
|
||||
```
|
||||
|
||||
> **与既有接口的区别**:既有 `POST /user/cancel` 为机构内注销(限定当前登录机构、且要求目标账号启用中),供原「注销申请/同意」流程使用,保持不变;本接口供系统管理员在用户管理页面跨机构直接注销。
|
||||
|
||||
### 1.4 修改用户姓名
|
||||
|
||||
修改用户账号的真实姓名(`real_name`)。平台超管可修改全部用户;机构管理员仅可修改本机构用户(同一账号属于多个机构时,改的是账号本身,各机构下展示同步生效)。
|
||||
|
||||
- **URL**:`POST /user/modify_real_name`
|
||||
- **权限**:平台超管(全部用户)/ 机构管理员(限本机构用户)
|
||||
- **Body**(application/json)
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| userId | Long | 是 | 目标用户 id |
|
||||
| realName | String | 是 | 新姓名,2-30 位,仅支持中文或英文姓名 |
|
||||
|
||||
- **返回**:`AlitaResult<Boolean>`,data 为 `true`
|
||||
- **失败场景**:
|
||||
- 用户不存在
|
||||
- 目标已注销(提示「已注销账号不可修改姓名」)
|
||||
- 姓名格式错误(提示「姓名格式错误!(长度2-30位,仅支持中文或英文姓名)」)
|
||||
- 与原姓名一致(提示「姓名与原姓名一致,无需修改!」)
|
||||
- 机构管理员操作非本机构用户(提示「只能操作本机构的用户」)
|
||||
|
||||
**请求示例**
|
||||
|
||||
```json
|
||||
POST /user/modify_real_name
|
||||
{ "userId": 10086, "realName": "张三" }
|
||||
```
|
||||
|
||||
**返回示例**
|
||||
|
||||
```json
|
||||
{ "code": "200", "success": true, "data": true }
|
||||
```
|
||||
|
||||
> **与既有接口的区别**:既有 `POST /user/modify_org_user_info` 为超管编辑企业用户完整信息(账号名/姓名/启用状态一起校验更新);本接口仅更新姓名,供用户管理页与机构账号页的「修改姓名」入口使用。
|
||||
|
||||
### 1.5 批量创建机构用户
|
||||
|
||||
一次性提交多条「手机号 + 姓名」创建机构用户。**单条失败不影响其他条目**(每条独立事务),返回与入参**顺序一一对应**的逐条结果,前端据此展示失败项。
|
||||
|
||||
- **URL**:`POST /user/batch_create_org_user`
|
||||
- **权限**:登录用户(机构取当前登录机构,与单条创建 `POST /user/create_org_user` 一致)
|
||||
- **Body**(application/json,对象数组,单次最多 **500** 条)
|
||||
|
||||
数组元素字段:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| phone | String | 是 | 手机号 |
|
||||
| realName | String | 否 | 姓名 |
|
||||
|
||||
- **返回**:`AlitaResult<List<OrgUserCreateResultDTO>>`
|
||||
|
||||
**OrgUserCreateResultDTO 字段**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| phone | String | 手机号(回显入参) |
|
||||
| realName | String | 姓名(回显入参) |
|
||||
| success | Boolean | 是否创建成功 |
|
||||
| userId | Long | 用户 id,失败时为 null |
|
||||
| msg | String | 失败原因,成功时为 null |
|
||||
|
||||
**请求示例**
|
||||
|
||||
```json
|
||||
POST /user/batch_create_org_user
|
||||
[
|
||||
{ "phone": "13800000001", "realName": "张三" },
|
||||
{ "phone": "13800000002", "realName": "李四" }
|
||||
]
|
||||
```
|
||||
|
||||
**返回示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "200",
|
||||
"success": true,
|
||||
"data": [
|
||||
{ "phone": "13800000001", "realName": "张三", "success": true, "userId": 1001, "msg": null },
|
||||
{ "phone": "13800000002", "realName": "李四", "success": false, "userId": null, "msg": "手机号已存在,不能重复添加" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **失败场景**(整体请求失败):列表为空;超过 500 条(提示「单次批量创建不能超过500条」)。单条业务失败(手机号格式不正确、手机号已存在等)不使请求失败,体现在对应条目的 `success/msg` 中。
|
||||
|
||||
---
|
||||
|
||||
## 2. 系统管理员:查看各机构下的账号信息
|
||||
|
||||
### 2.1 分页查询机构账号
|
||||
|
||||
按机构维度查看账号信息。一行 = 一个机构下的一个账号(同一账号属于多个机构时会出现多行)。不传 `orgId` 时查询全部机构,传 `orgId` 时查看指定机构。
|
||||
|
||||
- **URL**:`GET /user/page/org_account`
|
||||
- **权限**:平台管理员
|
||||
- **参数**(URL query)
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| orgId | Long | 否 | 机构 id,不传查全部机构 |
|
||||
| orgNameLike | String | 否 | 机构名称,模糊匹配 |
|
||||
| usernameLike | String | 否 | 账号,模糊匹配 |
|
||||
| realNameLike | String | 否 | 真实姓名,模糊匹配 |
|
||||
| phoneLike | String | 否 | 手机号,模糊匹配 |
|
||||
| enabled | Boolean | 否 | 是否启用:true/false |
|
||||
| status | Integer | 否 | 用户状态:1 待激活 / 2 已激活 / 3 已注销 |
|
||||
| createTimeStart | String | 否 | 创建时间起,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| createTimeEnd | String | 否 | 创建时间止,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| pageNo / pageSize | Integer | 否 | 分页参数,默认 1 / 20 |
|
||||
|
||||
- **返回**:`AlitaResult<Page<OrgAccountDTO>>`
|
||||
|
||||
**OrgAccountDTO 字段**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| orgId | Long | 机构 id |
|
||||
| orgName | String | 机构名称 |
|
||||
| administrator | Boolean | 是否该机构的管理员 |
|
||||
| userId | Long | 用户 id(可传给 1.1/1.2/1.3 接口做启停/注销操作) |
|
||||
| username | String | 账号 |
|
||||
| nickname | String | 昵称 |
|
||||
| realName | String | 真实姓名 |
|
||||
| phone | String | 手机号 |
|
||||
| email | String | 邮箱 |
|
||||
| userType | Integer | 用户类型:1 企业用户 / 2 个人用户 |
|
||||
| enabled | Boolean | 是否启用 |
|
||||
| status | Integer | 状态:1 待激活 / 2 已激活 / 3 已注销 |
|
||||
| createTime | String | 创建时间 `yyyy-MM-dd HH:mm:ss` |
|
||||
|
||||
- **排序规则**:机构 id 升序,机构管理员排前面,同机构内按用户 id 倒序。
|
||||
|
||||
**请求示例**
|
||||
|
||||
```
|
||||
GET /user/page/org_account?orgNameLike=教育&phoneLike=138&enabled=false&pageNo=1&pageSize=20
|
||||
```
|
||||
|
||||
**返回示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "200",
|
||||
"success": true,
|
||||
"data": {
|
||||
"pageNo": 1,
|
||||
"pageSize": 20,
|
||||
"pageCount": 1,
|
||||
"totalCount": 2,
|
||||
"entityList": [
|
||||
{
|
||||
"orgId": 12,
|
||||
"orgName": "XX市教育局",
|
||||
"administrator": true,
|
||||
"userId": 1001,
|
||||
"username": "admin_xx",
|
||||
"nickname": "张三",
|
||||
"realName": "张三",
|
||||
"phone": "13800000001",
|
||||
"email": "zhang@xx.gov.cn",
|
||||
"userType": 1,
|
||||
"enabled": false,
|
||||
"status": 2,
|
||||
"createTime": "2026-08-01 10:00:00"
|
||||
},
|
||||
{
|
||||
"orgId": 12,
|
||||
"orgName": "XX市教育局",
|
||||
"administrator": false,
|
||||
"userId": 1002,
|
||||
"username": "user_xx01",
|
||||
"nickname": "李四",
|
||||
"realName": "李四",
|
||||
"phone": "13800000002",
|
||||
"email": null,
|
||||
"userType": 1,
|
||||
"enabled": true,
|
||||
"status": 2,
|
||||
"createTime": "2026-08-03 14:30:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 按手机号查询文件记录(上传 / 发送 / 接收)
|
||||
|
||||
### 3.1 分页查询
|
||||
|
||||
输入手机号,精确匹配该手机号名下(可能是多个账号)的全部文件记录,可按记录类型筛选。
|
||||
|
||||
- **URL**:`GET /user_file/record/page_by_phone`
|
||||
- **权限**:平台管理员
|
||||
- **参数**(URL query)
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| phone | String | **是** | 用户手机号,精确匹配,同一手机号关联的所有账号均会命中 |
|
||||
| recordType | Integer | 否 | 1 上传记录 / 2 发送记录 / 3 接收记录,**不传查全部三类** |
|
||||
| fileNameLike | String | 否 | 文件名,模糊匹配 |
|
||||
| fileType | String | 否 | 文件类型:`1` 文件 / `2` 文件夹 |
|
||||
| fileStatus | String | 否 | 文件状态:1 正常 / 2 失效 / 11 解析中 / 12 待审核 / 13 传输中 / 14 失败 |
|
||||
| createTimeBegin | String | 否 | 记录时间起,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| createTimeEnd | String | 否 | 记录时间止,格式 `yyyy-MM-dd HH:mm:ss` |
|
||||
| pageNo / pageSize | Integer | 否 | 分页参数,默认 1 / 20 |
|
||||
|
||||
- **返回**:`AlitaResult<Page<UserFileRecordDTO>>`
|
||||
|
||||
**UserFileRecordDTO 字段**(继承用户文件全部字段,以下为主要字段)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| id | Long | 记录 id |
|
||||
| phone | String | 归属手机号 |
|
||||
| username | String | 归属账号 |
|
||||
| realName | String | 归属真实姓名 |
|
||||
| userType | Integer | 归属用户类型:1 企业用户 / 2 个人用户 |
|
||||
| recordType | Integer | 记录类型:1 上传 / 2 发送 / 3 接收 |
|
||||
| recordTypeDesc | String | 记录类型描述(「上传记录」「发送记录」「接收记录」) |
|
||||
| userId | Long | 归属用户 id |
|
||||
| rootType | String | 原始根类型:1 普通 / 2 内穿外 / 3 外传内(调试用,前端一般用 recordType 即可) |
|
||||
| fileType | String | 文件大类:1 文件 / 2 文件夹 |
|
||||
| fileName | String | 文件名 |
|
||||
| fileCode | String | 物理文件编码 |
|
||||
| fileSize | Long | 文件大小(字节) |
|
||||
| fileStatus | String | 文件状态:1 正常 / 2 失效 / 11 解析中 / 12 待审核 / 13 传输中 / 14 失败 |
|
||||
| createTime | String | 记录时间(上传/发送/接收发起时间)`yyyy-MM-dd HH:mm:ss` |
|
||||
| endTime | String | 有效期(分享类记录使用),可能为 null |
|
||||
| fileCategory | String | 文件种类(文档/图片/视频等,按后缀归类) |
|
||||
| maxRiskLevel | String | 最高风险等级:1 重度 / 2 中度 / 3 轻度,可能为 null |
|
||||
|
||||
- **排序**:记录 id 倒序(即时间倒序)。
|
||||
- **失败场景**:phone 为空;recordType 非法(提示「记录类型不合法,仅支持1上传/2发送/3接收」);非管理员。
|
||||
|
||||
**请求示例**
|
||||
|
||||
```
|
||||
GET /user_file/record/page_by_phone?phone=13800000001&recordType=2&pageNo=1&pageSize=10
|
||||
```
|
||||
|
||||
**返回示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "200",
|
||||
"success": true,
|
||||
"data": {
|
||||
"pageNo": 1,
|
||||
"pageSize": 10,
|
||||
"pageCount": 1,
|
||||
"totalCount": 2,
|
||||
"entityList": [
|
||||
{
|
||||
"id": 9001,
|
||||
"phone": "13800000001",
|
||||
"username": "user_xx01",
|
||||
"realName": "李四",
|
||||
"userType": 1,
|
||||
"recordType": 2,
|
||||
"recordTypeDesc": "发送记录",
|
||||
"userId": 1002,
|
||||
"rootType": "2",
|
||||
"fileType": "2",
|
||||
"fileName": "2026年预算材料_20260912103000",
|
||||
"fileCode": null,
|
||||
"fileSize": null,
|
||||
"fileStatus": "1",
|
||||
"createTime": "2026-09-12 10:30:00",
|
||||
"endTime": null,
|
||||
"fileCategory": null,
|
||||
"maxRiskLevel": null
|
||||
},
|
||||
{
|
||||
"id": 8802,
|
||||
"phone": "13800000001",
|
||||
"username": "user_xx01",
|
||||
"realName": "李四",
|
||||
"userType": 1,
|
||||
"recordType": 1,
|
||||
"recordTypeDesc": "上传记录",
|
||||
"userId": 1002,
|
||||
"rootType": "1",
|
||||
"fileType": "1",
|
||||
"fileName": "工作总结.docx",
|
||||
"fileCode": "fc_3a7d92",
|
||||
"fileSize": 1048576,
|
||||
"fileStatus": "1",
|
||||
"createTime": "2026-09-10 09:12:00",
|
||||
"endTime": null,
|
||||
"fileCategory": "文档",
|
||||
"maxRiskLevel": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **记录类型口径说明**(便于前端理解,无需处理):
|
||||
> - 上传记录:用户在网盘中上传的文件/文件夹(rootType=1)。
|
||||
> - 发送记录:用户发起的文件传输(文件夹整体外发,传输中状态为 13,完成后变 1,失败为 14)。
|
||||
> - 接收记录:对端传来的文件在本端解包后归属到该手机号用户的记录(若开启风险审查,先为 12 待审核,通过后为 1)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 错误处理建议
|
||||
|
||||
前端统一按 `success=false` 判定失败并 toast `msg`;特殊 code 沿用系统既有约定(`401` 未登录等,与现有接口一致)。
|
||||
|
||||
涉及本批接口的典型业务错误文案:
|
||||
|
||||
| 场景 | msg |
|
||||
|---|---|
|
||||
| 非平台管理员操作 | 当前用户无操作权限 |
|
||||
| 启用已注销账号 | 已注销账号不可启用 |
|
||||
| 禁用/注销自己 | 不能禁用当前登录账号 / 不能注销当前登录账号 |
|
||||
| 禁用/注销超管 | 不能禁用超级管理员账号 / 不能注销超级管理员账号 |
|
||||
| recordType 非法 | 记录类型不合法,仅支持1上传/2发送/3接收 |
|
||||
| phone 为空 | 手机号不能为空 |
|
||||
| 修改姓名格式错误 | 姓名格式错误!(长度2-30位,仅支持中文或英文姓名) |
|
||||
| 修改姓名与原值相同 | 姓名与原姓名一致,无需修改! |
|
||||
| 机构管理员越机构操作 | 只能操作本机构的用户 |
|
||||
220
backend/docs/生产部署说明.md
Normal file
220
backend/docs/生产部署说明.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# s-file 生产部署 / 上线说明
|
||||
|
||||
> 适用版本:2026-09-14(含用户启停/注销、机构账号查询、手机号查记录、机构上传配置、禁用踢下线黑名单)
|
||||
> 构建产物:`target/s-file.jar`(Spring Boot 可执行 fat jar,需 JDK 8)
|
||||
|
||||
---
|
||||
|
||||
## 0. 上线前必须处理(阻断项)
|
||||
|
||||
### 0.1 替换 spire.pdf 占位 jar ⚠️
|
||||
|
||||
本机构开发机的 `~/.m2/repository/e-iceblue/spire.pdf/3.7.4/` 中目前安装的是**编译级 stub(空实现)jar**——它能保证编译打包通过,但 **PDF 盖章功能(`PdfUtils.pdfStampPerPage`)运行时必然失败**。
|
||||
|
||||
上线前必须:
|
||||
|
||||
1. 从公司私服获取真实的 `spire.pdf-3.7.4.jar`(阿里云与 e-iceblue 官方仓库均无此版本);
|
||||
2. 覆盖 `~/.m2/repository/e-iceblue/spire.pdf/3.7.4/spire.pdf-3.7.4.jar`(pom 同步覆盖);
|
||||
3. 重新 `mvn clean package -DskipTests`;
|
||||
4. 验证:解包 jar 中 `com/spire/pdf/PdfDocument.class` 应为真实实现(stub 版仅 300~500 字节)。
|
||||
|
||||
> 若该业务暂时不用 PDF 盖章,可带 stub 上线,但需明确告知团队。
|
||||
|
||||
### 0.2 提供生产配置文件
|
||||
|
||||
**仓库内不含任何运行配置**(application.yml 是外置的)。启动时通过 `spring.config.additional-location` 挂载。生产模板如下(按实际环境修改):
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: s-file
|
||||
datasource:
|
||||
# 生产为达梦 DM8;驱动 com.dameng.jdbc.driver(pom 已含依赖)
|
||||
url: jdbc:dm://<DB_HOST>:5236/S_FILE
|
||||
driver-class-name: com.dameng.jdbc.driver
|
||||
username: <DB_USER>
|
||||
password: <DB_PASSWORD>
|
||||
druid:
|
||||
initial-size: 5
|
||||
min-idle: 5
|
||||
max-active: 20
|
||||
validation-query: SELECT 1
|
||||
redis: # 必须可用:登录缓存 + 禁用/注销踢下线黑名单依赖 Redis
|
||||
host: <REDIS_HOST>
|
||||
port: 6379
|
||||
timeout: 5000
|
||||
rabbitmq:
|
||||
host: <MQ_HOST>
|
||||
port: 5672
|
||||
username: <MQ_USER>
|
||||
password: <MQ_PASSWORD>
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 500MB # 按网盘需求调整
|
||||
max-request-size: 500MB
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:com/senmesh/**/mapper/*.xml # 必须有,否则自定义 XML 的接口全部报 Invalid bound statement
|
||||
|
||||
system:
|
||||
code: 1
|
||||
|
||||
# 发号器雪花 workerId:键格式 code.snowflake.workid.<本机IP>
|
||||
# 单机部署:直接写死本机 IP 的键;多机部署:每台机器一个键、值必须不同且 0~31
|
||||
"[code.snowflake.workid.192.168.1.10]": 1
|
||||
|
||||
security:
|
||||
jwt:
|
||||
enable: true
|
||||
secret: <上线前必须换成 64 字节以上随机串,勿用文档示例>
|
||||
expiration: 86400 # token 有效期(秒)
|
||||
header: Authorization
|
||||
ignoring: /error,/favicon.ico,/v2/api-docs,/swagger-resources/**
|
||||
login-page: /login
|
||||
superior-only: false # true=仅平台机构用户可登录系统;按部署侧实际选择
|
||||
use-local: true
|
||||
allow-if-all-abstain-decisions: true
|
||||
oauth2:
|
||||
enable: false
|
||||
anonymous: "" # 匿名白名单按需追加(逗号分隔,如 /file/upload)
|
||||
|
||||
user:
|
||||
default:
|
||||
password: <新用户初始密码>
|
||||
|
||||
file:
|
||||
upload-dir: /data/upload # 容器/主机上需存在且可写
|
||||
storage:
|
||||
type: local # 阿里云 OSS 则改 ali 并补齐 oss 配置
|
||||
transfer:
|
||||
location: "1" # 内网侧 "1",外网侧 "2" —— 两侧必须相反!
|
||||
send-dir: /data/transfer/send
|
||||
receive-dir: /data/transfer/receive
|
||||
unpack-risk-check: false
|
||||
|
||||
dify:
|
||||
risk:
|
||||
rule:
|
||||
api-key: <真实key或占位>
|
||||
scan:
|
||||
api-key: <真实key或占位> # 必填(可为占位),否则启动失败
|
||||
|
||||
rabbitmq:
|
||||
custom:
|
||||
topic-exchange-name: s-file-topic
|
||||
delayed-exchange-name: s-file-delayed
|
||||
broadcast-id: s-file-broadcast
|
||||
|
||||
swagger:
|
||||
enable: false # 生产必须 false
|
||||
```
|
||||
|
||||
### 0.3 数据库变更
|
||||
|
||||
- **已有生产库**:执行 `db/upgrade-20260914-upload-config.sql`(达梦执行「1. MySQL / 达梦 DM8」一节,建 `x_upload_config` 表)。其余本次功能无表结构变更。
|
||||
- **全新库**:先执行 `db/init-table.sql`(达梦按 MySQL 兼容模式)+ `db/init-data.sql`(平台、平台机构、admin 账号、文件种类字典)。
|
||||
- 执行后验证:`select count(*) from x_upload_config;` 无报错即建表成功(0 行为正常,未配置的机构走默认策略)。
|
||||
|
||||
### 0.4 目录与权限
|
||||
|
||||
```
|
||||
mkdir -p /data/upload /data/transfer/send /data/transfer/receive
|
||||
chown -R <运行用户> /data
|
||||
```
|
||||
|
||||
外网侧与内网侧通过 `file.transfer.location` 区分,`send-dir`/`receive-dir` 指向各自的摆渡目录(网闸/摆渡盘)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 启动方式
|
||||
|
||||
### 1.1 裸机 / 虚拟机(JDK 8)
|
||||
|
||||
```bash
|
||||
# 需 JDK 1.8(java -version 确认)
|
||||
java -Xms1g -Xmx2g \
|
||||
-Dspring.config.additional-location=file:/opt/sfile/config/application.yml \
|
||||
-jar /opt/sfile/s-file.jar \
|
||||
> /opt/sfile/logs/stdout.log 2>&1 &
|
||||
```
|
||||
|
||||
多节点时按节点 IP 配置不同的 `code.snowflake.workid.<ip>`(可用启动参数 `-DCODE_SNOWFLAKE_WORKID_<IP大写>=<n>` 环境变量方式,`_` 映射 `.`)。
|
||||
|
||||
### 1.2 Docker
|
||||
|
||||
```bash
|
||||
# 构建(context 内需 s-file.jar 与 Dockerfile,Dockerfile 在 src/main/resources/)
|
||||
cp target/s-file.jar <build-context>/ && docker build -t s-file:<版本> <build-context>
|
||||
|
||||
docker run -d --name sfile-app --restart unless-stopped -p 8080:8080 \
|
||||
-v /opt/sfile/config/application.yml:/config/application.yml:ro \
|
||||
-v /data/upload:/data/upload \
|
||||
-v /data/transfer/send:/data/transfer/send \
|
||||
-v /data/transfer/receive:/data/transfer/receive \
|
||||
-e SPRING_CONFIG_ADDITIONAL_LOCATION=file:/config/application.yml \
|
||||
-e CODE_SNOWFLAKE_WORKID_<容器IP大写下划线>=1 \
|
||||
s-file:<版本>
|
||||
```
|
||||
|
||||
> 注意:基础镜像 `freshworks/oraclejdk8` 为 amd64,arm64 主机需 `--platform linux/amd64`;workerId 键里的 IP 用容器固定 IP(自定义网络 `--ip`),容器重建 IP 不变。
|
||||
|
||||
---
|
||||
|
||||
## 2. 前端发布
|
||||
|
||||
```bash
|
||||
cd senmesh-zc-legacy
|
||||
bun install
|
||||
bun run build # 产出 dist/(.env.production 已带 VITE_APP_TITLE 引号修复,规避 Bun env 解析 bug)
|
||||
```
|
||||
|
||||
- `dist/` 交由 nginx 托管;
|
||||
- nginx 需将 `/api` 反代到后端并**去掉 `/api` 前缀**(与 vite dev 代理行为一致):
|
||||
|
||||
```nginx
|
||||
location /api/ {
|
||||
proxy_pass http://<后端IP>:8080/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
client_max_body_size 500m; # 与后端 multipart 上限匹配
|
||||
}
|
||||
```
|
||||
|
||||
- `.env.production` 当前值:`VITE_API_URL=`(空,走同域 /api)、`VITE_INIT_PASSWORD`(前端判断初始密码强制改密用)——如有调整需重新 build。
|
||||
|
||||
---
|
||||
|
||||
## 3. 本次版本功能与数据依赖清单(验收用)
|
||||
|
||||
| 功能 | 依赖 | 验收方法 |
|
||||
|---|---|---|
|
||||
| 用户启用/禁用/注销(管理员) | DB `uc_user.enabled/status`;**Redis**(禁用踢下线黑名单) | 禁用后该用户已登录 token 立即失效(302/401),重新登录被拒 |
|
||||
| 机构管理员启停本机构用户 | 无额外依赖 | 机构管理员可启停本机构用户;跨机构返回「只能操作本机构的用户」 |
|
||||
| 机构账号查询(超管) | 新增只读查询 | 管理后台-机构账号页正常出列表与筛选 |
|
||||
| 按手机号查上传/发送/接收记录 | 无额外依赖 | 输入手机号出记录;发送/接收方向由部署侧 `file.transfer.location` 决定 |
|
||||
| 机构上传配置 | 新表 `x_upload_config` | 管理后台-上传配置保存后,该机构网盘上传立即按策略拦截 |
|
||||
| 禁用踢下线 | Redis key `user:disabled:{userId}` | `redis-cli keys 'user:disabled:*'` 可见禁用标记 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 上线步骤建议
|
||||
|
||||
1. 生产库执行 0.3 升级 SQL(执行前备份);
|
||||
2. 替换真实 spire.pdf jar 并重新打包(0.1),核对 jar 时间戳;
|
||||
3. 准备 application.yml(0.2 模板),**核对 JWT secret 已更换、dify api-key 已填、swagger.enable=false**;
|
||||
4. 创建 /data 目录,确定内外网 location;
|
||||
5. 部署后端(1.1 或 1.2),观察启动日志出现 `Started SFileApplication`;
|
||||
6. 构建并发布前端(2),nginx reload;
|
||||
7. 验收第 3 节清单(至少:admin 登录、上传一个非白名单文件被拒、禁用一个测试用户并确认其会话失效、记录查询出数据);
|
||||
8. 观察日志无 `ERROR`,Redis 中 `user:disabled:*` 与登录缓存键正常。
|
||||
|
||||
## 5. 回滚
|
||||
|
||||
- 后端:保留上一版 jar/镜像,停新启旧即可(本次无破坏性 DDL/DML,`x_upload_config` 留存不影响旧版本运行);
|
||||
- 前端:nginx 指回上一版 dist 目录;
|
||||
- 数据库:无需回滚(仅新增表)。
|
||||
Reference in New Issue
Block a user