当前位置: 首页 > news >正文

从 PHP 到 AI + Golang,程序员自救转型手记(七):建立 CLAUDE.md 文件、整理目录结构

这是一个系列 Blog,作者将以一个 PHP 全栈工程师的身份,利用 AI 工具(claude code、codex、deepseek、豆包等):从零开始学习 golang 语言,并最终完成 ai-go-mall(github | gitee)开源项目的制作,全程记录分享。

在上一期,我们已经完成 “泛型基服务、控制器、仓储实现,自动发现和注册业务路由”,本期将完成:建立 CLAUDE.md 文件、整理目录结构

建立 CLAUDE.md 文件

这个文件根据 CLAUDE 官方的定义,应当在 Claude 第二次犯同样的错误,或者当某个约定给它讲过两次以上时,可以将这些内容写入 CLAUDE.md,当然也可以提前讲清项目定位、目录结构,以便 CLAUDE 更好的工作。

ai-go-mall 项目至今,已经使用 cc 实现了配置系统(带自动发现、环境配置文件、环境变量覆盖)、数据库连接(带读写分离,支持链式+函数式选项自定义 db)、模型自动发现和迁移、四层业务架构的基类实现、自动发现和注册路由、统一响应结构和函数(也支持链式调用和函数式选项)等一大堆的功能,却连个 CLAUDE.md 都没有,足以见得 cc 的强大,也可能说明了当开发者本身技术够硬时,这些都是花架子,无需过于纠结(因为总有小伙伴纠结这东西咋写,实际上就是随便写,没有固定格式,不要被网上卖课的哄骗(免费的可以看),大不了你多调几次,调好了能少几轮对话,或者多节约一些 token)。

我们直接使用/init命令,让 cc 先自己做一份CLAUDE.md出来,然后第一件事就是删除多余内容,因为此文件将作为系统级上下文融入每一次对话中,而 AI 很多时候并不需要特别细致的解释即可很好的完成工作,最终整理出来的CLAUDE.md如下:

PS:
其实里边还有很多都是特意改了给人看的,让初次上手的开发者也能快速了解项目总体情况;我个人认为目前最值得写入的内容:

  1. 尽量使用 GORM 的 Generics API 而不是 Traditional API
  2. 只使用 GET 和 POST 请求方式:大多数 CDN/全站加速 服务对 PUT、DELETE 兼容性差
  3. 当我询问某方案是否合理时,请先根据社区惯例判断是否合理,合理直接实现,不合理取消实现并解释原因
