service.xpcool.com/docs/change-log/2026-08-24.md
夏犀麟 43fa79686d refactor(api): 后台管理 API 按 base/system/admin 分组重构
- api/admin/v1 拆 base/system/admin 三子包,路由前缀 /admin/v1/{base,system,admin}
- system: auth/menu/role;admin: 管理员;base: log
- controller 改 import 子包;006_menu_paths_v2.sql 更新接口路径映射
- 权限码与路由分离,permission 标识不变
- 冒烟测试新路径 8 接口全通过
2026-08-24 23:40:39 +08:00

16 KiB
Raw Blame History

Change Log — 2026-08-24

请求

  1. 新增一个「公共模块」,模块下存放 tools 功能模块,tools 下再分各种子功能。
  2. 建立项目上下文记忆体系:记录每次请求与更改,方案对比后落地;要求后续使用其他 CodeBuddy 账号也能无缝衔接。

变更

新增文件:

  • common/doc.go — 公共模块说明(不依赖 internal、可独立抽取
  • common/tools/doc.go — tools 模块布局清单与封装规则
  • common/tools/md5/md5.go — MD5 摘要Md5Hex / Md5Bytes / Md5File
  • common/tools/cryptox/cryptox.go — AES-128-CBC / DES-ECB 加解密base64 输出,密钥任意长度自动规范化)
  • common/tools/uuid/uuid.go — 唯一 IDNew 32 位 / Short 短随机码)
  • common/tools/random/random.go — 随机数/随机串Int / String / Digits / Letters
  • common/tools/timex/timex.go — 时间工具Format / Timestamp / StartOfDay / EndOfDay
  • common/tools/convertx/convertx.go — 类型转换带默认值ToInt / ToInt64 / ToFloat64 / ToString / ToBool
  • common/tools/strx/strx.go — 字符串工具(命名转换 SnakeCase/CamelCase + 脱敏 MaskPhone/MaskIDCard/MaskName
  • common/tools/slicex/slicex.go — 泛型切片工具Contains / Unique / Chunk / Map / Filter
  • common/tools/ip/ip.go — IP 工具IsValid / LocalIP / IsInternal / ToLong / ToString
  • common/tools/filex/filex.go — 文件工具Exists / IsDir / ReadString / WriteString
  • AGENTS.md — 项目智能体说明书(架构、规范、命令、记忆体系索引)
  • docs/change-log/2026-08-24.md — 本文档

修改文件:

  • .gitignore — 追加 .workbuddy/(本机记忆不入库)
  • PROJECT_STRUCTURE.md — 目录树补充 common/docs/change-log/(见后续提交)

验证:go build ./...go vet ./common/... 全部通过。

决策与理由

  • 公共模块放顶层 common/ 而非 internal/common/Go 的 internal 包无法被外部模块引用,放顶层便于未来抽取为独立库/被同仓库其他服务复用。
  • tools 一律薄封装 GoFrame 内置组件v2.10.2 中 AES/DES 已拆为 crypto/gaescrypto/gdesUUID 为 util/guid,无 gslicer(用标准库 slices 替代);避免重复造轮子,保持与框架一致。
  • 记忆体系三层方案(对比见下):
    1. AGENTS.md(根目录)— 长期稳定规范跨工具标准Claude Code/CodeBuddy/Codex 等均识别),随 git 走;
    2. docs/change-log/YYYY-MM-DD.md — 每次请求变更的结构化记录,随 git 走,这是跨账号衔接的关键
    3. .workbuddy/memory/ — WorkBuddy 本机增强,不入库。
    • 对比过 CLAUDE.mdClaude Code 专属、已建议统一为 AGENTS.md.cursor/rulesCursor 专属)、.codebuddy/(仅 CodeBuddy 读取)——它们都不是最大公约数,故不采用。

待办与风险

  • 后续每次任务完成后:更新 docs/change-log/(当日文件追加)+ 提交 git确保其他账号 clone 即恢复上下文。
  • convertx 依赖 gconv 的"转换失败返回零值"行为(无法区分"0"与非法输入),需要严格转换的场景应在 service 层先校验。
  • 本次变更尚未 git 提交,建议尽快 commit。

追加17:30方向纠正 — 「公共模块」实为公共接口

请求

用户澄清:要新增的是给前端调用的公共 HTTP 接口(此前误建成 Go 工具包),确认应规划到 api/ 契约层。

变更

