(记录一下自己做项目的过程) 基于go-zero实现的简易的网盘系统,如果有小伙伴对这个项目感兴趣,可以去网上搜索一些资料。这里推荐一下我学习的来源:【项目实战】基于Go-zero、Xorm的网盘系统_哔哩哔哩_bilibili
确定功能模块:
目录
xorm连接数据库并创建表结构
集成go—zero
一、用户模块
(1)密码登录
(2)邮箱注册
(3)个人资料详情
二、存储池模块
(1)中心存储池资源管理
①文件上传
(2)个人存储池资源管理
①文件关联存储
②用户文件列表
③用户文件名称修改
④用户文件夹创建
⑤用户文件删除
⑥用户文件移动
三、文件共享模块
(1)文件分享
①创建分享记录
②获取资源详情
③资源保存
创建项目--安装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 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
}
在 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形式)
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
}
通过 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 的声明。(代码跟上面一样,不在复制!)
在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
}
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
}
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
}