# CLAUDE.md 本文件为 Claude Code 在当前代码库中工作时提供指导。 ## 项目概述 爱购商城(ai-go-mall),技术栈:Go 1.25 + Gin + GORM + PostgreSQL。 ## 回答偏好 - 回复使用中文 - 遇到有多种实现方案时,列出选项让我选择,而不是直接选一种 - 当我询问某方案是否合理时,请先根据社区惯例判断是否合理,合理直接实现,不合理取消实现并解释原因(社区惯例指对应技术栈的社区,如 golang 开源社区,Gin 开源社区,开源高星仓库,官方文档,权威 blog 等) ## 泛型基类体系 核心应用架构为:泛型驱动的四层架构模式,请求 → Handler(控制器)→ Service(业务逻辑)→ Repository(数据访问)→ Model(数据模型) 前三层都有**泛型基础实现**,具体模块通过**组合(嵌入)**复用: | 层级 | 泛型基类 | 接口 | 文件 | | ---------- | --------------- | ---------------- | ----------------------------- | | Repository | `Repository[T]` | `IRepository[T]` | `internal/repository/base.go` | | Service | `Service[T]` | `IService[T]` | `internal/service/base.go` | | Handler | `Handler[T]` | 无 | `internal/handler/base.go` | **扩展模式**(以 User 为例): - `UserRepository` 嵌入 `*Repository[model.User]`,可访问 `GetDB()` 编写自定义查询 - `UserService` 嵌入 `IService[model.User]` 并持有 `*UserRepository`,可覆盖业务逻辑 - `UserHandler` 嵌入 `*Handler[model.User]` 并持有 `*UserService`,注册路由可调用 `RegisterBaseRoutes` 后再追加自定义路由 ## 路由自动发现 `internal/router/registry/` 提供 `Routes` 切片 + `Register(fn)` 函数。 子模块在 `init()` 中调用 `registry.Register(func(r *gin.Engine) { ... })` 自注册路由分组,`internal/router/router.go` 通过空白导入触发 `init()`: router.go ──_ import──→ admin/ ──init()──→ registry.Register(...) ├─_ import──→ user/ ──init()──→ registry.Register(...) └─Setup() ──→ 遍历 registry.Routes 新增路由模块: 1. 新建子目录并于 `init()` 调 `registry.Register` 2. 在 `router.go` 加一行空白导入以触发 `init()` ## 启动流程 1. `config.Init()` — 合并 `config/*.yaml` + `.env.yaml`,环境变量覆盖,热加载 2. `database.Init()` — 连接 PostgreSQL,配置读写分离,`AutoMigrate` 所有 `model.Register()` 的模型 3. `engine.Use(database.Middleware())` — 注入 `*gorm.DB` 到 `gin.Context` 4. `router.Setup(engine)` — 遍历 `registry.Routes` 注册所有路由 5. `engine.Run()` ## 关键约定 - **只使用 GET 和 POST 请求方式**:大多数 CDN/全站加速 服务对 PUT、DELETE 兼容性差 - **包名**:全小写、单数、无下划线(`handler`、`service`) - **文件名**:全小写、下划线分隔(`user_service.go`) - **导出符号**:大驼峰,私有符号小驼峰 - **数据库字段**:蛇形命名(`user_name`、`created_at`) - **统一响应**:`response.Success(c, opts...)` / `response.Fail(c, opts...)`,支持 functional options 和链式调用两种风格,优先使用 functional options - **GORM**:尽量使用 `GORM` 的 `Generics API(gorm.G[Model](db)....)`,而不是 `Traditional API`;在使用 `Generics API` 时,一般应直接使用全局 db 实例(`internal\database\database.go` 中的 `GetDB` 可获取),调用操作方法时再传递合适的 ctx 即可。

整理目录结构

新增目录

比起最初的目录结构,我在internal下增加了database、response、router三个目录,它们三都是值得于 internal 单独一个目录存放的内容。

  1. database:数据库的重要性不必多少,单独一个包首先就是调用方法简洁方便,虽目前只有数据库连接实现、未来可能会有数据库中间件、一些数据库相关的全局类等等
  2. router:路由系统作为 WEB 项目核心,且未来路由定义文件可能很多,单目录(又支持子级目录)的路由自动发现和注册非常合理
  3. response:存放统一响应的实现,响应比数据库的调用会更频繁,单独包只为调用方便也是值得的

四层架构功能明确

即 Handler → Service → Repository → Model 四层应用架构,说实话,我还是第一次接触这种架构,只是基本理解,这里结合各大 AI 的解释,对架构各部分功能进行明确如下:

Handler
  1. 处理器层
  2. 解析请求(JSON → struct)> 调用 Service > 序列化响应(struct → JSON)
  3. 可以做请求合法性校验,如手机号格式、参数非空(一般是参数不对就可以直接返回错误信息的检查)
Service
  1. 服务层
  2. 处理业务逻辑,业务规则校验(如余额是否足够),逻辑编排,事务控制
  3. 接收 Handler 层调用,调用 Repository 层,返回结果给 Handler 层
Repository
  1. 仓储层
  2. 只处理数据库操作,把数据从 DB 搬到内存,或反过来
  3. 接收 Service 层调用,返回结果给 Service 层
Model
  1. 模型层
  2. 只定义表结构,可有表名函数
常见问题
如何区分某参数应该在 Handler 还是 Service 层检查?