新增文件:

  • api/common/v1/tools.go — 公共接口契约:UUIDReq/ResMD5Req/ResRandomReq/ResTimeReq/ResIPReq/Resg.Meta 路由元数据)
  • internal/controller/common/tools.go — 控制器实现,薄适配层,复用 common/tools/* Go 包

修改文件:

  • internal/cmd/cmd.go — 新增公开分组 s.Group("/api/common/v1", ...)Recover+CORS+HandlerResponse无鉴权
  • common/tools/timex/timex.go — 修复:Format 内部改用 Layout() 方法(见决策)
  • internal/controller/common/tools.goTimenow.Layout(...)
  • AGENTS.md — 目录树与新增「公共接口」小节、注意事项补 gtime 与包名差异
  • PROJECT_STRUCTURE.md — 目录树补充 api/common/v1

验证:go build ./...go vet 通过;实际启动服务冒烟测试 5 个端点全部返回正确(含修复后 time 格式化)。

决策与理由

  • 路由前缀 /api/common/v1:与 /api/v1(user)、/admin/v1(admin) 平级的独立公开前缀天然不套登录鉴权前端三个端mini/h5/app通用。
  • 契约层放 api/common/v1,实现放 internal/controller/common:与 user/admin 完全同构;公共接口不需要 service 层纯工具计算controller 直接复用 common/tools 包,避免过度分层。
  • 🐛 gtime v2.10.2 大坑(已修复)Time.Format() 参数是 PHP 风格"Y-m-d H:i:s"),传 Go layout"2006-01-02 15:04:05"会原样输出Go layout 必须用 Time.Layout()common/tools/timex 已统一封装为 Layout 语义,调用方直接 timex.Format(t, layout...) 即可。
  • 冒烟测试教训go run 会 spawn 子进程,kill %1 只杀包装进程,残留的 main.exe 会继续占用 8000 端口导致后续测试打到旧代码——杀进程需 netstat -ano | grep :8000 找 PID 后 Stop-Process

待办与风险

  • 本次追加变更同样未提交 git与上文合并为一次 commit。
  • 公共接口已开放无鉴权能力md5/random/uuid 等),后续新增接口时需评审是否应限流/加签名,避免被滥用。

追加18:05开放接口命名决策 + API 目录重构

请求

  1. 变更 API 目录设计:契约层改为 tools/<子功能>/index.go 结构(示例 /api/common/v1/tools/ocr/index.go)。
  2. 同步修改所有涉及处(控制器、路由)。
  3. 把命名规则记入项目记忆。
  4. 咨询:公共接口一般用 common 命名吗?有没有更好的?

变更

新增文件(契约层按子功能拆目录):

  • api/open/v1/tools/doc.go — tools 目录结构规则说明
  • api/open/v1/tools/uuid/index.gomd5/index.gorandom/index.gotime/index.goip/index.go — 每子功能一个目录index.go 内 package <子功能名>g.Meta tags 改 Open/Tools
  • internal/controller/open/controller.go — Controller 结构 + New()
  • internal/controller/open/uuid.gomd5.gorandom.gotime.goip.go — 按子功能拆文件import 对应契约子包

删除文件:

  • api/common/v1/tools.goapi/common/ 目录
  • internal/controller/common/tools.gointernal/controller/common/ 目录

修改文件:

  • internal/cmd/cmd.go — import commonctlopenctl;路由 /api/common/v1/api/open/v1,注释改 Open tools API
  • AGENTS.md — 目录树更新「公共接口」小节改为「开放接口api/open/v1与命名规则」记录 open 命名决策与 tools 子功能目录规则
  • PROJECT_STRUCTURE.md — 目录树同步

验证:go build ./...go vet 通过;冒烟测试:旧路由 /api/common/v1/tools/time 返回 404新路由 /api/open/v1/tools/* 5 端点全部正确。

决策与理由

  • 命名 common → open:用户询问"公共接口一般用 common 吗",对比后选 open(业界开放接口惯例,如支付宝 /open/api语义强调对外暴露、无鉴权public 为并列备选(更强调"公开"common 偏内部通用语义、弃用。命名规则已记入 AGENTS.md「开放接口」小节。
  • 契约层目录规则api/open/v1/tools/<子功能名>/index.go 每子功能一目录(与 GoFrame 惯例「api 下按功能分包」一致,用户示例 ocr 即为后续子功能,如未来加 OCR 识别接口即建 tools/ocr/index.go);控制器 internal/controller/open/<子功能名>.go 对应拆文件(同 package open
  • 路由绑定不受目录重构影响GoFrame 通过 controller 方法参数反射定位 g.Meta契约包拆成多个子包uuid/md5/random/time/ip不影响 group.Bind(openctl.New()) 自动注册cmd.go 只需改前缀。
  • 包名 timeapi/open/v1/tools/time与标准库 time 潜在同名controller 中统一用 import 别名 timeapi 规避。

待办与风险

  • 前端若已联调旧 /api/common/v1 路径需同步改 /api/open/v1(当前无线上前端,风险低)。
  • 新增子功能记得更新 api/open/v1/tools/doc.go 的示例清单。

追加18:08契约文件 index.go → <功能名>.go

请求

用户咨询"子功能目录内用 index.go 还是 <功能名>.go 更易扩展维护",确认推荐后执行改动。

变更

  • git mv 重命名 5 个契约文件:tools/{uuid,md5,random,time,ip}/index.gotools/{uuid,md5,random,time,ip}/{uuid,md5,random,time,ip}.go(包内容不变)
  • api/open/v1/tools/doc.go — 规则说明改为「目录名=包名=文件名三一致」,补充"功能变大后目录内加文件"的扩展指引
  • AGENTS.md / PROJECT_STRUCTURE.md — 同步 index.go 引用为 .go

验证:go build ./...go vet 通过;冒烟测试 5 端点全部正常。

决策与理由

  • 选 <功能名>.go 而非 index.go:① 目录名=包名=文件名三一致,导航直观;② index.go 是"入口"语义,功能膨胀后出现 index.go + idcard.go 混排会失去入口意义,而 <name>.go + idcard.go 自然;③ 符合 Go 生态主流strings/strings.go与 GoFrame 官方模板api/user/v1/user.go
  • 扩展路径已定型:子功能从 1 个端点到多个端点,只需在目录内加文件(如 ocr 目录 ocr.go → + idcard.go + invoice.go),无需重构文件名。

待办与风险

  • 无新增风险;后续新增子功能统一按 tools/<name>/<name>.go 建文件。

追加22:40后台管理功能开发后端完成

请求

使用 goframe-v2 开发登录、菜单、按钮级权限等常规后台管理功能(前端 vben5 对接backend 动态路由模式);登录后台后开发服务器日志管理功能。

变更commit 8fdb25e35 文件)

  • manifest/sql/003_schema_ext.sqladmin_menu 加 icon/component/hidden
  • manifest/sql/004_seed.sql:初始 admin/admin123、super_admin 角色、22 条菜单(含按钮权限码)、角色/用户绑定
  • manifest/sql/005_menu_paths.sql:按钮行 path 填 "METHOD /路径" 接口映射({id} 动态段)
  • auth/auth/info/auth/codes;路由拆分公开(NewAuth)/仅登录(NewProfile)/受保护(New)三组;新增 AdminAuthOnly 中间件
  • menu/menu/routes 返回 vben backend 动态路由树(按角色过滤+排序)
  • RBAC/admins/roles/menus/tree 及 CRUD含角色绑定、重置密码、菜单授权、删除校验子节点
  • log/log/files/log/tail(反向块扫描读尾部+关键词过滤+路径穿越防护)
  • 安全:接口鉴权改为「方法+路径→权限码」自动映射(PermissionForPath+matchRoute不再信任前端 X-Permission
  • 修复gf v2.10.2 ${ENV} 不自动替换 → cmd.injectEnv 注入MySQL driver 需 blank import contrib/drivers/mysql/v2gtime Format(PHP) 与 Layout(Go) 区分再次踩坑CreatedAt 输出 layout 原样)
  • log/ 加入 .gitignoreconfig.dev.yaml logger.path=log日志落盘

决策与理由

  • 接口鉴权用路径映射而非 X-Permission:原实现用户可用自己拥有的任意权限码访问任意受保护接口(越权漏洞);改为后端按 method+path 查 admin_menu 映射,未配置即拒绝。
  • 受保护接口分三层:公开 loginAdminAuthOnlyinfo/codes/routes登录即可取vben 登录后立即调用AdminAuthRBAC/日志)。
  • admin_menu 按钮行 path 存接口映射:与 type=1 菜单行的路由 path 语义区分开,避免冲突。
  • gf gen dao 不可用:本机 gf CLI 为公司定制版(生成 com.lib.gf.v2 import与项目官方 gf 不兼容;本次手动补齐 admin_menu 三字段entity/do/table后续换官方 CLI 或脚本化处理。

待办与风险

  • 前端对接vben5admin.xpcool.com 需配置 accessMode=backend、登录/信息/权限码/动态路由对接、系统管理三页面+日志监控页面、按钮级 v-access:code。
  • 冒烟测试已建 opuser/op 测试角色,可清理。
  • 菜单管理接口的 assignMenu 权限码暂无独立接口(角色授权在 role update 中完成),保留扩展位。
  • 生产环境 JWT_SECRET/DB_DSN 必须通过环境变量提供(${ENV} 不会自动替换)。

追加23:05前端 vben 对接(源码完成,本地启动验证受阻)

请求

继续做 vben 前端admin.xpcool.comvben 5.7.0 monorepo对接后端登录/菜单/按钮级权限/RBAC/日志监控。

变更(前端独立仓库 commit 4615e7c

  • .env.developmentVITE_GLOB_API_URL 置空、关闭 mock
  • vite.config.ts:代理 /admin/apihttp://localhost:8000
  • preferences.tsaccessMode: 'backend'(动态路由)、关闭 token 自动刷新
  • api/core/auth.ts:登录/权限码路径改 /admin/v1/...codes 解包 data.codes
  • api/core/user.ts/admin/v1/auth/info 字段映射adminId→userId、nickname→realName、homePath
  • api/core/menu.ts/admin/v1/menu/routes 解包 data.routes
  • 新增 api/system.tsapi/log.ts
  • 新增页面:views/system/admin|role|menu/index.vueCRUD+按钮级权限)、views/monitor/log/index.vue(文件列表/tail/关键词过滤/自动刷新)

决策与理由

  • 后端 component 值 system/admin/index 与 vben 映射vben normalizeViewPath 会去前缀、补前导 /、去 /views,最终匹配 views/**/*.vue,无需 .vue 后缀。
  • 前端请求路径写完整 /admin/v1/... + apiURL 置空:因 user(/api/v1)、open(/api/open/v1)、admin(/admin/v1) 前缀不同,写全路径最清晰,避免 proxy rewrite 混乱。
  • 字段映射在后端/前端约定:后端返回 adminId/nickname,前端映射为 vben UserInfo(userId/realName)

