go-zero 实战 - Food AddFood

172 阅读3分钟

我们通过一个系列文章跟大家详细展示一个 go-zero 微服务实例,整个系列分十三篇文章,目录结构如下:

  1. go-zero 实战 - 服务划分与项目创建
  2. go-zero 实战 - User API Gateway
  3. go-zero 实战 - User Login
  4. go-zero 实战 - User Register
  5. go-zero 实战 - User Userinfo
  6. go-zero 实战 - Food API Gateway
  7. go-zero 实战 - Food Search
  8. go-zero 实战 - Food AddFood
  9. go-zero 实战 - Food DeleteFood
  10. go-zero 实战 - Food Foodlist
  11. go-zero 实战进阶 - rpc 服务
  12. go-zero 实战进阶 - 用户管理 rpc 服务
  13. go-zero 实战进阶 - 食材管理 rpc 服务

期望通过本系列文章带你在本地利用 go-zero 快速开发一个《食谱指南》系统,让你快速上手微服务。

我们在 上一篇 中介绍了如何搭建食材搜索接口,本篇我们采用相同的方式搭建添加食材的接口。你将在本篇了解到以下内容:

  1. 创建数据库表 user_food,并定义数据库表结构,以便建立 用户食材 两者之间的关联;
  2. 利用 goctl model 命令生成对应的 mysql CRUD 代码;
  3. 完善添加食材接口的逻辑。

创建数据库表 user_food,并定义数据库表结构

在本机 mysql 中的 foodguides 数据库中,执行如下 SQL 语句新建 user_food 表:

CREATE TABLE `user_food` (
                             `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
                             `userid` bigint COMMENT '用户Id',
                             `foodid` bigint COMMENT '食物Id',
                             `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
                             `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                             PRIMARY KEY (`id`),
                             UNIQUE KEY `userid_index` (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

执行 goctl model 命令生成 mysql CRUD 代码

编辑 foodmanage/model/food.sql 文件,追加上述创建 user_food 表的 SQL 语句。

cdfoodmanage/model 目录下,执行 goctl model 命令生成 mysql CRUD 代码:

➜  service:
$ cd foodmanage/model
$ goctl model mysql ddl -src food.sql -dir .
foodmodel.go already exists, ignored.
Done.

注册服务上下文的依赖

$ vim foodmanage/api/internal/svc/serviceContext.go

package svc

import (
    "FoodGuides/service/foodmanage/api/internal/config"
    "FoodGuides/service/foodmanage/model"
    "github.com/zeromicro/go-zero/core/stores/sqlx"
)

type ServiceContext struct {
    Config    config.Config
    FoodModel model.FoodModel
    UserFoodModel model.UserFoodModel
}

func NewServiceContext(c config.Config) *ServiceContext {
    return &ServiceContext{
       Config:    c,
       FoodModel: model.NewFoodModel(sqlx.NewMysql(c.Mysql.DataSource)),
       UserFoodModel: model.NewUserFoodModel(sqlx.NewMysql(c.Mysql.DataSource)),
    }
}

完善添加食材接口的逻辑

$ vim foodmanage/api/internal/logic/addfoodlogic.go

package logic

import (
    "FoodGuides/service/foodmanage/model"
    "context"
    "database/sql"
    "encoding/json"
    "errors"
    "fmt"
    "strconv"

    "FoodGuides/service/foodmanage/api/internal/svc"
    "FoodGuides/service/foodmanage/api/internal/types"

    "github.com/zeromicro/go-zero/core/logx"
)

type AddFoodLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
}

func NewAddFoodLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddFoodLogic {
    return &AddFoodLogic{
       Logger: logx.WithContext(ctx),
       ctx:    ctx,
       svcCtx: svcCtx,
    }
}

func (l *AddFoodLogic) AddFood(req *types.AddFoodRequest) (*types.AddFoodResponse, error) {
    // 获取 jwt 载体中 `uid` 信息,
    uid, _ := l.ctx.Value("uid").(json.Number).Int64()

    foodId, _ := strconv.ParseInt(req.FoodId, 10, 64)

    _, err := l.svcCtx.FoodModel.FindOne(l.ctx, foodId)
    
    if err != nil {
       if err == model.ErrNotFound {
          return nil, errors.New(fmt.Sprintf("不存在 ID 为 %d 的食材", foodId))
       }
       return nil, err
    }
    
    data := model.UserFood{
       Userid: sql.NullInt64{
          Int64: uid,
          Valid: true,
       },
       Foodid: sql.NullInt64{
          Int64: foodId,
          Valid: true,
       },
    }

    _, err = l.svcCtx.UserFoodModel.Insert(l.ctx, &data)
    if err != nil {
       return nil, err
    }

    return &types.AddFoodResponse{}, nil
}

修改 Response 返回格式

$ vim foodmanage/api/internal/handler/addfoodhandler.go

package handler

import (
    "FoodGuides/common/responsex"
    "net/http"

    "FoodGuides/service/foodmanage/api/internal/logic"
    "FoodGuides/service/foodmanage/api/internal/svc"
    "FoodGuides/service/foodmanage/api/internal/types"
    "github.com/zeromicro/go-zero/rest/httpx"
)

func AddFoodHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
       var req types.AddFoodRequest
       if err := httpx.Parse(r, &req); err != nil {
          httpx.OkJson(w, responsex.FailureResponse(nil, err.Error(), 1000))
          return
       }

       l := logic.NewAddFoodLogic(r.Context(), svcCtx)
       resp, err := l.AddFood(&req)
       if err != nil {
          httpx.OkJson(w, responsex.FailureResponse(nil, err.Error(), 1000))
       } else {
          httpx.OkJson(w, responsex.SuccessResponse(resp, "新增成功"))
       }
    }
}

启动服务

启动 food api 服务, 运行成功后,food api 则运行在本机的 8889 端口

➜  service:  
$ go run foodmanage/api/food.go -f foodmanage/api/etc/food-api.yaml
Starting server at 0.0.0.0:8889...

我们用 Postman 尝试请求 /food/addfood 接口:

  1. PostmanAuthorization 选项中选择 Bearer Token,填写登录成功后 Api 返回的 accessToken 字段值。
  2. PostmanBody 选项中选择 raw,并设置内容为 {"foodId": "1"}

点击发送请求按钮,有如下截图的响应说明接口运行正常。

image.png

我们去查看本地数据库表 user_food 表,将会看到有一条新记录已被插入到表中。

image.png

这样 Food AddFood 接口就开发完成了。下一篇我们将介绍如何搭建删除食材接口。

上一篇《go-zero 实战 - Food Search》

下一篇《go-zero 实战 - Food DeleteFood》