用 Go 写小型 CRM 完全可行,关键在快速收敛复杂度:用 sqlc 生成类型安全数据层、chi 构建分层中间件、embed+html/template 托管前端、Mailjet SDK 异步发信,并优先实现 MVP 功能。

用 Go 写小型 CRM 完全可行,但别指望靠 net/http + 手写 SQL 就能撑住半年——核心在于快速收敛复杂度,而不是堆功能。
用 sqlc 代替手写 SQL 和 ORM
Go 生态里硬写 CRUD 最容易失控的环节就是数据层:自己拼 SQL、自己写 struct 映射、自己处理 nullable 字段、自己补事务逻辑。结果是 200 行 handler 里混着 80 行 db.QueryRow 和 Scan 调用。
-
sqlc只要一个query.sql文件(比如SELECT * FROM customers WHERE id = $1),就能生成类型安全的 Go 函数,比如GetCustomer(ctx, id),返回Customer结构体,字段名、类型、空值处理全由 SQL 注释或 schema 推导 - 不引入运行时反射,没魔法,IDE 跳转/补全正常;比
gorm省掉 70% 的调试时间 - 注意:
sqlc不生成 migration,DDL 还得靠goose或golang-migrate管理,SQL 文件和 migration 要人工对齐
路由和中间件别碰 gorilla/mux,直接上 chi
小型 CRM 需要带 auth、日志、panic 捕获、路径参数解析——但又不需要 Kubernetes 级别的路由能力。此时 gorilla/mux 的 API 设计太松散,容易写出难以测试的闭包链;而 chi 的中间件栈是函数式组合,天然适合分层切关注点。
- 比如登录校验中间件:
func AuthMiddleware(next http.Handler) http.Handler,内部用http.Request.Context()存用户 ID,下游 handler 直接取ctx.Value("user_id") -
chi支持Route分组,CRM 的 /api/customers、/api/contacts、/api/tasks 可以各自挂独立中间件,互不污染 - 避免在中间件里做重定向或写响应体——只做鉴权、注入、记录,把“写 response”这件事严格留给 endpoint handler
embed 静态资源 + html/template 足够应付管理后台首页
小型 CRM 不需要 React/Vite 构建流程。Go 1.16+ 的 embed 能把 ./ui/dist 整个目录编译进二进制,配合 html/template 做简单变量替换(如当前用户姓名、未读消息数),页面加载快、部署就一个文件。
立即学习“go语言免费学习笔记(深入)”;
- 静态资源走
http.FileServer(http.FS(assets)),其中assets是embed.FS实例,不用单独起 Nginx - 模板里别写复杂逻辑:
{{if .IsAdmin}}...{{end}}可以,但不要在.html里调用formatDate这类函数——提前在 handler 里算好字符串传进去 - 如果后期要加图表,直接用
,别把前端依赖打进 Go 编译过程
发邮件别自己连 SMTP,用 mailjet 或 sendgrid SDK
本地测 SMTP 很容易卡在 DNS、TLS 握手、认证失败上;自己实现重试、退信解析、模板渲染,投入产出比极低。CRM 的客户通知、密码重置、任务提醒,本质是「触发式事件」,不是「实时通信」。
- 用官方 SDK(如
mailjet/mailjet-apiv3-go)发邮件,3 行代码搞定:构造Info{From, To, Subject, HTMLPart}→ 调SendMail→ 检查Response.StatusCode == 200 - 关键动作(如创建客户、分配销售)后,启动 goroutine 异步发信,别阻塞主流程;失败日志打清楚,方便人工补发
- 别在代码里硬编码邮箱密码——从环境变量读
MAILJET_API_KEY和MAILJET_API_SECRET,本地用.env,生产走 Secret Manager
真正卡住进度的从来不是“怎么写”,而是“哪些东西必须现在做,哪些可以三个月后再补”。比如搜索客户用 WHERE name ILIKE '%?%' 起步没问题,等客户量过万再切 Elasticsearch;比如权限先按角色(admin/sales/support)粗粒度控制,别一上来就搞 RBAC 表设计。