待办与风险(本地启动验证受阻)

  • node 环境system node 23.0.0 未编译 node:sqliteERR_UNKNOWN_BUILTIN_MODULEpnpm 11.16.0 依赖它;已用 managed node 22.22.2 + corepack wrapper 绕过。
  • pnpm install 卡住:已配 .npmrcnpmmirror 镜像 + node-linker=hoisted + store-dir=E:/.pnpm-store),但 install 在 "added 27→98" 反复循环,疑似某 native 依赖 postinstall 失败重试。dev server 未能启动验证
  • 后续步骤:① 定位卡住的依赖(pnpm install --reporter=append-only 看具体包);② 或跳过 postinstallpnpm install --ignore-scripts 后手动补 esbuild 等二进制);③ 完成 install 后 pnpm dev:antd 启动,浏览器验证登录/菜单/权限/日志。

追加23:40API 层 base/system/admin 分组重构

请求

优化 API 层设计:/api/admin/v1/base(基础常规)、/api/admin/v1/systemmenu/role/auth 系统管理)、/api/admin/v1/admin(后台管理),并同步 service/controller 层。

变更

  • api/admin/v1/ 重组为三子包:base/log.go(日志)、system/{auth,menu,menu_manage,role}.go(认证+菜单+角色)、admin/admin.go(管理员)
  • 路由前缀变更:/auth/*/system/auth/*/menu/routes/system/menu/routes/menus/system/menu/roles/system/role/admins/admin/log/*/base/log/*
  • internal/controller/admin/*.go 改 import 对应子包basev1/systemv1/adminv1
  • manifest/sql/006_menu_paths_v2.sql:按钮-接口 path 映射更新;system:role:assignMenu 无独立接口path 清空
  • 冒烟测试全通过:新路径 8 接口正常,旧路径 /admins 返回 Not Found

决策与理由

  • 权限码与路由分离permission 保持 system:admin:list 等逻辑标识不变,只改物理路由 path。管理员管理路由在 /admin/v1/admin 但权限码仍 system:admin:*(逻辑归属系统权限体系)。
  • service/dao 层不硬拆子包service 保持 internal/service 单包按领域接口组织GoFrame 惯例dao 为生成代码按表组织api/controller 体现 base/system/admin 分组即可。
  • assignMenu 权限码保留(前端按钮),但无独立后端接口(授权合并进 role updatepath 置空不映射。

待办与风险

  • 前端 API 路径需同步为 /admin/v1/{base,system,admin} 前缀(当前前端代码仍是旧路径)。