签入脚本

This commit is contained in:
小蚂蚁云
2026-10-20 11:37:25 +08:00
commit 7f5e8470af
909 changed files with 4045080 additions and 0 deletions
+194
View File
@@ -0,0 +1,194 @@
================================================================================
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 模块接线)。