后端开发
路由注册、API/Controller/Logic开发规范
入口文件 module.go
module.go 是扩展的入口,在 init() 中完成路由注册和系统集成。
普通模式(纯后台管理)
大多数扩展只需要后台管理接口,使用单个 AdminAuth 路由组即可:
go
package shop
import (
"xygo/internal/addon"
"xygo/internal/middleware"
"xygo/addons/shop/controller"
// 空导入:触发 queues、crons 子包的 init() 注册
_ "xygo/addons/shop/queues"
_ "xygo/addons/shop/crons"
"github.com/gogf/gf/v2/net/ghttp"
)
func init() {
addon.Register(addon.Module{
Name: "shop",
Mount: func(s *ghttp.Server) {
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(
middleware.CORS,
middleware.ResponseHandler,
)
group.Group("/", func(ag *ghttp.RouterGroup) {
ag.Middleware(middleware.AdminAuth, middleware.DemoGuard)
ag.Bind(controller.NewV1())
})
})
},
})
}
注意:
queues/和crons/是独立子包,必须通过空导入_ "xygo/addons/shop/queues"来触发它们的init()。WebSocket 事件因为只是一行注册调用,通常直接写在module.go里即可。
系统启动时,addon.MountAll(s) 会自动调用所有已注册扩展的 Mount 函数挂载路由。
双控制器模式(含独立前台)
如果扩展同时提供平台管理端和独立用户端(如租户管理端、供应商端),需要使用双控制器模式:
go
package tenant
import (
"xygo/addons/tenant/controller"
"xygo/internal/addon"
"xygo/internal/middleware"
"github.com/gogf/gf/v2/net/ghttp"
)
func init() {
addon.Register(addon.Module{
Name: "tenant",
Mount: mountRoutes,
})
}
func mountRoutes(s *ghttp.Server) {
// 路由组 1:平台管理端 — 复用核心 AdminAuth
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(middleware.CORS, middleware.ResponseHandler)
group.Group("/", func(ag *ghttp.RouterGroup) {
ag.Middleware(middleware.AdminAuth, middleware.DemoGuard)
ag.Bind(controller.NewAdminV1())
})
})
// 路由组 2:扩展自身端 — 使用自定义鉴权
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(middleware.CORS, middleware.ResponseHandler)
group.Middleware(tenantResolve, tenantAdminAuth, middleware.DemoGuard)
group.Bind(controller.NewV1())
})
}
两个路由组使用不同的鉴权中间件,绑定不同的控制器 struct,互不干扰。使用脚手架创建时选择「是否有独立前台 → y」即可自动生成此结构。
Controller
普通模式
只需一个 ControllerV1,定义在 controller/controller.go 中:
go
package controller
type ControllerV1 struct{}
func NewV1() *ControllerV1 { return &ControllerV1{} }
所有业务方法的 receiver 都是 *ControllerV1。
双控制器模式
controller/controller.go 中定义两个 struct:
go
package controller
type AdminControllerV1 struct{} // 平台管理端 — 走 AdminAuth
type ControllerV1 struct{} // 扩展自身端 — 走自定义鉴权
func NewAdminV1() *AdminControllerV1 { return &AdminControllerV1{} }
func NewV1() *ControllerV1 { return &ControllerV1{} }
文件命名规则:
| 文件名 | Controller Receiver | 路由鉴权 | 示例 |
|--------|---------------------|---------|------|
| admin_*.go | *AdminControllerV1 | 核心 AdminAuth | admin_tenant.go |
| 其他(无 admin_ 前缀) | *ControllerV1 | 扩展自定义鉴权 | user.go、dept.go |
严格约束:
admin_*.go中的方法 receiver 必须是*AdminControllerV1,不能混用*ControllerV1,否则会导致接口走错鉴权中间件。
API 定义
在 api/ 目录下定义请求和响应结构体,使用 GoFrame 的规范路由标签:
go
package api
import "github.com/gogf/gf/v2/frame/g"
type ShopOrderListReq struct {
g.Meta `path:"/admin/shop/order/list" method:"get" tags:"商城" summary:"订单列表"`
Page int `json:"page" d:"1"`
PageSize int `json:"pageSize" d:"20"`
Status *int `json:"status"`
}
type ShopOrderListRes struct {
g.Meta `mime:"application/json"`
}
type ShopOrderEditReq struct {
g.Meta `path:"/admin/shop/order/edit" method:"post" tags:"商城" summary:"保存订单"`
Id uint64 `json:"id"`
}
type ShopOrderEditRes struct{}
type ShopOrderDeleteReq struct {
g.Meta `path:"/admin/shop/order/delete" method:"post" tags:"商城" summary:"删除订单"`
Id uint64 `json:"id" v:"required"`
}
type ShopOrderDeleteRes struct{}
路由路径规范:/admin/{扩展名}/{实体名}/{操作},如 /admin/shop/order/list。
Controller 方法
go
package controller
import (
"context"
api "xygo/addons/shop/api"
"xygo/addons/shop/service"
)
func (c *ControllerV1) ShopOrderList(ctx context.Context, req *api.ShopOrderListReq) (res *api.ShopOrderListRes, err error) {
// 调用 service 获取列表
res = &api.ShopOrderListRes{}
return
}
func (c *ControllerV1) ShopOrderEdit(ctx context.Context, req *api.ShopOrderEditReq) (res *api.ShopOrderEditRes, err error) {
return
}
func (c *ControllerV1) ShopOrderDelete(ctx context.Context, req *api.ShopOrderDeleteReq) (res *api.ShopOrderDeleteRes, err error) {
return
}
Logic
go
package logic
import (
"context"
api "xygo/addons/shop/api"
)
func OrderList(ctx context.Context, req *api.ShopOrderListReq) (res *api.ShopOrderListRes, err error) {
// 使用 g.DB() 或 dao 层查询数据库
// 表名规范:xy_{扩展名}_xxx,如 xy_shop_order
res = &api.ShopOrderListRes{}
return
}
func OrderEdit(ctx context.Context, req *api.ShopOrderEditReq) (res *api.ShopOrderEditRes, err error) {
return
}
func OrderDelete(ctx context.Context, req *api.ShopOrderDeleteReq) (res *api.ShopOrderDeleteRes, err error) {
return
}