3.10 完整代码清单
概述
本节汇总 example 模块的后端四件完整代码(api / controller / service / logic),全部摘自项目实际文件(含注释),可作为手动开发新模块的对照模板。dao 四件为工具产出(见 3.3 DAO 数据层),此处不再罗列。
文件对照表
| 文件 | 行数规模 | 职责 |
|---|---|---|
api/v1/example.go | ~200 行 | 全部 Req/Res + gmeta 注解 |
internal/controller/example.go | ~90 行 | 薄层透传 |
internal/service/example.go | ~75 行 | 手维护接口三件套 |
internal/logic/example.go | ~200 行 | ModuleSpec 声明 + 委托 + 扩展方法 |
一、api/v1/example.go(节选:分页 + 详情 + 新增 + 修改)
go
package v1
import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gmeta"
)
// ============================== 案例演示模块(goframe_example) ==============================
// ExampleItem 案例演示行数据
// 用于分页列表等场景的基础信息
type ExampleItem struct {
Id int `json:"id"` // 主键ID
Name string `json:"name"` // 案例名称
Avatar string `json:"avatar"` // 案例图片
Type int `json:"type"` // 案例类型
Status int `json:"status"` // 案例状态
Sort int `json:"sort"` // 案例排序
CreateUser string `json:"createUser"` // 创建人
CreateTime *gtime.Time `json:"createTime"` // 创建时间
StatusText string `json:"statusText"` // 状态显示名(dict_item 解析)
}
// ExamplePageReq 案例演示分页请求
type ExamplePageReq struct {
gmeta.Meta `method:"get" path:"/api/example/page" summary:"案例演示分页" permission:"sys:example:page" log:"案例演示-查询分页记录" logType:"query"`
PageReq
Name string `json:"name" dc:"案例名称"` // 案例名称
Type int `json:"type" dc:"案例类型"` // 案例类型
Status int `json:"status" dc:"案例状态"` // 案例状态
Keyword string `json:"keyword" dc:"关键词"` // 关键词
}
// ExamplePageRes 案例演示分页结果
// 统一壳:records/total/size/current/pages
type ExamplePageRes = PageData[ExampleItem]
// ExampleDetailReq 案例演示详情请求
type ExampleDetailReq struct {
gmeta.Meta `method:"get" path:"/api/example/detail/:id" summary:"案例演示详情" permission:"sys:example:detail" log:"案例演示-查询详情" logType:"query"`
Id int64 `json:"id" v:"required#缺少ID" dc:"记录ID"` // 记录ID
}
// ExampleDetailRes 案例演示详情结果
type ExampleDetailRes struct {
Id int64 `json:"id"` // 主键ID
Name string `json:"name"` // 案例名称
Avatar string `json:"avatar"` // 案例图片
Type int `json:"type"` // 案例类型
Status int `json:"status"` // 案例状态
Sort int `json:"sort"` // 案例排序
StatusText string `json:"statusText"` // 状态显示名(dict_item 解析)
}
// ExampleAddReq 新增案例演示请求
type ExampleAddReq struct {
gmeta.Meta `method:"post" path:"/api/example/add" summary:"新增案例演示" permission:"sys:example:add" log:"案例演示-新增记录" logType:"add"`
Name string `json:"name" v:"required#请输入案例名称" dc:"案例名称"` // 案例名称
Avatar string `json:"avatar" dc:"案例图片"` // 案例图片
Type int `json:"type" dc:"案例类型"` // 案例类型
Status int `json:"status" dc:"案例状态"` // 案例状态
Sort int `json:"sort" dc:"案例排序"` // 案例排序
}
// ExampleIdRes 新增返回ID结构
type ExampleIdRes struct {
Id int64 `json:"id"` // 主键ID
}
// ExampleUpdateReq 修改案例演示请求
type ExampleUpdateReq struct {
gmeta.Meta `method:"put" path:"/api/example/update" summary:"修改案例演示" permission:"sys:example:update" log:"案例演示-修改记录" logType:"update"`
Id int64 `json:"id" v:"required#缺少ID" dc:"记录ID"` // 记录ID
Name string `json:"name" v:"required#请输入案例名称" dc:"案例名称"` // 案例名称
Avatar string `json:"avatar" dc:"案例图片"` // 案例图片
Type int `json:"type" dc:"案例类型"` // 案例类型
Status int `json:"status" dc:"案例状态"` // 案例状态
Sort int `json:"sort" dc:"案例排序"` // 案例排序
}
// ExampleUpdateRes 修改案例演示结果
type ExampleUpdateRes struct{}导入导出请求(multipart 与筛选复用):
go
// ExampleImportReq 导入案例演示请求
type ExampleImportReq struct {
gmeta.Meta `method:"post" path:"/api/example/import" summary:"导入案例演示" permission:"sys:example:import" log:"案例演示-导入数据" logType:"import"`
File *ghttp.UploadFile `p:"file" dc:"导入文件"`
}
// ExampleExportReq 导出案例演示请求
// 筛选条件与分页一致,导出=所见
type ExampleExportReq struct {
gmeta.Meta `method:"get" path:"/api/example/export" summary:"导出案例演示" permission:"sys:example:export" log:"案例演示-导出数据" logType:"export"`
Name string `json:"name" dc:"案例名称"` // 案例名称
Type int `json:"type" dc:"案例类型"` // 案例类型
Status int `json:"status" dc:"案例状态"` // 案例状态
}二、internal/controller/example.go(全文结构)
见 3.6 控制器开发——全部方法为一行透传 return service.Example().Xxx(ctx, req)。
三、internal/service/example.go(接口三件套)
见 3.7 服务注册与路由——IExample 接口 + localExample 实例 + Example() 取用 + RegisterExample() 注册。
四、internal/logic/example.go(核心声明与扩展方法)
见 3.5 逻辑层开发。补充导入导出扩展方法(真实代码):
go
// Import 导入案例演示
// 表头为表单字段名,按列序写入
func (s *sExample) Import(ctx context.Context, req *v1.ExampleImportReq) (res *v1.ExampleImportRes, err error) {
_, rows, err := excel.ReadSheet(req.File)
if err != nil {
return nil, err
}
res = &v1.ExampleImportRes{Total: len(rows)}
for i, row := range rows {
if cellAt(row, 0) == "" {
res.Failed++
res.Reasons = append(res.Reasons, fmt.Sprintf("第%d行:案例名称为空", i+2))
continue
}
if _, e := dao.Example.Ctx(ctx).Data(map[string]any{
"name": cellAt(row, 0),
"avatar": cellAt(row, 1),
"type": cellInt(row, 2, 0),
"status": cellInt(row, 3, 0),
"sort": cellInt(row, 4, 0),
}).Insert(); e != nil {
res.Failed++
res.Reasons = append(res.Reasons, fmt.Sprintf("第%d行:写入失败 %v", i+2, e))
continue
}
res.Success++
}
return res, nil
}
// Export 导出案例演示
// 与列表同套筛选条件,枚举列输出字典文本
func (s *sExample) Export(ctx context.Context, req *v1.ExampleExportReq) (res *v1.ExampleExportRes, err error) {
rows, err := exampleSpec.applyFilters(ctx, exampleSpec.Model(ctx), req).Order("sort, id").All()
if err != nil {
return nil, err
}
headers := []string{"案例名称", "案例图片", "案例类型", "案例状态", "案例排序"}
data := make([][]any, 0, len(rows))
for _, r := range rows {
data = append(data, []any{r["name"].String(), r["avatar"].String(), DictGetText(ctx, "example_type", r["type"]), DictGetText(ctx, "example_status", r["status"]), r["sort"].String()})
}
fileName, _, err := excel.WriteSheet(upload.BaseDir(ctx), "案例演示数据", headers, data)
if err != nil {
return nil, err
}
url := upload.GetFileUrl(ctx, fileName)
return &url, nil
}使用方式
手动开发新模块时,以本清单为骨架逐文件替换模块名与字段;或直接用生成器产出同等结构后再微调(生成器模板即按此规范制作):
sh
go run ./cmd/generator -tables "goframe_example|案例演示表"
go build ./... # 编译并重启