基于Go语言的网盘开发(GloudDisk)
创始人
2024-03-04 14:09:17
0

(记录一下自己做项目的过程) 基于go-zero实现的简易的网盘系统,如果有小伙伴对这个项目感兴趣,可以去网上搜索一些资料。这里推荐一下我学习的来源:【项目实战】基于Go-zero、Xorm的网盘系统_哔哩哔哩_bilibili 

确定功能模块:

目录

 xorm连接数据库并创建表结构

 集成go—zero

一、用户模块

(1)密码登录

(2)邮箱注册

 (3)个人资料详情

二、存储池模块

(1)中心存储池资源管理

①文件上传

(2)个人存储池资源管理 

①文件关联存储

②用户文件列表 

③用户文件名称修改

④用户文件夹创建

⑤用户文件删除

⑥用户文件移动

三、文件共享模块

(1)文件分享

①创建分享记录

②获取资源详情

③资源保存


 xorm连接数据库并创建表结构

 创建项目--安装xorm( go get xorm.io/xorm)

文档地址:Xorm

连接数据库:

xorm.NewEngine("mysql", "root:123@/test?charset=utf8")

先创建UserBasic结构体,为了之后的测试,如下:(新建models文件夹,在文件夹下新建user_basic.go)

package Modelstype UserBasic struct {ID intIdentity stringName stringPassword stringEmail string
}func (table UserBasic) TableName() string {return "user_basic"
}

之后通过 engine.CreateTables(),参数为一个或多个空的对应Struct的指针,创建数据表,具体操作如下:(为了测试,新建一个文件夹test,新建xorm_test.go)

package testimport ("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, "", "  ") //转换为bufferif err!=nil {t.Fatal(err)}println(dst.String())
}

 集成go—zero

文档地址:简介 · go-zero document

 安装:

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 Modelsimport ("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 logicimport ("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.Loggerctx    context.ContextsvcCtx *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, "", "  ") //转换为bufferif err!=nil {log.Println("Indent Error:",err)}println(dst.String())resp=new(types.Response)resp.Message=dst.String()return
}

一、用户模块

(1)密码登录

