# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Status GoFrame v2 后端已按 `document/architecture.md`(唯一权威定稿)完整落地: - **25 张业务表 → 27 个模块**全部实现(auth/index + system7 + data7 + content3 + monitor4 + tool4);**目录平铺、不分域层级**:`api/v1/{m}.go`(单包 v1)、`internal/controller/{m}.go`(单包 controller)、`internal/logic/{m}.go`(单包 logic)、`internal/service/{m}.go`(接口+注册,手维护,勿整体运行 gf gen service);跨模块直接同包调用(如 user 使用 ResolveCityNames 无需 import) - 中间件链:CORS → Response → RateLimit → Demo → JWT → Tenant → OperationLog;RateLimit 为固定窗口限流(`rateLimit.*` 配置,登录/验证码独立更严阈值 `authLimit`,计数经 utility/cache 共享,仅限 `/api/` 路径);`app.demo=true` 时 Demo 拦截全部写操作(提示"演示环境,禁止操作!"),读操作与公开白名单路径放行;权限与日志经 `gmeta` 标签在 `internal/router/bind.go` 反射为路由元数据统一处理(bind 同时补注册进 gf goai,`/api.json` 原生 OpenAPI + `/swagger` UI) - 全局钩子 `internal/model/hooks`:审计字段盖章、软删除改写、租户隔离(`dao.{X}.Ctx` 是唯一查询入口) - 代码生成器(tool 域):读表结构渲染 `templates/`(后端 `{{ }}` 定界;`templates/ui*` 前端模板因 Vue 插值冲突用 `[[ ]]` 定界)产出 后端八件套(api/controller/logic/service + dao/index、dao/internal、model/entity、model/do,与 gf gen dao 产出同构,生成后统一 gofmt) + 前端文件,并**同步创建菜单+权限节点、字典数据、router.go 注册行**(幂等);支持 HTTP 与 CLI 两种方式 - 整库迁移(`internal/migrate` + `cmd/migrator`):源库内省(表/列/索引/注释)→ 中间表示 → 目标方言 DDL(mysql/pgsql/sqlite/mssql/oracle 五 renderer,对齐 `resource/sql` 定稿)→ 分批搬运 → pgsql 序列修复;纯 CLI、gdb 动态建连裸读裸写(不走 dao,不触发全局钩子);`-drop-target-first` 清空目标库全部表 ## 关键约定(与直觉相反的点) - **主键统一 int 数据库自增**(2026-10-13 定稿):禁用雪花算法(`utility/id` 已删除);新增后用 `result.LastInsertId()` 回读;hooks 不填 id - 软删列统一 `del_flag`;表前缀 `goframe_` - 新增模块四件套平铺放置(api/v1、controller、logic、service 各一文件,同包 init 自动注册,无需 blank import);service 接口手维护;路由注册由生成器自动写入或手动 `router.go` `Bind(controller.CXxx)` - **定时任务处理方法在 `internal/task`**:调度器与注册表在 internal/logic 的 job 模块(`logic.RegisterJobHandler`);任务实现平铺 `internal/task/{任务名}.go`,init 中自注册(注册名=url 字段值,http(s) 地址除外),依赖单向 task→logic(任务可复用 base 共享能力,logic 禁止反向 import task);由 `internal/cmd` 空白导入触发加载;调度消费 execute_policy(1并行/2单例/3放弃)与 is_sync(1同步/0异步),策略2/3叠加 `job:lock:{id}` 执行锁(utility/cache SET NX,多实例共享,TTL 300s 兜底);内置 LogClean 任务每日 02:30 分批物理清理三张日志表(保留天数 `job.logRetentionDays` 默认90,任务参数 retentionDays 可覆盖) - **monitor 域三个无表模块**:在线用户(`/onlineUser/page`+`forceLogout`,登录 MarkOnline/登出 MarkOffline 写 `online:jti` zset+info 键,踢出=拉黑 jti)、服务器监控(`/server/info`:runtime/磁盘分平台文件 server_monitor_windows/other.go/MySQL/Redis 连通)、缓存监控(`/cache/info`:Redis INFO 解析,内存降级时返回 mode=memory);前端 `ui/src/views/monitor/{online,server,cache}`;OpenAPI 经 bind.go 补注册后 `/api.json` 全量可用 - **文件命名对齐 dao(snake_case)**:`api/v1/{模块}.go`、`internal/controller/{模块}.go`、`internal/logic/{模块}.go`、`internal/service/{模块}.go` 均用表名去前缀形式(如 `config_item.go`/`file_template.go`/`operation_log.go`),生成器后端目标同步此规则(前端文件仍用驼峰如 `fileTemplate.ts`) - **log_type 值固定**(前端按此映射显示):add=1 update=2 delete=3 query=4 import=5 export=6 … generate=19 reset=24(见 `consts/log_type.go`,前端按此映射显示) - **筛选对齐三件套**(2026-10-14 定稿):前端 `querySchemas.ts` 每个输入框 ↔ `XxxPageReq` 同名驼峰字段 ↔ spec 声明——字符串 Input → `LikeFields`(逐列模糊,列名声明、值取 req 同名驼峰json),Select/InputNumber → `EqFields`(零值跳过);`KeywordFields`(req.keyword 多列 OR)**能力保留在基座**,模块不声明,未来需要时再用;导出统一走 `xxxSpec.applyFilters` 与列表同套筛选 - 超管角色编码 `ROLE_SUPER_ADMIN`;超管 `getUser` 返回通配权限 `*:*:*` - **角色数据权限**(`role.data_scope` 四档:1-全部 2-本部门及以下 3-本部门 4-仅本人):模块在 spec 声明 `DataScope: ApplyDataScope("dept_id")` 即生效(internal/logic/data_scope.go,共享工厂);多角色在身份加载层取最宽松,超管/平台绕过,未分配部门用户挂部门档降级为仅本人;**仅作用于 Page/List/导出**(applyFilters 链),Detail/Update/Delete 不过滤;档位文案走字典 `role_data_scope`;身份缓存(300s)含 data_scope+deptId,角色/用户变更主动清 - 定时任务 cron 用 gf gcron 的 `#` 前缀 6 段式(`# */5 * * * *` = 标准5段忽略秒) - multipart 上传字段用 `*ghttp.UploadFile` + `p:"file"` 标签 - **上传两段式**:`upload.SaveTemp` 先入 `resource/upload/temp/日期/`(白名单+大小+图片嗅探校验),表单保存时 `upload.SaveFromTemp` 移动到正式子目录(user/article/notice/link/example/tenant 等);静态访问 `/upload/**` 由 `SetServerRoot("resource")` 提供 - **命名**:入参/出参 json 一律驼峰;**例外**:登录/令牌接口返回 `access_token/refresh_token/expires_in/token_type`(OAuth2 惯例,前端按此读取,勿改驼峰) - IP 归属地:`utility/ip2region`(xdb 于 manifest/data/ip2region.xdb,配置 ip2region.path),登录日志/操作日志 location 自动填充;受信代理列表 `api.proxyIps`(支持 CIDR,信任 X-Forwarded-For) - **logic 共享能力层** `internal/logic/base.go`(横切能力,组合式复用):文件字段(SaveFileField/SaveFileFields/SaveMultiFileField)、富文本占位符(LocalizeContentFields/ExpandContentFields,`[IMG_URL]` 为持久化契约)、枚举文本(DictGetText/SerializeMapOnRead)、唯一校验(CheckUnique,仅为 UX 层,唯一性真值靠 `uk_*` 索引)、事务(WithTrans,事务内禁 HTTP/文件IO、bcrypt 先算)、分块查询(ChunkQuery)、泛型树(BuildTree)、临时目录清理(CleanTemp,每日 03:30 gcron 触发,file.tempExpireDays 控制保留天数);纪律:只依赖 dao/utility/consts,导出函数上限15个 - **{field}Text 字典驱动**:枚举字段显示名从 `dict_item` 解析(唯一真相),模块头部声明 `xxxSerializeMaps = map[字段]字典编码`(编码规范 `{模块snake}_{字段snake}`,如 `user_status`、`job_execute_policy`),响应行用 `DictGetText(ctx, code, value)` 填充;字典由生成器 createDict 按**列注释枚举**自动创建(支持 `1-正常` 与 `1=正常` 两种写法),文案在字典管理页修改;进程内缓存300s。例外:`operation_log.type` 存注解常量值,用静态表 `LogTypeText`(consts.LogTypeNames) - **生成器保护** `skipTables`:仅关联表(role_menu/user_role)整表跳过;其余表统一文件级处理——**缺失补生成、存在跳过、`generator.overwrite: true` 时覆盖**(唯一开关在 config.yaml,覆盖结果单独计入 Overwritten),菜单/字典/路由等幂等副作用照常执行 - 城市编解码:`ResolveCityNames / ResolveCityCodes`(internal/logic 包级函数),user 模块 Add/Update/Import/Detail 已接入 - 代码生成器 CLI:`go run ./cmd/generator -tables "goframe_x|注释" [-preview|-batch]`;覆盖模式由 `generator.overwrite` 配置统一控制;dao/entity/do 四件由生成器直接产出,生成后只需 `go build ./...` 并重启(`gf gen dao` 仅在**表结构变更**时用于刷新已实现模块的 dao) ## Commands Windows 环境(无 make,直接用 gf/go): ```sh go build ./... # 编译 go test ./... # 测试(sqlite 集成 + hooks MySQL 集成) go vet ./... && gofmt -l . # 静态检查 gf gen dao -c hack/config.yaml # 表结构变更后重新生成 dao(service 手维护,勿用 gf gen) go run ./cmd/generator -tables "goframe_order|订单表" # 代码生成(文件+菜单+字典+路由) go run ./cmd/migrator -src-type=mysql -src-host=127.0.0.1 -src-hostport=3306 -src-database=easygoadmin.goframe.elevue -src-username=root -src-password=root -dst-type=pgsql -dst-host=127.0.0.1 -dst-hostport=5432 -dst-database=easygoadmin.goframe.elevue -dst-username=postgres -dst-password=123456 -drop-target-first # 整库迁移(源→目标方言,含结构+注释+索引+数据+序列) go build -o bin/admin-server.exe main.go && ./bin/admin-server.exe # 运行(:8000) ``` ## 环境 - MySQL `127.0.0.1:3306` root/root 库 `easygoadmin.goframe.elevue`;定稿脚本 `document/easygoadmin.goframe.elevue.sql`(人工导入维护) - Redis `127.0.0.1:6379`(缓存降级内存);本地测试账号 admin/admin123 - 方言 DDL `resource/sql/{mysql,pgsql,sqlite,mssql,oracle}.sql`(后四份转换生成,**待人工校对**) - 前端 `ui/`(Element Plus;**接口统一 `/api/v1/*`**(`/api` 为通用网关前缀,`/v1` 为版本前缀)——gmeta 路径/公开白名单/健康检查均按此注册,前端 baseURL `/api` + 版本前缀 `/v1` 拼接;登录返回读 `data.access_token`。**例外**:公开路由(`middleware/public.go` 的 `publicPaths`:`/api/login`、`/api/register`、`/api/captcha`、`/api/oauth2/token`、`/api/health`)一律**不带 `/v1`**(网关免版本路径,gmeta path / `router.go` / 白名单三处须一致,勿加 `/v1`) - 生成器配置 `generator.parentMenuId=189`、`generator.overwrite=false`(覆盖开关);上传配置 `file.*`