# 认证与系统基础接口 **本文引用的文件** - [AuthController.java](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/AuthController.java) - [UserController.java](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/UserController.java) - [SystemController.java](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/SystemController.java) - [CaseInfoController.java](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/CaseInfoController.java) - [TableInfoController.java](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TableInfoController.java) - [TowerController.java](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TowerController.java) - [SysUserService.java](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java) - [LoginDTO.java](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/dto/LoginDTO.java) - [OpenCaseDTO.java](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/dto/OpenCaseDTO.java) - [UpdateCasePwdDTO.java](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/dto/UpdateCasePwdDTO.java) - [LoginVO.java](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/vo/LoginVO.java) - [UserVO.java](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/vo/UserVO.java) - [Result.java](file://ai-server/src/main/java/com/zsjz/ai/common/config/Result.java) - [AccountInUseException.java](file://ai-server/src/main/java/com/zsjz/ai/common/exception/AccountInUseException.java) - [GlobalExceptionHandler.java](file://ai-server/src/main/java/com/zsjz/ai/common/exception/GlobalExceptionHandler.java) - [SysUser.java](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/entity/SysUser.java) - [CaseInfo.java](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/entity/CaseInfo.java) - [TableInfo.java](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/entity/TableInfo.java) - [SystemInfo.java](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/entity/SystemInfo.java) - [authApi.ts](file://ai-electron/frontend/src/core/api/auth/authApi.ts) - [account.ts](file://ai-electron/frontend/src/core/store/modules/account.ts) - [user.sql](file://sql/user.sql) - [plat.sql](file://sql/plat.sql) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细接口说明](#详细接口说明) 6. [依赖关系分析](#依赖关系分析) 7. [性能与分页特性](#性能与分页特性) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) ## 简介 本文面向前端和后端维护者,系统性梳理 plat 模块下 `/auth`、`/user`、`/sys`、`/case`、`/tpl`、`/tower` 等控制器的 API 契约。重点包括: - 登录流程:前端如何调用 `/auth/login` 获取 token,后续请求如何携带 `x-token`,Sa-Token 与会话活跃检测如何配合完成“账号占用确认”“强制登录”“顶下线”。 - 用户信息、授权单元、系统信息、案件数据源、表模板、塔台基站的 HTTP 方法、URL、请求参数、响应字段、分页格式和错误码。 - 登录失效、账号被顶下线、管理员重置密码或强制下线时,前端缓存清理和状态恢复行为。 - curl 与前端调用示例,以及 401、403、503 等错误的排查步骤。 ## 项目结构 plat 模块采用典型的 Spring MVC 分层:控制器负责路由和统一响应包装,服务层封装认证、用户管理、案件、系统和塔台业务,实体类描述数据库模型,DTO/VO 描述接口入参出参。 ```mermaid graph TB Client["前端页面
浏览器 / Electron"] --> Gateway["网关 / 反向代理"] Gateway --> AuthCtl["/auth 认证控制器"] Gateway --> UserCtl["/user 用户管理控制器"] Gateway --> SysCtl["/sys 系统控制器"] Gateway --> CaseCtl["/case 案件控制器"] Gateway --> TplCtl["/tpl 表模板控制器"] Gateway --> TowerCtl["/tower 塔台控制器"] AuthCtl --> SysSvc["SysUserService
认证 + 用户管理"] UserCtl --> SysSvc CaseCtl --> CaseSvc["CaseInfoService"] TplCtl --> TableSvc["TableInfoService"] TowerCtl --> TowerSvc["TowerService"] SysCtl --> SystemSvc["SystemService"] SysCtl --> HealthSvc["SystemHealthService"] SysSvc --> Stp["Sa-Token
会话与 Token"] SysSvc --> DsReg["CaseDataSourceRegistry
案件数据源注册表"] SysSvc --> Cache["CaseDataCache
案件数据缓存"] ``` **图示来源** - [AuthController.java:1-82](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/AuthController.java#L1-L82) - [UserController.java:1-89](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/UserController.java#L1-L89) - [SystemController.java:1-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/SystemController.java#L1-L200) - [CaseInfoController.java:1-117](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/CaseInfoController.java#L1-L117) - [TableInfoController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TableInfoController.java#L1-L91) - [TowerController.java:1-54](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TowerController.java#L1-L54) - [SysUserService.java:1-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L1-L200) **章节来源** - [AuthController.java:1-82](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/AuthController.java#L1-L82) - [UserController.java:1-89](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/UserController.java#L1-L89) - [SystemController.java:1-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/SystemController.java#L1-L200) - [CaseInfoController.java:1-117](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/CaseInfoController.java#L1-L117) - [TableInfoController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TableInfoController.java#L1-L91) - [TowerController.java:1-54](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TowerController.java#L1-L54) ## 核心组件 - **认证控制器 `/auth`**:提供登录、登出、当前用户信息、修改密码、校验密码。其中 `/auth/login` 在 Sa-Token 拦截器白名单中,其余接口需要登录。 - **用户管理控制器 `/user`**:用户分页列表、创建、更新、重置密码、删除、强制下线。所有接口要求当前用户是管理员。 - **系统控制器 `/sys`**:健康检查、系统服务探测、授权信息、机器码、离线授权激活、缓存清理、升级包上传、文件下载、本地文件打开、离线地图瓦片与样式等。 - **案件控制器 `/case`**:查询自己的案件列表、新建或更新案件、删除、更新案件密码、开启案件、退出案件、查询当前已打开案件。 - **表模板控制器 `/tpl`**:模板分页查询、名称重复性检查、分类枚举、详情、创建、更新、删除。 - **塔台控制器 `/tower`**:基站查询、上传基站 xls/xlsx 转换、下载转换模板。 - **统一响应体 `Result`**:所有业务接口返回 `{code, message, data}`;成功默认 code 为 200,失败默认 code 为 400。 - **登录异常 `AccountInUseException`**:账号正在其他设备使用时返回 HTTP 200 + body.code=40901,前端据此弹出是否继续登录的确认框。 **章节来源** - [AuthController.java:1-82](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/AuthController.java#L1-L82) - [UserController.java:1-89](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/UserController.java#L1-L89) - [SystemController.java:1-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/SystemController.java#L1-L200) - [CaseInfoController.java:1-117](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/CaseInfoController.java#L1-L117) - [TableInfoController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TableInfoController.java#L1-L91) - [TowerController.java:1-54](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TowerController.java#L1-L54) - [Result.java:1-114](file://ai-server/src/main/java/com/zsjz/ai/common/config/Result.java#L1-L114) - [AccountInUseException.java:1-19](file://ai-server/src/main/java/com/zsjz/ai/common/exception/AccountInUseException.java#L1-L19) ## 架构总览 认证与系统基础接口的关键交互如下: ```mermaid sequenceDiagram participant FE as "前端" participant GW as "网关 / 反向代理" participant AC as "认证控制器" participant SVC as "SysUserService" participant ST as "Sa-Token" participant ACT as "登录活跃服务" participant DS as "案件数据源注册表" participant CACHE as "案件数据缓存" FE->>GW: POST /auth/login {username,password,force} GW->>AC: 转发登录请求 AC->>SVC: login(LoginDTO) SVC->>ACT: isInUse(userId) alt 账号正在使用且未 force SVC-->>AC: 抛出 AccountInUseException AC-->>FE: HTTP 200 {code:40901,message} FE->>FE: 弹出确认框 FE->>GW: POST /auth/login {...,force:true} GW->>AC: 转发强制登录 AC->>SVC: login(force=true) SVC->>ST: kickout(userId) SVC->>DS: 释放旧会话打开的案件 SVC->>CACHE: 清理旧会话案件缓存 SVC->>ST: login(userId) SVC-->>AC: LoginVO{token,...} AC-->>FE: {code:200,data:LoginVO} else 正常登录 SVC->>ST: login(userId) SVC-->>AC: LoginVO{token,...} AC-->>FE: {code:200,data:LoginVO} end Note over FE,GW: 后续请求通过 x-token 携带令牌 ``` **图示来源** - [AuthController.java:28-48](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/AuthController.java#L28-L48) - [SysUserService.java:63-118](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L63-L118) - [AccountInUseException.java:11-19](file://ai-server/src/main/java/com/zsjz/ai/common/exception/AccountInUseException.java#L11-L19) - [GlobalExceptionHandler.java:41-52](file://ai-server/src/main/java/com/zsjz/ai/common/exception/GlobalExceptionHandler.java#L41-L52) - [authApi.ts:77-85](file://ai-electron/frontend/src/core/api/auth/authApi.ts#L77-L85) - [account.ts:57-65](file://ai-electron/frontend/src/core/store/modules/account.ts#L57-L65) ## 详细接口说明 ### 认证接口 `/auth` | HTTP 方法 | URL | 鉴权 | 请求体 | 成功响应 | 说明 | |---|---|---|---|---|---| | POST | `/auth/login` | 无需登录 | `{username,password,force?}` | `{code:200,data:{token,userId,username,displayName,isAdmin}}` | 登录;账号被占用且未 force 时返回 HTTP 200 + code=40901 | | POST | `/auth/logout` | 可选(401 也允许) | 无 | `{code:200}` | 登出并释放该 token 打开的案件 | | GET | `/auth/info` | 需登录 | 无 | `{code:200,data:{id,username,displayName,isAdmin,status,createTime,lastLoginTime}}` | 刷新页面后恢复用户态 | | POST | `/auth/changePwd` | 需登录 | `{oldPwd,newPwd}` | `{code:200}` | 自助修改密码 | | POST | `/auth/verifyPwd` | 需登录 | `{password?}` | `{code:200,data:boolean}` | 锁屏解锁;密码错返回 false 而非 401 | **登录请求字段** - `username`:登录名,必填。 - `password`:明文密码,服务端用 BCrypt 比对,不落库。 - `force`:可选布尔值;当账号正在其他设备使用时首次登录返回 40901,用户确认后带 `force=true` 重新提交以接管旧会话。 **登录成功返回字段** - `token`:Sa-Token 登录凭据,前端写入缓存后由请求拦截器注入 `x-token` 请求头。 - `userId`:用户 ID。 - `username`:登录名。 - `displayName`:显示名。 - `isAdmin`:1 为管理员,0 为普通用户。 **错误行为** - 用户名或密码为空:HTTP 400 + code=400 + message。 - 用户名或密码错误:HTTP 400 + code=400 + message。 - 账号已被停用:HTTP 400 + code=400 + message。 - 账号正在其他设备使用:HTTP 200 + code=40901 + message,前端弹确认框。 **章节来源** - [AuthController.java:28-81](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/AuthController.java#L28-L81) - [LoginDTO.java:1-25](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/dto/LoginDTO.java#L1-L25) - [LoginVO.java:1-24](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/vo/LoginVO.java#L1-L24) - [SysUserService.java:63-118](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L63-L118) - [AccountInUseException.java:11-19](file://ai-server/src/main/java/com/zsjz/ai/common/exception/AccountInUseException.java#L11-L19) - [GlobalExceptionHandler.java:41-52](file://ai-server/src/main/java/com/zsjz/ai/common/exception/GlobalExceptionHandler.java#L41-L52) #### 前端登录调用示例 - **curl** - 正常登录:`curl -X POST http://localhost:端口/auth/login -H "Content-Type: application/json" -d '{"username":"admin","password":"admin123"}'` - 强制登录:`curl -X POST http://localhost:端口/auth/login -H "Content-Type: application/json" -d '{"username":"admin","password":"admin123","force":true}'` - **前端调用** - 调用 `/auth/login`,设置 `isTransformResponse:false`,因为 40901 是正常分支。 - 成功后从响应体取 `data.token`,写入 localStorage,并在后续请求中通过 `x-token` 请求头携带。 - 若返回 `code===40901`,弹出“是否确定登录”确认框;确认后再次调用同一接口并传入 `force:true`。 **章节来源** - [authApi.ts:24-30](file://ai-electron/frontend/src/core/api/auth/authApi.ts#L24-L30) - [authApi.ts:77-85](file://ai-electron/frontend/src/core/api/auth/authApi.ts#L77-L85) - [account.ts:57-65](file://ai-electron/frontend/src/core/store/modules/account.ts#L57-L65) ### 用户管理接口 `/user` | HTTP 方法 | URL | 鉴权 | 请求体 | 成功响应 | 说明 | |---|---|---|---|---|---| | GET | `/user/list` | 管理员 | `keyword?`, `page?`, `size?` | `{code:200,data:Page}` | 按登录名或显示名模糊匹配分页 | | POST | `/user/create` | 管理员 | `{username,password,displayName?,isAdmin?,status?}` | `{code:200}` | 新建用户 | | POST | `/user/update` | 管理员 | `{id,displayName?,isAdmin?,status?}` | `{code:200}` | 修改显示名、管理员标志、启用状态 | | POST | `/user/resetPwd` | 管理员 | `{id,newPassword}` | `{code:200}` | 重置密码并强制该用户下线 | | POST | `/user/delete` | 管理员 | `{id}` | `{code:200}` | 删除用户 | | POST | `/user/kickout` | 管理员 | `{id}` | `{code:200}` | 强制指定用户下线 | **分页字段** - `keyword`:可选,模糊匹配登录名或显示名。 - `page`:可选,默认 1。 - `size`:可选,默认 20。 - 返回的 `data` 是 MyBatis-Plus `Page` 对象,包含 `current`、`size`、`total`、`records`。 **用户返回字段** - `id`:用户 ID。 - `username`:登录名。 - `displayName`:显示名。 - `isAdmin`:1 为管理员,0 为普通用户。 - `status`:1 为启用,0 为停用。 - `createTime`:创建时间。 - `lastLoginTime`:最后登录时间。 **错误行为** - 非管理员调用:HTTP 403 + code=403 + message。 - 用户名或新密码为空:HTTP 400 + code=400 + message。 - 用户不存在:HTTP 400 + code=400 + message。 - 尝试删除最后一个管理员:HTTP 400 + code=400 + message。 - 用户名下还有案件:HTTP 400 + code=400 + message。 - 密码长度少于 6 位:HTTP 400 + code=400 + message。 **章节来源** - [UserController.java:18-88](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/UserController.java#L18-L88) - [SysUserService.java:138-429](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L138-L429) - [UserVO.java:1-28](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/vo/UserVO.java#L1-L28) - [SysUser.java:1-61](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/entity/SysUser.java#L1-L61) ### 系统接口 `/sys` | HTTP 方法 | URL | 鉴权 | 请求体 | 成功响应 | 说明 | |---|---|---|---|---|---| | GET | `/sys/health` | 通常无需登录 | 无 | `{code:200}` | 健康检查 | | GET | `/sys/serviceHealth` | 通常无需登录 | 无 | `{code:200,data:[ServiceHealthDTO]}` | 后端、PostgreSQL、Redis、Elasticsearch 可用性与耗时 | | GET | `/sys/info` | 通常无需登录 | 无 | `{code:200,data:SystemInfo}` | 系统授权信息 | | GET | `/sys/code` | 通常无需登录 | 无 | `{code:200,data:string}` | 服务器机器码 | | POST | `/sys/noNet` | 通常无需登录 | multipart/form-data `file=.xlts` | `{code:200}` | 离线授权激活 | | GET | `/sys/cleanCache` | 通常无需登录 | 无 | `{code:200}` | 清理系统缓存 | | POST | `/sys/uploadUpgrade` | 通常无需登录 | multipart/form-data `file=zip` | `{code:200}` | 上传升级包并执行升级 | | GET | `/sys/downloadFile` | 通常无需登录 | query `fid=file_info.id` | 文件流 | 按文件 ID 下载附件,不返回 Result | | GET | `/sys/map/pmtiles` | 通常无需登录 | header `Range?` | 瓦片字节流 | 离线地图 PMTiles,支持 Range | | GET | `/sys/map/style` | 通常无需登录 | 无 | 样式 JSON 或 404 | 离线地图自定义样式 | **系统信息字段** - `machinecode`:机器码。 - `clientNo`:客户编号。 - `unit`:单位。 - `phone`:手机号。 - `authdatetime`:授权时间。 - `overdatetime`:过期时间。 - `activeDateTime`:激活时间。 - `remainingDate`:剩余天数,非数据库字段。 **错误行为** - 健康检查失败或外部服务不可用时,`serviceHealth` 会返回各服务的探测结果;整体仍可能走 200,但前端应读取 `data` 中的状态字段判断可用性。 - 文件下载或离线地图资源缺失时,可能返回 404,前端应按业务逻辑处理回退。 **章节来源** - [SystemController.java:1-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/SystemController.java#L1-L200) - [SystemInfo.java:1-71](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/entity/SystemInfo.java#L1-L71) - [plat.sql:1-154](file://sql/plat.sql#L1-L154) ### 案件接口 `/case` | HTTP 方法 | URL | 鉴权 | 请求体 | 成功响应 | 说明 | |---|---|---|---|---|---| | GET | `/case/list` | 需登录 | 无 | `{code:200,data:[CaseInfo]}` | 仅返回当前登录用户自己的案件 | | POST | `/case/create` | 需登录 | `CaseInfo` JSON | `{code:200}` | 带 id 为更新,不带 id 为新建 | | GET | `/case/delete` | 需登录 | query `id` | `{code:200}` | 删除案件 | | POST | `/case/updatePwd` | 需登录 | `{id,newPwd}` | `{code:200}` | 更新案件密码 | | POST | `/case/open` | 需登录 | `{id,pwd?}` | `{code:200,data:CaseInfo}` | 开启案件,返回工作空间路径 | | GET | `/case/exit` | 需登录 | 无 | `{code:200}` | 退出案件 | | GET | `/case/current` | 需登录 | 无 | `{code:200,data:CaseInfo|null}` | 当前已打开案件 | **案件实体字段** - `id`:案件 ID。 - `name`:案件名称。 - `pwd`:案件密码,写保护,不返回给前端。 - `db`:数据库名称,忽略序列化。 - `dbPath`:数据库路径,忽略序列化。 - `type`:案件类型。 - `remark`:备注。 - `createTime`:创建时间。 - `init`:是否初始化。 - `ownerId`:归属用户 ID。 - `workspacePath`:工作空间目录,非数据库字段,由服务端补回。 **错误行为** - 未登录:Sa-Token 拦截器统一处理,通常为 401。 - 参数绑定失败:若前端用表单参数而非 JSON body 调用 create/open/updatePwd,会抛 500。 - 越权访问他人案件:服务层按 owner_id 隔离,越权请求会被拒绝。 **章节来源** - [CaseInfoController.java:18-116](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/CaseInfoController.java#L18-L116) - [CaseInfo.java:1-101](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/entity/CaseInfo.java#L1-L101) - [OpenCaseDTO.java:1-20](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/dto/OpenCaseDTO.java#L1-L20) - [UpdateCasePwdDTO.java:1-12](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/dto/UpdateCasePwdDTO.java#L1-L12) - [user.sql:1-75](file://sql/user.sql#L1-L75) ### 表模板接口 `/tpl` | HTTP 方法 | URL | 鉴权 | 请求体 | 成功响应 | 说明 | |---|---|---|---|---|---| | GET | `/tpl/page` | 需登录 | `table_name_cn?`, `has_inner?`, `classify?`, `page`, `limit` | `{code:200,data:Page}` | 模板分页查询 | | GET | `/tpl/checkName` | 需登录 | `old_table_name_cn?`, `name` | `{code:200,data:"Y"|"N"}` | 名称重复性检查 | | GET | `/tpl/cag` | 需登录 | 无 | `{code:200,data:[string]}` | 分类枚举 | | GET | `/tpl/detail` | 需登录 | `id` | `{code:200,data:TableInfo}` | 模板详情 | | POST | `/tpl/create` | 需登录 | `TableInfo` JSON | `{code:200}` | 创建模板 | | POST | `/tpl/update` | 需登录 | `TableInfo` JSON | `{code:200}` | 更新模板 | | GET | `/tpl/delete` | 需登录 | `id` | `{code:200}` | 删除模板 | **模板分页字段** - `table_name_cn`:可选,中文表名模糊匹配。 - `has_inner`:可选,1 表示有内嵌,0 表示无内嵌。 - `classify`:可选,按类别过滤。 - `page`、`limit`:分页页码与每页条数。 **模板实体字段** - `id`:主模版 ID。 - `mainId`:主模版 ID。 - `tableNameCn`:表名中文。 - `tableNameEn`:表名英文。 - `headers`:表头。 - `classify`:类别。 - `hasInner`:是否有内嵌。 - `hasTable`:是否有表。 - `createTime`:创建时间。 - `funcRegx`:模板函数正则。 - `lineNo`:表头行号。 - `tableFields`:表字段,非数据库字段。 **错误行为** - 未登录:401。 - 权限不足:本项目 `/tpl` 没有独立权限体系,但受全局登录拦截影响。 **章节来源** - [TableInfoController.java:15-90](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TableInfoController.java#L15-L90) - [TableInfo.java:1-88](file://ai-server/src/main/java/com/zsjz/ai/common/model/plat/entity/TableInfo.java#L1-L88) - [plat.sql:1-154](file://sql/plat.sql#L1-L154) ### 塔台接口 `/tower` | HTTP 方法 | URL | 鉴权 | 请求体 | 成功响应 | 说明 | |---|---|---|---|---|---| | GET | `/tower/qry` | 需登录 | `mnc`,`lac`,`ci` | `{code:200,data:[string]}` | 基站查询 | | POST | `/tower/covFile` | 需登录 | multipart/form-data `file=xls/xlsx` | 文件流 | 上传基站 xls 并转换,返回 xlsx 文件流 | | GET | `/tower/template` | 需登录 | 无 | 文件流 | 下载基站转换模板 | **错误行为** - 未登录:401。 - 文件格式错误或模板不匹配:由服务层抛出具体异常,前端按通用错误提示。 **章节来源** - [TowerController.java:18-53](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TowerController.java#L18-L53) ## 依赖关系分析 ```mermaid classDiagram class AuthController { +login(dto) +logout() +info() +changePwd(dto) +verifyPwd(dto) } class UserController { +list(keyword,page,size) +create(dto) +update(dto) +resetPwd(dto) +delete(dto) +kickout(dto) } class SystemController { +health() +serviceHealth() +info() +machineCode() +noNet(file) +cleanCache() +uploadUpgrade(file) +downloadFile(fid) +mapPmtiles(range) +mapStyle() } class CaseInfoController { +list() +create(caseInfo) +delete(id) +updatePwd(dto) +open(dto) +exit() +current() } class TableInfoController { +page(query) +checkName(old,name) +cag() +detail(id) +create(tableInfo) +update(tableInfo) +delete(id) } class TowerController { +qry(mnc,lac,ci) +covFile(file) +template() } class SysUserService { +login(dto) +logout() +info() +changePwd(dto) +verifyPwd(password) +list(keyword,page,size) +create(dto) +update(dto) +resetPwd(dto) +delete(id) +kickout(id) } AuthController --> SysUserService : "依赖" UserController --> SysUserService : "依赖" CaseInfoController --> CaseInfoService : "依赖" TableInfoController --> TableInfoService : "依赖" TowerController --> TowerService : "依赖" SystemController --> SystemService : "依赖" SystemController --> SystemHealthService : "依赖" ``` **图示来源** - [AuthController.java:1-82](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/AuthController.java#L1-L82) - [UserController.java:1-89](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/UserController.java#L1-L89) - [SystemController.java:1-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/SystemController.java#L1-L200) - [CaseInfoController.java:1-117](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/CaseInfoController.java#L1-L117) - [TableInfoController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TableInfoController.java#L1-L91) - [TowerController.java:1-54](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TowerController.java#L1-L54) - [SysUserService.java:1-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L1-L200) **章节来源** - [AuthController.java:1-82](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/AuthController.java#L1-L82) - [UserController.java:1-89](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/UserController.java#L1-L89) - [SystemController.java:1-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/SystemController.java#L1-L200) - [CaseInfoController.java:1-117](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/CaseInfoController.java#L1-L117) - [TableInfoController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TableInfoController.java#L1-L91) - [TowerController.java:1-54](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TowerController.java#L1-L54) - [SysUserService.java:1-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L1-L200) ## 性能与分页特性 - 用户列表分页使用 MyBatis-Plus `Page`,默认 page=1、size=20;返回对象包含 `current`、`size`、`total`、`records`。 - 表模板分页由 `TableInfoQuery` 承载 `page` 和 `limit`,同样返回 MyBatis-Plus `Page`。 - 登出时会关闭当前 token 打开的案件数据源,避免反复登录登出累积 DuckDB 实例占用内存预算。 - 管理员禁用用户、重置密码、删除用户或强制下线时,会调用 `StpUtil.kickout` 并释放对应用户的案件数据源及缓存。 - 系统升级接口 `/sys/uploadUpgrade` 会执行版本校验、解压、覆盖数据库、执行升级 SQL,属于长耗时操作,前端应提供进度或超时提示。 **章节来源** - [SysUserService.java:118-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L118-L200) - [SysUserService.java:201-429](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L201-L429) - [TableInfoController.java:23-34](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/TableInfoController.java#L23-L34) ## 故障排查指南 ### 登录相关 - **401 未登录** - 原因:Sa-Token 检测到 token 无效、过期或被替换。 - 排查:检查请求是否携带 `x-token`;检查前端是否在登录成功后写入 token;检查网关是否正确透传请求头。 - 处理:清空前端登录缓存,跳转到登录页重新登录。 - **40901 账号正在使用** - 原因:该账号存在有效会话且半小时内活跃过。 - 排查:查看后端日志是否打印“登录被拦截(账号正在使用)”。 - 处理:前端弹出确认框;用户确认后带 `force=true` 重新提交;服务端踢掉旧会话并让新会话登录。 - **400 用户名或密码错误** - 原因:用户名或密码为空、用户名不存在、密码不匹配、账号停用。 - 排查:检查请求体是否为 JSON,字段名是否正确。 - 处理:提示用户重试或联系管理员启用账号。 **章节来源** - [AccountInUseException.java:11-19](file://ai-server/src/main/java/com/zsjz/ai/common/exception/AccountInUseException.java#L11-L19) - [GlobalExceptionHandler.java:41-52](file://ai-server/src/main/java/com/zsjz/ai/common/exception/GlobalExceptionHandler.java#L41-L52) - [SysUserService.java:63-118](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L63-L118) ### 权限相关 - **403 无权限** - 原因:调用 `/user/**` 的用户不是管理员。 - 排查:检查当前登录用户 `isAdmin` 是否为 1;检查前端是否误把普通用户引导到用户管理入口。 - 处理:切换到管理员账号或移除普通用户对用户管理的访问。 **章节来源** - [SysUserService.java:389-398](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L389-L398) - [UserController.java:18-88](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/UserController.java#L18-L88) ### 系统与服务 - **503 或服务不可用** - 原因:网关不可达、后端进程未启动、数据库或 Redis/Elasticsearch 不可用。 - 排查:先调 `/sys/health`;再调 `/sys/serviceHealth` 查看各子服务状态;检查部署环境配置。 - 处理:重启后端服务、修复数据库连接、检查网关路由。 - **404 文件下载或地图样式缺失** - 原因:文件 ID 不存在、离线地图样式未配置。 - 排查:确认 `fid` 是否正确;检查系统配置的离线地图目录是否存在样式文件。 - 处理:前端回退到内置样式或提示资源缺失。 **章节来源** - [SystemController.java:1-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/controller/SystemController.java#L1-L200) ### 登录失效与缓存清理 - **登录失效** - 现象:后续请求返回 401。 - 前端行为:清除 localStorage 中的 token、用户信息、当前案件缓存,跳转到登录页。 - **账号被顶下线** - 触发条件:生产默认单端登录;新登录会替换旧 token。 - 后端行为:旧 token 下次请求抛未登录异常;若因强制登录接管,旧会话打开的案件会被释放。 - 前端行为:收到 401 后清理缓存并提示重新登录。 - **管理员重置密码或强制下线** - 后端行为:调用 `StpUtil.kickout`,释放该用户打开的案件数据源和缓存。 - 前端行为:后续请求 401,清理缓存并跳转登录。 **章节来源** - [SysUserService.java:118-200](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L118-L200) - [SysUserService.java:201-429](file://ai-server/src/main/java/com/zsjz/ai/module/plat/service/SysUserService.java#L201-L429) - [GlobalExceptionHandler.java:54-70](file://ai-server/src/main/java/com/zsjz/ai/common/exception/GlobalExceptionHandler.java#L54-L70) ## 结论 plat 模块的认证与系统基础接口以 Sa-Token 为核心,结合登录活跃检测实现“账号占用确认”和“强制登录”,并通过 `x-token` 请求头维持会话。用户管理接口仅限管理员,案件接口按 owner_id 隔离,表模板和塔台接口分别承担数据建模与基站处理能力。前端应在登录后持久化 token,在 401、403、40901 等场景下正确处理缓存清理和用户引导。运维侧可通过 `/sys/health` 和 `/sys/serviceHealth` 快速定位后端及依赖服务状态。