6.5 添加新功能与数据库迁移
添加新功能与数据库迁移
新功能沿现有分层实现,并从一开始处理工作区隔离、权限、迁移和测试。不要先把逻辑堆进 Controller 再补架构。
实现顺序
- 在
app/model定义实体、状态常量和表名。 - 在
app/dto定义请求/响应及校验标签。 - 在
app/dao编写带workspace_id的查询。 - 在
app/service实现业务规则和事务。 - 在
app/controller绑定输入、检查角色、映射错误和返回统一响应。 - 在
config/autoload/router.go注册明确的 HTTP 方法和路径。 - 为三种数据库添加同版本迁移,并补测试与文档。
- 在管理端
src/api增加类型化客户端,再实现页面。
Controller 示例
func (ctrl WidgetController) Create(c httpInterfaces.RouterContextInterface) {
if !middleware.CanManageBusinessResource(c) {
ctrl.Error(c, constants.ErrCodeForbidden, "无权限创建资源")
return
}
var req dto.CreateWidgetRequest
if err := c.ShouldBindJSON(&req); err != nil {
ctrl.Error(c, constants.ErrCodeBadRequest, "请求参数错误: "+err.Error())
return
}
result, err := service.NewWidgetService(helper.GetHelper()).Create(
middleware.GetCurrentWorkspaceID(c),
middleware.GetCurrentUserID(c),
&req,
)
if err != nil {
ctrl.Error(c, constants.ErrCodeInternal, err.Error())
return
}
ctrl.Success(c, result)
}真实代码应把可预期的“不存在”“冲突”“无权限”分别映射为 404、409、403,不要全部返回 500。
迁移约定
结构变更在三个目录使用相同版本号和等价语义:
migrations/mysql/2026072209000000_add_widgets.sql
migrations/postgresql/2026072209000000_add_widgets.sql
migrations/sqlite/2026072209000000_add_widgets.sql每个 SQL 文件提供 Goose Up/Down 段。不要编辑已发布文件;新修复通过更高版本迁移完成。跨方言的数据回填确实需要 Go 逻辑时,可参考 migrations/0015_backfill_click_statistics_region.go 的注册方式,但需明确幂等、并发、多实例和失败恢复策略。
工作区与权限检查
- DAO 的详情、更新和删除条件同时包含资源 ID 与
workspace_id。 - 读操作允许当前工作区成员;业务写操作通常允许 owner/admin/member;系统管理写操作只允许 owner/admin。
- 返回 404 时避免泄露其他工作区存在同 ID 资源。
- 后台菜单权限只是体验层,服务端必须独立检查。
缓存与副作用
数据库写成功后再失效缓存。异步统计或回填需要可观察的错误日志和幂等设计;不要让 Goroutine 捕获即将失效的请求对象。涉及短链跳转时同时验证安全策略、高级路由、A/B、Query 透传和点击记录。
完成标准
- 三方言迁移可从空库和升级库执行;
- Service 规则、权限和错误分支有测试;
go test ./...、前端 typecheck 和构建通过;- API 文档、前端类型与后端 DTO 一致;
- 不记录或返回不必要的 Secret 和个人数据。