惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Vercel News
Vercel News
Microsoft Azure Blog
Microsoft Azure Blog
Stack Overflow Blog
Stack Overflow Blog
Martin Fowler
Martin Fowler
Hacker News - Newest:
Hacker News - Newest: "LLM"
Cyberwarzone
Cyberwarzone
Recorded Future
Recorded Future
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Threat Research - Cisco Blogs
Know Your Adversary
Know Your Adversary
Recent Announcements
Recent Announcements
L
LINUX DO - 热门话题
D
DataBreaches.Net
K
Kaspersky official blog
T
Threatpost
F
Full Disclosure
T
The Exploit Database - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
S
Securelist
I
Intezer
有赞技术团队
有赞技术团队
罗磊的独立博客
爱范儿
爱范儿
S
Schneier on Security
P
Privacy & Cybersecurity Law Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
L
LangChain Blog
美团技术团队
G
Google Developers Blog
T
Tor Project blog
Project Zero
Project Zero
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Hacker News
The Hacker News
W
WeLiveSecurity
Engineering at Meta
Engineering at Meta
Apple Machine Learning Research
Apple Machine Learning Research
aimingoo的专栏
aimingoo的专栏
PCI Perspectives
PCI Perspectives
L
LINUX DO - 最新话题
MyScale Blog
MyScale Blog
阮一峰的网络日志
阮一峰的网络日志
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
Hugging Face - Blog
Hugging Face - Blog
P
Proofpoint News Feed
Webroot Blog
Webroot Blog
T
Troy Hunt's Blog

Chenxu's Blog

RAG检索优化:从 68% 到 82.2% 学习深度学习 北邮计算机研究生选课指北 学习机器学习 上海篇 提出一个好问题 写代码的环境 选购一台合适的设备 计算机的第零课 序言 内蒙古篇 山东篇 一篇对transformers的疑惑 浅析GPUStack 浅谈后端项目分层 浙江篇 河南篇 一些三端协同的开发工具记录 浅谈编程语言中的 GC 在 Windows 上安装 Rust 美化博客 消息队列 MyBatis 现代交换原理2024年题目 回忆版 北邮计科本科课程指北 ClkLog埋点用户分析系统研究报告 大数据梧桐实验分享 大数据HBase实验分享 国产向量数据库研究实践 —— 基于 Milvus 的电影推荐系统 服务器环境配置 大数据HDFS实验分享 SSE多服务之间推送数据 数据库系统原理 操作系统 编译原理 手把手教你gozero从开发到部署 (番外篇): 介绍 gtodolist 前端项目 天津篇 北京篇 手把手教你gozero从开发到部署 (5): gtodolist 之任务的删除 前端相关问题 vue3 + element-plus学习 git相关命令 人工智能原理 使用 MongoTemplate 时开启事务 MySQL Redis 力扣刷题笔记之动态规划 记录一次 VSCode 出现的各种奇怪的问题 手把手教你gozero从开发到部署 (4): 在 model 中自己编写函数实现数据库分页查询 记录平时用到的 windows 右键管理 记录平时使用的 linux 的指令 安装 gstore 并使用 java api 手把手教你gozero从开发到部署 (番外篇): 记录第一次部署 CI/CD 的流程 记录完成 gtodolist 中遇到的 bug 手把手教你gozero从开发到部署 (2): gtodolist 之 user 模块开发 手把手教你gozero从开发到部署 (1): gtodolist 项目说明和环境准备 快速入门 gozero 框架 vue部署在nginx上的相关问题 记一次安装scrapy的报错 将博客部署至服务器 Java CS143: Compliers 《MySQL必知必会》读书笔记(2) 初识 GORM 初识 gin 框架 Spring 《MySQL必知必会》读书笔记(1) 本地搭建博客 🤝友链 🙋🏻‍♂️关于 Browser and Device Check
手把手教你gozero从开发到部署 (3): gtodolist 之任务的创建和修改
Chenxu · 2023-09-03 · via Chenxu's Blog

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

准备模块框架

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

生成 api 代码

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

syntax = "v1"

info(
	title: "Task API"
	desc: "API for task"
	author: "GuoChenxu"
	email: "guochenxu11@outlook.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 接口的实现, 所以后面我们会分开实现)