本章节内容均在 task 目录下完成

准备模块框架

这里我们先把所有接口的框架都生成好, 基本步骤都和 user 模块一样

生成 api 代码

cmd/api 目录下新建一个 desc 目录编写 api 文件

syntax = "v1"

info(
	title: "Task API"
	desc: "API for task"
	author: "GuoChenxu"
	email: "2269409349@qq.com"
	version: "1.0"
)

// task_create
type (
	CreateReq {
		Title   string `form:"title"`
		Content string `form:"content"`
		Status  string `form:"status"`
	}

	CreateResp {
		Status  int    `json:"status"`
		Data    string `json:"data"`
		Message string `json:"msg"`
		Error   string `json:"error"`
	}
)

type Task {
	Id         int64  `json:"id"`
	Title      string `json:"title"`
	Content    string `json:"content"`
	View       int    `json:"view"`
	Status     int    `json:"status"`
	CreateAt  int64  `json:"create_at"`
	StartTime int64  `json:"start_time"`
	EndTime   int64  `json:"end_time"`
}

// task_list
type (
	ListReq {
		Limit string `form:"limit"`
		Start string `form:"start"`
	}

	Data {
		Task  []Task `json:"item"`
		Total int    `json:"total"`
	}

	ListResp {
		Status  int    `json:"status"`
		Data    Data   `json:"data"`
		Message string `json:"msg"`
		Error   string `json:"error"`
	}
)

// task_show
type (
	ShowReq {
		Id int64 `form:"id"`
	}

	ShowResp {
		Status  int    `json:"status"`
		Data    Task   `json:"data"`
		Message string `json:"msg"`
		Error   string `json:"error"`
	}
)

// task_update
type (
	UpdateReq {
		Id      string `form:"id"`
		Title   string `form:"title"`
		Content string `form:"content"`
		Status  string `form:"status"`
	}

	UpdateResp {
		Status  int    `json:"status"`
		Data    string `json:"data"`
		Message string `json:"msg"`
		Error   string `json:"error"`
	}
)

// task_search
type (
	SearchReq {
		Info string `form:"info"`
	}

	SearchResp {
		Status  int    `json:"status"`
		Data    Data   `json:"data"`
		Message string `json:"msg"`
		Error   string `json:"error"`
	}
)

// task_delete
type (
	DeleteReq {
		Id string `form:"id"`
	}

	DeleteResp {
		Status  int    `json:"status"`
		Data    string `json:"data"`
		Message string `json:"msg"`
		Error   string `json:"error"`
	}
)

@server(
	prefix: api/v1
	jwt: JwtAuth
)

service user {
	@doc "task_create"
	@handler TaskCreate
	post /task_create (CreateReq) returns (CreateResp)

	@doc "task_list"
	@handler TaskList
	get /task_list (ListReq) returns (ListResp)

	@doc "task_show"
	@handler TaskShow
	get /task_show (ShowReq) returns (ShowResp)

	@doc "task_update"
	@handler TaskUpdate
	post /task_update (UpdateReq) returns (UpdateResp)

	@doc "task_search"
	@handler TaskSearch
	post /task_search (SearchReq) returns (SearchResp)

	@doc "task_delete"
	@handler TaskDelete
	post /task_delete (DeleteReq) returns (DeleteResp)
}

然后在 api 目录下生成代码文件

goctl api go --api .\desc\task.api --dir .\ --style=go_zero

生成 rpc 代码

同样地, 在 cmd/rpc 目录下新建 desc 目录, 编写 proto 文件

syntax = "proto3";

package pb;
option go_package = "./pb";

// 定义消息类型
message CreateReq {
  int64 Uid = 1;
  string Title = 2;
  string Content = 3;
  int32 Status = 4;
}

message CreateResp{
  int32 Status = 1;
  string Data = 2;
  string Message = 3;
  string Error = 4;
}

message Task{
  int64 Id = 1;
  string Title = 2;
  string Content = 3;
  int64 View = 4;
  int32 Status = 5;
  int64 CreateAt = 6;
  int64 StartTime = 7;
  int64 EndTime = 8;
}

message Data{
  repeated Task Task = 1;
  int64 Total = 2;
}

message ListReq{
  int64 Uid = 1;
  int64 Limit = 2;
  int64 start = 3;
}

message ListResp{
  int32 Status = 1;
  Data Data = 2;
  string Message = 3;
  string Error = 4;
}

