Files
EasyGoAdmin_GoFrame_EleVue/document/wiki/13_开发FAQ与排障.txt
T
2026-10-20 11:37:25 +08:00

195 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
================================================================================
EasyGoAdmin 后端开发者文档 · 13 开发 FAQ 与排障
================================================================================
按主题分组的常见问题,结论均可回溯到代码位置(括号内为文件)。
--------------------------------------------------------------------------------
A. 接口与路由
--------------------------------------------------------------------------------
Q1: 为什么接口 404?项目文档里不是说有 /v1 吗?
A: 实际路由无 /v1 版本段(api/v1 只是包名)。全部 gmeta path 直接以 /api
开头(如 GET /api/user/page)。新增接口后必须:重启服务;若为新模块还需
router.go 中 Bind(controller.CXxx)(internal/router/router.go)。
确认路由是否注册可开 server.dumpRouterMap 或查 GET /api.json。
Q2: 返回 403 "没有该操作权限"?
A: gmeta permission 非空且当前用户 Perms 不含该节点(Bind 包装层判定)。
处理:角色管理 → 勾选对应菜单下权限节点 → 保存(会清身份缓存)。
超管(ROLE_SUPER_ADMIN)不受限。permission 留空 = 登录即可。
Q3: 返回 401 但 token 刚签发?
A: 依次排查:① Authorization 头格式(Bearer + 空格);② jwt.secret 是否
多实例/重启后不一致;③ 令牌是否已入黑名单(登出/轮换/强退,
blacklist:token:{jti});④ access 已过期(默认 2h)应先走
/api/oauth2/token 刷新。
Q4: 演示环境提示“演示环境,禁止操作!”?
A: app.demo=true 时写操作全部拒绝(middleware/demo.go)。生产改 false。
Q5: 偶发 429 “请求过于频繁”?
A: 固定窗口限流:普通 /api 120 次/分钟/IP/路径;登录/验证码/令牌兑换
10 次/分钟/IP(rateLimit.*)。反向代理后如全部请求变成同一 IP,
检查 api.proxyIps 是否包含代理地址(否则 XFF 不被信任)。
Q6: 在线调试文档在哪里?
A: GET /swagger(本地 Redoc,不依赖境外 CDN);GET /api.json(OpenAPI JSON,
含全部补注册的闭包路由)。
--------------------------------------------------------------------------------
B. 认证与权限
--------------------------------------------------------------------------------
Q7: 验证码总是失败?
A: ① 校验为一次性(命中即删),前端须每次先取 /api/captcha;② Redis 不可用
降级内存后多实例不共享(验证码与请求可能落在不同实例);③ 开发可用
captcha.bypassCode 万能码,生产必须留空。
Q8: 修改了角色权限但用户仍被拒绝(或仍放行)?
A: 身份缓存 user:identity:{uid} TTL 300s。保存角色权限/分配角色会主动清缓存
(role/user 模块);手工改库不会——等 5 分钟或删除对应缓存键。
Q9: 忘记密码/重置密码?
A: 超管走 PUT /api/user/resetPwd;本人走 PUT /api/index/updatePassword
(需旧密码)。密码存储 bcrypt(password+salt);旧 md5 数据登录会被
IsBcrypt 拒绝,需重置。
Q10: 登录报“该用户名存在于多个租户,请携带租户编码登录”?
A: 未指定 tenantCode 时全局查 username,多租户同名优先平台租户(tenant_id=0),
仍歧义则要求携带 tenantCode(logic/auth.go findUser)。
--------------------------------------------------------------------------------
C. 数据与租户
--------------------------------------------------------------------------------
Q11: Update 报 1062 Duplicate entry(如 uk_tenant_mobile)?
A: 唯一预检范围必须等于唯一索引范围。索引含 tenant_id + del_flag,预检须按
“被改行的现租户”检查(user 模块已按行现租户实现;spec 用
UniqueRule{Field, Group:"tenant_id"})。注意:直接 UPDATE 全表数据/迁移
后预检与索引不一致是最常见根因。
Q12: 租户用户看到了别的租户数据?
A: 排查是否绕过 dao.Ctx 直连 g.DB()(钩子条件不生效);排查该表是否登记进
hooks.tenantTables(新增租户级表必须手工加入,共 9 张)。
Q13: 超管新增数据落到哪个租户?
A: 未显式指定时 hooks.insertHook 填 tenant.defaultId(默认 1);显式传 0
可创建平台共享行。业务取归属租户用 consts.GetTenantID(超管=默认租户)。
Q14: 数据被“删除”了但数据库里还有?
A: 删除接口全部是软删(del_flag=1,deleteHook 改写)。物理清除走 LogClean
仅限三张日志表;业务表恢复需手工 UPDATE del_flag=0。
Q15: 新加了表/列,代码里读不到?
A: gf gen dao -c hack/config.yaml 刷新 dao/entity/do → go build → 重启。
service 接口文件手工维护,勿运行 gf gen service。
--------------------------------------------------------------------------------
D. 文件与富文本
--------------------------------------------------------------------------------
Q16: 上传成功但页面图片 404?
A: ① 库中存相对 uploadDir 路径,展示必须经 GetFileUrl(基座 readTransform
自动处理,手写查询勿直接输出原值);② file.domainUrl 与实际可访问地址
一致性;③ 静态服务根为 resource/(SetServerRoot),确认 uploadDir 位于
resource/upload 或已做目录映射。
Q17: 富文本图片路径是 [IMG_URL]/...?
A: 这是持久化契约(存库格式),读取侧必须经 Detail/List(基座自动
ExpandContentFields)或手工调用 ExpandContentFields;禁止业务直接读
content 原值给第三方。
Q18: temp 目录越来越大?
A: CleanTemp 由 cmd.go 的 gcron 每日 03:30 触发,删除 temp 下超过
file.tempExpireDays(默认 7 天)的日期目录;确认配置非 0 且服务在运行。
Q19: 上传被拒绝(类型/大小)?
A: 扩展名白名单 file.fileExt / file.imageExt;大小 file.maxSize(MB);
图片另做魔数嗅探。.svg 可上传但响应强制 attachment+nosniff(防 XSS)。
--------------------------------------------------------------------------------
E. 定时任务
--------------------------------------------------------------------------------
Q20: 任务到点不执行?
A: ① 任务 status 必须=1(运行中);② cron 为 gcron 6 段(秒 分 时 日 月 周),
可用 # 前缀写标准 5 段("# */5 * * * *");③ 调度器启动延时 2s 且仅
DaemonInit 一次——改库后需 Resume/Status 或重启;④ 查
GET /api/job/daemonStatus 与 /api/server/info 的 Scheduler
(mounted vs expected 差值=挂载失败)。
Q21: 任务执行失败(任务日志 exception)?
A: url 为 http(s):检查可达性、params 是否为合法 JSON(作为 POST body)、
30s 超时;url 为本地方法名:须在 internal/task 注册且进程已加载
(RegisterJobHandler 重复注册会 panic)。多实例下策略 2/3 依赖 Redis 锁。
Q22: 如何新增内置任务?
A: 见 09 章第 8 节:task 包新文件 + init 注册 + 任务表配置 url=注册名 +
go build 重启。
--------------------------------------------------------------------------------
F. 缓存与降级
--------------------------------------------------------------------------------
Q23: Redis 挂了会怎样?
A: utility/cache 自动降级进程内存(启动探测 + 运行期自动切换,Warning 日志)。
功能可用,但限流计数/任务锁/在线用户/黑名单/验证码退化为单机语义,
多实例部署会不一致。/api/cache/info 与 /api/server/info 可查当前模式。
Q24: 字典改了文案没生效?
A: DictGetText 进程内缓存 300s;可调 GET /api/dict/refreshCache 主动刷新
(清 dict:item:{code} 键),或等自然过期。
--------------------------------------------------------------------------------
G. 代码生成器
--------------------------------------------------------------------------------
Q25: 生成器把我的手工修改覆盖了?
A: generator.overwrite=true 时生成即覆盖。恢复手段:git 还原;或改回
false 仅补缺失文件。批量覆盖前务必备份。
Q26: 生成后编译报错/路由没生效?
A: ① go build ./...(生成的 go 文件需编译);② 确认 router.go 已插入
Bind 行(生成器自动写入,幂等);③ 重启服务;④ 菜单/权限已建,
需给角色勾选权限节点。
Q27: 非 MySQL 库生成效果差?
A: 表注释/枚举解析依赖 information_schema(MySQL)。建议在 MySQL 上执行
生成,或人工补齐注释后重新生成。
--------------------------------------------------------------------------------
H. 数据库迁移
--------------------------------------------------------------------------------
Q28: 迁移报“目标库已存在同名表”?
A: 非 -drop-target-first 模式下同名表拦截(建表为 IF NOT EXISTS,数据会
追加,必须前置拦截)。加 -drop-target-first 会清空目标库全部表,慎用。
Q29: 迁移报“表 xxx 无主键,无法分页搬运”?
A: 搬运按主键游标分页,无主键表先补主键。
Q30: pgsql 迁移后插入主键冲突?
A: 序列修复(setval MAX(id)+1)在迁移末段执行;若手工导数据未走迁移器,
需手工修序。mssql 用 DBCC CHECKIDENT;mysql/sqlite/oracle 自动续接。
--------------------------------------------------------------------------------
I. 工程与质量
--------------------------------------------------------------------------------
Q31: 标准命令有哪些?
A: go build ./... | go test ./... | go vet ./... && gofmt -l .
| gf gen dao -c hack/config.yaml | make run/build/test/vet/fmt。
Q32: 接口回归怎么比对?
A: hack/golden/ 与 hack/after* 存有响应黄金样本;比对时带录制参数
(如 pageSize=3)对齐动态值后再 DIFF。
Q33: 日志在哪里?
A: 控制台 + log/ 目录(logger.path)。操作日志通道满丢弃会有
“操作日志通道已满”Warning;中间件依赖缺失启动时有 Warning 提示。
Q34: Windows 重启服务的快捷方式?
A: 见 document/启动命令.txt:按端口找 PID → taskkill → 编译 → 启动。
Q35: 用户列表数据权限(data_scope)不生效/过滤范围不对?
A: 逐项排查(机制见 03 章第 6 节):
1) 角色 data_scope 值是否正确(SELECT data_scope FROM goframe_role);
2) 用户是否挂了多角色(多角色取最宽松——任一角色为"全部"即不过滤);
3) 身份缓存陈旧:DEL easygoadmin:user:identity:{uid} 或重登
(正常经角色/用户编辑接口变更会主动清);
4) 直接改库 dept_id 不经 API 不清缓存(最长 300s 延迟);
5) 未分配部门(dept_id=0)挂部门档会降级为"仅本人"——属安全兜底设计;
6) 超管/ROLE_SUPER_ADMIN 永远绕过,测试过滤效果需用普通账号;
7) 模块未声明 DataScope 挂点则完全不过滤(当前仅 user 模块接线)。