feat: 用户启停/注销、机构账号查询、手机号查记录、机构上传配置
1. 用户管理:新增启用(user/enable)/禁用(user/disable)/注销(user/admin_cancel)接口,
平台超管可操作全部用户,机构管理员限本机构;注销带分布式锁、禁自己/超管守卫
2. 禁用/注销立即踢下线:Redis黑名单(user:disabled:{uid}) + JWT过滤器拦截,
TTL与token有效期一致
3. 系统管理员查看各机构账号:GET user/page/org_account(机构/账号/手机号等筛选)
4. 按手机号查上传/发送/接收记录:GET user_file/record/page_by_phone,
发送/接收方向按部署侧 file.transfer.location 自动映射
5. 机构上传配置:新表 x_upload_config + upload_config/info|modify|check 接口,
上传总开关/后缀白名单/单文件大小限制,网盘落库时强制校验
6. 加固:UserInfoDTO.setPassword 缺省密码未配置时不再抛 rawPassword cannot be null
7. 新增 x_upload_config 建表脚本(upgrade-20260915-release.sql 为上线版)、
生产部署说明、前端接口文档×2、运维脚本 sfile-ctl.sh、AGENTS.md
This commit is contained in:
181
docs/接口文档-机构上传配置.md
Normal file
181
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`。
|
||||
407
docs/接口文档-用户管理与记录查询.md
Normal file
407
docs/接口文档-用户管理与记录查询.md
Normal file
@@ -0,0 +1,407 @@
|
||||
# 前端接口文档 —— 用户管理增强 & 机构账号查询 & 按手机号查询文件记录
|
||||
|
||||
> 版本:v1.0(2026-09-14)
|
||||
> 适用后端: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` 为机构内注销(限定当前登录机构、且要求目标账号启用中),供原「注销申请/同意」流程使用,保持不变;本接口供系统管理员在用户管理页面跨机构直接注销。
|
||||
|
||||
---
|
||||
|
||||
## 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 为空 | 手机号不能为空 |
|
||||
220
docs/生产部署说明.md
Normal file
220
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