message ShowReq{
  int64 Id = 1;
  int64  Uid = 2;
}

message ShowResp{
  int32 status = 1;
  Task Task = 2;
  string Message = 3;
  string Error = 4;
}

message UpdateReq{
  string Id = 1;
  int64 Uid = 2;
  string Title = 3;
  string Content = 4;
  int32 Status = 5;
}

message UpdateResp{
  int32 Status = 1;
  string Data = 2;
  string Message = 3;
  string Error = 4;
}

message SearchReq{
  int64 Uid = 1;
  string Info = 2;
}

message SearchResp{
  int32 Status = 1;
  Data Data = 2;
  string Message = 3;
  string Error = 4;
}

message DeleteReq{
  int64 Uid = 1;
  string Id = 2;
}

message DeleteResp{
  int32 Status = 1;
  string Data = 2;
  string Message = 3;
  string Error = 4;
}

// 定义服务
service taskrpc {
  rpc CreateTask(CreateReq) returns(CreateResp);
  rpc ListTask(ListReq) returns (ListResp);
  rpc ShowTask(ShowReq) returns (ShowResp);
  rpc UpdateTask(UpdateReq) returns (UpdateResp);
  rpc SearchTask(SearchReq) returns (SearchResp);
  rpc DeleteTask(DeleteReq) returns (DeleteResp);
}

然后在 rpc 目录下生成代码

goctl rpc protoc .\desc\task.proto --go_out=.\ --go-grpc_out=.\ --zrpc_out=.\ --style=go_zero

生成 model 代码

model 目录下我们根据数据库中的 task 表生成代码文件, 和

goctl model mysql datasource -url="root:101325@tcp(127.0.0.1:3306)/gtodolist" -table="task" --dir="./" --home="../template/gorm-gozero/1.4.2" --style=go_zero --cache=true

到目前为止我们 task 模块的代码就全部生成好了

修改配置文件

接下来我们要修改项目的配置文件

api

修改 etc/task.yaml 配置文件

Name: task
Host: 0.0.0.0
Port: 22302

Log:
    Encoding: plain

TaskRpcConfig:
    Etcd:
        Hosts:
            - 127.0.0.1:2379
        Key: task.rpc

# jwt验证
JwtAuth:
    AccessSecret: gtodolist
    AccessExpire: 31536000

internal/config/config.go

type Config struct {
	rest.RestConf
	JwtAuth struct {
		AccessSecret string
		AccessExpire int64
	}
	TaskRpcConfig zrpc.RpcClientConf
}

internal/svc/service_context.go

type ServiceContext struct {
	Config          config.Config
	TaskRpcClient   taskrpc.Taskrpc
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config:          c,
	    TaskRpcClient:   taskrpc.NewTaskrpc(zrpc.MustNewClient(c.TaskRpcConfig)),
	}
}

rpc

同样是 etc/task.yaml 文件

Name: task.rpc
ListenOn: 0.0.0.0:22352

Etcd:
    Hosts:
        - 0.0.0.0:2379
    Key: task.rpc

# 日志
Log:
    Encoding: plain

# jwt验证
JwtAuth:
    AccessSecret: gtodolist
    AccessExpire: 31536000

# mysql
Mysql:
    Path: 127.0.0.1
    Port: 3306
    Dbname: gtodolist
    Username: root
    Password: "101325"
    MaxIdleConns: 10
    MaxOpenConns: 10
	LogZap: false
    Config: parseTime=True&loc=Local

Cache:
    - Host: 127.0.0.1:6379
      Pass: "101325"

Redis:
    Host: 127.0.0.1:6379
    Pass: "101325"
    Type: node
    Key: task.rpc

internal/config/config.go

type Config struct {
	zrpc.RpcServerConf
	Mysql gormc.Mysql
	Cache cache.CacheConf
}

internal/svc/service_context.go

type ServiceContext struct {
	Config    config.Config
	TaskModel model.TaskModel
	RedisClient *redis.Redis
}

func NewServiceContext(c config.Config) *ServiceContext {
	db, err := gormc.ConnectMysql(c.Mysql)
	if err != nil {
		log.Fatal(err)
	}
	return &ServiceContext{
		Config:    c,
		TaskModel: model.NewTaskModel(db, c.Cache),
		RedisClient: redis.MustNewRedis(c.Redis.RedisConf),
	}
}

