本文引用的文件
本指南面向运维和后端工程师,聚焦以下故障面:
regInfo.exe(Linux 为 regInfo)不可执行、命令返回异常、机器码变化。x-token 缺失、过期、被单端登录顶掉;Gateway 粘性路由、节点绑定、健康检查。阅读顺序建议:先看「快速诊断清单」定位大类,再进入对应场景,最后用「数据库字段与配置项对照表」核对落地数据。
本系统由三层组成:
x-token 访问后端。x-token 解析、节点选择与健康检查。ai-server:业务服务,负责 Sa-Token 登录态、许可证校验、业务接口。
graph TB
Client["客户端<br/>Electron 前端"] --> GW["AI 网关<br/>StickyRouteFilter / LoginResponseFilter"]
GW --> NodeA["后端节点 A<br/>ai-server"]
GW --> NodeB["后端节点 B<br/>ai-server"]
GW -.-> Redis["共享 Redis<br/>Sa-Token / Token 缓存"]
NodeA -.-> DB["PostgreSQL<br/>gateway_* 表 + 业务表"]
NodeB -.-> DB
NodeA -.-> License["本地授权文件<br/>QingJian/license/license.xlts"]
NodeA -.-> RegInfo["regInfo.exe / regInfo"]
图表来源
章节来源
| 步骤 | 检查点 | 正常期望 | 异常信号 |
|---|---|---|---|
| 1 | 能否访问 Gateway 暴露地址 | HTTP 200,后端健康接口返回成功 | 404、连接超时、502 |
| 2 | 是否先走 /js/a/sys/checkAuth |
返回授权状态 | 返回“未授权”“文件不存在” |
| 3 | 是否能获取机器码 | 返回机器码字符串 | 返回“获取机器码失败” |
| 4 | 授权文件是否存在 | QingJian/license/license.xlts 存在 |
提示“授权文件不存在” |
| 5 | regInfo.exe 或 regInfo 是否可执行 |
子进程返回 0 | 子进程非 0、权限不足、找不到文件 |
| 6 | 登录请求是否带 username | 请求体含 username |
登录按匿名轮询,可能绑错节点 |
| 7 | 登录后是否返回 x-token |
响应包含 token |
无 token,后续请求全部 401 |
| 8 | Redis 中是否有 Sa-Token | x-token 能解析出 userId |
解析失败、登录失效 |
| 9 | Gateway 中是否有用户绑定 | gateway_user_route 有记录 |
503 “用户未绑定节点” |
| 10 | 绑定节点是否健康 | node_health 不是 DOWN |
503 “节点不可用” |
| 11 | 多节点 token 是否一致 | 同一用户所有节点 token 相同 | 某节点 401,另一节点正常 |
许可证校验发生在 ai-server 启动和业务调用前;认证使用 Sa-Token,并依赖 Redis;Gateway 只读 token,不签发 token,同时维护用户到节点的粘性绑定。
sequenceDiagram
participant C as "客户端"
participant G as "Gateway"
participant S as "ai-server"
participant R as "Redis"
participant D as "PostgreSQL"
C->>G: POST /js/a/auth/login {username, password}
G->>S: 转发登录请求
S->>R: Sa-Token 登录并写入 x-token
S-->>G: 登录成功 Result{code=200, data.token}
G->>D: 写 gateway_user_route(用户→节点)
G->>R: 写 gateway:token:{serviceId}:{token} → userId
G-->>C: 登录响应原样透传
C->>G: 业务请求(携带 x-token)
G->>R: 解析 token → userId
G->>D: 查询用户绑定节点
G->>S: 转发到绑定节点
S-->>G: 业务结果
G-->>C: 返回结果
图表来源
章节来源
QingJian/license/regInfo.exe(Windows)或 QingJian/license/regInfo(Linux)存在。/sys/code 或 /sys/checkAuth,观察是否抛出激活异常。QingJian/license 目录及 license.xlts 的读写权限。regInfo.exe 或 regInfo 命令开始执行、输出、返回值。IOException、InterruptedException、ExecutionException,会统一包装为服务器异常。PathConst.RUN_QINGJIAN_PATH 基于 user.dir。QingJian/license/regInfo.exe 是否存在且未被安全软件拦截。QingJian/license/regInfo 是否具有可执行权限;license.xlts 是否可写。QingJian/license 执行权限修改操作。此场景主要影响本地授权文件,不涉及 Gateway 表。若离线授权成功,会更新平台系统中的系统信息表中的授权信息字段。
QingJian/license 路径生效。regInfo.exe。regInfo 可执行权限,并确保当前用户可读写 QingJian/license。章节来源
license.xlts。QingJian/license/license.xlts 是否确实是当前机器码对应的授权文件。离线授权成功后,系统信息表中会写入 machinecode、unit、phone、activeDateTime、authdatetime、overdatetime 等字段。
该场景发生在授权阶段,不影响 Gateway 的 token 路由逻辑;但授权未通过时,前端可能无法进入主界面或限制功能。
license.xlts,重新申请并上传。章节来源
QingJian/license/license.xlts 是否存在且可读。overdatetime:用于计算剩余天数。clientNo、machinecode、unit、phone、activeDateTime、authdatetime:离线授权后写入。授权页面本身属于系统信息接口,不一定依赖登录;但若系统整体因授权失败拒绝核心能力,用户可能无法继续使用分析功能。
.xlts 文件。LicenseUtil.checkAuthStatus 对授权结果做了 60 秒缓存,修改授权文件后可能需要等待缓存过期或重启服务才能刷新。章节来源
token。x-token。token 缓存读取失败、sa-token 解析 token 失败。未登录访问,包含 NotLogin 类型。HTTP 200 + body.code=401。sa-token.token-name=x-token。Gateway 侧不会因 token 解析失败而修改业务表;但登录成功后会在 gateway_user_route 写入用户绑定。
x-token 为空:Gateway 按匿名请求处理,后端返回 401,前端引导重新登录。x-token 无效:Gateway 解析不到 userId,同样按匿名处理。token,前端已将其放入请求头 x-token。gateway:token:{serviceId}:{token} 和 Sa-Token 对应键。章节来源
gateway_service、gateway_node、gateway_user_route 是否有记录。服务不存在或已停用、用户未绑定节点、用户绑定的后端节点不可用。节点 xxx 健康状态 -> UP/DOWN。application.yaml 中的 gateway.services 首次播种是否覆盖到数据库。health-check.interval、timeout、failure-threshold。default-node-id 是否指向有效节点。| 表 | 关键字段 | 作用 |
|---|---|---|
gateway_service |
id、path_prefix、login_path、health_path、default_node_id |
定义对外路径前缀、登录路径、健康检查路径和兜底节点 |
gateway_node |
id、service_id、base_url、enabled |
登记 ai-server 节点地址 |
gateway_user_route |
service_id、user_id、username、node_id |
用户粘性绑定到节点 |
gateway_service 已创建,且 path_prefix=/js/a。gateway_node 指向正确的 ai-server 地址。gateway_user_route。/js/a/sys/health 是否正常。章节来源
未登录访问,类型为 BE_REPLACED 或 KICK_OUT。is-concurrent=false 表示单端登录。replaced-login-exit-mode=OLD_DEVICE、replaced-range=ALL_DEVICE_TYPE:新登录会替换旧设备。ZSJZ_AUTH_IN_USE_WINDOW_MINUTES:默认 30 分钟,用于“账号正在使用”判定窗口。该场景主要影响 Redis 中的 Sa-Token 和活跃标记,不直接修改业务表。
HTTP 200 + body.code=401,前端跳转到登录页。若出现“账号正在使用”弹窗,说明 LoginActivityService 检测到窗口内活跃;确认后才会允许强制登录。
flowchart TD
Start["新客户端登录"] --> CheckActive["检测账号窗口内是否活跃"]
CheckActive --> InUse{"窗口内活跃?"}
InUse -->|否| DoLogin["直接登录并顶掉旧会话"]
InUse -->|是| Confirm["提示账号正在使用,需用户确认"]
Confirm --> Confirmed{"用户确认继续?"}
Confirmed -->|是| ForceLogin["强制登录并顶掉旧会话"]
Confirmed -->|否| Cancel["取消登录"]
DoLogin --> OldClient["旧客户端继续请求"]
ForceLogin --> OldClient
OldClient --> TokenInvalid{"token 仍有效?"}
TokenInvalid -->|否| Redirect["返回 401,前端跳转登录"]
TokenInvalid -->|是| Retry["重试一次"]
图表来源
章节来源
token 缓存读取失败、sa-token 解析 token 失败。SPRING_DATA_REDIS_HOST、SPRING_DATA_REDIS_PORT。timeout=2592000(约 30 天),前提是 Redis 持久化配置正确。会话本身保存在 Redis,不在 PostgreSQL 业务表中;但 Gateway 的用户绑定记录仍在数据库中,因此重新登录后可复用绑定。
章节来源
| 表名 | 字段 | 说明 |
|---|---|---|
gateway_service |
id |
服务唯一标识 |
gateway_service |
path_prefix |
对外路径前缀,如 /js/a |
gateway_service |
login_path |
相对登录路径,如 /auth/login |
gateway_service |
health_path |
完整健康检查路径 |
gateway_service |
default_node_id |
存量用户兜底节点 |
gateway_service |
enabled |
服务是否启用 |
gateway_node |
id |
节点唯一标识 |
gateway_node |
service_id |
所属服务 |
gateway_node |
base_url |
不含路径的后端地址 |
gateway_node |
enabled |
节点是否启用 |
gateway_user_route |
service_id |
所属服务 |
gateway_user_route |
user_id |
绑定用户 ID |
gateway_user_route |
username |
绑定用户名 |
gateway_user_route |
node_id |
目标节点 |
章节来源
| 配置项 | 位置 | 含义 |
|---|---|---|
sa-token.token-name |
Gateway application.yaml | 请求头名称,必须为 x-token |
sa-token.timeout |
Gateway application.yaml | token 超时时间 |
sa-token.is-concurrent |
Gateway application.yaml | 是否允许多端同时在线 |
gateway.health-check.interval |
Gateway application.yaml | 健康检查间隔 |
gateway.health-check.timeout |
Gateway application.yaml | 健康检查超时 |
gateway.health-check.failure-threshold |
Gateway application.yaml | 连续失败阈值 |
gateway.admin.ip-whitelist |
Gateway application.yaml | 管理端 IP 白名单 |
SPRING_DATA_REDIS_HOST |
env.sh | Redis 主机 |
SPRING_DATA_REDIS_PORT |
env.sh | Redis 端口 |
ZSJZ_AUTH_IN_USE_WINDOW_MINUTES |
env.sh | 账号正在使用判定窗口(分钟) |
章节来源
可能原因:
token 放到请求头 x-token。排查要点:
token。x-token。章节来源
可能原因:
排查要点:
gateway_user_route 中用户绑定的 node_id。章节来源
设计原因: 粘性路由明确不自动切换,因为案件数据、附件、Agent 记忆都在节点本地磁盘。切到其他节点后,请求虽然能到达健康节点,却看不到该用户的业务数据。
章节来源
可能原因:
QingJian/license/license.xlts 不存在。regInfo.exe 或 regInfo 不可执行。章节来源
可能原因:
章节来源
机制说明:
LoginActivityService 会维护 Redis 键 zsjz:auth:last-active:{userId},TTL 等于判定窗口。判断条件包括:
章节来源
排查许可证与认证问题时,应按“本地授权 → 登录态 → Gateway 粘性路由 → 节点健康”的顺序逐步缩小范围:
regInfo.exe 可执行、授权文件存在且机器码一致。x-token,Redis 中 Sa-Token 有效。