在 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 helperimport ("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.Token

jwt.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 defineimport "github.com/dgrijalva/jwt-go"type UserClaim struct {Id intIdentity stringName stringjwt.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 logicimport ("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.Loggerctx    context.ContextsvcCtx *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 lineuser := 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.生成tokentoken, err := helper.GenerateToken(user.ID, user.Name, user.Identity)if err!=nil {return nil,err}resp=new(types.LoginReply)resp.Token=tokenreturn
}

之后我们进行重新编译(下面两句话会频繁的用到,每次写完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形式)

(2)邮箱注册

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 Modelsimport ("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 setDB:       0,  // use default DB})
}

 在 helper 中定义随机数生成,为了生成随机的验证码;以及 uuid ,为了生成 identity

package helperimport ("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 logicimport ("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.Loggerctx    context.ContextsvcCtx *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 logicimport ("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.Loggerctx    context.ContextsvcCtx *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
}

 (3)个人资料详情

 通过 identity 来获取用户信息

code.api新增:

service code-api {//用户登录@handler UserLogin//这个handler指的是生成在handler与logic下的文件所处理的句柄post /user/login (LoginRequest) returns (LoginReply)//用户详情@handler UserDetailpost /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 logicimport ("CloudDisk/code/Models""context""errors""CloudDisk/code/internal/svc""CloudDisk/code/internal/types""github.com/zeromicro/go-zero/core/logx"
)type UserDetailLogic struct {logx.Loggerctx    context.ContextsvcCtx *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 lineresp=&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.Nameresp.Email=m.Emailreturn
}

二、存储池模块

我们先修改一下数据库配置路由,在etc文件的 code_api.yaml 中加入如下:

Name: code-api
Host: 0.0.0.0
Port: 8888Mysql:DataSource: root:123456@tcp(127.0.0.1:3306)/clouddisk?charset=utf8Redis:Addr: 127.0.0.1:6379

在config文件中加入:

package configimport "github.com/zeromicro/go-zero/rest"type Config struct {rest.RestConfMysql struct{DataSource string}Redis struct{Addr string}
}

同样在 service_context.go 中也要加入:

package svcimport ("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.ConfigEngine *xorm.EngineRDB *redis.ClientAuth 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 middlewareimport ("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 implementationauth:=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 neednext(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 的声明。(代码跟上面一样,不在复制!)

(1)中心存储池资源管理

①文件上传

在Models里加入 RepositoryPool

package Modelsimport "time"type RepositoryPool struct {Id        intIdentity  stringHash      stringName      stringExt       stringSize      int64Path      stringcreatedAt 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 FileUploadpost /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 handlerimport ("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.FileUploadRequestif 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 中传递reqreq.Name=fileHeader.Filenamereq.Ext=path.Ext(fileHeader.Filename)req.Size= int(fileHeader.Size)req.Hash=hashreq.Path=cosPathl := 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 logicimport ("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.Loggerctx    context.ContextsvcCtx *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 linerp := &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.Identityresp.Ext=rp.Extresp.Name=rp.Namefmt.Println(resp.Identity)return
}

(2)个人存储池资源管理 

①文件关联存储

code.api

//用户文件的关联存储@handler UserRepositorySavepost /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 Modelsimport "time"type UserRepository struct {Id                 intIdentity           stringUserIdentity       stringParentId           int64RepositoryIdentity stringExt                stringName               stringCreatedAt          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 handlerimport ("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.UserRepositorySaveRequestif 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 logicimport ("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.Loggerctx    context.ContextsvcCtx *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 lineur := &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 UserFileListget /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 logicimport ("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.Loggerctx    context.ContextsvcCtx *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 lineuf := make( []*types.UserFile,0)//var cnt int64resp = new(types.UserFileListReply)size:=req.Sizeif size==0 {size=define.PageSize}page:=req.Pageif 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 = ufresp.Count = int(count)return
}

③用户文件名称修改

code.api

	//用户文件名称修改@handler UserFileNameUpdatepost /user/file/name/update (UserFileNameUpdateReguest) returns (UserFileNameUpdateReply)
type UserFileNameUpdateReguest {Identity string `json:"identity"`Name     string `json:"name"`
}type UserFileNameUpdateReply {
}

 logic 业务实现

package logicimport ("CloudDisk/code/Models""context""errors""CloudDisk/code/internal/svc""CloudDisk/code/internal/types""github.com/zeromicro/go-zero/core/logx"
)type UserFileNameUpdateLogic struct {logx.Loggerctx    context.ContextsvcCtx *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 UserFolderCreatepost /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 logicimport ("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.Loggerctx    context.ContextsvcCtx *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 UserFileDeletepost /user/file/delete (UserFileDeleteReguest) returns (UserFileDeleteReply)
type UserFileDeleteReguest {Identity string `json:"identity"`
}type UserFileDeleteReply {
}

 logic

package logicimport ("CloudDisk/code/Models""context""CloudDisk/code/internal/svc""CloudDisk/code/internal/types""github.com/zeromicro/go-zero/core/logx"
)type UserFileDeleteLogic struct {logx.Loggerctx    context.ContextsvcCtx *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 UserFileMoveput  /user/file/move (UserFileMoveReguest) returns (UserFileMoveReply)
type UserFileMoveReguest {Identity       string `json:"identity"`ParentIdentity string `json:"parentIdentity"`
}type UserFileMoveReply {
}

 logic

package logicimport ("CloudDisk/code/Models""context""errors""CloudDisk/code/internal/svc""CloudDisk/code/internal/types""github.com/zeromicro/go-zero/core/logx"
)type UserFileMoveLogic struct {logx.Loggerctx    context.ContextsvcCtx *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 lineparentDate := 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
}

三、文件共享模块

(1)文件分享

①创建分享记录

code.api

	//创建分享记录@handler ShareBasicCreatepost /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 logicimport ("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.Loggerctx    context.ContextsvcCtx *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 lineuuid := 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 ShareBasicDetailget /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 Modelsimport "time"type ShareBasic struct {Id intIdentity stringUserIdentity stringRepositoryIdentity stringExpiredTime intClickNum intCreatedAt time.Time `xorm:"created"`UpdatedAt time.Time `xorm:"updated"`DeletedAt time.Time `xorm:"deleted"`
}func (table ShareBasic)TableName( ) string {return "share_basic"
}

logic

package logicimport ("context""CloudDisk/code/internal/svc""CloudDisk/code/internal/types""github.com/zeromicro/go-zero/core/logx"
)type ShareBasicDetailLogic struct {logx.Loggerctx    context.ContextsvcCtx *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 ShareBasicSavepost /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 logicimport ("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.Loggerctx    context.ContextsvcCtx *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.Identityreturn
}

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...