摘要:当前系统基于一个开源 PHP 论坛项目进行二开,其本身在长期迭代过程中积累了一定的技术债。同时,采用的 Laravel 框架本身存在一定的性能瓶颈:其组件较为臃肿,复杂的 ORM 层与冗长的请求处理链路导致单次请求的 CPU 和内存占用偏高。更关键的是,业务中
本文结合一次把 PHP 论坛服务重构为 Go 的真实路径,分享关于如何驱动 Cursor 做对的事、按对的优先级和工程规范执行。
当前系统基于一个开源 PHP 论坛项目进行二开,其本身在长期迭代过程中积累了一定的技术债。同时,采用的 Laravel 框架本身存在一定的性能瓶颈:其组件较为臃肿,复杂的 ORM 层与冗长的请求处理链路导致单次请求的 CPU 和内存占用偏高。更关键的是,业务中因导量策略引发的瞬时高并发流量,多次导致系统负载飙高,即便频繁扩容仍难以稳定支撑。这种状况使系统在成本控制和长期稳定性方面面临挑战,优化迫在眉睫。考虑到团队核心技术栈为 Go 语言,其在并发处理和内存管理方面具有天然优势,预期迁移后能显著提升 QPS 并降低资源成本,因而开启重构之路。
把 Go 工程添加至 PHP 项目的工作区,如果有基础组件库,把组件库的工程也一并放入工作区,让其能够同时全面了解多个仓库的代码上下文。
清晰的项目结构是 Cursor 高效工作的基础。以下是一个示例,按照实际情况调整:
api:仅路由/DTO/中间件挂载,接口文件与 http 测试代码自动生成,root.go 暴露挂载点,Service.go 自动注册,不写业务逻辑。cmd/server/inject:依赖注入装配代码,自动生成,禁止人工改动。config:acm.go 定义配置项与加载器,config.gen.go 通过 make cfg/make wire 自动生成依赖暴露。internal/app:Gin 初始化、服务前缀、统一入参出参规范(svrless.go)。internal/database:通过 store.go 统一拿 MySQL/Redis/Mongo 等连接,不允许跨层直连。internal/dao:只放“数据库模型接口”与抽象;实现统一放 internal/DAO/db_xxx,通过 @autowire(set=db,dao.XxxDAO) 提供依赖;Service 只能依赖接口,绝不直接引用实现包。internal/svc_impls:服务接口的实现(svc_xxx),只被注入使用,外层不直接 import。internal/table:表结构定义,给 DAO/实现复用。internal/sdks:外部服务 SDK 的配置、结构、接口与实现。internal/pkgs:工具与常量的唯一归宿(替代“到处新建 utils”),强制集中,避免散落在业务文件。service:对外“服务接口契约层”,含入参/出参定义,是代码生成的唯一源头。scripts:框架相关构建脚本(如 service2api)。提示词示例:"请按照上述目录结构为我的论坛项目初始化 Go 脚手架,使用 Gin 框架和 GORM,遵循领域驱动设计原则。"
在 .cursorrules 文件中明确项目规范:
工具函数/常量只能放在 pkgs;禁止在 api/service/dao/svc_impls 内新建工具。只通过 internal/database/store.go 获取数据库/缓存连接。api 层只做参数绑定、鉴权、中间件、DTO 转换;业务编排在 internal/svc_impls。DAO 只暴露接口在 internal/dao,实现放 db_xxx 包,Service 仅依赖接口。注入代码仅由生成器维护(cmd/server/inject),禁止人工修改。将这些规则放在项目根目录的 .cursorrules 文件中,Cursor 会在每次生成代码时自动参考这些约束。
给 Cursor 建立清晰的映射关系,确保重构不是简单的代码翻译,而是合理的抽象和优化:
Controller(入口/路由/绑定) → api(自动生成 + 中间件)。Service(业务编排) → internal/svc_impls/svc_xxx。Model(ORM/查询) → internal/dao(接口)+ internal/dao/db_mysql(实现)。Entity/DTO → service(对外 DTO)与 internal/table(表结构/DO)。工具/常量 → internal/pkgs/gotil。字段与类型特别注意:由于是从弱类型语言重构到强类型语言,这方面容易出现问题:
明确nullability、默认值、枚举边界;避免"约定俗成"的隐式类型转换PHP返回的null/""/0/与Go的零值语义差异,必须在DTO与DAO层显式化JSON Tag、时间与时区、精度(decimal)、ID(雪花ID/大整型)要严格规范这里可以给 Cursor 明确具体的类型映射提示词,如:"PHP 中的可空字符串字段在 Go 中应该使用 sql.NullString 类型,确保数据库空值正确处理。"
始终遵循“单一接口优先,复杂接口再分解”的实施顺序,并在每个子任务完成后进行及时验证:
1)接口级拆分
执行原则:以单个接口为最小任务单元,逐个完成重构。
优势:降低变更风险,便于聚焦测试和验证。
Cursor 操作:一次只让 Cursor 处理一个文件,生成->测试->评审→下一步
例如:"请将 PHP 论坛的用户注册接口重构为 Go 版本,保持相同的参数校验和错误处理逻辑。"
2)模块级细化(针对复杂接口)
执行原则:当接口逻辑复杂时,按子功能/子查询进行二次拆分。
优先重构基础支撑模块(如中间件、baseController中的通用方法)再逐步处理具体接口业务逻辑采用自底向上的实现路径建议采用分步骤的提示词策略:
第一步:先让Cursor分析PHP代码逻辑第二步:基于分析结果生成Go实现"根据上述分析,用 Go 实现相同逻辑,注意错误处理和事务边界"
3)整体 review
让 Cursor 阅读一轮新项目的代码逻辑,判断是否与旧项目是否存在逻辑不一致的地方并指出。
提示词示例:"请对比 PHP 的 UserController.php 和 Go 的 user_api.go,指出两者在用户权限验证逻辑上的差异。"
重构完一个接口,立即进行自测。通过 curl 请求新旧服务,观察返回结构、字段值、排序等是否一致
校验清单:
结构一致性:字段是否齐全、类型是否一致值一致性:每个字段值一致(含默认值、空值处理)排序一致性:列表排序规则、稳定性分页一致性:total/page/page_size、边界页处理错误一致性:错误码、HTTP状态码、错误消息兼容性:老客户端关键字段是否仍可用Cursor 使用技巧:让 Cursor 生成自动化测试脚本,例如:"请编写一个对比测试脚本,能够同时向 PHP 和 Go 版本的用户查询接口发送请求,并对比关键字段的一致性。"
有条件的话还需做双读对比,把小范围线上流量通过网关镜像同一请求至 PHP 与 Go,两边独立处理但仅以 PHP 的结果对用户可见。在网关或比对服务中对两份响应做标准化后逐字段对比,记录差异。
通过 Cursor 驱动的重构方法,我们实现了:
性能显著提升:Go版本QPS达到PHP版本的3-5倍,资源消耗降低60%代码质量提高:强类型约束减少了运行时错误,清晰的分层提高了可维护性开发效率提升:Cursor处理了约60%的样板代码和简单逻辑,团队专注于核心业务重构不仅是语言迁移,更是架构优化和代码质量提升的机会。通过合理使用 Cursor 这类 AI 编程工具,可以大幅提高重构效率,同时保证代码质量。
来源:墨码行者