(记录一下自己做项目的过程) 基于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 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—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 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
}
一、用户模块
(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 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.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 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形式)
(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 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("<h1>"+code+"</h1>")
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
}
(3)个人资料详情
通过 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 的声明。(代码跟上面一样,不在复制!)
(1)中心存储池资源管理
①文件上传
在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
}
(2)个人存储池资源管理
①文件关联存储
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
}
三、文件共享模块
(1)文件分享
①创建分享记录
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
}