编写核心逻辑

api

task_create

internal\logic\task_create_logic.go

func (l *TaskCreateLogic) TaskCreate(req *types.CreateReq) (resp *types.CreateResp, err error) {
	status, err := strconv.Atoi(req.Status)
	if err != nil {
		status = 1
	}

	createResp, err := l.svcCtx.TaskRpcClient.CreateTask(l.ctx, &pb.CreateReq{
		Uid:     ctxdata.GetUidFromCtx(l.ctx),
		Title:   req.Title,
		Content: req.Content,
		Status:  int32(status),
	})

	if err != nil {
		return &types.CreateResp{
			Status:  int(vo.ErrServerCommonError.GetErrCode()),
			Message: err.Error(),
			Error:   err.Error(),
		}, nil
	}

	resp = &types.CreateResp{}
	_ = copier.Copy(resp, createResp)
	return resp, err
}

task_update

internal\logic\task_update_logic.go

func (l *TaskUpdateLogic) TaskUpdate(req *types.UpdateReq) (resp *types.UpdateResp, err error) {
	status, err := strconv.Atoi(req.Status)
	if err != nil {
		status = 1
	}

	updateResp, err := l.svcCtx.TaskRpcClient.UpdateTask(l.ctx, &pb.UpdateReq{
		Id:      req.Id,
		Uid:     ctxdata.GetUidFromCtx(l.ctx),
		Title:   req.Title,
		Content: req.Content,
		Status:  int32(status),
	})
	if err != nil {
		return &types.UpdateResp{
			Status:  int(vo.ErrServerCommonError.GetErrCode()),
			Message: err.Error(),
			Error:   err.Error(),
		}, nil
	}

	resp = &types.UpdateResp{}
	_ = copier.Copy(resp, updateResp)
	return resp, err
}

rpc

create_task

internal\logic\create_task_logic.go

func (l *CreateTaskLogic) CreateTask(in *pb.CreateReq) (*pb.CreateResp, error) {
	task := &model.Task{
		Uid:    in.Uid,
		Title:  in.Title,
		Status: int64(in.Status),
		Content: sql.NullString{
			String: in.Content,
			Valid:  true,
		},
		StartTime: time.Now(),
	}

	err := l.svcCtx.TaskModel.Insert(l.ctx, nil, task)
	if err != nil {
		return nil, errors.Wrap(vo.ErrDBerror, "数据插入出错")
	}

	return &pb.CreateResp{
		Status:  vo.OK,
		Data:    vo.SUCCESS,
		Message: vo.SUCCESS,
	}, nil
}

update_task

internal\logic\update_create_logic.go

执行流程: 查询该用户是否存在该任务 (捕获错误) -> 修改任务 -> 返回结果

func (l *UpdateTaskLogic) UpdateTask(in *pb.UpdateReq) (*pb.UpdateResp, error) {
	// 先根据 id 查询该用户是否存在该任务
	id, err := strconv.Atoi(in.Id)
	if err != nil {
		return nil, errors.Wrap(vo.ErrRequestParamError, "任务id错误")
	}

	task, err := l.svcCtx.TaskModel.FindOne(l.ctx, int64(id))
	if err != nil && err != model.ErrNotFound {
		return nil, errors.Wrap(vo.ErrDBerror, "数据库查询出错")
	}

	if task.Uid != in.Uid {
		return nil, errors.Wrap(vo.ErrRequestParamError, "没有这条任务信息")
	}

	// 修改任务
	task = &model.Task{
		Id:        task.Id,
		CreatedAt: task.CreatedAt,
		Uid:       task.Uid,
		Title:     in.Title,
		Status:    int64(in.Status),
		Content: sql.NullString{
			String: in.Content,
			Valid:  true,
		},
		StartTime: time.Now(),
	}
	err = l.svcCtx.TaskModel.Update(l.ctx, nil, task)
	if err != nil {
		return nil, errors.Wrap(vo.ErrDBerror, "数据库修改失败")
	}

	return &pb.UpdateResp{
		Status:  vo.OK,
		Data:    vo.SUCCESS,
		Message: vo.SUCCESS,
	}, nil
}

现在任务的创建和修改就完成了 (现在我们在修改任务信息时都会先获取一下这个任务是否存在, 事实上, 这一部分就是另一个 task_show 接口的实现, 所以后面我们会分开实现)