# 3.12 常见问题与排错 :::tip 概述 本页汇总模块开发高频问题。核心口诀:**新接口只加 RouteSpec 声明 + handler 方法,数据访问只走 Ctx,业务错误原样上抛。** ::: ## 路由与注册 **Q:加了 handler 方法但接口 404?** 方法签名不匹配。必须是 `func (c *CXxx) Method(ctx context.Context, req *dto.XxxReq) (*dto.XxxRes, error)`,且 RouteSpec 表中有同名键。反射扫描按签名识别,不匹配的方法静默跳过。 **Q:改了 router.go 需要重启吗?** 需要。路由注册发生在启动时,改动后重启服务(`make run`)。 **Q:新模块忘记在 registerControllers 加 Bind?** 所有控制器必须在 `internal/router/router.go` 的 `registerControllers` 中有一条 `Bind` 声明,否则整个控制器不注册。代码生成器会自动追加。 ## 数据访问 **Q:查询能搜到已删除的数据?** 说明绕过了 `Ctx`。检查是否用了 `Raw()` 或裸 `*gorm.DB`——一切查询走 `NewXxxRepo().Ctx(ctx)`。 **Q:租户 A 看到了租户 B 的数据?** 两个检查点:表是否在 `internal/model/hooks` 的租户白名单中(必须含 tenant_id 列);写入时 Create 钩子是否正常补齐 tenant_id。 **Q:物理删除报错"被拦截"?** 设计如此。业务删除用 `SoftDelete`;定时清理等场景用 `hooks.WithPhysicalDelete(ctx)` 显式放行。 ## 业务层 **Q:Update 后某些字段变回零值?** Update 是全量表单语义:未提交的字段按零值落库。前端必须回传全部业务字段(未修改的传原值)。 **Q:字典字段没有 statusText?** 检查三点:SerializeMaps 键是 snake 字段名(status)、字典编码正确(example_status)、DTO 中声明了 `StatusText` 字段。字典本身需在字典管理中有对应项。 **Q:文件上传后访问 404?** 上传先进 temp,**保存记录时**才经 saveTransform 转正到 `FileSaveDir`。只调上传接口不保存记录,文件不会出现在正式目录。域名问题见 [7.2 上传配置详解](/zh/tools/upload-config.html)。 ## 钩子与事务 **Q:钩子里能做 HTTP 请求 / append 吗?** 不能。钩子禁 HTTP/文件 IO、禁 append(可就地改元素),错误原样上抛。 **Q:事务里查到的还是旧数据?** 事务内必须用回调入参的 ctx:`db.WithTrans(ctx, func(ctx context.Context) error { ... })`——内层 ctx 才携带事务连接。 ## 效率建议 - 建表后先跑 `make gen TABLES="gin_xxx|说明"` 让生成器出四件套与前端,再手工改——比从零手写快且不违例 - 新配置键记得同步 `.env.example`,否则 `make check` 卡 check-config - 不确定写法时,对照本章 example 模块逐字核对——它是全项目规约的参考实现