(记录一下自己做项目的过程) 基于go-zero实现的简易的网盘系统,如果有小伙伴对这个项目感兴趣,可以去网上搜索一些资料。这里推荐一下我学习的来源:【项目实战】基于Go-zero、Xorm的网盘系统_哔哩哔哩_bilibili
确定功能模块:
目录
创建项目--安装xorm( go get xorm.io/xorm)
文档地址:Xorm
连接数据库:
xorm.NewEngine("mysql", "root:123@/test?charset=utf8")
先创建UserBasic结构体,为了之后的测试,如下:(新建models文件夹,在文件夹下新建user_basic.go)
- package Models
-
- type UserBasic struct {
- ID int
- Identity string
- Name string
- Password string
- Email string
- }
-
- func (table UserBasic) TableName() string {
- return "user_basic"
- }
之后通过 engine.CreateTables(),参数为一个或多个空的对应Struct的指针,创建数据表,具体操作如下:(为了测试,新建一个文件夹test,新建xorm_test.go)
- package test
-
- import (
- "CloudDisk/Models"
- bytes2 "bytes"
- "encoding/json"
- _ "github.com/go-sql-driver/mysql"
- "testing"
- "xorm.io/xorm"
- )
-
- func TestXorm(t *testing.T) {
- engine, err := xorm.NewEngine("mysql", "root:123456@tcp(127.0.0.1:3306)/clouddisk?charset=utf8")
- if err!=nil {
- t.Fatal(err)
- }
- TableUser:=make([]*Models.UserBasic,0)
- m := new(Models.UserBasic)
- err = engine.CreateTables(m)
- if err!=nil {
- t.Fatal(err)
- }
- err = engine.Find(&TableUser)//这里查出来的是地址
- if err!=nil {
- t.Fatal(err)
- }
- //将以json格式输出
- bytes, err:= json.Marshal(TableUser)//转换成BYTE数组
- if err!=nil {
- t.Fatal(err)
- }
- dst := new(bytes2.Buffer)
- err = json.Indent(dst, bytes, "", " ") //转换为buffer
- if err!=nil {
- t.Fatal(err)
- }
- println(dst.String())
- }
安装:
go get -u github.com/zeromicro/go-zero
因为项目比较小,所以直接选择单体服务就可以了,
安装Goctl,完成后执行 goctl -v,如果输出版本信息则代表安装成功
- # Go 1.15 及之前版本
- go get -u github.com/zeromicro/go-zero/tools/goctl@latest
-
- # Go 1.16 及以后版本
- go install github.com/zeromicro/go-zero/tools/goctl@latest
创建greet服务,如下图,因为我们 mkdir 和 cd 都已经完成了,所以直接 goctl api new [可自定义名字] (我的是goctl api new code,因为下面我会直接说code文件,所以希望大家不要迷糊我这code是啥了)
之后启动服务:
- $ cd greet
- $ go run greet.go -f etc/greet-api.yaml
之后通过http://localhost:8888/from/you来访问服务,如下图:
可以看到当前的返回值为null,那是因为我们在 logic 文件里的 .go文件 没有写任何的业务逻辑(我的名字是code,所以这里是codelogic.go),如下图:

如果我们改成 
重新启动服务,会出现这样的页面:

但是为什么返回的是Message呢?因为返回的response里面定义的是一个Message集。在code.api中可以看到,如下图:
以后我们就可以在这里写业务逻辑了。
接下来我们来整合一下,将models文件放入code目录下,并在models中创建init.go用来连接数据库。如下:

代码:
- package Models
-
- import (
- "log"
- "xorm.io/xorm"
- _ "github.com/go-sql-driver/mysql"
- )
- var Engine=Init()
- func Init() *xorm.Engine {
- engine, err := xorm.NewEngine("mysql", "root:123456@tcp(127.0.0.1:3306)/clouddisk?charset=utf8")
- if err!=nil {
- log.Println("engine Error:",err)//log包的println会自动return
- }
- return engine
- }
然后在 codelogic.go 进行业务逻辑的实现: (其实就是将文件 xorm_test.go 中的代码复制过来进行测试)
- package logic
-
- import (
- "CloudDisk/code/Models"
- bytes2 "bytes"
- "context"
- "encoding/json"
- "log"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type CodeLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CodeLogic {
- return &CodeLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *CodeLogic) Code(req *types.Request) (resp *types.Response, err error) {
- TableUser:=make([]*Models.UserBasic,0)
- err = Models.Engine.Find(&TableUser)//这里查出来的是地址
- if err!=nil {
- log.Println("Find Error:",err)
- }
- //将以json格式输出
- bytes, err:= json.Marshal(TableUser)//转换成BYTE数组
- if err!=nil {
- log.Println("json Error:",err)
- }
- dst := new(bytes2.Buffer)
- err = json.Indent(dst, bytes, "", " ") //转换为buffer
- if err!=nil {
- log.Println("Indent Error:",err)
- }
- println(dst.String())
- resp=new(types.Response)
- resp.Message=dst.String()
- return
- }
在 code.api 中写登录handler的相关配置
- service code-api {
- @handler User//这个handler指的是生成在handler与logic下的文件所处理的句柄
- get /user/login (LoginRequest) returns (LoginReply)
- }
-
- type LoginRequest {
- Name string `form:"name"`
- Password string `json:"password"`
- }
-
- type LoginReply {
- Token string `json:"token"`
- }
更新api,之后在 handler 与 logic 文件下都会出现新的.go文件
goctl api go -api code.api -dir . -style go_zero
在用户成功登陆之后,那么存到数据库的密码,我们也需要加密一下,这里用到的是MD5。
两个方法:
(1)md5.New() 为初始化一个MD5对象,返回的是hash.Hash对象。
函数原型: func New() hash.Hash。
该对象实现了hash.Hash的Sum接口。
(2)md5.Sum() 计算出MD5校验和。
函数原型:func Sum(data []byte) [Size]byte。
他并不是对data进行校验计算,而是对hash.Hash对象内部存储的内容进行校验和计算然后将其追加到data的后面形成一个新的byte切片。该方法返回一个Size大小为16的byte数组,对于MD5来说就是一个128bit的16字节byte数组。
新建文件 helper,创建 helper.go 写入MD5函数
- package helper
-
- import (
- "crypto/md5"
- "fmt"
- )
-
- func Md5(s string) string {
- return fmt.Sprintf("%x",md5.Sum([]byte(s)))
- }
我们需要生成 token,而 token 需要用到 jwt 进行加密。这里介绍一下 jwt-go 库
使用 jwt-go 库生成 token,我们需要定义需求(claims),也就是说我们需要通过 jwt 传输的数据。假如我们需要传输 ID 和 Username,我们可以定义 Claims 结构体,其中包含 ID 和 Username 字段,还有在 jwt-go 包预定义的 jwt.StandardClaims。
使用 jwt-go 库根据指定的算法生成 jwt token ,主要用到两个方法:
(1)jwt.NewWithClaims 方法:
func jwt.NewWithClaims(method jwt.SigningMethod, claims jwt.Claims) *jwt.Tokenjwt.NewWithClaims 方法根据 Claims 结构体创建 Token 示例。
参数 1 是 jwt.SigningMethod,最常用的是 jwt.SigningMethodHS256 这种 crypto.Hash 加密算法的方案。
参数 2 是 Claims,也就是我们自己定义的UserClaim,UserClaim嵌入在自定义类型中,以方便对标准声明进行编码,解析和验证。
(2)SignedString 方法:
func (*jwt.Token).SignedString(key interface{}) (string, error)SignedString 方法根据传入的空接口类型参数 key,返回完整的签名令牌。
解析 token
使用 jwt-go 库解析 token
(1)jwt.ParseWithClaims 方法:
func jwt.ParseWithClaims(tokenString string, claims jwt.Claims, keyFunc jwt.Keyfunc) (*jwt.Token, error)jwt.ParseWithClaims 方法用于解析鉴权的声明,返回 *jwt.Token。
创建文件夹define,用来定义token需要用到的claims
- package define
-
- import "github.com/dgrijalva/jwt-go"
-
- type UserClaim struct {
- Id int
- Identity string
- Name string
- jwt.StandardClaims
- }
- var JwtKey="cloud-disk-key"
接下来定义生成token的方法,写在helper文件里
- func GenerateToken(id int,name,identity string) (string,error) {
- uc:=define.UserClaim{
- Id:id,
- Identity: identity,
- Name: name,
- }
- token := jwt.NewWithClaims(jwt.SigningMethodHS256, uc) //生成token
- //给token进行加密
- tokenString, err := token.SignedString([]byte(define.JwtKey))
- if err!=nil {
- return "", err
- }
- return tokenString,nil
- }
准备工作都做好了,接下来我们来写user-login业务逻辑,user_logic.go 代码如下:
- package logic
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/helper"
- "context"
- "errors"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type UserLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLogic {
- return &UserLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *UserLogic) User(req *types.LoginRequest) (resp *types.LoginReply, err error) {
- // todo: add your logic here and delete this line
- user := new(Models.UserBasic)
- //1.从数据库中查询当前用户
- get, err := Models.Engine.Where("name= ? AND password= ?", req.Name, helper.Md5(req.Password)).Get(user)
- if err!=nil {
- return nil, err
- }
- if !get {
- return nil,errors.New("用户名或密码错误")
- }
- //2.生成token
- token, err := helper.GenerateToken(user.ID, user.Name, user.Identity)
- if err!=nil {
- return nil,err
- }
- resp=new(types.LoginReply)
- resp.Token=token
- return
- }
之后我们进行重新编译(下面两句话会频繁的用到,每次写完code.api都要goctl一下)
goctl api go -api code.api -dir . -style go_zero
然后运行
go run code.go -f etc/code-api.yaml
使用postman软件,帮助测试 (这里在code.api中我将输入json改为了form形式)

go get github.com/jordan-wright/email
安装email,源址https://github.com/jordan-wright/email
go get github.com/go-redis/redis/v8
安装redis,源址GitHub - go-redis/redis: Type-safe Redis client for Golang
$ go get github.com/satori/go.uuid
安装uuid ,源址GitHub - satori/go.uuid: UUID package for Go
在 init.go 中加入 redis 的配置
- package Models
-
- import (
- "context"
- "github.com/go-redis/redis/v8"
- _ "github.com/go-sql-driver/mysql"
- "log"
- "xorm.io/xorm"
- )
- var Engine=Init()
- var ctx = context.Background()
- var Redis = InitRedis()
- func Init() *xorm.Engine {
- engine, err := xorm.NewEngine("mysql", "root:123456@tcp(127.0.0.1:3306)/clouddisk?charset=utf8")
- if err!=nil {
- log.Println("engine Error:",err)//log包的println会自动return
- }
- return engine
- }
- func InitRedis() *redis.Client {
- return redis.NewClient(&redis.Options{
- Addr: "127.0.0.1:6379",
- Password: "", // no password set
- DB: 0, // use default DB
- })
- }
在 helper 中定义随机数生成,为了生成随机的验证码;以及 uuid ,为了生成 identity
- package helper
-
- import (
- "CloudDisk/code/define"
- "crypto/md5"
- "fmt"
- "github.com/dgrijalva/jwt-go"
- "github.com/jordan-wright/email"
- uuid "github.com/satori/go.uuid"
- "math/rand"
- "net/smtp"
- "time"
- )
-
- func Md5(s string) string {
- return fmt.Sprintf("%x",md5.Sum([]byte(s)))
- }
- func GenerateToken(id int,name,identity string) (string,error) {
- uc:=define.UserClaim{
- Id:id,
- Identity: identity,
- Name: name,
- }
- token := jwt.NewWithClaims(jwt.SigningMethodHS256, uc) //生成token
- //给token进行加密
- tokenString, err := token.SignedString([]byte(define.JwtKey))
- if err!=nil {
- return "", err
- }
- return tokenString,nil
- }
- func MailSendCode(mail,code string) error {
- e := email.NewEmail()
- e.From = ""//发送者姓名,发送者邮箱地址
- e.To = []string{mail}//接收着
- e.Subject = "验证码测试"//发送的主题
- e.HTML = []byte("
"
+code+"") - err := e.Send("smtp.qq.com:25", smtp.PlainAuth("", "@qq.com", "jjj", "smtp.qq.com"), )
- if err!=nil {
- return err
- }
- return nil
- }
- func RandCode() string {
- s:="1234567890"
- rand.Seed(time.Now().UnixNano())
- code:=""
- for i := 0; i < 6; i++ {
- code+=string(s[rand.Intn(len(s))])
- }
- return code
- }
- func GetUUID() string {
- return uuid.NewV4().String()
- }
发送验证码的业务逻辑
- package logic
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/helper"
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
- "context"
- "errors"
- "time"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type MailCodeSendLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewMailCodeSendLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MailCodeSendLogic {
- return &MailCodeSendLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *MailCodeSendLogic) MailCodeSend(req *types.MailCodeSendRequest) (resp *types.MailCodeSendReply, err error) {
- // todo: add your logic here and delete this line
- //该邮箱未被注册
- count, err := Models.Engine.Where("email= ?", req.Email).Count(new(Models.UserBasic))
- if err!=nil {
- return
- }
- if count>0 {
- err=errors.New("该邮箱已经被注册过了")
- return
- }
- //获取验证码
- code:=helper.RandCode()
- //存储验证码
- Models.Redis.Set(l.ctx, req.Email,code,time.Second * 300)
- //发送验证码
- err = helper.MailSendCode(req.Email, code)
- if err!=nil {
- return nil, err
- }
- return
- }
用户注册的业务逻辑
- package logic
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/helper"
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
- "context"
- "errors"
- "github.com/zeromicro/go-zero/core/logx"
- "log"
- )
-
- type UserRegisterLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewUserRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserRegisterLogic {
- return &UserRegisterLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterRequest) (resp *types.UserRegisterReply, err error) {
- //判断code是否一致
- result, err := Models.Redis.Get(l.ctx, req.Email).Result()
- if err!=nil {
- return nil, errors.New("未获取该邮箱的验证码")
- }
- if result!=req.Code {
- err=errors.New("验证码错误")
- return
- }
- //判断用户名是否已经存在
- count, err := Models.Engine.Where("name= ?", req.Name).Count(new(Models.UserBasic))
- if err!=nil {
- return nil, err
- }
- if count>0 {
- err=errors.New("用户名已经存在")
- return
- }
- //数据入库,开始注册信息
- user:=&Models.UserBasic{
- Name: req.Name,
- Identity:helper.GetUUID(),
- Email: req.Email,
- Password: helper.Md5(req.Password),
- }
- insert, err := Models.Engine.Insert(user)
- if err!=nil {
- return nil, err
- }
- log.Println("insert user row:",insert)
- return
- }
通过 identity 来获取用户信息
code.api新增:
- service code-api {
- //用户登录
- @handler UserLogin//这个handler指的是生成在handler与logic下的文件所处理的句柄
- post /user/login (LoginRequest) returns (LoginReply)
- //用户详情
- @handler UserDetail
- post /user/detail (DetailRequest) returns (DetailReply)
- }
-
- type LoginRequest {
- Name string `form:"name"`
- Password string `form:"password"`
- }
-
- type LoginReply {
- Token string `json:"token"`
- }
- type DetailRequest {
- Identity string `json:"identity"`
- }
- type DetailReply {
- Name string `json:"name"`
- Email string `json:"email"`
- }
之后 goctl api go -api code.api -dir . -style go_zero 生成编译,编写detail业务逻辑
- package logic
-
- import (
- "CloudDisk/code/Models"
- "context"
- "errors"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type UserDetailLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewUserDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserDetailLogic {
- return &UserDetailLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *UserDetailLogic) UserDetail(req *types.DetailRequest) (resp *types.DetailReply, err error) {
- // todo: add your logic here and delete this line
- resp=&types.DetailReply{}
- m := new(Models.UserBasic)
- get, err := Models.Engine.Where("identity= ? ", req.Identity).Get(m)
- if err!=nil {
- return nil, err
- }
- if !get {
- return nil, errors.New("该用户不存在")
- }
- resp.Name=m.Name
- resp.Email=m.Email
- return
- }
我们先修改一下数据库配置路由,在etc文件的 code_api.yaml 中加入如下:
- Name: code-api
- Host: 0.0.0.0
- Port: 8888
-
- Mysql:
- DataSource: root:123456@tcp(127.0.0.1:3306)/clouddisk?charset=utf8
-
- Redis:
- Addr: 127.0.0.1:6379
在config文件中加入:
- package config
-
- import "github.com/zeromicro/go-zero/rest"
-
- type Config struct {
- rest.RestConf
- Mysql struct{
- DataSource string
- }
- Redis struct{
- Addr string
- }
- }
同样在 service_context.go 中也要加入:
- package svc
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/internal/config"
- "CloudDisk/code/internal/middleware"
- "github.com/go-redis/redis/v8"
- "github.com/zeromicro/go-zero/rest"
- "xorm.io/xorm"
- )
-
- type ServiceContext struct {
- Config config.Config
- Engine *xorm.Engine
- RDB *redis.Client
- Auth rest.Middleware
- }
-
- func NewServiceContext(c config.Config) *ServiceContext {
- return &ServiceContext{
- Config: c,
- Engine:Models.Init(c.Mysql.DataSource),
- RDB:Models.InitRedis(c),
- Auth: middleware.NewAuthMiddleware().Handle,
- }
- }
当然,在做以下业务逻辑的时候,首先需要加一个中间件,用来验证用户是否已经登录,如果不登录是无法使用以下功能的。在go-zero中,中间件可以分为路由中间件和全局中间件,路由中间件是指某一些特定路由需要实现中间件逻辑,而全局中间件的服务范围则是整个服务。我们这里使用的是路由中间件 。对于中间件,需要添加 Middleware 声明。
所以我们需要再写一对 service code-api{},在这之上需要 Middleware 的声明。例如文件上传的code.api配置,然后 goctl api go -api code.api -dir . -style go_zero 一下会自动生成Middleware的文件,在文件中配置如下:
- package middleware
-
- import (
- "CloudDisk/code/helper"
- "net/http"
- )
-
- type AuthMiddleware struct {
- }
-
- func NewAuthMiddleware() *AuthMiddleware {
- return &AuthMiddleware{}
- }
-
- func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- // TODO generate middleware implement function, delete after code implementation
- auth:=r.Header.Get("Authorization")
- if auth =="" {
- w.WriteHeader(http.StatusUnauthorized)
- w.Write([]byte("Unauthorized"))
- return
- }
- uc, err := helper.AnalyzeToken(auth)
- if err!=nil {
- w.WriteHeader(http.StatusUnauthorized)
- w.Write([]byte(err.Error()))
- return
- }
- r.Header.Set("UserId",string(rune(uc.Id)))
- r.Header.Set("UserIdentity",uc.Identity)
- r.Header.Set("UserName",uc.Name)
- // Passthrough to next handler if need
- next(w, r)
- }
- }
这里用到了token的解析,具体如下:
- //AnalyzeToken Token解析
- func AnalyzeToken(token string) (*define.UserClaim, error) {
- uc:=new(define.UserClaim)
- claims, err := jwt.ParseWithClaims(token, uc, func(token *jwt.Token) (interface{}, error) {
- return []byte(define.JwtKey), nil
- })
- if err != nil {
- return nil,err
- }
- if !claims .Valid{
- return uc,errors.New("token is invalid")
- }
- return uc,err
- }
当然同样也要在 service_context.go 中也要加入Middleware 的声明。(代码跟上面一样,不在复制!)
在Models里加入 RepositoryPool
- package Models
-
- import "time"
-
- type RepositoryPool struct {
- Id int
- Identity string
- Hash string
- Name string
- Ext string
- Size int64
- Path string
- createdAt time.Time `xorm:"created"`
- UpdatedAt time.Time `xorm:"updated"`
- DeletedAt time.Time `xorm:"deleted"`
- }
- func (table RepositoryPool) TableName() string {
- return "repository_pool"
- }
-
在code.api代码中,添加如下代码:
- @server(
- middleware : Auth
- )
- service code-api {
- //文件上传
- @handler FileUpload
- post /file/upload (FileUploadRequest) returns (FileUploadReply)
- }
- type FileUploadRequest {
- Hash string `json:"hash,optional"`
- Name string `json:"name,optional"`
- Ext string `json:"ext,optional"`
- Size int `json:"size,optional"`
- Path string `json:"path,optional"`
- }
- type FileUploadReply {
- Identity string `json:"identity"`
- Ext string `json:"ext"`
- Name string `json:"name"`
- }
在文件上传这里我们采用 CosUpload 文件上传到腾讯云,帮助文档在这里---------------------------------------------------------------》》》》》对象存储 快速入门-SDK 文档-文档中心-腾讯云
在 helper 文件中加入 CosUpload 文件上传的业务逻辑
- //CosUpload 文件上传到腾讯云
- func CosUpload(r *http.Request) (string,error) {
- u, _ := url.Parse(define.CosBucket)
- b := &cos.BaseURL{BucketURL: u}
- c := cos.NewClient(b, &http.Client{
- Transport: &cos.AuthorizationTransport{
- //如实填写账号和密钥,也可以设置为环境变量
- SecretID: define.TencentSecretID,
- SecretKey: define.TencentSecretKey,
- },
- })
- file, fileHeader, err := r.FormFile("file")
- name := "cloud-disk/"+GetUUID()+path.Ext(fileHeader.Filename)
- _, err = c.Object.Put(context.Background(), name, file,nil)
- if err != nil {
- panic(err)
- }
- return define.CosBucket+"/"+name,nil
- }
在 file_upload_handler.go 中写入如下代码:这次我们是先判断一下文件是否上传过,之后将信息写给 logic 中的 req 。
- package handler
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/helper"
- "crypto/md5"
- "fmt"
- "net/http"
- "path"
-
- "CloudDisk/code/internal/logic"
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
- "github.com/zeromicro/go-zero/rest/httpx"
- )
-
- func FileUploadHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- var req types.FileUploadRequest
- if err := httpx.Parse(r, &req); err != nil {
- httpx.Error(w, err)
- return
- }
- file, fileHeader, err2 := r.FormFile("file")
- if err2!=nil {
- return
- }
- //判断文件是否存在
- bytes := make([]byte, fileHeader.Size)
- _, err2 = file.Read(bytes)
- if err2!=nil {
- return
- }
- hash:=fmt.Sprintf("%x",md5.Sum(bytes))
- rp:=new(Models.RepositoryPool)
- get, err2 := svcCtx.Engine.Where("hash=?", hash).Get(rp)
- if err2!=nil {
- return
- }
- if get {
- httpx.OkJson(w,&types.FileUploadReply{Identity: rp.Identity, Ext: rp.Ext, Name: rp.Name})
- return
- }
- //往cos中存储文件
- cosPath, err2 := helper.CosUpload(r)
- if err2!=nil {
- return
- }
- //往 logic 中传递req
- req.Name=fileHeader.Filename
- req.Ext=path.Ext(fileHeader.Filename)
- req.Size= int(fileHeader.Size)
- req.Hash=hash
- req.Path=cosPath
-
- l := logic.NewFileUploadLogic(r.Context(), svcCtx)
- resp, err := l.FileUpload(&req)
- if err != nil {
- httpx.Error(w, err)
- } else {
- httpx.OkJson(w, resp)
- }
- }
- }
file_upload_logic.go 业务逻辑代码如下:
- package logic
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/helper"
- "context"
- "fmt"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type FileUploadLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewFileUploadLogic(ctx context.Context, svcCtx *svc.ServiceContext) *FileUploadLogic {
- return &FileUploadLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *FileUploadLogic) FileUpload(req *types.FileUploadRequest) (resp *types.FileUploadReply, err error) {
- // todo: add your logic here and delete this line
- rp := &Models.RepositoryPool{
- Identity: helper.GetUUID(),
- Hash: req.Hash,
- Name: req.Name,
- Ext: req.Ext,
- Size: int64(req.Size),
- Path: req.Path,
- }
- _, err = l.svcCtx.Engine.Insert(rp)
- if err!=nil {
- return nil, err
- }
- resp=new(types.FileUploadReply)
- resp.Identity=rp.Identity
- resp.Ext=rp.Ext
- resp.Name=rp.Name
- fmt.Println(resp.Identity)
- return
- }
code.api
- //用户文件的关联存储
- @handler UserRepositorySave
- post /user/repository/save (UserRepositorySaveRequest) returns (UserRepositorySaveReply)
- type UserRepositorySaveRequest {
- ParentId int64 `json:"parentId"`
- RepositoryIdentity string `json:"repositoryIdentity"`
- Ext string `json:"ext"`
- Name string `json:"name"`
- }
- type UserRepositorySaveReply {
- }
Models新增
- package Models
-
- import "time"
-
- type UserRepository struct {
- Id int
- Identity string
- UserIdentity string
- ParentId int64
- RepositoryIdentity string
- Ext string
- Name string
- CreatedAt time.Time `xorm:"created"`
- UpdatedAt time.Time `xorm:"updated"`
- DeletedAt time.Time `xorm:"deleted"`
- }
-
- func (table UserRepository) TableName() string {
- return "user_repository"
- }
在 user_repository_save_handler.go 修改一下 UserRepositorySave 函数的参数,将UserIdentity 传入,代码如下:
- package handler
-
- import (
- "net/http"
-
- "CloudDisk/code/internal/logic"
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
- "github.com/zeromicro/go-zero/rest/httpx"
- )
-
- func UserRepositorySaveHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- var req types.UserRepositorySaveRequest
- if err := httpx.Parse(r, &req); err != nil {
- httpx.Error(w, err)
- return
- }
-
- l := logic.NewUserRepositorySaveLogic(r.Context(), svcCtx)
- resp, err := l.UserRepositorySave(&req,r.Header.Get("UserIdentity"))
- if err != nil {
- httpx.Error(w, err)
- } else {
- httpx.OkJson(w, resp)
- }
- }
- }
接下来的每一个业务逻辑块都需要在 handler 里加入,之后我就不再详细说明了。
logic业务逻辑:(非常简单,就是个插入)
- package logic
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/helper"
- "context"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type UserRepositorySaveLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewUserRepositorySaveLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserRepositorySaveLogic {
- return &UserRepositorySaveLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *UserRepositorySaveLogic) UserRepositorySave(req *types.UserRepositorySaveRequest, userIdentity string) (resp *types.UserRepositorySaveReply, err error) {
- // todo: add your logic here and delete this line
- ur := &Models.UserRepository{
- Identity: helper.GetUUID(),
- UserIdentity: userIdentity,
- ParentId: req.ParentId,
- RepositoryIdentity: req.RepositoryIdentity,
- Ext: req.Ext,
- Name: req.Name,
- }
- _, err = l.svcCtx.Engine.Insert(ur)
- if err!=nil {
- return
- }
- return
- }
code.api
- //用户文件列表
- @handler UserFileList
- get /user/file/list (UserFileListReguest) returns (UserFileListReply)
- type UserFileListReguest {
- Id int64 `json:"id,optional"`
- Page int `json:"page,optional"`
- Size int `json:"size,optional"`
- }
-
- type UserFileListReply {
- List []*UserFile `json:"list"`
- Count int `json:"count"`
- }
logic 业务逻辑做到了分页的多表 join 查询
- package logic
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/define"
- "context"
- "time"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type UserFileListLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewUserFileListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFileListLogic {
- return &UserFileListLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *UserFileListLogic) UserFileList(req *types.UserFileListReguest,userIdentity string) (resp *types.UserFileListReply, err error) {
- // todo: add your logic here and delete this line
- uf := make( []*types.UserFile,0)
- //var cnt int64
- resp = new(types.UserFileListReply)
- size:=req.Size
- if size==0 {
- size=define.PageSize
- }
- page:=req.Page
- if page ==0{
- page=1
- }
- offset:=(page-1)*size
- //查询用户文件列表
- l.svcCtx.Engine.ShowSQL(true)//这样就可以看到运行时的sql语句
- err = l.svcCtx.Engine.Table("user_repository"). Where("parent_id = ? AND user_identity = ? ",req.Id,userIdentity).
- Select("user_repository.id,user_repository.identity,user_repository.repository_identity,user_repository.ext,"+
- "user_repository.name,repository_pool.path,repository_pool.size").
- Join("LEFT","repository_pool","user_repository.repository_identity=repository_pool.identity").
- Where("user_repository.deleted_at=? OR user_repository.deleted_at IS NULL",time.Time{}.Format("2006-01-02 15:04:05")).
- Limit(size,offset).Find(&uf)
- if err != nil {
- return
- }
- count, err := l.svcCtx.Engine.Where("parent_id = ? AND user_identity = ? ", req.Id, userIdentity).Count(new(Models.UserRepository))
- if err!=nil {
- return
- }
- resp.List = uf
- resp.Count = int(count)
-
- return
- }
code.api
- //用户文件名称修改
- @handler UserFileNameUpdate
- post /user/file/name/update (UserFileNameUpdateReguest) returns (UserFileNameUpdateReply)
- type UserFileNameUpdateReguest {
- Identity string `json:"identity"`
- Name string `json:"name"`
- }
-
- type UserFileNameUpdateReply {
- }
logic 业务实现
- package logic
-
- import (
- "CloudDisk/code/Models"
- "context"
- "errors"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type UserFileNameUpdateLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewUserFileNameUpdateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFileNameUpdateLogic {
- return &UserFileNameUpdateLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *UserFileNameUpdateLogic) UserFileNameUpdate(req *types.UserFileNameUpdateReguest,userIdentity string) (resp *types.UserFileNameUpdateReply, err error) {
- // todo: add your logic here and delete this line
- //判断当前名称在该层下面是否存在
- count, err := l.svcCtx.Engine.Where("name=? AND parent_id=(SELECT parent_id FROM user_repository ur WHERE ur.identity=?)", req.Name, req.Identity).Count(new(Models.UserRepository))
- if err!=nil {
- return nil, err
- }
- if count >0{
- return nil,errors.New("该名称已经存在")
- }
- //文件修改
- data := &Models.UserRepository{Name: req.Name}
- l.svcCtx.Engine.Where("identity = ? AND user_identity = ? ", req.Identity, userIdentity).Update(data)
- if err != nil {
- return
- }
- return
- }
code.api
- //用户文件夹创建
- @handler UserFolderCreate
- post /user/folder/create (UserFolderCreateReguest) returns (UserFolderCreateReply)
- type UserFolderCreateReguest {
- Name string `json:"name"`
- ParentId int64 `json:"parent_id"`
- }
-
- type UserFolderCreateReply {
- Identity string `json:"identity"`
- }
logic
- package logic
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/helper"
- "context"
- "errors"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type UserFolderCreateLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewUserFolderCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFolderCreateLogic {
- return &UserFolderCreateLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *UserFolderCreateLogic) UserFolderCreate(req *types.UserFolderCreateReguest,userIdentity string) (resp *types.UserFolderCreateReply, err error) {
- // todo: add your logic here and delete this line
- //判断当前名称在该层下面是否存在
- count, err := l.svcCtx.Engine.Where("name=? AND parent_id=?", req.Name, req.ParentId).Count(new(Models.UserRepository))
- if err!=nil {
- return nil, err
- }
- if count >0{
- return nil,errors.New("该名称已经存在")
- }
- //创建文件夹
- data:=&Models.UserRepository{
- Identity: helper.GetUUID(),
- UserIdentity: userIdentity,
- ParentId: req.ParentId,
- Name: req.Name,
- }
- _, err = l.svcCtx.Engine.Insert(data)
- if err != nil {
- return
- }
- return
- }
code.api
- //用户文件删除
- @handler UserFileDelete
- post /user/file/delete (UserFileDeleteReguest) returns (UserFileDeleteReply)
- type UserFileDeleteReguest {
- Identity string `json:"identity"`
- }
-
- type UserFileDeleteReply {
- }
logic
- package logic
-
- import (
- "CloudDisk/code/Models"
- "context"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type UserFileDeleteLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewUserFileDeleteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFileDeleteLogic {
- return &UserFileDeleteLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *UserFileDeleteLogic) UserFileDelete(req *types.UserFileDeleteReguest,userIdentity string) (resp *types.UserFileDeleteReply, err error) {
- // todo: add your logic here and delete this line
- _, err = l.svcCtx.Engine.Where("user_identity=? AND identity=?", userIdentity, req.Identity).Delete(new(Models.UserRepository))
- if err!=nil {
- return
- }
- return
- }
code.api,注意是 PUT 请求
- //用户文件移动
- @handler UserFileMove
- put /user/file/move (UserFileMoveReguest) returns (UserFileMoveReply)
- type UserFileMoveReguest {
- Identity string `json:"identity"`
- ParentIdentity string `json:"parentIdentity"`
- }
-
- type UserFileMoveReply {
- }
logic
- package logic
-
- import (
- "CloudDisk/code/Models"
- "context"
- "errors"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type UserFileMoveLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewUserFileMoveLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserFileMoveLogic {
- return &UserFileMoveLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *UserFileMoveLogic) UserFileMove(req *types.UserFileMoveReguest,userIdentity string) (resp *types.UserFileMoveReply, err error) {
- // todo: add your logic here and delete this line
- parentDate := new(Models.UserRepository)
- has, err := l.svcCtx.Engine.Where("identity=? AND user_identity=?", req.ParentIdentity, userIdentity).Get(parentDate)
- if err!=nil {
- return nil, err
- }
- if !has {
- return nil,errors.New("文件夹不存在!")
- }
- //更新记录的ParentID
- _, err = l.svcCtx.Engine.Where("identity=?", req.Identity).Update(Models.UserRepository{
- ParentId: int64(parentDate.Id),
- })
- return
- }
code.api
- //创建分享记录
- @handler ShareBasicCreate
- post /share/basic/create (ShareBasicCreateRequest) returns (ShareBasicCreateReply)
- type ShareBasicCreateRequest {
- RepositoryIdentity string `json:"repository_identity"`
- ExpiredTime int `json:"expired_time"`
- }
-
- type ShareBasicCreateReply {
- Identity string `json:"identity"`
- }
logic
- package logic
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/helper"
- "context"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type ShareBasicCreateLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewShareBasicCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShareBasicCreateLogic {
- return &ShareBasicCreateLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *ShareBasicCreateLogic) ShareBasicCreate(req *types.ShareBasicCreateRequest, userIdentity string) (resp *types.ShareBasicCreateReply, err error) {
- // todo: add your logic here and delete this line
- uuid := helper.GetUUID()
- data := &Models.ShareBasic{
- Identity: uuid,
- UserIdentity: userIdentity,
- RepositoryIdentity: req.RepositoryIdentity,
- ExpiredTime: req.ExpiredTime,
- }
- _, err = l.svcCtx.Engine.Insert(data)
- if err != nil {
- return
- }
- resp = &types.ShareBasicCreateReply{
- Identity: uuid,
- }
- return
- }
对于这个模块,就不用登录的中间件了,因为未登录的应该也可以获取到别人分享的文件。所以就写在用户登录等的那个api块里就行。
code.api
- //获取资源详情
- @handler ShareBasicDetail
- get /share/basic/detail (ShareBasicDetailRequest) returns (ShareBasicDetailReply)
- type ShareBasicDetailRequest {
- Identity string `json:"identity"`
- }
-
- type ShareBasicDetailReply {
- RepositoryIdentity string `json:"repository_identity"`
- Name string `json:"name"`
- Ext string `json:"ext"`
- Size int64 `json:"size"`
- Path string `json:"path"`
- }
Models
- package Models
-
- import "time"
-
- type ShareBasic struct {
- Id int
- Identity string
- UserIdentity string
- RepositoryIdentity string
- ExpiredTime int
- ClickNum int
- CreatedAt time.Time `xorm:"created"`
- UpdatedAt time.Time `xorm:"updated"`
- DeletedAt time.Time `xorm:"deleted"`
- }
-
- func (table ShareBasic)TableName( ) string {
- return "share_basic"
- }
logic
- package logic
-
- import (
- "context"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type ShareBasicDetailLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewShareBasicDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShareBasicDetailLogic {
- return &ShareBasicDetailLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *ShareBasicDetailLogic) ShareBasicDetail(req *types.ShareBasicDetailRequest) (resp *types.ShareBasicDetailReply, err error) {
- // todo: add your logic here and delete this line
- //对分享记录的点击次数进行 +1 操作
- _, err = l.svcCtx.Engine.Exec("UPDATE share_basic SET click_num = click_num + 1 WHERE identity = ?", req.Identity)
- if err!=nil {
- return
- }
- //获取资源的详细信息
- resp= new(types.ShareBasicDetailReply)
- _, err = l.svcCtx.Engine.Table("share_basic").
- Select("share_basic.repository_identity,repository_pool.name,repository_pool.ext,repository_pool.size,repository_pool.path").
- Join("LEFT", "repository_pool", "share_basic.repository_identity=repository_pool.identity").
- Where("share_basic.identity=?", req.Identity).Get(resp)
- return
- }
需要用户登录的中间件,code.api
- //资源保存
- @handler ShareBasicSave
- post /share/basic/save (ShareBasicSaveRequest) returns (ShareBasicSaveReply)
- type ShareBasicSaveRequest {
- RepositoryIdentity string `json:"repository_identity"`
- ParentId int64 `json:"parent_id"`
- }
-
- type ShareBasicSaveReply {
- Identity string `json:"identity"`
- }
logic
- package logic
-
- import (
- "CloudDisk/code/Models"
- "CloudDisk/code/helper"
- "context"
- "errors"
-
- "CloudDisk/code/internal/svc"
- "CloudDisk/code/internal/types"
-
- "github.com/zeromicro/go-zero/core/logx"
- )
-
- type ShareBasicSaveLogic struct {
- logx.Logger
- ctx context.Context
- svcCtx *svc.ServiceContext
- }
-
- func NewShareBasicSaveLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShareBasicSaveLogic {
- return &ShareBasicSaveLogic{
- Logger: logx.WithContext(ctx),
- ctx: ctx,
- svcCtx: svcCtx,
- }
- }
-
- func (l *ShareBasicSaveLogic) ShareBasicSave(req *types.ShareBasicSaveRequest,userIdentity string) (resp *types.ShareBasicSaveReply, err error) {
- // todo: add your logic here and delete this line
- //获取资源详情
- rp:=new(Models.RepositoryPool)
- has, err := l.svcCtx.Engine.Where("identity=?", req.RepositoryIdentity).Get(rp)
- if err!=nil {
- return nil, err
- }
- if !has {
- return nil,errors.New("资源不存在")
- }
- //user_repository 资源保存
- ur:=&Models.UserRepository{
- Identity: helper.GetUUID(),
- UserIdentity: userIdentity,
- ParentId: req.ParentId,
- RepositoryIdentity: req.RepositoryIdentity,
- Ext: rp.Ext,
- Name: rp.Name,
- }
- _, err = l.svcCtx.Engine.Insert(ur)
- resp=new(types.ShareBasicSaveReply)
- resp.Identity=ur.Identity
- return
- }