diff --git a/app/auth/api/auth.api b/app/auth/api/auth.api index 02720bc..7d36b40 100644 --- a/app/auth/api/auth.api +++ b/app/auth/api/auth.api @@ -403,8 +403,8 @@ type ( ) @server ( - group: upscale // 微服务分组 - prefix: /api/auth/upscale // 微服务前缀 + group: phone // 微服务分组 + prefix: /api/auth/phone // 微服务前缀 timeout: 10s // 超时时间 maxBytes: 10485760 // 最大请求大小 signature: false // 是否开启签名验证 @@ -414,7 +414,7 @@ type ( ) service auth { @handler uploadImage - post /phone/upload (UploadRequest) + post /upload (UploadRequest) } // 文件上传配置请求参数 @@ -529,6 +529,10 @@ type ( AllImageListResponse { records []AllImageDetail `json:"records"` } + RecentListRequest { + Provider string `json:"provider"` + Bucket string `json:"bucket"` + } // 最近上传图片列表请求参数 RecentListResponse { records []AllImageDetail `json:"records"` @@ -626,6 +630,19 @@ type ( Used string `json:"used"` Percentage float64 `json:"percentage"` } + ShareAlbumRequest { + ID int64 `json:"id"` + 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"` + } + DownloadAlbumRequest { + ID int64 `json:"id"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` + } ) // 文件上传 @@ -691,7 +708,7 @@ service auth { // 获取最近上传图片列表 @handler queryRecentImageList - post /image/recent/list returns (RecentListResponse) + post /image/recent/list (RecentListRequest) returns (RecentListResponse) // 获取地点相册列表 @handler queryLocationImageList @@ -728,6 +745,14 @@ service auth { // 获取存储桶的容量信息 @handler getBucketCapacity post /bucket/capacity (BucketCapacityRequest) returns (BucketCapacityResponse) + + // 分享相册 + @handler shareAlbum + post /album/share (ShareAlbumRequest) returns (string) + + // 下载相册 + @handler downloadAlbum + post /album/download (DownloadAlbumRequest) returns (string) } type ( diff --git a/app/auth/api/internal/handler/upscale/upload_image_handler.go b/app/auth/api/internal/handler/phone/upload_image_handler.go similarity index 88% rename from app/auth/api/internal/handler/upscale/upload_image_handler.go rename to app/auth/api/internal/handler/phone/upload_image_handler.go index fc1723c..18e0174 100644 --- a/app/auth/api/internal/handler/upscale/upload_image_handler.go +++ b/app/auth/api/internal/handler/phone/upload_image_handler.go @@ -1,12 +1,12 @@ -package upscale +package phone import ( - "github.com/zeromicro/go-zero/rest/httpx" "net/http" - "schisandra-album-cloud-microservices/app/auth/api/internal/logic/upscale" + + "github.com/zeromicro/go-zero/rest/httpx" + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/phone" "schisandra-album-cloud-microservices/app/auth/api/internal/svc" "schisandra-album-cloud-microservices/app/auth/api/internal/types" - "schisandra-album-cloud-microservices/common/xhttp" ) @@ -18,7 +18,7 @@ func UploadImageHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return } - l := upscale.NewUploadImageLogic(r.Context(), svcCtx) + l := phone.NewUploadImageLogic(r.Context(), svcCtx) err := l.UploadImage(r, &req) xhttp.JsonBaseResponseCtx(r.Context(), w, err) } diff --git a/app/auth/api/internal/handler/routes.go b/app/auth/api/internal/handler/routes.go index 29162f6..a15b094 100644 --- a/app/auth/api/internal/handler/routes.go +++ b/app/auth/api/internal/handler/routes.go @@ -11,11 +11,11 @@ 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" + phone "schisandra-album-cloud-microservices/app/auth/api/internal/handler/phone" 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" - upscale "schisandra-album-cloud-microservices/app/auth/api/internal/handler/upscale" user "schisandra-album-cloud-microservices/app/auth/api/internal/handler/user" websocket "schisandra-album-cloud-microservices/app/auth/api/internal/handler/websocket" "schisandra-album-cloud-microservices/app/auth/api/internal/svc" @@ -159,6 +159,22 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { rest.WithMaxBytes(1048576), ) + server.AddRoutes( + rest.WithMiddlewares( + []rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.NonceMiddleware}, + []rest.Route{ + { + Method: http.MethodPost, + Path: "/upload", + Handler: phone.UploadImageHandler(serverCtx), + }, + }..., + ), + rest.WithPrefix("/api/auth/phone"), + rest.WithTimeout(10000*time.Millisecond), + rest.WithMaxBytes(10485760), + ) + server.AddRoutes( rest.WithMiddlewares( []rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware}, @@ -241,6 +257,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/album/detail/list", Handler: storage.GetAlbumDetailHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/album/download", + Handler: storage.DownloadAlbumHandler(serverCtx), + }, { Method: http.MethodPost, Path: "/album/list", @@ -251,6 +272,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/album/rename", Handler: storage.RenameAlbumHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/album/share", + Handler: storage.ShareAlbumHandler(serverCtx), + }, { Method: http.MethodPost, Path: "/bucket/capacity", @@ -361,22 +387,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { rest.WithMaxBytes(1048576), ) - server.AddRoutes( - rest.WithMiddlewares( - []rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.NonceMiddleware}, - []rest.Route{ - { - Method: http.MethodPost, - Path: "/phone/upload", - Handler: upscale.UploadImageHandler(serverCtx), - }, - }..., - ), - rest.WithPrefix("/api/auth/upscale"), - rest.WithTimeout(10000*time.Millisecond), - rest.WithMaxBytes(10485760), - ) - server.AddRoutes( rest.WithMiddlewares( []rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.NonceMiddleware}, diff --git a/app/auth/api/internal/handler/storage/download_album_handler.go b/app/auth/api/internal/handler/storage/download_album_handler.go new file mode 100644 index 0000000..1e287cb --- /dev/null +++ b/app/auth/api/internal/handler/storage/download_album_handler.go @@ -0,0 +1,29 @@ +package storage + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/storage" + "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 DownloadAlbumHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.DownloadAlbumRequest + if err := httpx.Parse(r, &req); err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + return + } + + l := storage.NewDownloadAlbumLogic(r.Context(), svcCtx) + resp, err := l.DownloadAlbum(&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/query_recent_image_list_handler.go b/app/auth/api/internal/handler/storage/query_recent_image_list_handler.go index 538451b..98349c9 100644 --- a/app/auth/api/internal/handler/storage/query_recent_image_list_handler.go +++ b/app/auth/api/internal/handler/storage/query_recent_image_list_handler.go @@ -3,15 +3,23 @@ package storage import ( "net/http" + "github.com/zeromicro/go-zero/rest/httpx" "schisandra-album-cloud-microservices/app/auth/api/internal/logic/storage" "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 QueryRecentImageListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + var req types.RecentListRequest + if err := httpx.Parse(r, &req); err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + return + } + l := storage.NewQueryRecentImageListLogic(r.Context(), svcCtx) - resp, err := l.QueryRecentImageList() + resp, err := l.QueryRecentImageList(&req) if err != nil { xhttp.JsonBaseResponseCtx(r.Context(), w, err) } else { diff --git a/app/auth/api/internal/handler/storage/share_album_handler.go b/app/auth/api/internal/handler/storage/share_album_handler.go new file mode 100644 index 0000000..1b0eaf8 --- /dev/null +++ b/app/auth/api/internal/handler/storage/share_album_handler.go @@ -0,0 +1,29 @@ +package storage + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + "schisandra-album-cloud-microservices/app/auth/api/internal/logic/storage" + "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 ShareAlbumHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.ShareAlbumRequest + if err := httpx.Parse(r, &req); err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + return + } + + l := storage.NewShareAlbumLogic(r.Context(), svcCtx) + resp, err := l.ShareAlbum(&req) + if err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + } else { + xhttp.JsonBaseResponseCtx(r.Context(), w, resp) + } + } +} diff --git a/app/auth/api/internal/logic/upscale/upload_image_logic.go b/app/auth/api/internal/logic/phone/upload_image_logic.go similarity index 97% rename from app/auth/api/internal/logic/upscale/upload_image_logic.go rename to app/auth/api/internal/logic/phone/upload_image_logic.go index 0cbfefa..7f1b444 100644 --- a/app/auth/api/internal/logic/upscale/upload_image_logic.go +++ b/app/auth/api/internal/logic/phone/upload_image_logic.go @@ -1,17 +1,16 @@ -package upscale +package phone import ( "context" "encoding/json" "net/http" "schisandra-album-cloud-microservices/app/auth/api/internal/logic/websocket" - "schisandra-album-cloud-microservices/app/auth/api/internal/svc" - "schisandra-album-cloud-microservices/app/auth/api/internal/types" - - "schisandra-album-cloud-microservices/common/xhttp" - "schisandra-album-cloud-microservices/common/errors" "schisandra-album-cloud-microservices/common/jwt" + "schisandra-album-cloud-microservices/common/xhttp" + + "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" ) @@ -30,7 +29,7 @@ func NewUploadImageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Uploa } } -func (l *UploadImageLogic) UploadImage(r *http.Request, req *types.UploadRequest) (err error) { +func (l *UploadImageLogic) UploadImage(r *http.Request, req *types.UploadRequest) error { token, ok := jwt.ParseAccessToken(l.svcCtx.Config.Auth.AccessSecret, req.AccessToken) if !ok { return errors.New(http.StatusForbidden, "invalid access token") 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 index cf5bc05..803bb88 100644 --- a/app/auth/api/internal/logic/share/query_share_image_logic.go +++ b/app/auth/api/internal/logic/share/query_share_image_logic.go @@ -140,6 +140,18 @@ func (l *QueryShareImageLogic) queryShareImageFromSource(storageShare *model.Sca return nil, err } + // 加载用户oss配置信息 + cacheOssConfigKey := constant.UserOssConfigPrefix + storageShare.UserID + ":" + storageShare.Provider + ossConfig, err := l.getOssConfigFromCacheOrDb(cacheOssConfigKey, storageShare.UserID, storageShare.Provider) + if err != nil { + return nil, err + } + + service, err := l.svcCtx.StorageManager.GetStorage(storageShare.UserID, ossConfig) + if err != nil { + return nil, errors.New("get storage failed") + } + // 使用 errgroup 和 semaphore 并发处理图片信息 var ResultList []types.ShareImageListMeta g, ctx := errgroup.WithContext(l.ctx) @@ -152,17 +164,6 @@ func (l *QueryShareImageLogic) queryShareImageFromSource(storageShare *model.Sca } 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") 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 index 9aca651..0f4740c 100644 --- a/app/auth/api/internal/logic/share/upload_share_image_logic.go +++ b/app/auth/api/internal/logic/share/upload_share_image_logic.go @@ -101,6 +101,8 @@ func (l *UploadShareImageLogic) UploadShareImage(req *types.ShareImageRequest) ( ValidityPeriod: int64(duration), ExpireTime: expiryTime, ImageCount: int64(len(req.Images)), + Provider: req.Provider, + Bucket: req.Bucket, } err = tx.ScaStorageShare.Create(&storageShare) if err != nil { diff --git a/app/auth/api/internal/logic/storage/delete_album_logic.go b/app/auth/api/internal/logic/storage/delete_album_logic.go index de42bbd..5a1a6ae 100644 --- a/app/auth/api/internal/logic/storage/delete_album_logic.go +++ b/app/auth/api/internal/logic/storage/delete_album_logic.go @@ -29,16 +29,31 @@ func (l *DeleteAlbumLogic) DeleteAlbum(req *types.AlbumDeleteRequest) (resp stri if !ok { return "", errors.New("user_id not found") } - info, err := l.svcCtx.DB.ScaStorageAlbum.Where(l.svcCtx.DB.ScaStorageAlbum.ID.Eq(req.ID), l.svcCtx.DB.ScaStorageAlbum.UserID.Eq(uid)).Delete() + tx := l.svcCtx.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + info, err := tx.ScaStorageAlbum.Where(tx.ScaStorageAlbum.ID.Eq(req.ID), tx.ScaStorageAlbum.UserID.Eq(uid)).Delete() if err != nil { + tx.Rollback() return "", err } if info.RowsAffected == 0 { + tx.Rollback() return "", errors.New("album not found") } - storageInfo := l.svcCtx.DB.ScaStorageInfo - _, err = storageInfo.Where(storageInfo.AlbumID.Eq(req.ID), storageInfo.UserID.Eq(uid)).Update(storageInfo.AlbumID, 0) + storageInfo := tx.ScaStorageInfo + _, err = storageInfo.Where(storageInfo.AlbumID.Eq(req.ID), storageInfo.UserID.Eq(uid)).Delete() if err != nil { + tx.Rollback() + return "", err + } + err = tx.Commit() + if err != nil { + tx.Rollback() return "", err } return "success", nil diff --git a/app/auth/api/internal/logic/storage/download_album_logic.go b/app/auth/api/internal/logic/storage/download_album_logic.go new file mode 100644 index 0000000..6d70b43 --- /dev/null +++ b/app/auth/api/internal/logic/storage/download_album_logic.go @@ -0,0 +1,30 @@ +package storage + +import ( + "context" + + "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 DownloadAlbumLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewDownloadAlbumLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DownloadAlbumLogic { + return &DownloadAlbumLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *DownloadAlbumLogic) DownloadAlbum(req *types.DownloadAlbumRequest) (resp string, err error) { + // todo: add your logic here and delete this line + + return +} diff --git a/app/auth/api/internal/logic/storage/query_all_image_list_logic.go b/app/auth/api/internal/logic/storage/query_all_image_list_logic.go index 94148cc..83724ff 100644 --- a/app/auth/api/internal/logic/storage/query_all_image_list_logic.go +++ b/app/auth/api/internal/logic/storage/query_all_image_list_logic.go @@ -7,6 +7,8 @@ import ( "fmt" "github.com/redis/go-redis/v9" "github.com/zeromicro/go-zero/core/logx" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" "gorm.io/gen" "math/rand" "net/url" @@ -59,8 +61,11 @@ func (l *QueryAllImageListLogic) QueryAllImageList(req *types.AllImageListReques if err := json.Unmarshal([]byte(cachedResult), &cachedResponse); err == nil { return &cachedResponse, nil } - logx.Error("Failed to unmarshal cached image list:", err) - return nil, errors.New("get cached image list failed") + logx.Error("Failed to unmarshal cached image list, deleting invalid cache:", err) + // 删除无效缓存 + if err := l.svcCtx.RedisClient.Del(l.ctx, cacheKey).Err(); err != nil { + logx.Error("Failed to delete invalid cache:", err) + } } else if !errors.Is(err, redis.Nil) { logx.Error("Redis error:", err) return nil, errors.New("get cached image list failed") @@ -115,25 +120,30 @@ func (l *QueryAllImageListLogic) QueryAllImageList(req *types.AllImageListReques } // 按日期进行分组 - var wg sync.WaitGroup + g, ctx := errgroup.WithContext(l.ctx) + sem := semaphore.NewWeighted(10) // 限制并发数为 10 groupedImages := sync.Map{} for _, dbFileInfo := range storageInfoList { - wg.Add(1) - go func(dbFileInfo *types.FileInfoResult) { - defer wg.Done() + dbFileInfo := dbFileInfo // 创建局部变量以避免闭包问题 + if err := sem.Acquire(ctx, 1); err != nil { + logx.Error("Failed to acquire semaphore:", err) + continue + } + g.Go(func() error { + defer sem.Release(1) weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()] date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday) reqParams := make(url.Values) presignedUrl, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, dbFileInfo.ThumbPath, time.Hour*24*7, reqParams) if err != nil { logx.Error(err) - return + return err } url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, dbFileInfo.Path, time.Hour*24*7) if err != nil { logx.Error(err) - return + return err } // 使用 Load 或 Store 确保原子操作 value, _ := groupedImages.LoadOrStore(date, []types.ImageMeta{}) @@ -151,9 +161,13 @@ func (l *QueryAllImageListLogic) QueryAllImageList(req *types.AllImageListReques // 重新存储更新后的图像列表 groupedImages.Store(date, images) - }(&dbFileInfo) + return nil + }) + } + // 等待所有 goroutine 完成 + if err = g.Wait(); err != nil { + return nil, err } - wg.Wait() var imageList []types.AllImageDetail groupedImages.Range(func(key, value interface{}) bool { imageList = append(imageList, types.AllImageDetail{ @@ -162,11 +176,13 @@ func (l *QueryAllImageListLogic) QueryAllImageList(req *types.AllImageListReques }) return true }) - // 按日期排序,最新的在最上面 sort.Slice(imageList, func(i, j int) bool { - dateI, _ := time.Parse("2006年1月2日 星期一", imageList[i].Date) - dateJ, _ := time.Parse("2006年1月2日 星期一", imageList[j].Date) - return dateI.After(dateJ) + if len(imageList[i].List) == 0 || len(imageList[j].List) == 0 { + return false // 空列表不参与排序 + } + createdAtI, _ := time.Parse("2006-01-02 15:04:05", imageList[i].List[0].CreatedAt) + createdAtJ, _ := time.Parse("2006-01-02 15:04:05", imageList[j].List[0].CreatedAt) + return createdAtI.After(createdAtJ) // 降序排序 }) resp = &types.AllImageListResponse{ Records: imageList, diff --git a/app/auth/api/internal/logic/storage/query_recent_image_list_logic.go b/app/auth/api/internal/logic/storage/query_recent_image_list_logic.go index 72bf06f..39146bc 100644 --- a/app/auth/api/internal/logic/storage/query_recent_image_list_logic.go +++ b/app/auth/api/internal/logic/storage/query_recent_image_list_logic.go @@ -4,7 +4,17 @@ import ( "context" "encoding/json" "errors" + "fmt" + "github.com/redis/go-redis/v9" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" + "math/rand" + "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" + "sort" "sync" "time" @@ -28,72 +38,121 @@ func NewQueryRecentImageListLogic(ctx context.Context, svcCtx *svc.ServiceContex } } -func (l *QueryRecentImageListLogic) QueryRecentImageList() (resp *types.RecentListResponse, err error) { +func (l *QueryRecentImageListLogic) QueryRecentImageList(req *types.RecentListRequest) (resp *types.RecentListResponse, err error) { uid, ok := l.ctx.Value("user_id").(string) if !ok { return nil, errors.New("user_id not found") } - - redisKeyPattern := constant.ImageRecentPrefix + uid + ":*" - iter := l.svcCtx.RedisClient.Scan(l.ctx, 0, redisKeyPattern, 0).Iterator() - var keys []string - for iter.Next(l.ctx) { - keys = append(keys, iter.Val()) + // 缓存获取数据 v1.0.0 + cacheKey := fmt.Sprintf("%s%s:%s:%s:%s", constant.ImageCachePrefix, uid, "recent", req.Provider, req.Bucket) + // 尝试从缓存获取 + cachedResult, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result() + if err == nil { + var cachedResponse types.RecentListResponse + if err := json.Unmarshal([]byte(cachedResult), &cachedResponse); err == nil { + return &cachedResponse, nil + } + logx.Error("Failed to unmarshal cached image list, deleting invalid cache:", err) + // 删除无效缓存 + if err := l.svcCtx.RedisClient.Del(l.ctx, cacheKey).Err(); err != nil { + logx.Error("Failed to delete invalid cache:", err) + } + } else if !errors.Is(err, redis.Nil) { + logx.Error("Redis error:", err) + return nil, errors.New("get cached image list failed") } - if err := iter.Err(); err != nil { - logx.Error(err) - return nil, errors.New("scan recent file list failed") - } - - if len(keys) == 0 { - return &types.RecentListResponse{Records: []types.AllImageDetail{}}, nil - } - - cmds, err := l.svcCtx.RedisClient.MGet(l.ctx, keys...).Result() + // 缓存未命中,从数据库中查询 + storageInfo := l.svcCtx.DB.ScaStorageInfo + storageThumb := l.svcCtx.DB.ScaStorageThumb + // 获取当前时间 + now := time.Now() + // 计算30天前的时间 + thirtyDaysAgo := now.AddDate(0, 0, -30) + var storageInfoList []types.FileInfoResult + err = storageInfo.Select( + storageInfo.ID, + storageInfo.FileName, + storageInfo.CreatedAt, + storageThumb.ThumbPath, + storageInfo.Path, + storageThumb.ThumbW, + storageThumb.ThumbH, + storageThumb.ThumbSize). + LeftJoin(storageThumb, storageInfo.ID.EqCol(storageThumb.InfoID)). + Where(storageInfo.UserID.Eq(uid), + storageInfo.Provider.Eq(req.Provider), + storageInfo.Bucket.Eq(req.Bucket), + storageInfo.Type.Neq(constant.ImageTypeShared), + storageInfo.CreatedAt.Gt(thirtyDaysAgo)). + Order(storageInfo.CreatedAt.Desc()).Scan(&storageInfoList) if err != nil { - logx.Error(err) - return nil, errors.New("get recent file list failed") + return nil, err + } + if len(storageInfoList) == 0 { + return &types.RecentListResponse{}, nil } - var wg sync.WaitGroup + // 加载用户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") + } + + // 按日期进行分组 + g, ctx := errgroup.WithContext(l.ctx) + sem := semaphore.NewWeighted(10) // 限制并发数为 10 groupedImages := sync.Map{} - for _, cmd := range cmds { - if cmd == nil { + for _, dbFileInfo := range storageInfoList { + dbFileInfo := dbFileInfo // 创建局部变量以避免闭包问题 + if err := sem.Acquire(ctx, 1); err != nil { + logx.Error("Failed to acquire semaphore:", err) continue } - val, ok := cmd.(string) - if !ok { - logx.Error("invalid value type") - return nil, errors.New("invalid value type") - } - var imageMeta types.ImageMeta - err = json.Unmarshal([]byte(val), &imageMeta) - if err != nil { - logx.Error(err) - return nil, errors.New("unmarshal recent file list failed") - } - parse, err := time.Parse("2006-01-02 15:04:05", imageMeta.CreatedAt) - if err != nil { - logx.Error(err) - return nil, errors.New("parse recent file list failed") - } - date := parse.Format("2006年1月2日 星期" + WeekdayMap[parse.Weekday()]) - // 使用LoadOrStore来检查并存储或者追加 - wg.Add(1) - go func(date string, imageMeta types.ImageMeta) { - defer wg.Done() - value, loaded := groupedImages.LoadOrStore(date, []types.ImageMeta{imageMeta}) - if loaded { - images := value.([]types.ImageMeta) - images = append(images, imageMeta) - groupedImages.Store(date, images) + g.Go(func() error { + defer sem.Release(1) + weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()] + date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday) + reqParams := make(url.Values) + presignedUrl, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, dbFileInfo.ThumbPath, time.Hour*24*7, reqParams) + if err != nil { + logx.Error(err) + return err } - }(date, imageMeta) + url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, dbFileInfo.Path, time.Hour*24*7) + if err != nil { + logx.Error(err) + return err + } + // 使用 Load 或 Store 确保原子操作 + value, _ := groupedImages.LoadOrStore(date, []types.ImageMeta{}) + images := value.([]types.ImageMeta) + + images = append(images, types.ImageMeta{ + ID: dbFileInfo.ID, + FileName: dbFileInfo.FileName, + Thumbnail: presignedUrl.String(), + URL: url, + Width: dbFileInfo.ThumbW, + Height: dbFileInfo.ThumbH, + CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"), + }) + + // 重新存储更新后的图像列表 + groupedImages.Store(date, images) + return nil + }) + } + // 等待所有 goroutine 完成 + if err = g.Wait(); err != nil { + return nil, err } - - wg.Wait() - var imageList []types.AllImageDetail groupedImages.Range(func(key, value interface{}) bool { imageList = append(imageList, types.AllImageDetail{ @@ -102,7 +161,87 @@ func (l *QueryRecentImageListLogic) QueryRecentImageList() (resp *types.RecentLi }) return true }) - return &types.RecentListResponse{ + sort.Slice(imageList, func(i, j int) bool { + if len(imageList[i].List) == 0 || len(imageList[j].List) == 0 { + return false // 空列表不参与排序 + } + createdAtI, _ := time.Parse("2006-01-02 15:04:05", imageList[i].List[0].CreatedAt) + createdAtJ, _ := time.Parse("2006-01-02 15:04:05", imageList[j].List[0].CreatedAt) + return createdAtI.After(createdAtJ) // 降序排序 + }) + resp = &types.RecentListResponse{ Records: imageList, + } + + // 缓存结果 + if data, err := json.Marshal(resp); err == nil { + expireTime := 5*time.Minute + time.Duration(rand.Intn(300))*time.Second + if err := l.svcCtx.RedisClient.Set(l.ctx, cacheKey, data, expireTime).Err(); err != nil { + logx.Error("Failed to cache image list:", err) + } + } else { + logx.Error("Failed to marshal image list for caching:", err) + } + + return resp, nil +} + +// 提取解密操作为函数 +func (l *QueryRecentImageListLogic) 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 *QueryRecentImageListLogic) 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/storage/share_album_logic.go b/app/auth/api/internal/logic/storage/share_album_logic.go new file mode 100644 index 0000000..7f30d82 --- /dev/null +++ b/app/auth/api/internal/logic/storage/share_album_logic.go @@ -0,0 +1,126 @@ +package storage + +import ( + "context" + "encoding/json" + "errors" + "github.com/ccpwcn/kgo" + "schisandra-album-cloud-microservices/app/auth/model/mysql/model" + "schisandra-album-cloud-microservices/common/constant" + "strconv" + "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 ShareAlbumLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewShareAlbumLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShareAlbumLogic { + return &ShareAlbumLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *ShareAlbumLogic) ShareAlbum(req *types.ShareAlbumRequest) (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() + } + }() + + storageAlbum := tx.ScaStorageAlbum + info, err := storageAlbum.Where(storageAlbum.ID.Eq(req.ID), storageAlbum.UserID.Eq(uid)). + Update(storageAlbum.AlbumType, constant.AlbumTypeShared) + if err != nil { + tx.Rollback() + return "", err + } + if info.RowsAffected == 0 { + tx.Rollback() + return "", errors.New("album not found") + } + // 更新图片信息 + storageInfo := tx.ScaStorageInfo + _, err = storageInfo.Where(storageInfo.AlbumID.Eq(req.ID), storageInfo.UserID.Eq(uid)). + Update(storageInfo.Type, constant.ImageTypeShared) + if err != nil { + tx.Rollback() + return "", err + } + // 查询图片数量 + var imageCount int64 + err = storageInfo.Select( + storageInfo.ID.Count().As("image_count")). + Where(storageInfo.AlbumID.Eq(req.ID), storageInfo.UserID.Eq(uid)). + Group(storageInfo.AlbumID).Scan(&imageCount) + if 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 := tx.ScaStorageShare + storageShareInfo := &model.ScaStorageShare{ + UserID: uid, + AlbumID: req.ID, + InviteCode: kgo.SimpleUuid(), + ValidityPeriod: int64(duration), + ExpireTime: expiryTime, + AccessPassword: req.AccessPassword, + VisitLimit: req.AccessLimit, + ImageCount: imageCount, + Provider: req.Provider, + Bucket: req.Bucket, + Status: 0, + } + err = storageShare.Create(storageShareInfo) + if err != nil { + tx.Rollback() + return "", err + } + marshal, err := json.Marshal(storageShareInfo) + if err != nil { + tx.Rollback() + return "", err + } + cacheKey := constant.ImageSharePrefix + storageShareInfo.InviteCode + err = l.svcCtx.RedisClient.Set(l.ctx, cacheKey, marshal, time.Duration(duration)*time.Hour*24).Err() + if err != nil { + tx.Rollback() + return "", err + } + err = tx.Commit() + if err != nil { + tx.Rollback() + return "", err + } + + return "success", nil +} + +// GenerateExpiryTime 函数接受当前时间和有效期(天为单位),返回过期时间 +func (l *ShareAlbumLogic) GenerateExpiryTime(currentTime time.Time, durationInDays int) time.Time { + // 创建一个持续时间对象 + duration := time.Duration(durationInDays) * 24 * time.Hour + // 将当前时间加上持续时间,得到过期时间 + expiryTime := currentTime.Add(duration) + return expiryTime +} 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 a015eff..9a795ba 100644 --- a/app/auth/api/internal/logic/storage/upload_file_logic.go +++ b/app/auth/api/internal/logic/storage/upload_file_logic.go @@ -15,7 +15,6 @@ import ( "io" "mime/multipart" "net/http" - "net/url" "path" "path/filepath" "schisandra-album-cloud-microservices/app/aisvc/rpc/pb" @@ -83,7 +82,6 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) { faceId int64 filePath string minioFilePath string - presignedURL string ) g, ctx := errgroup.WithContext(context.Background()) // 创建信号量,限制最大并发上传数(比如最多同时 5 个任务) @@ -136,12 +134,11 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) { } defer sem.Release(1) - path, url, err := l.uploadFileToMinio(uid, header, thumbnail, result) + path, err := l.uploadFileToMinio(uid, header, thumbnail, result) if err != nil { return err } minioFilePath = path - presignedURL = url return nil }) @@ -151,13 +148,12 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) { } fileUploadMessage := &types.FileUploadMessage{ - UID: uid, - Data: result, - FaceID: faceId, - FileHeader: header, - FilePath: filePath, - PresignedURL: presignedURL, - ThumbPath: minioFilePath, + UID: uid, + Data: result, + FaceID: faceId, + FileHeader: header, + FilePath: filePath, + ThumbPath: minioFilePath, } // 转换为 JSON messageData, err := json.Marshal(fileUploadMessage) @@ -241,7 +237,7 @@ func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHead return objectKey, nil } -func (l *UploadFileLogic) uploadFileToMinio(uid string, header *multipart.FileHeader, file multipart.File, result types.File) (string, string, error) { +func (l *UploadFileLogic) uploadFileToMinio(uid string, header *multipart.FileHeader, file multipart.File, result types.File) (string, error) { objectKey := path.Join( uid, time.Now().Format("2006/01"), // 按年/月划分目录 @@ -253,7 +249,7 @@ func (l *UploadFileLogic) uploadFileToMinio(uid string, header *multipart.FileHe 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 + return "", err } } // 上传到MinIO @@ -268,14 +264,14 @@ func (l *UploadFileLogic) uploadFileToMinio(uid string, header *multipart.FileHe }, ) if err != nil { - return "", "", err + return "", err } - reqParams := make(url.Values) - presignedURL, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, objectKey, time.Hour*24*7, reqParams) - if err != nil { - return "", "", err - } - return objectKey, presignedURL.String(), nil + //reqParams := make(url.Values) + //presignedURL, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, objectKey, time.Hour*24*7, reqParams) + //if err != nil { + // return "", "", err + //} + return objectKey, nil } // 提取解密操作为函数 diff --git a/app/auth/api/internal/mq/image_process_consumer.go b/app/auth/api/internal/mq/image_process_consumer.go index 24b5f6f..7ed09c5 100644 --- a/app/auth/api/internal/mq/image_process_consumer.go +++ b/app/auth/api/internal/mq/image_process_consumer.go @@ -19,7 +19,6 @@ import ( "schisandra-album-cloud-microservices/common/nsqx" "schisandra-album-cloud-microservices/common/storage/config" "strconv" - "time" ) type NsqImageProcessConsumer struct { @@ -61,7 +60,7 @@ func (c *NsqImageProcessConsumer) HandleMessage(msg *nsq.Message) error { } // 将文件信息存入数据库 - storageId, err := c.saveFileInfoToDB(message.UID, message.Data.Bucket, message.Data.Provider, message.FileHeader, message.Data, message.FaceID, message.FilePath, locationId) + storageId, err := c.saveFileInfoToDB(message.UID, message.Data.Bucket, message.Data.Provider, message.FileHeader, message.Data, message.FaceID, message.FilePath, locationId, message.Data.AlbumId) if err != nil { return err } @@ -75,10 +74,10 @@ func (c *NsqImageProcessConsumer) HandleMessage(msg *nsq.Message) error { c.afterImageUpload(message.UID) // redis 保存最近7天上传的文件列表 - err = c.saveRecentFileList(message.UID, message.PresignedURL, storageId, message.Data, message.FileHeader.Filename) - if err != nil { - return err - } + //err = c.saveRecentFileList(message.UID, message.Thumbnail, message.URL, storageId, message.Data, message.FileHeader.Filename) + //if err != nil { + // return err + //} return nil } @@ -167,30 +166,31 @@ func (c *NsqImageProcessConsumer) classifyFile(mimeType string, isScreenshot boo return "unknown" } -// 保存最近7天上传的文件列表 -func (c *NsqImageProcessConsumer) saveRecentFileList(uid, url string, id int64, result types.File, filename string) error { - - redisKey := constant.ImageRecentPrefix + uid + ":" + strconv.FormatInt(id, 10) - imageMeta := types.ImageMeta{ - ID: id, - URL: url, - FileName: filename, - Width: result.Width, - Height: result.Height, - CreatedAt: time.Now().Format("2006-01-02 15:04:05"), - } - marshal, err := json.Marshal(imageMeta) - if err != nil { - logx.Error(err) - return errors.New("marshal image meta failed") - } - err = c.svcCtx.RedisClient.Set(c.ctx, redisKey, marshal, time.Hour*24*7).Err() - if err != nil { - logx.Error(err) - return errors.New("save recent file list failed") - } - return nil -} +//// 保存最近7天上传的文件列表 +//func (c *NsqImageProcessConsumer) saveRecentFileList(uid, thumbnail, url string, id int64, result types.File, filename string) error { +// +// redisKey := constant.ImageRecentPrefix + uid + ":" + strconv.FormatInt(id, 10) +// imageMeta := types.ImageMeta{ +// ID: id, +// URL: url, +// FileName: filename, +// Width: result.ThumbW, +// Height: result.ThumbH, +// Thumbnail: thumbnail, +// CreatedAt: time.Now().Format("2006-01-02 15:04:05"), +// } +// marshal, err := json.Marshal(imageMeta) +// if err != nil { +// logx.Error(err) +// return errors.New("marshal image meta failed") +// } +// err = c.svcCtx.RedisClient.Set(c.ctx, redisKey, marshal, time.Hour*24*7).Err() +// if err != nil { +// logx.Error(err) +// return errors.New("save recent file list failed") +// } +// return nil +//} // 提取解密操作为函数 func (c *NsqImageProcessConsumer) decryptConfig(dbConfig *model.ScaStorageConfig) (*config.StorageConfig, error) { @@ -259,7 +259,7 @@ func (c *NsqImageProcessConsumer) saveFileThumbnailInfoToDB(uid string, filePath } // 将 EXIF 和文件信息存入数据库 -func (c *NsqImageProcessConsumer) saveFileInfoToDB(uid, bucket, provider string, header *multipart.FileHeader, result types.File, faceId int64, filePath string, locationID int64) (int64, error) { +func (c *NsqImageProcessConsumer) saveFileInfoToDB(uid, bucket, provider string, header *multipart.FileHeader, result types.File, faceId int64, filePath string, locationID, albumId int64) (int64, error) { tx := c.svcCtx.DB.Begin() defer func() { if r := recover(); r != nil { @@ -281,6 +281,7 @@ func (c *NsqImageProcessConsumer) saveFileInfoToDB(uid, bucket, provider string, Width: result.Width, Height: result.Height, LocationID: locationID, + AlbumID: albumId, } err := tx.ScaStorageInfo.Create(scaStorageInfo) if err != nil { diff --git a/app/auth/api/internal/types/file_types.go b/app/auth/api/internal/types/file_types.go index 2bfb483..9d3b3e9 100644 --- a/app/auth/api/internal/types/file_types.go +++ b/app/auth/api/internal/types/file_types.go @@ -22,17 +22,19 @@ type File struct { ThumbW float64 `json:"thumb_w"` ThumbH float64 `json:"thumb_h"` ThumbSize float64 `json:"thumb_size"` + AlbumId int64 `json:"albumId"` } // FileUploadMessage represents a message sent to the user after a file upload. type FileUploadMessage struct { - FaceID int64 `json:"face_id"` - FileHeader *multipart.FileHeader `json:"fileHeader"` - Data File `json:"data"` - UID string `json:"uid"` - FilePath string `json:"filePath"` - PresignedURL string `json:"presignedURL"` - ThumbPath string `json:"thumbPath"` + FaceID int64 `json:"face_id"` + FileHeader *multipart.FileHeader `json:"fileHeader"` + Data File `json:"data"` + UID string `json:"uid"` + FilePath string `json:"filePath"` + URL string `json:"url"` + ThumbPath string `json:"thumbPath"` + Thumbnail string `json:"thumbnail"` } type FileInfoResult struct { diff --git a/app/auth/api/internal/types/types.go b/app/auth/api/internal/types/types.go index 0f3c95c..bc70773 100644 --- a/app/auth/api/internal/types/types.go +++ b/app/auth/api/internal/types/types.go @@ -168,6 +168,12 @@ type DeleteRecordListResponse struct { Records []AllImageDetail `json:"records"` } +type DownloadAlbumRequest struct { + ID int64 `json:"id"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` +} + type FaceDetailListRequest struct { FaceID int64 `json:"face_id"` Provider string `json:"provider"` @@ -293,6 +299,11 @@ type QueryShareInfoRequest struct { InviteCode string `json:"invite_code"` } +type RecentListRequest struct { + Provider string `json:"provider"` + Bucket string `json:"bucket"` +} + type RecentListResponse struct { Records []AllImageDetail `json:"records"` } @@ -345,6 +356,15 @@ type RotateCaptchaResponse struct { Thumb string `json:"thumb"` } +type ShareAlbumRequest struct { + ID int64 `json:"id"` + 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"` +} + type ShareImageListMeta struct { ID int64 `json:"id"` FileName string `json:"file_name"` diff --git a/app/auth/model/mysql/model/sca_storage_share.gen.go b/app/auth/model/mysql/model/sca_storage_share.gen.go index 20f878a..d9abbda 100644 --- a/app/auth/model/mysql/model/sca_storage_share.gen.go +++ b/app/auth/model/mysql/model/sca_storage_share.gen.go @@ -18,6 +18,8 @@ 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 + Provider string `gorm:"column:provider;type:varchar(50);comment:存储商" json:"provider"` // 存储商 + Bucket string `gorm:"column:bucket;type:varchar(50);comment:存储桶" json:"bucket"` // 存储桶 InviteCode string `gorm:"column:invite_code;type:varchar(50);comment:邀请码(用于访问分享链接)" json:"invite_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"` // 有效期 diff --git a/app/auth/model/mysql/query/sca_storage_share.gen.go b/app/auth/model/mysql/query/sca_storage_share.gen.go index ac7611c..e1b07ca 100644 --- a/app/auth/model/mysql/query/sca_storage_share.gen.go +++ b/app/auth/model/mysql/query/sca_storage_share.gen.go @@ -30,6 +30,8 @@ func newScaStorageShare(db *gorm.DB, opts ...gen.DOOption) scaStorageShare { _scaStorageShare.ID = field.NewInt64(tableName, "id") _scaStorageShare.UserID = field.NewString(tableName, "user_id") _scaStorageShare.AlbumID = field.NewInt64(tableName, "album_id") + _scaStorageShare.Provider = field.NewString(tableName, "provider") + _scaStorageShare.Bucket = field.NewString(tableName, "bucket") _scaStorageShare.InviteCode = field.NewString(tableName, "invite_code") _scaStorageShare.ExpireTime = field.NewTime(tableName, "expire_time") _scaStorageShare.ValidityPeriod = field.NewInt64(tableName, "validity_period") @@ -55,6 +57,8 @@ type scaStorageShare struct { ID field.Int64 // 主键 UserID field.String // 用户ID AlbumID field.Int64 // 相册ID + Provider field.String // 存储商 + Bucket field.String // 存储桶 InviteCode field.String // 邀请码(用于访问分享链接) ExpireTime field.Time // 过期时间 ValidityPeriod field.Int64 // 有效期 @@ -85,6 +89,8 @@ func (s *scaStorageShare) updateTableName(table string) *scaStorageShare { s.ID = field.NewInt64(table, "id") s.UserID = field.NewString(table, "user_id") s.AlbumID = field.NewInt64(table, "album_id") + s.Provider = field.NewString(table, "provider") + s.Bucket = field.NewString(table, "bucket") s.InviteCode = field.NewString(table, "invite_code") s.ExpireTime = field.NewTime(table, "expire_time") s.ValidityPeriod = field.NewInt64(table, "validity_period") @@ -112,10 +118,12 @@ func (s *scaStorageShare) GetFieldByName(fieldName string) (field.OrderExpr, boo } func (s *scaStorageShare) fillFieldMap() { - s.fieldMap = make(map[string]field.Expr, 14) + s.fieldMap = make(map[string]field.Expr, 16) s.fieldMap["id"] = s.ID s.fieldMap["user_id"] = s.UserID s.fieldMap["album_id"] = s.AlbumID + s.fieldMap["provider"] = s.Provider + s.fieldMap["bucket"] = s.Bucket s.fieldMap["invite_code"] = s.InviteCode s.fieldMap["expire_time"] = s.ExpireTime s.fieldMap["validity_period"] = s.ValidityPeriod