diff --git a/app/auth/api/auth.api b/app/auth/api/auth.api index 38ec393..56860dc 100644 --- a/app/auth/api/auth.api +++ b/app/auth/api/auth.api @@ -467,15 +467,15 @@ type ( } // 相册列表请求参数 AlbumListRequest { - Type string `json:"type"` - Sort bool `json:"sort"` + Type int64 `json:"type,omitempty"` + Sort bool `json:"sort"` } // 相册列表响应参数 Album { ID int64 `json:"id"` Name string `json:"name"` CreatedAt string `json:"created_at"` - Type string `json:"type"` + Type int64 `json:"type"` CoverImage string `json:"cover_image"` } AlbumListResponse { @@ -593,6 +593,18 @@ type ( SingleImageRequest { ID int64 `json:"id"` } + StorageMeta { + Name string `json:"name"` + Value string `json:"value"` + } + StroageNode { + Value string `json:"value"` + Name string `json:"name"` + Children []StorageMeta `json:"children"` + } + StorageListResponse { + Records []StroageNode `json:"records"` + } ) // 文件上传 @@ -679,5 +691,88 @@ service auth { // 获取单张图片连接 @handler getImageUrl post /image/url/single (SingleImageRequest) returns (string) + + // 获取用户存储配置列表 + @handler getUserStorageList + post /user/config/list returns (StorageListResponse) +} + +type ( + ShareImageMeta { + FileName string `json:"file_name"` + OriginImage string `json:"origin_image"` + FileType string `json:"file_type"` + Thumbnail string `json:"thumbnail"` + ThumbW float64 `json:"thumb_w"` + ThumbH float64 `json:"thumb_h"` + ThumbSize int64 `json:"thumb_size"` + } + ShareImageRequest { + Title string `json:"title,omitempty"` + ExpireDate string `json:"expire_date"` + AccessLimit int64 `json:"access_limit,omitempty"` + AccessPassword string `json:"access_password,omitempty"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` + Images []ShareImageMeta `json:"images"` + } + QueryShareImageRequest { + ShareCode string `json:"share_code"` + AccessPassword string `json:"access_password,omitempty"` + } + ShareImageListMeta { + ID int64 `json:"id"` + FileName string `json:"file_name"` + URL string `json:"url"` + Thumbnail string `json:"thumbnail"` + ThumbW float64 `json:"thumb_w"` + ThumbH float64 `json:"thumb_h"` + ThumbSize float64 `json:"thumb_size"` + CreatedAt string `json:"created_at"` + } + QueryShareImageResponse { + List []ShareImageListMeta `json:"list"` + } + ShareRecordListRequest { + DateRange []string `json:"date_range"` + } + // 分享记录列表响应参数 + ShareRecord { + ID int64 `json:"id"` + CoverImage string `json:"cover_image"` + CreatedAt string `json:"created_at"` + ShareCode string `json:"share_code"` + VisitLimit int64 `json:"visit_limit"` + AccessPassword string `json:"access_password"` + ValidityPeriod int64 `json:"validity_period"` + } + ShareRecordListResponse { + records []ShareRecord `json:"records"` + } +) + +// 分享服务 +@server ( + group: share // 微服务分组 + prefix: /api/auth/share // 微服务前缀 + timeout: 10s // 超时时间 + maxBytes: 104857600 // 最大请求大小 + signature: false // 是否开启签名验证 + middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware // 注册中间件 + MaxConns: true // 是否开启最大连接数限制 + Recover: true // 是否开启自动恢复 + jwt: Auth // 是否开启jwt验证 +) +service auth { + @handler uploadShareImage + post /upload (ShareImageRequest) returns (string) + + //查看分享图片 + @handler queryShareImage + post /image/list (QueryShareImageRequest) returns (QueryShareImageResponse) + + // 列出分享记录 + @handler listShareRecord + post /record/list (ShareRecordListRequest) returns (ShareRecordListResponse) } diff --git a/app/auth/api/internal/handler/routes.go b/app/auth/api/internal/handler/routes.go index 7889fc7..92ff573 100644 --- a/app/auth/api/internal/handler/routes.go +++ b/app/auth/api/internal/handler/routes.go @@ -11,6 +11,7 @@ import ( client "schisandra-album-cloud-microservices/app/auth/api/internal/handler/client" comment "schisandra-album-cloud-microservices/app/auth/api/internal/handler/comment" oauth "schisandra-album-cloud-microservices/app/auth/api/internal/handler/oauth" + share "schisandra-album-cloud-microservices/app/auth/api/internal/handler/share" sms "schisandra-album-cloud-microservices/app/auth/api/internal/handler/sms" storage "schisandra-album-cloud-microservices/app/auth/api/internal/handler/storage" token "schisandra-album-cloud-microservices/app/auth/api/internal/handler/token" @@ -158,6 +159,33 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { rest.WithMaxBytes(1048576), ) + server.AddRoutes( + rest.WithMiddlewares( + []rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware}, + []rest.Route{ + { + Method: http.MethodPost, + Path: "/image/list", + Handler: share.QueryShareImageHandler(serverCtx), + }, + { + Method: http.MethodPost, + Path: "/record/list", + Handler: share.ListShareRecordHandler(serverCtx), + }, + { + Method: http.MethodPost, + Path: "/upload", + Handler: share.UploadShareImageHandler(serverCtx), + }, + }..., + ), + rest.WithJwt(serverCtx.Config.Auth.AccessSecret), + rest.WithPrefix("/api/auth/share"), + rest.WithTimeout(10000*time.Millisecond), + rest.WithMaxBytes(104857600), + ) + server.AddRoutes( rest.WithMiddlewares( []rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.NonceMiddleware}, @@ -278,6 +306,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/uploads", Handler: storage.UploadFileHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/user/config/list", + Handler: storage.GetUserStorageListHandler(serverCtx), + }, }..., ), rest.WithJwt(serverCtx.Config.Auth.AccessSecret), diff --git a/app/auth/api/internal/handler/share/list_share_record_handler.go b/app/auth/api/internal/handler/share/list_share_record_handler.go new file mode 100644 index 0000000..a81c80e --- /dev/null +++ b/app/auth/api/internal/handler/share/list_share_record_handler.go @@ -0,0 +1,29 @@ +package share + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/share" + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + "schisandra-album-cloud-microservices/common/xhttp" +) + +func ListShareRecordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.ShareRecordListRequest + if err := httpx.Parse(r, &req); err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + return + } + + l := share.NewListShareRecordLogic(r.Context(), svcCtx) + resp, err := l.ListShareRecord(&req) + if err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + } else { + xhttp.JsonBaseResponseCtx(r.Context(), w, resp) + } + } +} diff --git a/app/auth/api/internal/handler/share/query_share_image_handler.go b/app/auth/api/internal/handler/share/query_share_image_handler.go new file mode 100644 index 0000000..71361c4 --- /dev/null +++ b/app/auth/api/internal/handler/share/query_share_image_handler.go @@ -0,0 +1,29 @@ +package share + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/share" + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + "schisandra-album-cloud-microservices/common/xhttp" +) + +func QueryShareImageHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.QueryShareImageRequest + if err := httpx.Parse(r, &req); err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + return + } + + l := share.NewQueryShareImageLogic(r.Context(), svcCtx) + resp, err := l.QueryShareImage(&req) + if err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + } else { + xhttp.JsonBaseResponseCtx(r.Context(), w, resp) + } + } +} diff --git a/app/auth/api/internal/handler/share/upload_share_image_handler.go b/app/auth/api/internal/handler/share/upload_share_image_handler.go new file mode 100644 index 0000000..f52dfda --- /dev/null +++ b/app/auth/api/internal/handler/share/upload_share_image_handler.go @@ -0,0 +1,29 @@ +package share + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/share" + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + "schisandra-album-cloud-microservices/common/xhttp" +) + +func UploadShareImageHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.ShareImageRequest + if err := httpx.Parse(r, &req); err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + return + } + + l := share.NewUploadShareImageLogic(r.Context(), svcCtx) + resp, err := l.UploadShareImage(&req) + if err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + } else { + xhttp.JsonBaseResponseCtx(r.Context(), w, resp) + } + } +} diff --git a/app/auth/api/internal/handler/storage/get_user_storage_list_handler.go b/app/auth/api/internal/handler/storage/get_user_storage_list_handler.go new file mode 100644 index 0000000..ce6d43a --- /dev/null +++ b/app/auth/api/internal/handler/storage/get_user_storage_list_handler.go @@ -0,0 +1,21 @@ +package storage + +import ( + "net/http" + + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/storage" + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/common/xhttp" +) + +func GetUserStorageListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + l := storage.NewGetUserStorageListLogic(r.Context(), svcCtx) + resp, err := l.GetUserStorageList() + if err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + } else { + xhttp.JsonBaseResponseCtx(r.Context(), w, resp) + } + } +} diff --git a/app/auth/api/internal/logic/share/list_share_record_logic.go b/app/auth/api/internal/logic/share/list_share_record_logic.go new file mode 100644 index 0000000..5e3f641 --- /dev/null +++ b/app/auth/api/internal/logic/share/list_share_record_logic.go @@ -0,0 +1,67 @@ +package share + +import ( + "context" + "errors" + "time" + + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type ListShareRecordLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewListShareRecordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListShareRecordLogic { + return &ListShareRecordLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *ListShareRecordLogic) ListShareRecord(req *types.ShareRecordListRequest) (resp *types.ShareRecordListResponse, err error) { + uid, ok := l.ctx.Value("user_id").(string) + if !ok { + return nil, errors.New("user_id not found") + } + storageShare := l.svcCtx.DB.ScaStorageShare + storageAlbum := l.svcCtx.DB.ScaStorageAlbum + var recordList []types.ShareRecord + query := storageShare. + Select(storageShare.ID, + storageShare.ShareCode, + storageShare.VisitLimit, + storageShare.AccessPassword, + storageShare.ValidityPeriod, + storageShare.CreatedAt, + storageAlbum.CoverImage). + LeftJoin(storageAlbum, storageShare.AlbumID.EqCol(storageAlbum.ID)). + Where(storageShare.UserID.Eq(uid)). + Order(storageShare.CreatedAt.Desc()) + + if len(req.DateRange) == 2 { + startDate, errStart := time.Parse("2006-01-02", req.DateRange[0]) + endDate, errEnd := time.Parse("2006-01-02", req.DateRange[1]) + if errStart != nil || errEnd != nil { + return nil, errors.New("invalid date format") + } + // Ensure endDate is inclusive by adding 24 hours + endDate = endDate.AddDate(0, 0, 1) + query = query.Where(storageShare.CreatedAt.Between(startDate, endDate)) + } + err = query.Scan(&recordList) + if err != nil { + return nil, err + } + + resp = &types.ShareRecordListResponse{ + Records: recordList, + } + return resp, nil +} diff --git a/app/auth/api/internal/logic/share/query_share_image_logic.go b/app/auth/api/internal/logic/share/query_share_image_logic.go new file mode 100644 index 0000000..88fb59b --- /dev/null +++ b/app/auth/api/internal/logic/share/query_share_image_logic.go @@ -0,0 +1,316 @@ +package share + +import ( + "context" + "encoding/json" + "errors" + "github.com/redis/go-redis/v9" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" + "gorm.io/gorm" + "net/url" + "schisandra-album-cloud-microservices/app/auth/model/mysql/model" + "schisandra-album-cloud-microservices/common/constant" + "schisandra-album-cloud-microservices/common/encrypt" + storageConfig "schisandra-album-cloud-microservices/common/storage/config" + "time" + + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type QueryShareImageLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewQueryShareImageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryShareImageLogic { + return &QueryShareImageLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *QueryShareImageLogic) QueryShareImage(req *types.QueryShareImageRequest) (resp *types.QueryShareImageResponse, err error) { + uid, ok := l.ctx.Value("user_id").(string) + if !ok { + return nil, errors.New("user_id not found") + } + // 获取分享记录 + cacheKey := constant.ImageSharePrefix + req.ShareCode + shareData, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result() + if err != nil { + if errors.Is(err, redis.Nil) { + return nil, errors.New("share code not found") + } + return nil, err + } + var storageShare model.ScaStorageShare + if err := json.Unmarshal([]byte(shareData), &storageShare); err != nil { + return nil, errors.New("unmarshal share data failed") + } + + // 验证密码 + if storageShare.AccessPassword != "" && storageShare.AccessPassword != req.AccessPassword { + return nil, errors.New("incorrect password") + } + + // 检查分享是否过期 + if storageShare.ExpireTime.Before(time.Now()) { + return nil, errors.New("share link has expired") + } + + // 检查访问限制 + if storageShare.VisitLimit > 0 { + err = l.incrementVisitCount(req.ShareCode, storageShare.VisitLimit) + if err != nil { + return nil, err + } + } + // 记录用户访问 + err = l.recordUserVisit(storageShare.ID, uid) + if err != nil { + logx.Error("Failed to record user visit:", err) + return nil, err + } + + // 生成缓存键(在验证通过后) + resultCacheKey := constant.ImageListPrefix + req.ShareCode + ":" + req.AccessPassword + + // 尝试从缓存中获取结果 + cachedResult, err := l.svcCtx.RedisClient.Get(l.ctx, resultCacheKey).Result() + if err == nil { + // 缓存命中,直接返回缓存结果 + var cachedResponse types.QueryShareImageResponse + if err := json.Unmarshal([]byte(cachedResult), &cachedResponse); err == nil { + return &cachedResponse, nil + } + logx.Error("Failed to unmarshal cached result:", err) + } else if !errors.Is(err, redis.Nil) { + // 如果 Redis 查询出错(非缓存未命中),记录错误并继续回源查询 + logx.Error("Failed to get cached result from Redis:", err) + } + // 缓存未命中,执行回源查询逻辑 + resp, err = l.queryShareImageFromSource(&storageShare) + if err != nil { + return nil, err + } + + // 将查询结果缓存到 Redis + respBytes, err := json.Marshal(resp) + if err != nil { + logx.Error("Failed to marshal response for caching:", err) + } else { + // 设置缓存,过期时间为 5 分钟 + err = l.svcCtx.RedisClient.Set(l.ctx, resultCacheKey, respBytes, 5*time.Minute).Err() + if err != nil { + logx.Error("Failed to cache result in Redis:", err) + } + } + + return resp, nil +} + +func (l *QueryShareImageLogic) queryShareImageFromSource(storageShare *model.ScaStorageShare) (resp *types.QueryShareImageResponse, err error) { + // 查询相册图片列表 + storageInfo := l.svcCtx.DB.ScaStorageInfo + storageThumb := l.svcCtx.DB.ScaStorageThumb + var storageInfoList []types.ShareFileInfoResult + err = storageInfo.Select( + storageInfo.ID, + storageInfo.FileName, + storageInfo.CreatedAt, + storageInfo.Provider, + storageInfo.Bucket, + storageInfo.Path, + storageThumb.ThumbPath, + storageThumb.ThumbW, + storageThumb.ThumbH, + storageThumb.ThumbSize). + LeftJoin(storageThumb, storageInfo.ThumbID.EqCol(storageThumb.ID)). + Where( + storageInfo.Type.Eq(constant.ImageTypeShared), + storageInfo.AlbumID.Eq(storageShare.AlbumID)). + Order(storageInfo.CreatedAt.Desc()).Scan(&storageInfoList) + if err != nil { + return nil, err + } + + // 使用 errgroup 和 semaphore 并发处理图片信息 + var ResultList []types.ShareImageListMeta + g, ctx := errgroup.WithContext(l.ctx) + sem := semaphore.NewWeighted(10) // 限制并发数为 10 + + for _, imgInfo := range storageInfoList { + imgInfo := imgInfo // 创建局部变量,避免闭包问题 + if err := sem.Acquire(ctx, 1); err != nil { + return nil, err + } + g.Go(func() error { + defer sem.Release(1) + // 加载用户oss配置信息 + cacheOssConfigKey := constant.UserOssConfigPrefix + storageShare.UserID + ":" + imgInfo.Provider + ossConfig, err := l.getOssConfigFromCacheOrDb(cacheOssConfigKey, storageShare.UserID, imgInfo.Provider) + if err != nil { + return err + } + + service, err := l.svcCtx.StorageManager.GetStorage(storageShare.UserID, ossConfig) + if err != nil { + return errors.New("get storage failed") + } + ossURL, err := service.PresignedURL(ctx, ossConfig.BucketName, imgInfo.Path, 30*time.Minute) + if err != nil { + return errors.New("get presigned url failed") + } + reqParams := make(url.Values) + presignedURL, err := l.svcCtx.MinioClient.PresignedGetObject(ctx, constant.ThumbnailBucketName, imgInfo.ThumbPath, 30*time.Minute, reqParams) + if err != nil { + return errors.New("get presigned thumbnail url failed") + } + ResultList = append(ResultList, types.ShareImageListMeta{ + ID: imgInfo.ID, + FileName: imgInfo.FileName, + ThumbH: imgInfo.ThumbH, + ThumbW: imgInfo.ThumbW, + ThumbSize: imgInfo.ThumbSize, + CreatedAt: imgInfo.CreatedAt.Format(constant.TimeFormat), + URL: ossURL, + Thumbnail: presignedURL.String(), + }) + return nil + }) + } + + // 等待所有并发任务完成 + if err := g.Wait(); err != nil { + return nil, err + } + + return &types.QueryShareImageResponse{ + List: ResultList}, nil +} + +func (l *QueryShareImageLogic) recordUserVisit(shareID int64, userID string) error { + // 查询是否已经存在该用户对该分享的访问记录 + var visitRecord model.ScaStorageShareVisit + scaStorageShareVisit := l.svcCtx.DB.ScaStorageShareVisit + _, err := scaStorageShareVisit. + Where(scaStorageShareVisit.ShareID.Eq(shareID), scaStorageShareVisit.UserID.Eq(userID)). + First() + + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + // 如果记录不存在,创建新的访问记录 + visitRecord = model.ScaStorageShareVisit{ + UserID: userID, + ShareID: shareID, + Views: 1, + } + err = l.svcCtx.DB.ScaStorageShareVisit.Create(&visitRecord) + if err != nil { + return errors.New("failed to create visit record") + } + return nil + } + return errors.New("failed to query visit record") + } + + // 如果记录存在,增加访问次数 + info, err := scaStorageShareVisit. + Where(scaStorageShareVisit.UserID.Eq(userID), scaStorageShareVisit.ShareID.Eq(shareID)). + Update(scaStorageShareVisit.Views, scaStorageShareVisit.Views.Add(1)) + if err != nil { + return errors.New("failed to update visit record") + } + if info.RowsAffected == 0 { + return errors.New("failed to update visit record") + } + + return nil +} +func (l *QueryShareImageLogic) incrementVisitCount(shareCode string, limit int64) error { + // Redis 键值 + cacheKey := constant.ImageShareVisitPrefix + shareCode + currentVisitCount, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Int64() + if err != nil && !errors.Is(err, redis.Nil) { + return err + } + + // 如果访问次数超过限制,返回错误 + if currentVisitCount >= limit { + return errors.New("access limit reached") + } + + // 增加访问次数 + err = l.svcCtx.RedisClient.Incr(l.ctx, cacheKey).Err() + if err != nil { + return err + } + + return nil +} + +// 提取解密操作为函数 +func (l *QueryShareImageLogic) decryptConfig(config *model.ScaStorageConfig) (*storageConfig.StorageConfig, error) { + accessKey, err := encrypt.Decrypt(config.AccessKey, l.svcCtx.Config.Encrypt.Key) + if err != nil { + return nil, errors.New("decrypt access key failed") + } + secretKey, err := encrypt.Decrypt(config.SecretKey, l.svcCtx.Config.Encrypt.Key) + if err != nil { + return nil, errors.New("decrypt secret key failed") + } + return &storageConfig.StorageConfig{ + Provider: config.Provider, + Endpoint: config.Endpoint, + AccessKey: accessKey, + SecretKey: secretKey, + BucketName: config.Bucket, + Region: config.Region, + }, nil +} + +// 从缓存或数据库中获取 OSS 配置 +func (l *QueryShareImageLogic) getOssConfigFromCacheOrDb(cacheKey, uid, provider string) (*storageConfig.StorageConfig, error) { + result, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result() + if err != nil && !errors.Is(err, redis.Nil) { + return nil, errors.New("get oss config failed") + } + + var ossConfig *storageConfig.StorageConfig + if result != "" { + var redisOssConfig model.ScaStorageConfig + if err = json.Unmarshal([]byte(result), &redisOssConfig); err != nil { + return nil, errors.New("unmarshal oss config failed") + } + return l.decryptConfig(&redisOssConfig) + } + + // 缓存未命中,从数据库中加载 + scaOssConfig := l.svcCtx.DB.ScaStorageConfig + dbOssConfig, err := scaOssConfig.Where(scaOssConfig.UserID.Eq(uid), scaOssConfig.Provider.Eq(provider)).First() + if err != nil { + return nil, err + } + + // 缓存数据库配置 + ossConfig, err = l.decryptConfig(dbOssConfig) + if err != nil { + return nil, err + } + marshalData, err := json.Marshal(dbOssConfig) + if err != nil { + return nil, errors.New("marshal oss config failed") + } + err = l.svcCtx.RedisClient.Set(l.ctx, cacheKey, marshalData, 0).Err() + if err != nil { + return nil, errors.New("set oss config failed") + } + + return ossConfig, nil +} diff --git a/app/auth/api/internal/logic/share/upload_share_image_logic.go b/app/auth/api/internal/logic/share/upload_share_image_logic.go new file mode 100644 index 0000000..b3b329c --- /dev/null +++ b/app/auth/api/internal/logic/share/upload_share_image_logic.go @@ -0,0 +1,347 @@ +package share + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "github.com/ccpwcn/kgo" + "github.com/minio/minio-go/v7" + "github.com/redis/go-redis/v9" + "github.com/zeromicro/go-zero/core/logx" + "golang.org/x/sync/errgroup" + "image" + "path" + "path/filepath" + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + "schisandra-album-cloud-microservices/app/auth/model/mysql/model" + "schisandra-album-cloud-microservices/app/auth/model/mysql/query" + "schisandra-album-cloud-microservices/common/constant" + "schisandra-album-cloud-microservices/common/encrypt" + storageConfig "schisandra-album-cloud-microservices/common/storage/config" + "strconv" + "time" +) + +type UploadShareImageLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewUploadShareImageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadShareImageLogic { + return &UploadShareImageLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *UploadShareImageLogic) UploadShareImage(req *types.ShareImageRequest) (resp string, err error) { + uid, ok := l.ctx.Value("user_id").(string) + if !ok { + return "", errors.New("user_id not found") + } + + // 启动事务,确保插入操作的原子性 + tx := l.svcCtx.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() // 如果有panic发生,回滚事务 + logx.Errorf("transaction rollback: %v", r) + } + }() + albumName := req.Title + if albumName == "" { + albumName = "快传照片" + } + // 创建一个相册 + album := model.ScaStorageAlbum{ + UserID: uid, + AlbumName: albumName, + CoverImage: req.Images[0].Thumbnail, + AlbumType: constant.AlbumTypeShared, + } + err = tx.ScaStorageAlbum.Create(&album) + if err != nil { + return "", err + } + + var g errgroup.Group + + // 为每张图片启动一个协程 + for _, img := range req.Images { + img := img // 确保每个协程有独立的 img 参数副本 + g.Go(func() error { + return l.uploadImageAndRecord(tx, uid, album, img, req) + }) + } + + // 等待所有任务完成并返回第一个错误 + if err = g.Wait(); err != nil { + tx.Rollback() + return "", err + } + + duration, err := strconv.Atoi(req.ExpireDate) + if err != nil { + return "", errors.New("invalid expire date") + } + expiryTime := l.GenerateExpiryTime(time.Now(), duration) + storageShare := model.ScaStorageShare{ + UserID: uid, + AlbumID: album.ID, + ShareCode: kgo.SimpleUuid(), + Status: 0, + AccessPassword: req.AccessPassword, + VisitLimit: req.AccessLimit, + ValidityPeriod: int64(duration), + ExpireTime: expiryTime, + } + err = tx.ScaStorageShare.Create(&storageShare) + if err != nil { + tx.Rollback() + return "", err + } + // 缓存分享码 + marshal, err := json.Marshal(storageShare) + if err != nil { + tx.Rollback() + return "", err + } + cacheKey := constant.ImageSharePrefix + storageShare.ShareCode + err = l.svcCtx.RedisClient.Set(l.ctx, cacheKey, marshal, time.Duration(duration)*time.Hour*24).Err() + if err != nil { + tx.Rollback() + return "", err + } + // 提交事务 + if err = tx.Commit(); err != nil { + tx.Rollback() + logx.Errorf("Transaction commit failed: %v", err) + return "", err + } + return storageShare.ShareCode, nil +} + +func (l *UploadShareImageLogic) uploadImageAndRecord(tx *query.QueryTx, uid string, album model.ScaStorageAlbum, img types.ShareImageMeta, req *types.ShareImageRequest) error { + // 上传缩略图到 Minio + thumbnail, err := base64.StdEncoding.DecodeString(img.Thumbnail) + if err != nil { + return fmt.Errorf("base64 decode failed: %v", err) + } + thumbObjectKey := path.Join( + uid, + time.Now().Format("2006/01"), + l.classifyFile(img.FileType), + fmt.Sprintf("%s_%s.jpg", time.Now().Format("20060102150405"), kgo.SimpleUuid()), + ) + exists, err := l.svcCtx.MinioClient.BucketExists(l.ctx, constant.ThumbnailBucketName) + if err != nil || !exists { + err = l.svcCtx.MinioClient.MakeBucket(l.ctx, constant.ThumbnailBucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true}) + if err != nil { + logx.Errorf("Failed to create MinIO bucket: %v", err) + return err + } + } + _, err = l.svcCtx.MinioClient.PutObject( + l.ctx, + constant.ThumbnailBucketName, + thumbObjectKey, + bytes.NewReader(thumbnail), + int64(len(thumbnail)), + minio.PutObjectOptions{ + ContentType: "image/jpeg", + }, + ) + if err != nil { + logx.Errorf("Failed to upload MinIO object: %v", err) + return err + } + + // 记录缩略图 + thumbRecord := model.ScaStorageThumb{ + UserID: uid, + ThumbPath: thumbObjectKey, + ThumbW: img.ThumbW, + ThumbH: img.ThumbH, + ThumbSize: float64(len(thumbnail)), + } + err = tx.ScaStorageThumb.Create(&thumbRecord) + if err != nil { + return err + } + + // 上传原始图片到用户的存储桶 + originImage, err := base64.StdEncoding.DecodeString(img.OriginImage) + if err != nil { + return fmt.Errorf("base64 decode failed: %v", err) + } + originObjectKey := path.Join( + "share_space", + uid, + time.Now().Format("2006/01"), + fmt.Sprintf("%s_%s%s", img.FileName, kgo.SimpleUuid(), filepath.Ext(img.FileName)), + ) + // 获取存储服务 + ossConfig, err := l.getOssConfigFromCacheOrDb(constant.UserOssConfigPrefix+uid+":"+req.Provider, uid, req.Provider) + if err != nil { + return err + } + service, err := l.svcCtx.StorageManager.GetStorage(uid, ossConfig) + if err != nil { + return errors.New("get storage failed") + } + _, err = service.UploadFileSimple(l.ctx, ossConfig.BucketName, originObjectKey, bytes.NewReader(originImage), map[string]string{ + "Content-Type": img.FileType, + }) + if err != nil { + logx.Errorf("Failed to upload object to storage: %v", err) + return err + } + + // 获取图片信息 + width, height, size, err := l.GetImageInfo(img.OriginImage) + if err != nil { + return err + } + + // 记录原始图片信息 + imageRecord := model.ScaStorageInfo{ + UserID: uid, + Provider: req.Provider, + Bucket: req.Bucket, + Path: originObjectKey, + FileName: img.FileName, + FileSize: strconv.Itoa(size), + FileType: img.FileType, + Width: float64(width), + Height: float64(height), + Type: constant.ImageTypeShared, + AlbumID: album.ID, + ThumbID: thumbRecord.ID, + } + err = tx.ScaStorageInfo.Create(&imageRecord) + if err != nil { + return err + } + + return nil +} + +func (l *UploadShareImageLogic) GetImageInfo(base64Str string) (width, height int, size int, err error) { + // 解码 Base64 + data, err := base64.StdEncoding.DecodeString(base64Str) + if err != nil { + return 0, 0, 0, fmt.Errorf("base64 decode failed: %v", err) + } + + // 获取图片大小 + size = len(data) + + // 解析图片宽高 + reader := bytes.NewReader(data) + imgCfg, _, err := image.DecodeConfig(reader) + if err != nil { + return 0, 0, 0, fmt.Errorf("decode image config failed: %v", err) + } + + return imgCfg.Width, imgCfg.Height, size, nil +} + +// 提取解密操作为函数 +func (l *UploadShareImageLogic) decryptConfig(config *model.ScaStorageConfig) (*storageConfig.StorageConfig, error) { + accessKey, err := encrypt.Decrypt(config.AccessKey, l.svcCtx.Config.Encrypt.Key) + if err != nil { + return nil, errors.New("decrypt access key failed") + } + secretKey, err := encrypt.Decrypt(config.SecretKey, l.svcCtx.Config.Encrypt.Key) + if err != nil { + return nil, errors.New("decrypt secret key failed") + } + return &storageConfig.StorageConfig{ + Provider: config.Provider, + Endpoint: config.Endpoint, + AccessKey: accessKey, + SecretKey: secretKey, + BucketName: config.Bucket, + Region: config.Region, + }, nil +} + +// 从缓存或数据库中获取 OSS 配置 +func (l *UploadShareImageLogic) getOssConfigFromCacheOrDb(cacheKey, uid, provider string) (*storageConfig.StorageConfig, error) { + result, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result() + if err != nil && !errors.Is(err, redis.Nil) { + return nil, errors.New("get oss config failed") + } + + var ossConfig *storageConfig.StorageConfig + if result != "" { + var redisOssConfig model.ScaStorageConfig + if err = json.Unmarshal([]byte(result), &redisOssConfig); err != nil { + return nil, errors.New("unmarshal oss config failed") + } + return l.decryptConfig(&redisOssConfig) + } + + // 缓存未命中,从数据库中加载 + scaOssConfig := l.svcCtx.DB.ScaStorageConfig + dbOssConfig, err := scaOssConfig.Where(scaOssConfig.UserID.Eq(uid), scaOssConfig.Provider.Eq(provider)).First() + if err != nil { + return nil, err + } + + // 缓存数据库配置 + ossConfig, err = l.decryptConfig(dbOssConfig) + if err != nil { + return nil, err + } + marshalData, err := json.Marshal(dbOssConfig) + if err != nil { + return nil, errors.New("marshal oss config failed") + } + err = l.svcCtx.RedisClient.Set(l.ctx, cacheKey, marshalData, 0).Err() + if err != nil { + return nil, errors.New("set oss config failed") + } + + return ossConfig, nil +} + +// GenerateExpiryTime 函数接受当前时间和有效期(天为单位),返回过期时间 +func (l *UploadShareImageLogic) GenerateExpiryTime(currentTime time.Time, durationInDays int) time.Time { + // 创建一个持续时间对象 + duration := time.Duration(durationInDays) * 24 * time.Hour + // 将当前时间加上持续时间,得到过期时间 + expiryTime := currentTime.Add(duration) + return expiryTime +} + +func (l *UploadShareImageLogic) classifyFile(mimeType string) string { + // 使用map存储MIME类型及其对应的分类 + typeMap := map[string]string{ + "image/jpeg": "image", + "image/png": "image", + "image/gif": "gif", + "image/bmp": "image", + "image/tiff": "image", + "image/webp": "image", + "video/mp4": "video", + "video/avi": "video", + "video/mpeg": "video", + "video/quicktime": "video", + "video/x-msvideo": "video", + "video/x-flv": "video", + "video/x-matroska": "video", + } + + // 根据MIME类型从map中获取分类 + if classification, exists := typeMap[mimeType]; exists { + return classification + } + return "other" +} diff --git a/app/auth/api/internal/logic/storage/create_album_logic.go b/app/auth/api/internal/logic/storage/create_album_logic.go index b424730..11b9793 100644 --- a/app/auth/api/internal/logic/storage/create_album_logic.go +++ b/app/auth/api/internal/logic/storage/create_album_logic.go @@ -4,6 +4,7 @@ import ( "context" "errors" "schisandra-album-cloud-microservices/app/auth/model/mysql/model" + "schisandra-album-cloud-microservices/common/constant" "schisandra-album-cloud-microservices/app/auth/api/internal/svc" "schisandra-album-cloud-microservices/app/auth/api/internal/types" @@ -33,7 +34,7 @@ func (l *CreateAlbumLogic) CreateAlbum(req *types.AlbumCreateRequest) (resp *typ storageAlbum := &model.ScaStorageAlbum{ UserID: uid, AlbumName: req.Name, - AlbumType: "0", + AlbumType: constant.AlbumTypeMine, } err = l.svcCtx.DB.ScaStorageAlbum.Create(storageAlbum) if err != nil { diff --git a/app/auth/api/internal/logic/storage/get_album_detail_logic.go b/app/auth/api/internal/logic/storage/get_album_detail_logic.go index a6bddd9..43f5d8f 100644 --- a/app/auth/api/internal/logic/storage/get_album_detail_logic.go +++ b/app/auth/api/internal/logic/storage/get_album_detail_logic.go @@ -68,6 +68,7 @@ func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest) storageInfo.FileName, storageInfo.CreatedAt, storageThumb.ThumbPath, + storageInfo.Path, storageThumb.ThumbW, storageThumb.ThumbH, storageThumb.ThumbSize). @@ -86,17 +87,17 @@ func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest) return &types.AlbumDetailListResponse{}, nil } - //// 加载用户oss配置信息 - //cacheOssConfigKey := constant.UserOssConfigPrefix + uid + ":" + req.Provider - //ossConfig, err := l.getOssConfigFromCacheOrDb(cacheOssConfigKey, uid, req.Provider) - //if err != nil { - // return nil, err - //} - // - //service, err := l.svcCtx.StorageManager.GetStorage(uid, ossConfig) - //if err != nil { - // return nil, errors.New("get storage failed") - //} + // 加载用户oss配置信息 + cacheOssConfigKey := constant.UserOssConfigPrefix + uid + ":" + req.Provider + ossConfig, err := l.getOssConfigFromCacheOrDb(cacheOssConfigKey, uid, req.Provider) + if err != nil { + return nil, err + } + + service, err := l.svcCtx.StorageManager.GetStorage(uid, ossConfig) + if err != nil { + return nil, errors.New("get storage failed") + } // 按日期进行分组 var wg sync.WaitGroup @@ -114,6 +115,11 @@ func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest) logx.Error(err) return } + url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, dbFileInfo.Path, time.Hour*24*7) + if err != nil { + logx.Error(err) + return + } // 使用 Load 或 Store 确保原子操作 value, _ := groupedImages.LoadOrStore(date, []types.ImageMeta{}) images := value.([]types.ImageMeta) @@ -121,7 +127,8 @@ func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest) images = append(images, types.ImageMeta{ ID: dbFileInfo.ID, FileName: dbFileInfo.FileName, - URL: presignedUrl.String(), + Thumbnail: presignedUrl.String(), + URL: url, Width: dbFileInfo.ThumbW, Height: dbFileInfo.ThumbH, CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"), diff --git a/app/auth/api/internal/logic/storage/get_album_list_logic.go b/app/auth/api/internal/logic/storage/get_album_list_logic.go index 9c3aed1..66ad52c 100644 --- a/app/auth/api/internal/logic/storage/get_album_list_logic.go +++ b/app/auth/api/internal/logic/storage/get_album_list_logic.go @@ -3,6 +3,7 @@ package storage import ( "context" "errors" + "gorm.io/gen" "gorm.io/gen/field" "schisandra-album-cloud-microservices/app/auth/api/internal/svc" @@ -37,7 +38,15 @@ func (l *GetAlbumListLogic) GetAlbumList(req *types.AlbumListRequest) (resp *typ } else { orderConditions = append(orderConditions, storageAlbum.AlbumName.Desc()) } - albums, err := storageAlbum.Where(storageAlbum.UserID.Eq(uid), storageAlbum.AlbumType.Eq(req.Type)).Order(orderConditions...).Find() + var typeConditions []gen.Condition + if req.Type != -1 { + // 获取全部相册 + typeConditions = append(typeConditions, storageAlbum.AlbumType.Eq(req.Type)) + typeConditions = append(typeConditions, storageAlbum.UserID.Eq(uid)) + } + albums, err := storageAlbum.Where( + typeConditions...). + Order(orderConditions...).Find() if err != nil { return nil, err } diff --git a/app/auth/api/internal/logic/storage/get_user_storage_list_logic.go b/app/auth/api/internal/logic/storage/get_user_storage_list_logic.go new file mode 100644 index 0000000..4ae3c2d --- /dev/null +++ b/app/auth/api/internal/logic/storage/get_user_storage_list_logic.go @@ -0,0 +1,80 @@ +package storage + +import ( + "context" + "errors" + + "schisandra-album-cloud-microservices/app/auth/api/internal/svc" + "schisandra-album-cloud-microservices/app/auth/api/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type GetUserStorageListLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewGetUserStorageListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserStorageListLogic { + return &GetUserStorageListLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +// providerNameMap 存储商映射表 +var providerNameMap = map[string]string{ + "ali": "阿里云OSS", + "tencent": "腾讯云COS", + "aws": "Amazon S3", + "qiniu": "七牛云", + "huawei": "华为云OBS", +} + +func (l *GetUserStorageListLogic) GetUserStorageList() (resp *types.StorageListResponse, err error) { + uid, ok := l.ctx.Value("user_id").(string) + if !ok { + return nil, errors.New("user_id not found") + } + storageConfig := l.svcCtx.DB.ScaStorageConfig + storageConfigs, err := storageConfig.Select( + storageConfig.Provider, + storageConfig.Bucket). + Where( + storageConfig.UserID.Eq(uid)).Find() + if err != nil { + return nil, err + } + // 使用 map 组织数据 + providerMap := make(map[string][]types.StorageMeta) + + for _, config := range storageConfigs { + providerMap[config.Provider] = append(providerMap[config.Provider], types.StorageMeta{ + Value: config.Bucket, + Name: config.Bucket, + }) + } + // 组装返回结构 + var records []types.StroageNode + for provider, buckets := range providerMap { + records = append(records, types.StroageNode{ + Value: provider, + Name: l.getProviderName(provider), + Children: buckets, + }) + } + // 返回数据 + return &types.StorageListResponse{ + Records: records, + }, nil +} + +// getProviderName 获取存储商的中文名称 +func (l *GetUserStorageListLogic) getProviderName(provider string) string { + if name, exists := providerNameMap[provider]; exists { + return name + } + return provider +} diff --git a/app/auth/api/internal/logic/storage/upload_file_logic.go b/app/auth/api/internal/logic/storage/upload_file_logic.go index 2e86358..e710f32 100644 --- a/app/auth/api/internal/logic/storage/upload_file_logic.go +++ b/app/auth/api/internal/logic/storage/upload_file_logic.go @@ -288,6 +288,7 @@ func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHead } objectKey := path.Join( + "image_space", uid, time.Now().Format("2006/01"), // 按年/月划分目录 l.classifyFile(result.FileType, result.IsScreenshot), diff --git a/app/auth/api/internal/middleware/casbinverify_middleware.go b/app/auth/api/internal/middleware/casbinverify_middleware.go index df91e5b..40c1153 100644 --- a/app/auth/api/internal/middleware/casbinverify_middleware.go +++ b/app/auth/api/internal/middleware/casbinverify_middleware.go @@ -3,7 +3,9 @@ package middleware import ( "github.com/casbin/casbin/v2" "net/http" - "schisandra-album-cloud-microservices/common/middleware" + "schisandra-album-cloud-microservices/common/constant" + "schisandra-album-cloud-microservices/common/errors" + "schisandra-album-cloud-microservices/common/xhttp" ) type CasbinVerifyMiddleware struct { @@ -18,7 +20,12 @@ func NewCasbinVerifyMiddleware(casbin *casbin.SyncedCachedEnforcer) *CasbinVerif func (m *CasbinVerifyMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - middleware.CasbinMiddleware(w, r, m.casbin) + userId := r.Header.Get(constant.UID_HEADER_KEY) + correct, err := m.casbin.Enforce(userId, r.URL.Path, r.Method) + if err != nil || !correct { + xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusNotFound, "not found")) + return + } next(w, r) } } diff --git a/app/auth/api/internal/middleware/nonce_middleware.go b/app/auth/api/internal/middleware/nonce_middleware.go index a50274c..6a059dc 100644 --- a/app/auth/api/internal/middleware/nonce_middleware.go +++ b/app/auth/api/internal/middleware/nonce_middleware.go @@ -3,7 +3,10 @@ package middleware import ( "github.com/redis/go-redis/v9" "net/http" - "schisandra-album-cloud-microservices/common/middleware" + "schisandra-album-cloud-microservices/common/constant" + "schisandra-album-cloud-microservices/common/errors" + "schisandra-album-cloud-microservices/common/xhttp" + "time" ) type NonceMiddleware struct { @@ -18,7 +21,25 @@ func NewNonceMiddleware(redisClient *redis.Client) *NonceMiddleware { func (m *NonceMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - middleware.NonceMiddleware(w, r, m.RedisClient) + nonce := r.Header.Get("X-Nonce") + if nonce == "" { + xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusBadRequest, "bad request!")) + return + } + if len(nonce) != 32 { + xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusBadRequest, "bad request!")) + return + } + result := m.RedisClient.Get(r.Context(), constant.SystemApiNoncePrefix+nonce).Val() + if result != "" { + xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusBadRequest, "bad request!")) + return + } + err := m.RedisClient.Set(r.Context(), constant.SystemApiNoncePrefix+nonce, nonce, time.Minute*1).Err() + if err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusInternalServerError, "internal server error!")) + return + } next(w, r) } } diff --git a/app/auth/api/internal/types/file_types.go b/app/auth/api/internal/types/file_types.go index d807210..6f6c216 100644 --- a/app/auth/api/internal/types/file_types.go +++ b/app/auth/api/internal/types/file_types.go @@ -54,3 +54,12 @@ type ThingImageList struct { ThumbPath string `json:"thumb_path"` Path string `json:"path"` } + +type ShareImageInfo struct { + Title string `json:"title"` + ExpireDate string `json:"expire_date"` + AccessLimit int64 `json:"access_limit"` + AccessPassword string `json:"access_password"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` +} diff --git a/app/auth/api/internal/types/share_types.go b/app/auth/api/internal/types/share_types.go new file mode 100644 index 0000000..9b4fc6e --- /dev/null +++ b/app/auth/api/internal/types/share_types.go @@ -0,0 +1,16 @@ +package types + +import "time" + +type ShareFileInfoResult struct { + ID int64 `json:"id"` + FileName string `json:"file_name"` + ThumbPath string `json:"thumb_path"` + ThumbW float64 `json:"thumb_w"` + ThumbH float64 `json:"thumb_h"` + ThumbSize float64 `json:"thumb_size"` + CreatedAt time.Time `json:"created_at"` + Path string `json:"path"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` +} diff --git a/app/auth/api/internal/types/types.go b/app/auth/api/internal/types/types.go index e4b9373..8869c93 100644 --- a/app/auth/api/internal/types/types.go +++ b/app/auth/api/internal/types/types.go @@ -15,7 +15,7 @@ type Album struct { ID int64 `json:"id"` Name string `json:"name"` CreatedAt string `json:"created_at"` - Type string `json:"type"` + Type int64 `json:"type"` CoverImage string `json:"cover_image"` } @@ -42,8 +42,8 @@ type AlbumDetailListResponse struct { } type AlbumListRequest struct { - Type string `json:"type"` - Sort bool `json:"sort"` + Type int64 `json:"type,omitempty"` + Sort bool `json:"sort"` } type AlbumListResponse struct { @@ -254,6 +254,15 @@ type PhoneLoginRequest struct { AutoLogin bool `json:"auto_login"` } +type QueryShareImageRequest struct { + ShareCode string `json:"share_code"` + AccessPassword string `json:"access_password,omitempty"` +} + +type QueryShareImageResponse struct { + List []ShareImageListMeta `json:"list"` +} + type RecentListResponse struct { Records []AllImageDetail `json:"records"` } @@ -306,6 +315,55 @@ type RotateCaptchaResponse struct { Thumb string `json:"thumb"` } +type ShareImageListMeta struct { + ID int64 `json:"id"` + FileName string `json:"file_name"` + URL string `json:"url"` + Thumbnail string `json:"thumbnail"` + ThumbW float64 `json:"thumb_w"` + ThumbH float64 `json:"thumb_h"` + ThumbSize float64 `json:"thumb_size"` + CreatedAt string `json:"created_at"` +} + +type ShareImageMeta struct { + FileName string `json:"file_name"` + OriginImage string `json:"origin_image"` + FileType string `json:"file_type"` + Thumbnail string `json:"thumbnail"` + ThumbW float64 `json:"thumb_w"` + ThumbH float64 `json:"thumb_h"` + ThumbSize int64 `json:"thumb_size"` +} + +type ShareImageRequest struct { + Title string `json:"title,omitempty"` + ExpireDate string `json:"expire_date"` + AccessLimit int64 `json:"access_limit,omitempty"` + AccessPassword string `json:"access_password,omitempty"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` + Images []ShareImageMeta `json:"images"` +} + +type ShareRecord struct { + ID int64 `json:"id"` + CoverImage string `json:"cover_image"` + CreatedAt string `json:"created_at"` + ShareCode string `json:"share_code"` + VisitLimit int64 `json:"visit_limit"` + AccessPassword string `json:"access_password"` + ValidityPeriod int64 `json:"validity_period"` +} + +type ShareRecordListRequest struct { + DateRange []string `json:"date_range"` +} + +type ShareRecordListResponse struct { + Records []ShareRecord `json:"records"` +} + type SingleImageRequest struct { ID int64 `json:"id"` } @@ -335,6 +393,21 @@ type StorageConfigRequest struct { Region string `json:"region"` } +type StorageListResponse struct { + Records []StroageNode `json:"records"` +} + +type StorageMeta struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type StroageNode struct { + Value string `json:"value"` + Name string `json:"name"` + Children []StorageMeta `json:"children"` +} + type ThingDetailListRequest struct { TagName string `json:"tag_name"` Provider string `json:"provider"` diff --git a/app/auth/model/mysql/generate/generate.go b/app/auth/model/mysql/generate/generate.go index 336619a..8f583e4 100644 --- a/app/auth/model/mysql/generate/generate.go +++ b/app/auth/model/mysql/generate/generate.go @@ -118,6 +118,8 @@ func main() { scaStorageAlbum := g.GenerateModel("sca_storage_album", fieldOpts...) scaStorageLocation := g.GenerateModel("sca_storage_location", fieldOpts...) scaStorageThumb := g.GenerateModel("sca_storage_thumb", fieldOpts...) + scaStorageShare := g.GenerateModel("sca_storage_share", fieldOpts...) + scaStorageShareVisit := g.GenerateModel("sca_storage_share_visit", fieldOpts...) g.ApplyBasic( scaAuthMenu, @@ -139,6 +141,8 @@ func main() { scaStorageAlbum, scaStorageLocation, scaStorageThumb, + scaStorageShare, + scaStorageShareVisit, ) g.Execute() diff --git a/app/auth/model/mysql/model/sca_storage_album.gen.go b/app/auth/model/mysql/model/sca_storage_album.gen.go index 5da5c7f..16455c3 100644 --- a/app/auth/model/mysql/model/sca_storage_album.gen.go +++ b/app/auth/model/mysql/model/sca_storage_album.gen.go @@ -17,8 +17,8 @@ type ScaStorageAlbum struct { ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键;primary_key" json:"id,string"` // 主键 UserID string `gorm:"column:user_id;type:varchar(50);comment:用户ID" json:"user_id"` // 用户ID AlbumName string `gorm:"column:album_name;type:varchar(50);comment:相册名称" json:"album_name"` // 相册名称 - AlbumType string `gorm:"column:album_type;type:varchar(50);comment:相册类型" json:"album_type"` // 相册类型 - CoverImage string `gorm:"column:cover_image;type:text;comment:封面图片" json:"cover_image"` // 封面图片 + AlbumType int64 `gorm:"column:album_type;type:tinyint(4);comment:相册类型(0 我的 1 分享 2 收藏)" json:"album_type"` // 相册类型(0 我的 1 分享 2 收藏) + CoverImage string `gorm:"column:cover_image;type:longtext;comment:封面图片" json:"cover_image"` // 封面图片 CreatedAt time.Time `gorm:"column:created_at;type:timestamp;autoCreateTime;comment:创建时间" json:"created_at"` // 创建时间 UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;autoUpdateTime;comment:更新时间" json:"updated_at"` // 更新时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp;comment:删除时间" json:"deleted_at"` // 删除时间 diff --git a/app/auth/model/mysql/model/sca_storage_share.gen.go b/app/auth/model/mysql/model/sca_storage_share.gen.go new file mode 100644 index 0000000..c930ada --- /dev/null +++ b/app/auth/model/mysql/model/sca_storage_share.gen.go @@ -0,0 +1,36 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package model + +import ( + "time" + + "gorm.io/gorm" + "gorm.io/plugin/optimisticlock" +) + +const TableNameScaStorageShare = "sca_storage_share" + +// ScaStorageShare 快传分享表 +type ScaStorageShare struct { + ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键;primary_key" json:"id,string"` // 主键 + UserID string `gorm:"column:user_id;type:varchar(50);comment:用户ID" json:"user_id"` // 用户ID + AlbumID int64 `gorm:"column:album_id;type:bigint(20);comment:相册ID" json:"album_id"` // 相册ID + ShareCode string `gorm:"column:share_code;type:varchar(50);comment:分享码(用于访问分享链接)" json:"share_code"` // 分享码(用于访问分享链接) + ExpireTime time.Time `gorm:"column:expire_time;type:datetime;comment:过期时间" json:"expire_time"` // 过期时间 + ValidityPeriod int64 `gorm:"column:validity_period;type:int(11);comment:有效期" json:"validity_period"` // 有效期 + Status int64 `gorm:"column:status;type:tinyint(4);comment:是否失效(0 有效 -1已失效 1 永久)" json:"status"` // 是否失效(0 有效 -1已失效 1 永久) + AccessPassword string `gorm:"column:access_password;type:varchar(50);comment:访问密码" json:"access_password"` // 访问密码 + VisitLimit int64 `gorm:"column:visit_limit;type:bigint(20);comment:限制次数" json:"visit_limit"` // 限制次数 + Version optimisticlock.Version `gorm:"column:version;type:bigint(20);comment:版本" json:"version"` // 版本 + CreatedAt time.Time `gorm:"column:created_at;type:timestamp;autoCreateTime;comment:创建时间" json:"created_at"` // 创建时间 + UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;autoUpdateTime;comment:更新时间" json:"updated_at"` // 更新时间 + DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp;comment:删除时间" json:"deleted_at"` // 删除时间 +} + +// TableName ScaStorageShare's table name +func (*ScaStorageShare) TableName() string { + return TableNameScaStorageShare +} diff --git a/app/auth/model/mysql/model/sca_storage_share_visit.gen.go b/app/auth/model/mysql/model/sca_storage_share_visit.gen.go new file mode 100644 index 0000000..2bf7fe5 --- /dev/null +++ b/app/auth/model/mysql/model/sca_storage_share_visit.gen.go @@ -0,0 +1,31 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package model + +import ( + "time" + + "gorm.io/gorm" + "gorm.io/plugin/optimisticlock" +) + +const TableNameScaStorageShareVisit = "sca_storage_share_visit" + +// ScaStorageShareVisit 分享浏览记录 +type ScaStorageShareVisit struct { + ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键;primary_key" json:"id,string"` // 主键 + UserID string `gorm:"column:user_id;type:varchar(50);comment:用户ID" json:"user_id"` // 用户ID + ShareID int64 `gorm:"column:share_id;type:bigint(20);comment:分享ID" json:"share_id"` // 分享ID + Views int64 `gorm:"column:views;type:bigint(20);comment:浏览次数" json:"views"` // 浏览次数 + Version optimisticlock.Version `gorm:"column:version;type:bigint(20);comment:版本" json:"version"` // 版本 + CreatedAt time.Time `gorm:"column:created_at;type:timestamp;autoCreateTime;comment:创建时间" json:"created_at"` // 创建时间 + UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;autoUpdateTime;comment:更新时间" json:"updated_at"` // 更新时间 + DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:timestamp;comment:删除时间" json:"deleted_at"` // 删除时间 +} + +// TableName ScaStorageShareVisit's table name +func (*ScaStorageShareVisit) TableName() string { + return TableNameScaStorageShareVisit +} diff --git a/app/auth/model/mysql/model/sca_storage_thumb.gen.go b/app/auth/model/mysql/model/sca_storage_thumb.gen.go index 6efaf85..d9d398b 100644 --- a/app/auth/model/mysql/model/sca_storage_thumb.gen.go +++ b/app/auth/model/mysql/model/sca_storage_thumb.gen.go @@ -12,7 +12,7 @@ import ( const TableNameScaStorageThumb = "sca_storage_thumb" -// ScaStorageThumb mapped from table +// ScaStorageThumb 缩略图表 type ScaStorageThumb struct { ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键;primary_key" json:"id,string"` // 主键 UserID string `gorm:"column:user_id;type:varchar(50);not null;comment:用户ID" json:"user_id"` // 用户ID diff --git a/app/auth/model/mysql/query/gen.go b/app/auth/model/mysql/query/gen.go index db4c7f6..6a64ef3 100644 --- a/app/auth/model/mysql/query/gen.go +++ b/app/auth/model/mysql/query/gen.go @@ -30,6 +30,8 @@ var ( ScaStorageConfig *scaStorageConfig ScaStorageInfo *scaStorageInfo ScaStorageLocation *scaStorageLocation + ScaStorageShare *scaStorageShare + ScaStorageShareVisit *scaStorageShareVisit ScaStorageTag *scaStorageTag ScaStorageTagInfo *scaStorageTagInfo ScaStorageThumb *scaStorageThumb @@ -53,6 +55,8 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) { ScaStorageConfig = &Q.ScaStorageConfig ScaStorageInfo = &Q.ScaStorageInfo ScaStorageLocation = &Q.ScaStorageLocation + ScaStorageShare = &Q.ScaStorageShare + ScaStorageShareVisit = &Q.ScaStorageShareVisit ScaStorageTag = &Q.ScaStorageTag ScaStorageTagInfo = &Q.ScaStorageTagInfo ScaStorageThumb = &Q.ScaStorageThumb @@ -77,6 +81,8 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query { ScaStorageConfig: newScaStorageConfig(db, opts...), ScaStorageInfo: newScaStorageInfo(db, opts...), ScaStorageLocation: newScaStorageLocation(db, opts...), + ScaStorageShare: newScaStorageShare(db, opts...), + ScaStorageShareVisit: newScaStorageShareVisit(db, opts...), ScaStorageTag: newScaStorageTag(db, opts...), ScaStorageTagInfo: newScaStorageTagInfo(db, opts...), ScaStorageThumb: newScaStorageThumb(db, opts...), @@ -102,6 +108,8 @@ type Query struct { ScaStorageConfig scaStorageConfig ScaStorageInfo scaStorageInfo ScaStorageLocation scaStorageLocation + ScaStorageShare scaStorageShare + ScaStorageShareVisit scaStorageShareVisit ScaStorageTag scaStorageTag ScaStorageTagInfo scaStorageTagInfo ScaStorageThumb scaStorageThumb @@ -128,6 +136,8 @@ func (q *Query) clone(db *gorm.DB) *Query { ScaStorageConfig: q.ScaStorageConfig.clone(db), ScaStorageInfo: q.ScaStorageInfo.clone(db), ScaStorageLocation: q.ScaStorageLocation.clone(db), + ScaStorageShare: q.ScaStorageShare.clone(db), + ScaStorageShareVisit: q.ScaStorageShareVisit.clone(db), ScaStorageTag: q.ScaStorageTag.clone(db), ScaStorageTagInfo: q.ScaStorageTagInfo.clone(db), ScaStorageThumb: q.ScaStorageThumb.clone(db), @@ -161,6 +171,8 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query { ScaStorageConfig: q.ScaStorageConfig.replaceDB(db), ScaStorageInfo: q.ScaStorageInfo.replaceDB(db), ScaStorageLocation: q.ScaStorageLocation.replaceDB(db), + ScaStorageShare: q.ScaStorageShare.replaceDB(db), + ScaStorageShareVisit: q.ScaStorageShareVisit.replaceDB(db), ScaStorageTag: q.ScaStorageTag.replaceDB(db), ScaStorageTagInfo: q.ScaStorageTagInfo.replaceDB(db), ScaStorageThumb: q.ScaStorageThumb.replaceDB(db), @@ -184,6 +196,8 @@ type queryCtx struct { ScaStorageConfig IScaStorageConfigDo ScaStorageInfo IScaStorageInfoDo ScaStorageLocation IScaStorageLocationDo + ScaStorageShare IScaStorageShareDo + ScaStorageShareVisit IScaStorageShareVisitDo ScaStorageTag IScaStorageTagDo ScaStorageTagInfo IScaStorageTagInfoDo ScaStorageThumb IScaStorageThumbDo @@ -207,6 +221,8 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx { ScaStorageConfig: q.ScaStorageConfig.WithContext(ctx), ScaStorageInfo: q.ScaStorageInfo.WithContext(ctx), ScaStorageLocation: q.ScaStorageLocation.WithContext(ctx), + ScaStorageShare: q.ScaStorageShare.WithContext(ctx), + ScaStorageShareVisit: q.ScaStorageShareVisit.WithContext(ctx), ScaStorageTag: q.ScaStorageTag.WithContext(ctx), ScaStorageTagInfo: q.ScaStorageTagInfo.WithContext(ctx), ScaStorageThumb: q.ScaStorageThumb.WithContext(ctx), diff --git a/app/auth/model/mysql/query/sca_storage_album.gen.go b/app/auth/model/mysql/query/sca_storage_album.gen.go index ddcf91d..029994d 100644 --- a/app/auth/model/mysql/query/sca_storage_album.gen.go +++ b/app/auth/model/mysql/query/sca_storage_album.gen.go @@ -30,7 +30,7 @@ func newScaStorageAlbum(db *gorm.DB, opts ...gen.DOOption) scaStorageAlbum { _scaStorageAlbum.ID = field.NewInt64(tableName, "id") _scaStorageAlbum.UserID = field.NewString(tableName, "user_id") _scaStorageAlbum.AlbumName = field.NewString(tableName, "album_name") - _scaStorageAlbum.AlbumType = field.NewString(tableName, "album_type") + _scaStorageAlbum.AlbumType = field.NewInt64(tableName, "album_type") _scaStorageAlbum.CoverImage = field.NewString(tableName, "cover_image") _scaStorageAlbum.CreatedAt = field.NewTime(tableName, "created_at") _scaStorageAlbum.UpdatedAt = field.NewTime(tableName, "updated_at") @@ -49,7 +49,7 @@ type scaStorageAlbum struct { ID field.Int64 // 主键 UserID field.String // 用户ID AlbumName field.String // 相册名称 - AlbumType field.String // 相册类型 + AlbumType field.Int64 // 相册类型(0 我的 1 分享 2 收藏) CoverImage field.String // 封面图片 CreatedAt field.Time // 创建时间 UpdatedAt field.Time // 更新时间 @@ -73,7 +73,7 @@ func (s *scaStorageAlbum) updateTableName(table string) *scaStorageAlbum { s.ID = field.NewInt64(table, "id") s.UserID = field.NewString(table, "user_id") s.AlbumName = field.NewString(table, "album_name") - s.AlbumType = field.NewString(table, "album_type") + s.AlbumType = field.NewInt64(table, "album_type") s.CoverImage = field.NewString(table, "cover_image") s.CreatedAt = field.NewTime(table, "created_at") s.UpdatedAt = field.NewTime(table, "updated_at") diff --git a/app/auth/model/mysql/query/sca_storage_share.gen.go b/app/auth/model/mysql/query/sca_storage_share.gen.go new file mode 100644 index 0000000..877300c --- /dev/null +++ b/app/auth/model/mysql/query/sca_storage_share.gen.go @@ -0,0 +1,429 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package query + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/field" + + "gorm.io/plugin/dbresolver" + + "schisandra-album-cloud-microservices/app/auth/model/mysql/model" +) + +func newScaStorageShare(db *gorm.DB, opts ...gen.DOOption) scaStorageShare { + _scaStorageShare := scaStorageShare{} + + _scaStorageShare.scaStorageShareDo.UseDB(db, opts...) + _scaStorageShare.scaStorageShareDo.UseModel(&model.ScaStorageShare{}) + + tableName := _scaStorageShare.scaStorageShareDo.TableName() + _scaStorageShare.ALL = field.NewAsterisk(tableName) + _scaStorageShare.ID = field.NewInt64(tableName, "id") + _scaStorageShare.UserID = field.NewString(tableName, "user_id") + _scaStorageShare.AlbumID = field.NewInt64(tableName, "album_id") + _scaStorageShare.ShareCode = field.NewString(tableName, "share_code") + _scaStorageShare.ExpireTime = field.NewTime(tableName, "expire_time") + _scaStorageShare.ValidityPeriod = field.NewInt64(tableName, "validity_period") + _scaStorageShare.Status = field.NewInt64(tableName, "status") + _scaStorageShare.AccessPassword = field.NewString(tableName, "access_password") + _scaStorageShare.VisitLimit = field.NewInt64(tableName, "visit_limit") + _scaStorageShare.Version = field.NewField(tableName, "version") + _scaStorageShare.CreatedAt = field.NewTime(tableName, "created_at") + _scaStorageShare.UpdatedAt = field.NewTime(tableName, "updated_at") + _scaStorageShare.DeletedAt = field.NewField(tableName, "deleted_at") + + _scaStorageShare.fillFieldMap() + + return _scaStorageShare +} + +// scaStorageShare 快传分享表 +type scaStorageShare struct { + scaStorageShareDo + + ALL field.Asterisk + ID field.Int64 // 主键 + UserID field.String // 用户ID + AlbumID field.Int64 // 相册ID + ShareCode field.String // 分享码(用于访问分享链接) + ExpireTime field.Time // 过期时间 + ValidityPeriod field.Int64 // 有效期 + Status field.Int64 // 是否失效(0 有效 -1已失效 1 永久) + AccessPassword field.String // 访问密码 + VisitLimit field.Int64 // 限制次数 + Version field.Field // 版本 + CreatedAt field.Time // 创建时间 + UpdatedAt field.Time // 更新时间 + DeletedAt field.Field // 删除时间 + + fieldMap map[string]field.Expr +} + +func (s scaStorageShare) Table(newTableName string) *scaStorageShare { + s.scaStorageShareDo.UseTable(newTableName) + return s.updateTableName(newTableName) +} + +func (s scaStorageShare) As(alias string) *scaStorageShare { + s.scaStorageShareDo.DO = *(s.scaStorageShareDo.As(alias).(*gen.DO)) + return s.updateTableName(alias) +} + +func (s *scaStorageShare) updateTableName(table string) *scaStorageShare { + s.ALL = field.NewAsterisk(table) + s.ID = field.NewInt64(table, "id") + s.UserID = field.NewString(table, "user_id") + s.AlbumID = field.NewInt64(table, "album_id") + s.ShareCode = field.NewString(table, "share_code") + s.ExpireTime = field.NewTime(table, "expire_time") + s.ValidityPeriod = field.NewInt64(table, "validity_period") + s.Status = field.NewInt64(table, "status") + s.AccessPassword = field.NewString(table, "access_password") + s.VisitLimit = field.NewInt64(table, "visit_limit") + s.Version = field.NewField(table, "version") + s.CreatedAt = field.NewTime(table, "created_at") + s.UpdatedAt = field.NewTime(table, "updated_at") + s.DeletedAt = field.NewField(table, "deleted_at") + + s.fillFieldMap() + + return s +} + +func (s *scaStorageShare) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := s.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (s *scaStorageShare) fillFieldMap() { + s.fieldMap = make(map[string]field.Expr, 13) + s.fieldMap["id"] = s.ID + s.fieldMap["user_id"] = s.UserID + s.fieldMap["album_id"] = s.AlbumID + s.fieldMap["share_code"] = s.ShareCode + s.fieldMap["expire_time"] = s.ExpireTime + s.fieldMap["validity_period"] = s.ValidityPeriod + s.fieldMap["status"] = s.Status + s.fieldMap["access_password"] = s.AccessPassword + s.fieldMap["visit_limit"] = s.VisitLimit + s.fieldMap["version"] = s.Version + s.fieldMap["created_at"] = s.CreatedAt + s.fieldMap["updated_at"] = s.UpdatedAt + s.fieldMap["deleted_at"] = s.DeletedAt +} + +func (s scaStorageShare) clone(db *gorm.DB) scaStorageShare { + s.scaStorageShareDo.ReplaceConnPool(db.Statement.ConnPool) + return s +} + +func (s scaStorageShare) replaceDB(db *gorm.DB) scaStorageShare { + s.scaStorageShareDo.ReplaceDB(db) + return s +} + +type scaStorageShareDo struct{ gen.DO } + +type IScaStorageShareDo interface { + gen.SubQuery + Debug() IScaStorageShareDo + WithContext(ctx context.Context) IScaStorageShareDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() IScaStorageShareDo + WriteDB() IScaStorageShareDo + As(alias string) gen.Dao + Session(config *gorm.Session) IScaStorageShareDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) IScaStorageShareDo + Not(conds ...gen.Condition) IScaStorageShareDo + Or(conds ...gen.Condition) IScaStorageShareDo + Select(conds ...field.Expr) IScaStorageShareDo + Where(conds ...gen.Condition) IScaStorageShareDo + Order(conds ...field.Expr) IScaStorageShareDo + Distinct(cols ...field.Expr) IScaStorageShareDo + Omit(cols ...field.Expr) IScaStorageShareDo + Join(table schema.Tabler, on ...field.Expr) IScaStorageShareDo + LeftJoin(table schema.Tabler, on ...field.Expr) IScaStorageShareDo + RightJoin(table schema.Tabler, on ...field.Expr) IScaStorageShareDo + Group(cols ...field.Expr) IScaStorageShareDo + Having(conds ...gen.Condition) IScaStorageShareDo + Limit(limit int) IScaStorageShareDo + Offset(offset int) IScaStorageShareDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) IScaStorageShareDo + Unscoped() IScaStorageShareDo + Create(values ...*model.ScaStorageShare) error + CreateInBatches(values []*model.ScaStorageShare, batchSize int) error + Save(values ...*model.ScaStorageShare) error + First() (*model.ScaStorageShare, error) + Take() (*model.ScaStorageShare, error) + Last() (*model.ScaStorageShare, error) + Find() ([]*model.ScaStorageShare, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.ScaStorageShare, err error) + FindInBatches(result *[]*model.ScaStorageShare, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*model.ScaStorageShare) (info gen.ResultInfo, err error) + Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error) + UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) + Updates(value interface{}) (info gen.ResultInfo, err error) + UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error) + UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) + UpdateColumns(value interface{}) (info gen.ResultInfo, err error) + UpdateFrom(q gen.SubQuery) gen.Dao + Attrs(attrs ...field.AssignExpr) IScaStorageShareDo + Assign(attrs ...field.AssignExpr) IScaStorageShareDo + Joins(fields ...field.RelationField) IScaStorageShareDo + Preload(fields ...field.RelationField) IScaStorageShareDo + FirstOrInit() (*model.ScaStorageShare, error) + FirstOrCreate() (*model.ScaStorageShare, error) + FindByPage(offset int, limit int) (result []*model.ScaStorageShare, count int64, err error) + ScanByPage(result interface{}, offset int, limit int) (count int64, err error) + Scan(result interface{}) (err error) + Returning(value interface{}, columns ...string) IScaStorageShareDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (s scaStorageShareDo) Debug() IScaStorageShareDo { + return s.withDO(s.DO.Debug()) +} + +func (s scaStorageShareDo) WithContext(ctx context.Context) IScaStorageShareDo { + return s.withDO(s.DO.WithContext(ctx)) +} + +func (s scaStorageShareDo) ReadDB() IScaStorageShareDo { + return s.Clauses(dbresolver.Read) +} + +func (s scaStorageShareDo) WriteDB() IScaStorageShareDo { + return s.Clauses(dbresolver.Write) +} + +func (s scaStorageShareDo) Session(config *gorm.Session) IScaStorageShareDo { + return s.withDO(s.DO.Session(config)) +} + +func (s scaStorageShareDo) Clauses(conds ...clause.Expression) IScaStorageShareDo { + return s.withDO(s.DO.Clauses(conds...)) +} + +func (s scaStorageShareDo) Returning(value interface{}, columns ...string) IScaStorageShareDo { + return s.withDO(s.DO.Returning(value, columns...)) +} + +func (s scaStorageShareDo) Not(conds ...gen.Condition) IScaStorageShareDo { + return s.withDO(s.DO.Not(conds...)) +} + +func (s scaStorageShareDo) Or(conds ...gen.Condition) IScaStorageShareDo { + return s.withDO(s.DO.Or(conds...)) +} + +func (s scaStorageShareDo) Select(conds ...field.Expr) IScaStorageShareDo { + return s.withDO(s.DO.Select(conds...)) +} + +func (s scaStorageShareDo) Where(conds ...gen.Condition) IScaStorageShareDo { + return s.withDO(s.DO.Where(conds...)) +} + +func (s scaStorageShareDo) Order(conds ...field.Expr) IScaStorageShareDo { + return s.withDO(s.DO.Order(conds...)) +} + +func (s scaStorageShareDo) Distinct(cols ...field.Expr) IScaStorageShareDo { + return s.withDO(s.DO.Distinct(cols...)) +} + +func (s scaStorageShareDo) Omit(cols ...field.Expr) IScaStorageShareDo { + return s.withDO(s.DO.Omit(cols...)) +} + +func (s scaStorageShareDo) Join(table schema.Tabler, on ...field.Expr) IScaStorageShareDo { + return s.withDO(s.DO.Join(table, on...)) +} + +func (s scaStorageShareDo) LeftJoin(table schema.Tabler, on ...field.Expr) IScaStorageShareDo { + return s.withDO(s.DO.LeftJoin(table, on...)) +} + +func (s scaStorageShareDo) RightJoin(table schema.Tabler, on ...field.Expr) IScaStorageShareDo { + return s.withDO(s.DO.RightJoin(table, on...)) +} + +func (s scaStorageShareDo) Group(cols ...field.Expr) IScaStorageShareDo { + return s.withDO(s.DO.Group(cols...)) +} + +func (s scaStorageShareDo) Having(conds ...gen.Condition) IScaStorageShareDo { + return s.withDO(s.DO.Having(conds...)) +} + +func (s scaStorageShareDo) Limit(limit int) IScaStorageShareDo { + return s.withDO(s.DO.Limit(limit)) +} + +func (s scaStorageShareDo) Offset(offset int) IScaStorageShareDo { + return s.withDO(s.DO.Offset(offset)) +} + +func (s scaStorageShareDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IScaStorageShareDo { + return s.withDO(s.DO.Scopes(funcs...)) +} + +func (s scaStorageShareDo) Unscoped() IScaStorageShareDo { + return s.withDO(s.DO.Unscoped()) +} + +func (s scaStorageShareDo) Create(values ...*model.ScaStorageShare) error { + if len(values) == 0 { + return nil + } + return s.DO.Create(values) +} + +func (s scaStorageShareDo) CreateInBatches(values []*model.ScaStorageShare, batchSize int) error { + return s.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (s scaStorageShareDo) Save(values ...*model.ScaStorageShare) error { + if len(values) == 0 { + return nil + } + return s.DO.Save(values) +} + +func (s scaStorageShareDo) First() (*model.ScaStorageShare, error) { + if result, err := s.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.ScaStorageShare), nil + } +} + +func (s scaStorageShareDo) Take() (*model.ScaStorageShare, error) { + if result, err := s.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.ScaStorageShare), nil + } +} + +func (s scaStorageShareDo) Last() (*model.ScaStorageShare, error) { + if result, err := s.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.ScaStorageShare), nil + } +} + +func (s scaStorageShareDo) Find() ([]*model.ScaStorageShare, error) { + result, err := s.DO.Find() + return result.([]*model.ScaStorageShare), err +} + +func (s scaStorageShareDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.ScaStorageShare, err error) { + buf := make([]*model.ScaStorageShare, 0, batchSize) + err = s.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (s scaStorageShareDo) FindInBatches(result *[]*model.ScaStorageShare, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return s.DO.FindInBatches(result, batchSize, fc) +} + +func (s scaStorageShareDo) Attrs(attrs ...field.AssignExpr) IScaStorageShareDo { + return s.withDO(s.DO.Attrs(attrs...)) +} + +func (s scaStorageShareDo) Assign(attrs ...field.AssignExpr) IScaStorageShareDo { + return s.withDO(s.DO.Assign(attrs...)) +} + +func (s scaStorageShareDo) Joins(fields ...field.RelationField) IScaStorageShareDo { + for _, _f := range fields { + s = *s.withDO(s.DO.Joins(_f)) + } + return &s +} + +func (s scaStorageShareDo) Preload(fields ...field.RelationField) IScaStorageShareDo { + for _, _f := range fields { + s = *s.withDO(s.DO.Preload(_f)) + } + return &s +} + +func (s scaStorageShareDo) FirstOrInit() (*model.ScaStorageShare, error) { + if result, err := s.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.ScaStorageShare), nil + } +} + +func (s scaStorageShareDo) FirstOrCreate() (*model.ScaStorageShare, error) { + if result, err := s.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.ScaStorageShare), nil + } +} + +func (s scaStorageShareDo) FindByPage(offset int, limit int) (result []*model.ScaStorageShare, count int64, err error) { + result, err = s.Offset(offset).Limit(limit).Find() + if err != nil { + return + } + + if size := len(result); 0 < limit && 0 < size && size < limit { + count = int64(size + offset) + return + } + + count, err = s.Offset(-1).Limit(-1).Count() + return +} + +func (s scaStorageShareDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = s.Count() + if err != nil { + return + } + + err = s.Offset(offset).Limit(limit).Scan(result) + return +} + +func (s scaStorageShareDo) Scan(result interface{}) (err error) { + return s.DO.Scan(result) +} + +func (s scaStorageShareDo) Delete(models ...*model.ScaStorageShare) (result gen.ResultInfo, err error) { + return s.DO.Delete(models) +} + +func (s *scaStorageShareDo) withDO(do gen.Dao) *scaStorageShareDo { + s.DO = *do.(*gen.DO) + return s +} diff --git a/app/auth/model/mysql/query/sca_storage_share_visit.gen.go b/app/auth/model/mysql/query/sca_storage_share_visit.gen.go new file mode 100644 index 0000000..69a5e6a --- /dev/null +++ b/app/auth/model/mysql/query/sca_storage_share_visit.gen.go @@ -0,0 +1,409 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package query + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/field" + + "gorm.io/plugin/dbresolver" + + "schisandra-album-cloud-microservices/app/auth/model/mysql/model" +) + +func newScaStorageShareVisit(db *gorm.DB, opts ...gen.DOOption) scaStorageShareVisit { + _scaStorageShareVisit := scaStorageShareVisit{} + + _scaStorageShareVisit.scaStorageShareVisitDo.UseDB(db, opts...) + _scaStorageShareVisit.scaStorageShareVisitDo.UseModel(&model.ScaStorageShareVisit{}) + + tableName := _scaStorageShareVisit.scaStorageShareVisitDo.TableName() + _scaStorageShareVisit.ALL = field.NewAsterisk(tableName) + _scaStorageShareVisit.ID = field.NewInt64(tableName, "id") + _scaStorageShareVisit.UserID = field.NewString(tableName, "user_id") + _scaStorageShareVisit.ShareID = field.NewInt64(tableName, "share_id") + _scaStorageShareVisit.Views = field.NewInt64(tableName, "views") + _scaStorageShareVisit.Version = field.NewField(tableName, "version") + _scaStorageShareVisit.CreatedAt = field.NewTime(tableName, "created_at") + _scaStorageShareVisit.UpdatedAt = field.NewTime(tableName, "updated_at") + _scaStorageShareVisit.DeletedAt = field.NewField(tableName, "deleted_at") + + _scaStorageShareVisit.fillFieldMap() + + return _scaStorageShareVisit +} + +// scaStorageShareVisit 分享浏览记录 +type scaStorageShareVisit struct { + scaStorageShareVisitDo + + ALL field.Asterisk + ID field.Int64 // 主键 + UserID field.String // 用户ID + ShareID field.Int64 // 分享ID + Views field.Int64 // 浏览次数 + Version field.Field // 版本 + CreatedAt field.Time // 创建时间 + UpdatedAt field.Time // 更新时间 + DeletedAt field.Field // 删除时间 + + fieldMap map[string]field.Expr +} + +func (s scaStorageShareVisit) Table(newTableName string) *scaStorageShareVisit { + s.scaStorageShareVisitDo.UseTable(newTableName) + return s.updateTableName(newTableName) +} + +func (s scaStorageShareVisit) As(alias string) *scaStorageShareVisit { + s.scaStorageShareVisitDo.DO = *(s.scaStorageShareVisitDo.As(alias).(*gen.DO)) + return s.updateTableName(alias) +} + +func (s *scaStorageShareVisit) updateTableName(table string) *scaStorageShareVisit { + s.ALL = field.NewAsterisk(table) + s.ID = field.NewInt64(table, "id") + s.UserID = field.NewString(table, "user_id") + s.ShareID = field.NewInt64(table, "share_id") + s.Views = field.NewInt64(table, "views") + s.Version = field.NewField(table, "version") + s.CreatedAt = field.NewTime(table, "created_at") + s.UpdatedAt = field.NewTime(table, "updated_at") + s.DeletedAt = field.NewField(table, "deleted_at") + + s.fillFieldMap() + + return s +} + +func (s *scaStorageShareVisit) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := s.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (s *scaStorageShareVisit) fillFieldMap() { + s.fieldMap = make(map[string]field.Expr, 8) + s.fieldMap["id"] = s.ID + s.fieldMap["user_id"] = s.UserID + s.fieldMap["share_id"] = s.ShareID + s.fieldMap["views"] = s.Views + s.fieldMap["version"] = s.Version + s.fieldMap["created_at"] = s.CreatedAt + s.fieldMap["updated_at"] = s.UpdatedAt + s.fieldMap["deleted_at"] = s.DeletedAt +} + +func (s scaStorageShareVisit) clone(db *gorm.DB) scaStorageShareVisit { + s.scaStorageShareVisitDo.ReplaceConnPool(db.Statement.ConnPool) + return s +} + +func (s scaStorageShareVisit) replaceDB(db *gorm.DB) scaStorageShareVisit { + s.scaStorageShareVisitDo.ReplaceDB(db) + return s +} + +type scaStorageShareVisitDo struct{ gen.DO } + +type IScaStorageShareVisitDo interface { + gen.SubQuery + Debug() IScaStorageShareVisitDo + WithContext(ctx context.Context) IScaStorageShareVisitDo + WithResult(fc func(tx gen.Dao)) gen.ResultInfo + ReplaceDB(db *gorm.DB) + ReadDB() IScaStorageShareVisitDo + WriteDB() IScaStorageShareVisitDo + As(alias string) gen.Dao + Session(config *gorm.Session) IScaStorageShareVisitDo + Columns(cols ...field.Expr) gen.Columns + Clauses(conds ...clause.Expression) IScaStorageShareVisitDo + Not(conds ...gen.Condition) IScaStorageShareVisitDo + Or(conds ...gen.Condition) IScaStorageShareVisitDo + Select(conds ...field.Expr) IScaStorageShareVisitDo + Where(conds ...gen.Condition) IScaStorageShareVisitDo + Order(conds ...field.Expr) IScaStorageShareVisitDo + Distinct(cols ...field.Expr) IScaStorageShareVisitDo + Omit(cols ...field.Expr) IScaStorageShareVisitDo + Join(table schema.Tabler, on ...field.Expr) IScaStorageShareVisitDo + LeftJoin(table schema.Tabler, on ...field.Expr) IScaStorageShareVisitDo + RightJoin(table schema.Tabler, on ...field.Expr) IScaStorageShareVisitDo + Group(cols ...field.Expr) IScaStorageShareVisitDo + Having(conds ...gen.Condition) IScaStorageShareVisitDo + Limit(limit int) IScaStorageShareVisitDo + Offset(offset int) IScaStorageShareVisitDo + Count() (count int64, err error) + Scopes(funcs ...func(gen.Dao) gen.Dao) IScaStorageShareVisitDo + Unscoped() IScaStorageShareVisitDo + Create(values ...*model.ScaStorageShareVisit) error + CreateInBatches(values []*model.ScaStorageShareVisit, batchSize int) error + Save(values ...*model.ScaStorageShareVisit) error + First() (*model.ScaStorageShareVisit, error) + Take() (*model.ScaStorageShareVisit, error) + Last() (*model.ScaStorageShareVisit, error) + Find() ([]*model.ScaStorageShareVisit, error) + FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.ScaStorageShareVisit, err error) + FindInBatches(result *[]*model.ScaStorageShareVisit, batchSize int, fc func(tx gen.Dao, batch int) error) error + Pluck(column field.Expr, dest interface{}) error + Delete(...*model.ScaStorageShareVisit) (info gen.ResultInfo, err error) + Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error) + UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) + Updates(value interface{}) (info gen.ResultInfo, err error) + UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error) + UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) + UpdateColumns(value interface{}) (info gen.ResultInfo, err error) + UpdateFrom(q gen.SubQuery) gen.Dao + Attrs(attrs ...field.AssignExpr) IScaStorageShareVisitDo + Assign(attrs ...field.AssignExpr) IScaStorageShareVisitDo + Joins(fields ...field.RelationField) IScaStorageShareVisitDo + Preload(fields ...field.RelationField) IScaStorageShareVisitDo + FirstOrInit() (*model.ScaStorageShareVisit, error) + FirstOrCreate() (*model.ScaStorageShareVisit, error) + FindByPage(offset int, limit int) (result []*model.ScaStorageShareVisit, count int64, err error) + ScanByPage(result interface{}, offset int, limit int) (count int64, err error) + Scan(result interface{}) (err error) + Returning(value interface{}, columns ...string) IScaStorageShareVisitDo + UnderlyingDB() *gorm.DB + schema.Tabler +} + +func (s scaStorageShareVisitDo) Debug() IScaStorageShareVisitDo { + return s.withDO(s.DO.Debug()) +} + +func (s scaStorageShareVisitDo) WithContext(ctx context.Context) IScaStorageShareVisitDo { + return s.withDO(s.DO.WithContext(ctx)) +} + +func (s scaStorageShareVisitDo) ReadDB() IScaStorageShareVisitDo { + return s.Clauses(dbresolver.Read) +} + +func (s scaStorageShareVisitDo) WriteDB() IScaStorageShareVisitDo { + return s.Clauses(dbresolver.Write) +} + +func (s scaStorageShareVisitDo) Session(config *gorm.Session) IScaStorageShareVisitDo { + return s.withDO(s.DO.Session(config)) +} + +func (s scaStorageShareVisitDo) Clauses(conds ...clause.Expression) IScaStorageShareVisitDo { + return s.withDO(s.DO.Clauses(conds...)) +} + +func (s scaStorageShareVisitDo) Returning(value interface{}, columns ...string) IScaStorageShareVisitDo { + return s.withDO(s.DO.Returning(value, columns...)) +} + +func (s scaStorageShareVisitDo) Not(conds ...gen.Condition) IScaStorageShareVisitDo { + return s.withDO(s.DO.Not(conds...)) +} + +func (s scaStorageShareVisitDo) Or(conds ...gen.Condition) IScaStorageShareVisitDo { + return s.withDO(s.DO.Or(conds...)) +} + +func (s scaStorageShareVisitDo) Select(conds ...field.Expr) IScaStorageShareVisitDo { + return s.withDO(s.DO.Select(conds...)) +} + +func (s scaStorageShareVisitDo) Where(conds ...gen.Condition) IScaStorageShareVisitDo { + return s.withDO(s.DO.Where(conds...)) +} + +func (s scaStorageShareVisitDo) Order(conds ...field.Expr) IScaStorageShareVisitDo { + return s.withDO(s.DO.Order(conds...)) +} + +func (s scaStorageShareVisitDo) Distinct(cols ...field.Expr) IScaStorageShareVisitDo { + return s.withDO(s.DO.Distinct(cols...)) +} + +func (s scaStorageShareVisitDo) Omit(cols ...field.Expr) IScaStorageShareVisitDo { + return s.withDO(s.DO.Omit(cols...)) +} + +func (s scaStorageShareVisitDo) Join(table schema.Tabler, on ...field.Expr) IScaStorageShareVisitDo { + return s.withDO(s.DO.Join(table, on...)) +} + +func (s scaStorageShareVisitDo) LeftJoin(table schema.Tabler, on ...field.Expr) IScaStorageShareVisitDo { + return s.withDO(s.DO.LeftJoin(table, on...)) +} + +func (s scaStorageShareVisitDo) RightJoin(table schema.Tabler, on ...field.Expr) IScaStorageShareVisitDo { + return s.withDO(s.DO.RightJoin(table, on...)) +} + +func (s scaStorageShareVisitDo) Group(cols ...field.Expr) IScaStorageShareVisitDo { + return s.withDO(s.DO.Group(cols...)) +} + +func (s scaStorageShareVisitDo) Having(conds ...gen.Condition) IScaStorageShareVisitDo { + return s.withDO(s.DO.Having(conds...)) +} + +func (s scaStorageShareVisitDo) Limit(limit int) IScaStorageShareVisitDo { + return s.withDO(s.DO.Limit(limit)) +} + +func (s scaStorageShareVisitDo) Offset(offset int) IScaStorageShareVisitDo { + return s.withDO(s.DO.Offset(offset)) +} + +func (s scaStorageShareVisitDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IScaStorageShareVisitDo { + return s.withDO(s.DO.Scopes(funcs...)) +} + +func (s scaStorageShareVisitDo) Unscoped() IScaStorageShareVisitDo { + return s.withDO(s.DO.Unscoped()) +} + +func (s scaStorageShareVisitDo) Create(values ...*model.ScaStorageShareVisit) error { + if len(values) == 0 { + return nil + } + return s.DO.Create(values) +} + +func (s scaStorageShareVisitDo) CreateInBatches(values []*model.ScaStorageShareVisit, batchSize int) error { + return s.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (s scaStorageShareVisitDo) Save(values ...*model.ScaStorageShareVisit) error { + if len(values) == 0 { + return nil + } + return s.DO.Save(values) +} + +func (s scaStorageShareVisitDo) First() (*model.ScaStorageShareVisit, error) { + if result, err := s.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.ScaStorageShareVisit), nil + } +} + +func (s scaStorageShareVisitDo) Take() (*model.ScaStorageShareVisit, error) { + if result, err := s.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.ScaStorageShareVisit), nil + } +} + +func (s scaStorageShareVisitDo) Last() (*model.ScaStorageShareVisit, error) { + if result, err := s.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.ScaStorageShareVisit), nil + } +} + +func (s scaStorageShareVisitDo) Find() ([]*model.ScaStorageShareVisit, error) { + result, err := s.DO.Find() + return result.([]*model.ScaStorageShareVisit), err +} + +func (s scaStorageShareVisitDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.ScaStorageShareVisit, err error) { + buf := make([]*model.ScaStorageShareVisit, 0, batchSize) + err = s.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (s scaStorageShareVisitDo) FindInBatches(result *[]*model.ScaStorageShareVisit, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return s.DO.FindInBatches(result, batchSize, fc) +} + +func (s scaStorageShareVisitDo) Attrs(attrs ...field.AssignExpr) IScaStorageShareVisitDo { + return s.withDO(s.DO.Attrs(attrs...)) +} + +func (s scaStorageShareVisitDo) Assign(attrs ...field.AssignExpr) IScaStorageShareVisitDo { + return s.withDO(s.DO.Assign(attrs...)) +} + +func (s scaStorageShareVisitDo) Joins(fields ...field.RelationField) IScaStorageShareVisitDo { + for _, _f := range fields { + s = *s.withDO(s.DO.Joins(_f)) + } + return &s +} + +func (s scaStorageShareVisitDo) Preload(fields ...field.RelationField) IScaStorageShareVisitDo { + for _, _f := range fields { + s = *s.withDO(s.DO.Preload(_f)) + } + return &s +} + +func (s scaStorageShareVisitDo) FirstOrInit() (*model.ScaStorageShareVisit, error) { + if result, err := s.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.ScaStorageShareVisit), nil + } +} + +func (s scaStorageShareVisitDo) FirstOrCreate() (*model.ScaStorageShareVisit, error) { + if result, err := s.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.ScaStorageShareVisit), nil + } +} + +func (s scaStorageShareVisitDo) FindByPage(offset int, limit int) (result []*model.ScaStorageShareVisit, count int64, err error) { + result, err = s.Offset(offset).Limit(limit).Find() + if err != nil { + return + } + + if size := len(result); 0 < limit && 0 < size && size < limit { + count = int64(size + offset) + return + } + + count, err = s.Offset(-1).Limit(-1).Count() + return +} + +func (s scaStorageShareVisitDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = s.Count() + if err != nil { + return + } + + err = s.Offset(offset).Limit(limit).Scan(result) + return +} + +func (s scaStorageShareVisitDo) Scan(result interface{}) (err error) { + return s.DO.Scan(result) +} + +func (s scaStorageShareVisitDo) Delete(models ...*model.ScaStorageShareVisit) (result gen.ResultInfo, err error) { + return s.DO.Delete(models) +} + +func (s *scaStorageShareVisitDo) withDO(do gen.Dao) *scaStorageShareVisitDo { + s.DO = *do.(*gen.DO) + return s +} diff --git a/app/auth/model/mysql/query/sca_storage_thumb.gen.go b/app/auth/model/mysql/query/sca_storage_thumb.gen.go index 3f830d0..119200a 100644 --- a/app/auth/model/mysql/query/sca_storage_thumb.gen.go +++ b/app/auth/model/mysql/query/sca_storage_thumb.gen.go @@ -42,6 +42,7 @@ func newScaStorageThumb(db *gorm.DB, opts ...gen.DOOption) scaStorageThumb { return _scaStorageThumb } +// scaStorageThumb 缩略图表 type scaStorageThumb struct { scaStorageThumbDo diff --git a/common/constant/album_type.go b/common/constant/album_type.go new file mode 100644 index 0000000..168529c --- /dev/null +++ b/common/constant/album_type.go @@ -0,0 +1,8 @@ +package constant + +const ( + AlbumTypeMine = 0 + AlbumTypeShared = 1 + + AlbumTypeCollection = 2 +) diff --git a/common/constant/bucket_name.go b/common/constant/bucket_name.go index bf2a50a..bc54716 100644 --- a/common/constant/bucket_name.go +++ b/common/constant/bucket_name.go @@ -1,7 +1,8 @@ package constant const ( - FaceBucketName = "schisandra-face-samples" - CommentImagesBucketName = "schisandra-comment-images" - ThumbnailBucketName = "schisandra-thumbnail-images" + FaceBucketName = "schisandra-face-samples" + CommentImagesBucketName = "schisandra-comment-images" + ThumbnailBucketName = "schisandra-thumbnail-images" + ShareThumbnailBucketName = "schisandra-share-thumbnails" ) diff --git a/common/constant/image_type.go b/common/constant/image_type.go new file mode 100644 index 0000000..f08a158 --- /dev/null +++ b/common/constant/image_type.go @@ -0,0 +1,9 @@ +package constant + +const ( + ImageTypeImage = "image" + ImageTypeVideo = "video" + ImageTypeScreenshot = "screenshot" + ImageTypeGif = "gif" + ImageTypeShared = "shared" +) diff --git a/common/constant/redis_prefix.go b/common/constant/redis_prefix.go index 06ce4c5..9b21157 100644 --- a/common/constant/redis_prefix.go +++ b/common/constant/redis_prefix.go @@ -21,8 +21,10 @@ const ( ) const ( - ImageListPrefix = "image:list:" - ImageRecentPrefix = "image:recent:" - ImageFaceListPrefix = "image:faces:" - ImageSinglePrefix = "image:single:" + ImageListPrefix = "image:list:" + ImageRecentPrefix = "image:recent:" + ImageFaceListPrefix = "image:faces:" + ImageSinglePrefix = "image:single:" + ImageSharePrefix = "image:share:" + ImageShareVisitPrefix = "image:share:visit:" ) diff --git a/common/middleware/casbin_middleware.go b/common/middleware/casbin_middleware.go deleted file mode 100644 index c8ea6e4..0000000 --- a/common/middleware/casbin_middleware.go +++ /dev/null @@ -1,18 +0,0 @@ -package middleware - -import ( - "github.com/casbin/casbin/v2" - "net/http" - "schisandra-album-cloud-microservices/common/constant" - "schisandra-album-cloud-microservices/common/errors" - "schisandra-album-cloud-microservices/common/xhttp" -) - -func CasbinMiddleware(w http.ResponseWriter, r *http.Request, casbin *casbin.SyncedCachedEnforcer) { - userId := r.Header.Get(constant.UID_HEADER_KEY) - correct, err := casbin.Enforce(userId, r.URL.Path, r.Method) - if err != nil || !correct { - xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusNotFound, "not found")) - return - } -} diff --git a/common/middleware/nonce_middleware.go b/common/middleware/nonce_middleware.go deleted file mode 100644 index d918e4e..0000000 --- a/common/middleware/nonce_middleware.go +++ /dev/null @@ -1,32 +0,0 @@ -package middleware - -import ( - "github.com/redis/go-redis/v9" - "net/http" - "schisandra-album-cloud-microservices/common/constant" - "schisandra-album-cloud-microservices/common/errors" - "schisandra-album-cloud-microservices/common/xhttp" - "time" -) - -func NonceMiddleware(w http.ResponseWriter, r *http.Request, redisClient *redis.Client) { - nonce := r.Header.Get("X-Nonce") - if nonce == "" { - xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusBadRequest, "bad request!")) - return - } - if len(nonce) != 32 { - xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusBadRequest, "bad request!")) - return - } - result := redisClient.Get(r.Context(), constant.SystemApiNoncePrefix+nonce).Val() - if result != "" { - xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusBadRequest, "bad request!")) - return - } - err := redisClient.Set(r.Context(), constant.SystemApiNoncePrefix+nonce, nonce, time.Minute*1).Err() - if err != nil { - xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusInternalServerError, "internal server error!")) - return - } -} diff --git a/go.mod b/go.mod index 8aad989..1b1f5ca 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20241220152942-06eb5c6e8230 github.com/lxzan/gws v1.8.8 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/minio/minio-go/v7 v7.0.85 + github.com/minio/minio-go/v7 v7.0.86 github.com/mssola/useragent v1.0.0 github.com/nicksnyder/go-i18n/v2 v2.5.1 github.com/nsqio/go-nsq v1.1.0 @@ -28,12 +28,13 @@ require ( github.com/redis/go-redis/v9 v9.7.0 github.com/tencentyun/cos-go-sdk-v5 v0.7.61 github.com/wenlng/go-captcha-assets v1.0.1 - github.com/wenlng/go-captcha/v2 v2.0.2 + github.com/wenlng/go-captcha/v2 v2.0.3 github.com/yitter/idgenerator-go v1.3.3 github.com/zeromicro/go-zero v1.8.0 github.com/zmexing/go-sensitive-word v1.3.0 gocv.io/x/gocv v0.40.0 golang.org/x/crypto v0.33.0 + golang.org/x/sync v0.11.0 golang.org/x/text v0.22.0 google.golang.org/grpc v1.70.0 google.golang.org/protobuf v1.36.5 @@ -62,7 +63,6 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/disintegration/imaging v1.6.2 // indirect github.com/dolthub/maphash v0.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect @@ -114,6 +114,7 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/microsoft/go-mssqldb v1.8.0 // indirect + github.com/minio/crc64nvme v1.0.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -159,12 +160,11 @@ require ( go.uber.org/mock v0.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect + golang.org/x/exp v0.0.0-20250215185904-eff6e970281f // indirect golang.org/x/image v0.24.0 // indirect golang.org/x/mod v0.23.0 // indirect golang.org/x/net v0.35.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect - golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/term v0.29.0 // indirect golang.org/x/time v0.10.0 // indirect diff --git a/go.sum b/go.sum index 0279e21..2dc479a 100644 --- a/go.sum +++ b/go.sum @@ -85,8 +85,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= -github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ= @@ -124,6 +122,7 @@ github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= @@ -176,6 +175,7 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= @@ -250,10 +250,14 @@ github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/microsoft/go-mssqldb v1.8.0 h1:7cyZ/AT7ycDsEoWPIXibd+aVKFtteUNhDGf3aobP+tw= github.com/microsoft/go-mssqldb v1.8.0/go.mod h1:6znkekS3T2vp0waiMhen4GPU1BiAsrP+iXHcE7a7rFo= +github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY= +github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.85 h1:9psTLS/NTvC3MWoyjhjXpwcKoNbkongaCSF3PNpSuXo= github.com/minio/minio-go/v7 v7.0.85/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY= +github.com/minio/minio-go/v7 v7.0.86 h1:DcgQ0AUjLJzRH6y/HrxiZ8CXarA70PAIufXHodP4s+k= +github.com/minio/minio-go/v7 v7.0.86/go.mod h1:VbfO4hYwUu3Of9WqGLBZ8vl3Hxnxo4ngxK4hzQDf4x4= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -359,6 +363,8 @@ github.com/wenlng/go-captcha-assets v1.0.1 h1:AdjRFMKmadPRWRTv0XEYfjDvcaayZ2yExI github.com/wenlng/go-captcha-assets v1.0.1/go.mod h1:yQqc7rRbxgLCg+tWtVp+7Y317D1wIZDan/yIwt8wSac= github.com/wenlng/go-captcha/v2 v2.0.2 h1:8twz6pI6xZwPvEGFezoFX395oFso1MuOlJt/tLiv7pk= github.com/wenlng/go-captcha/v2 v2.0.2/go.mod h1:5hac1em3uXoyC5ipZ0xFv9umNM/waQvYAQdr0cx/h34= +github.com/wenlng/go-captcha/v2 v2.0.3 h1:QTZ39/gVDisPSgvL9O2X2HbTuj5P/z8QsdGB/aayg9c= +github.com/wenlng/go-captcha/v2 v2.0.3/go.mod h1:5hac1em3uXoyC5ipZ0xFv9umNM/waQvYAQdr0cx/h34= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -442,7 +448,8 @@ golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= -golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20250215185904-eff6e970281f h1:oFMYAjX0867ZD2jcNiLBrI9BdpmEkvPyi5YrBGXbamg= +golang.org/x/exp v0.0.0-20250215185904-eff6e970281f/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs= golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=