Handler 中的检查,和业务无关,只看 “请求对不对”,非法请求在入口直接拦截,不进业务、不查库,包括:

  1. 参数非空(必传字段)
  2. 参数格式错误(手机号、邮箱、身份证、UUID)
  3. 参数长度超限(用户名太短 / 太长)
  4. 参数类型错误(数字传成字符串)
  5. 枚举值不在范围内(性别只允许男 / 女 / 未知)
  6. 路径参数 / 查询参数格式错误
  7. 请求体结构非法

总结:前端传错了、格式错了、少传了 → Handler 直接拦掉

Service 中的检查和业务强相关,必须依赖业务逻辑 / 数据库 / 状态判断的,包括:

  1. 数据是否已存在(用户名重复、手机号已注册)
  2. 数据状态是否允许操作(已取消的订单不能支付)
  3. 权限校验(不是自己的地址不能删除)
  4. 业务规则限制(余额不足、库存不足、年龄不够)
  5. 多参数关联校验(开始时间不能晚于结束时间)
  6. 跨表 / 跨服务校验(用户是否存在、商品是否上架)

总结:需要查库、需要业务状态、需要走流程、前端不可能知道的

Handler 和 Service 能否融合?

不能,Service 层可以脱离 HTTP 单独测试,Service 层还可以供定时任务、消息队列直接使用。

http://www.gsyq.cn/news/1591831.html

相关文章:

  • 终极指南:如何免费快速安装大气层整合包系统
  • FastAPI+LangChain打造智能招聘系统-网易云课堂
  • 头油头痒夏天总反复?用藿香正气水洗个头,比控油洗发水管用
  • 如何彻底清理Windows“此电脑“中的顽固图标:MyComputerManager完整指南
  • 别再重装系统了!IntelliJ IDEA迁移/重装后秒恢复全部配置的3种军工级备份法(含自动化脚本)
  • snscrape+Hugging Face实现无API推文情感分析
  • 诡异!MLCC 储存后随机短路?这篇复盘帮你揪出隐形元凶
  • MySQL 8.4.9 部署
  • 3000万加注中医垂直大模型:ChatiSS 正在走一条 DeepSeek 式的「自造血」技术路线
  • 【通配符使用指南】
  • 基于HNN的化学家教统计计算平台的搭建
  • 全能电商3D立体促销标题字效样机
  • 【课程设计/毕业设计】消防知识数字化竞赛学习平台的设计与实现(SpringBoot) 社区消防安全教育竞赛小程序系统设计与实现【附源码、数据库、万字文档】
  • ScrapeGraphAI:用自然语言驱动的AI网页爬虫实战指南
  • 布格替尼(Brigatinib)不良反应,做好安全管控
  • 【计算机毕业设计案例】基于 SpringBoot 的书籍拍卖订单管理系统设计与实现 微信端图书拍卖交易运维管理系统设计与实现(程序+文档+讲解+定制)
  • VortMall微服务商城系统v1.3.7重磅更新|『邮箱登录+Facebook社交升级』
  • 太阳能板光伏缺陷检测数据集VOC+YOLO格式719张4类别
  • 代购商品多源采集架构实战:策略模式+责任链处理淘宝/1688链接
  • Pyodide终极指南:在浏览器中无缝运行Python的完整解决方案
  • 微信小程序毕设项目:基于 SpringBoot + 小程序的校园社团智能管理系统设计与实现高校社团招新、活动、档案一体化管理系统设计与实现 (源码+文档,讲解、调试运行,定制等)
  • 并查集的典型应用:统计省份数量
  • 跨语言项目开发:Cursor 联动 Claude Code 搞定 Java+Python 混合工程难题
  • KMP与AC自动机:让字符串匹配“跳着走”
  • 奇门WMS-A与金蝶云星空的数据集成价值分析
  • 全光校园网络等保合规建设方案
  • sqlserver设置最大占用内存
  • 华为交换机风扇异常处理
  • 抢演唱会门票稳了|鸿蒙6.1+抢票引擎,华为nova16系列让我抢票率飙升
  • 计算机小程序毕设实战-基于 SpringBoot 的移动端消防知识答题竞赛平台设计与实现 面向校园普及的消防安全知识竞赛小程序设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】