diff --git a/app/auth/api/auth.api b/app/auth/api/auth.api index d932288..ab6e71d 100644 --- a/app/auth/api/auth.api +++ b/app/auth/api/auth.api @@ -450,7 +450,12 @@ type ( result string `json:"result"` } FaceDetailListRequest { - FaceID int64 `json:"face_id"` + FaceID int64 `json:"face_id"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` + } + FaceDetailListResponse { + records []AllImageDetail `json:"records"` } // 创建相册请求参数 AlbumCreateRequest { @@ -478,7 +483,13 @@ type ( } // 相册详情请求参数 AlbumDetailListRequest { - ID int64 `json:"id"` + ID int64 `json:"id"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` + } + // 相册详情响应参数 + AlbumDetailListResponse { + records []AllImageDetail `json:"records"` } // 重命名相册请求参数 AlbumRenameRequest { @@ -522,42 +533,56 @@ type ( records []AllImageDetail `json:"records"` } LocationMeta { - ID int64 `json:"id"` - City string `json:"city"` - Total int64 `json:"total"` + ID int64 `json:"id"` + City string `json:"city"` + Total int64 `json:"total"` + CoverImage string `json:"cover_image"` } LocationListData { location string `json:"location"` // 中国 新疆维吾尔自治区 list []LocationMeta `json:"list"` // 图片列表 } + LocationListRequest { + Provider string `json:"provider"` + Bucket string `json:"bucket"` + } // 地点相册列表响应参数 LocationListResponse { records []LocationListData `json:"records"` } // 地点详情列表请求参数 LocationDetailListRequest { - ID int64 `json:"id"` + ID int64 `json:"id"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` } // 地点详情列表响应参数 LocationDetailListResponse { records []AllImageDetail `json:"records"` } ThingMeta { - TagName string `json:"tag_name"` - CreatedAt string `json:"created_at"` - TagCount int64 `json:"tag_count"` + TagName string `json:"tag_name"` + CreatedAt string `json:"created_at"` + TagCount int64 `json:"tag_count"` + CoverImage string `json:"cover_image"` } ThingListData { Category string `json:"category"` // 分类 list []ThingMeta `json:"list"` // 图片列表 } + ThingListRequest { + Provider string `json:"provider"` + Bucket string `json:"bucket"` + } // 事物相册列表响应参数 ThingListResponse { records []ThingListData `json:"records"` } // 事物详情列表请求参数 ThingDetailListRequest { - ID int64 `json:"id"` + TagName string `json:"tag_name"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` } // 事物详情列表响应参数 ThingDetailListResponse { @@ -598,9 +623,9 @@ service auth { @handler modifyFaceLibraryType post /face/sample/modify/type (ModifyFaceTypeRequest) returns (ModifyFaceTypeResponse) - // 获取人脸详情列表 (暂定) + // 获取人脸详情列表 @handler getFaceDetailList - post /face/detail/list (FaceDetailListRequest) returns (string) + post /face/detail/list (FaceDetailListRequest) returns (FaceDetailListResponse) // 创建相册 @handler createAlbum @@ -610,9 +635,9 @@ service auth { @handler getAlbumList post /album/list (AlbumListRequest) returns (AlbumListResponse) - // 获取相册详情(暂定) + // 获取相册详情 @handler getAlbumDetail - post /album/detail/list (AlbumDetailListRequest) returns (string) + post /album/detail/list (AlbumDetailListRequest) returns (AlbumDetailListResponse) // 重命名相册 @handler renameAlbum @@ -632,7 +657,7 @@ service auth { // 获取地点相册列表 @handler queryLocationImageList - post /image/location/list returns (LocationListResponse) + post /image/location/list (LocationListRequest) returns (LocationListResponse) // 获取地点详情列表 @handler queryLocationDetailList @@ -640,7 +665,7 @@ service auth { // 获取事物相册列表 @handler queryThingImageList - post /image/thing/list returns (ThingListResponse) + post /image/thing/list (ThingListRequest) returns (ThingListResponse) // 获取事物详情列表 @handler queryThingDetailList diff --git a/app/auth/api/internal/handler/storage/get_album_detail_handler.go b/app/auth/api/internal/handler/storage/get_album_detail_handler.go index 6d7b3b6..86b14c3 100644 --- a/app/auth/api/internal/handler/storage/get_album_detail_handler.go +++ b/app/auth/api/internal/handler/storage/get_album_detail_handler.go @@ -17,7 +17,6 @@ func GetAlbumDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { xhttp.JsonBaseResponseCtx(r.Context(), w, err) return } - l := storage.NewGetAlbumDetailLogic(r.Context(), svcCtx) resp, err := l.GetAlbumDetail(&req) if err != nil { diff --git a/app/auth/api/internal/handler/storage/query_location_image_list_handler.go b/app/auth/api/internal/handler/storage/query_location_image_list_handler.go index e8698d5..c8d25d5 100644 --- a/app/auth/api/internal/handler/storage/query_location_image_list_handler.go +++ b/app/auth/api/internal/handler/storage/query_location_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 QueryLocationImageListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + var req types.LocationListRequest + if err := httpx.Parse(r, &req); err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + return + } + l := storage.NewQueryLocationImageListLogic(r.Context(), svcCtx) - resp, err := l.QueryLocationImageList() + resp, err := l.QueryLocationImageList(&req) if err != nil { xhttp.JsonBaseResponseCtx(r.Context(), w, err) } else { diff --git a/app/auth/api/internal/handler/storage/query_thing_image_list_handler.go b/app/auth/api/internal/handler/storage/query_thing_image_list_handler.go index 1425411..f899bec 100644 --- a/app/auth/api/internal/handler/storage/query_thing_image_list_handler.go +++ b/app/auth/api/internal/handler/storage/query_thing_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 QueryThingImageListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + var req types.ThingListRequest + if err := httpx.Parse(r, &req); err != nil { + xhttp.JsonBaseResponseCtx(r.Context(), w, err) + return + } + l := storage.NewQueryThingImageListLogic(r.Context(), svcCtx) - resp, err := l.QueryThingImageList() + resp, err := l.QueryThingImageList(&req) if err != nil { xhttp.JsonBaseResponseCtx(r.Context(), w, err) } else { 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 cee457d..823e356 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 @@ -2,6 +2,18 @@ package storage import ( "context" + "encoding/json" + "errors" + "fmt" + "github.com/redis/go-redis/v9" + "math/rand" + "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" + "sync" + "time" "schisandra-album-cloud-microservices/app/auth/api/internal/svc" "schisandra-album-cloud-microservices/app/auth/api/internal/types" @@ -23,8 +35,172 @@ func NewGetAlbumDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ge } } -func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest) (resp string, err error) { - // todo: add your logic here and delete this line +func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest) (resp *types.AlbumDetailListResponse, err error) { + uid, ok := l.ctx.Value("user_id").(string) + if !ok { + return nil, errors.New("user_id not found") + } + // 缓存获取数据 v1.0.0 + cacheKey := fmt.Sprintf("%s%s:%s:%s:%v", constant.ImageListPrefix, uid, req.Provider, req.Bucket, req.ID) + // 尝试从缓存获取 + cachedResult, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result() + if err == nil { + var cachedResponse types.AlbumDetailListResponse + 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") + } else if !errors.Is(err, redis.Nil) { + logx.Error("Redis error:", err) + return nil, errors.New("get cached image list failed") + } - return + // 缓存未命中,从数据库中查询 + storageInfo := l.svcCtx.DB.ScaStorageInfo + // 数据库查询文件信息列表 + var storageInfoQuery query.IScaStorageInfoDo + + storageInfoQuery = storageInfo.Where( + storageInfo.UserID.Eq(uid), + storageInfo.Provider.Eq(req.Provider), + storageInfo.Bucket.Eq(req.Bucket), + storageInfo.AlbumID.Eq(req.ID)). + Order(storageInfo.CreatedAt.Desc()) + storageInfoList, err := storageInfoQuery.Find() + if err != nil { + return nil, err + } + if len(storageInfoList) == 0 { + 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") + } + + // 按日期进行分组 + var wg sync.WaitGroup + groupedImages := sync.Map{} + + for _, dbFileInfo := range storageInfoList { + wg.Add(1) + go func(dbFileInfo *model.ScaStorageInfo) { + defer wg.Done() + weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()] + date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday) + 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) + + images = append(images, types.ImageMeta{ + ID: dbFileInfo.ID, + FileName: dbFileInfo.FileName, + URL: url, + Width: dbFileInfo.Width, + Height: dbFileInfo.Height, + CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"), + }) + + // 重新存储更新后的图像列表 + groupedImages.Store(date, images) + }(dbFileInfo) + } + wg.Wait() + var imageList []types.AllImageDetail + groupedImages.Range(func(key, value interface{}) bool { + imageList = append(imageList, types.AllImageDetail{ + Date: key.(string), + List: value.([]types.ImageMeta), + }) + return true + }) + resp = &types.AlbumDetailListResponse{ + Records: imageList, + } + + // 缓存结果 + if data, err := json.Marshal(resp); err == nil { + expireTime := 7*24*time.Hour - time.Duration(rand.Intn(60))*time.Minute + 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 *GetAlbumDetailLogic) 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 *GetAlbumDetailLogic) 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/get_face_detail_list_logic.go b/app/auth/api/internal/logic/storage/get_face_detail_list_logic.go index f5c0d0c..6634629 100644 --- a/app/auth/api/internal/logic/storage/get_face_detail_list_logic.go +++ b/app/auth/api/internal/logic/storage/get_face_detail_list_logic.go @@ -2,6 +2,18 @@ package storage import ( "context" + "encoding/json" + "errors" + "fmt" + "github.com/redis/go-redis/v9" + "math/rand" + "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" + "sync" + "time" "schisandra-album-cloud-microservices/app/auth/api/internal/svc" "schisandra-album-cloud-microservices/app/auth/api/internal/types" @@ -23,8 +35,172 @@ func NewGetFaceDetailListLogic(ctx context.Context, svcCtx *svc.ServiceContext) } } -func (l *GetFaceDetailListLogic) GetFaceDetailList(req *types.FaceDetailListRequest) (resp string, err error) { - // todo: add your logic here and delete this line +func (l *GetFaceDetailListLogic) GetFaceDetailList(req *types.FaceDetailListRequest) (resp *types.FaceDetailListResponse, err error) { + uid, ok := l.ctx.Value("user_id").(string) + if !ok { + return nil, errors.New("user_id not found") + } + // 缓存获取数据 v1.0.0 + cacheKey := fmt.Sprintf("%s%s:%s:%s:%v", constant.ImageFaceListPrefix, uid, req.Provider, req.Bucket, req.FaceID) + // 尝试从缓存获取 + cachedResult, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result() + if err == nil { + var cachedResponse types.FaceDetailListResponse + 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") + } else if !errors.Is(err, redis.Nil) { + logx.Error("Redis error:", err) + return nil, errors.New("get cached image list failed") + } - return + // 缓存未命中,从数据库中查询 + storageInfo := l.svcCtx.DB.ScaStorageInfo + // 数据库查询文件信息列表 + var storageInfoQuery query.IScaStorageInfoDo + + storageInfoQuery = storageInfo.Where( + storageInfo.UserID.Eq(uid), + storageInfo.Provider.Eq(req.Provider), + storageInfo.Bucket.Eq(req.Bucket), + storageInfo.FaceID.Eq(req.FaceID)). + Order(storageInfo.CreatedAt.Desc()) + storageInfoList, err := storageInfoQuery.Find() + if err != nil { + return nil, err + } + if len(storageInfoList) == 0 { + return &types.FaceDetailListResponse{}, 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") + } + + // 按日期进行分组 + var wg sync.WaitGroup + groupedImages := sync.Map{} + + for _, dbFileInfo := range storageInfoList { + wg.Add(1) + go func(dbFileInfo *model.ScaStorageInfo) { + defer wg.Done() + weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()] + date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday) + 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) + + images = append(images, types.ImageMeta{ + ID: dbFileInfo.ID, + FileName: dbFileInfo.FileName, + URL: url, + Width: dbFileInfo.Width, + Height: dbFileInfo.Height, + CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"), + }) + + // 重新存储更新后的图像列表 + groupedImages.Store(date, images) + }(dbFileInfo) + } + wg.Wait() + var imageList []types.AllImageDetail + groupedImages.Range(func(key, value interface{}) bool { + imageList = append(imageList, types.AllImageDetail{ + Date: key.(string), + List: value.([]types.ImageMeta), + }) + return true + }) + resp = &types.FaceDetailListResponse{ + Records: imageList, + } + + // 缓存结果 + if data, err := json.Marshal(resp); err == nil { + expireTime := 7*24*time.Hour - time.Duration(rand.Intn(60))*time.Minute + 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 *GetFaceDetailListLogic) 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 *GetFaceDetailListLogic) 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/modify_face_library_type_logic.go b/app/auth/api/internal/logic/storage/modify_face_library_type_logic.go index 2d2ad40..ee1c9de 100644 --- a/app/auth/api/internal/logic/storage/modify_face_library_type_logic.go +++ b/app/auth/api/internal/logic/storage/modify_face_library_type_logic.go @@ -35,7 +35,7 @@ func (l *ModifyFaceLibraryTypeLogic) ModifyFaceLibraryType(req *types.ModifyFace return nil, err } storageInfo := l.svcCtx.DB.ScaStorageInfo - resultInfo, err := storageInfo.Where(storageInfo.FaceID.In(req.IDs...)).Update(storageInfo.ImgShow, req.FaceType) + resultInfo, err := storageInfo.Where(storageInfo.FaceID.In(req.IDs...)).Update(storageInfo.IsDisplayed, req.FaceType) if err != nil { return nil, err } 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 b7b1ea7..73d02ca 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 @@ -173,7 +173,7 @@ func (l *QueryAllImageListLogic) decryptConfig(config *model.ScaStorageConfig) ( return nil, errors.New("decrypt secret key failed") } return &storageConfig.StorageConfig{ - Provider: config.Type, + Provider: config.Provider, Endpoint: config.Endpoint, AccessKey: accessKey, SecretKey: secretKey, @@ -200,7 +200,7 @@ func (l *QueryAllImageListLogic) getOssConfigFromCacheOrDb(cacheKey, uid, provid // 缓存未命中,从数据库中加载 scaOssConfig := l.svcCtx.DB.ScaStorageConfig - dbOssConfig, err := scaOssConfig.Where(scaOssConfig.UserID.Eq(uid), scaOssConfig.Type.Eq(provider)).First() + dbOssConfig, err := scaOssConfig.Where(scaOssConfig.UserID.Eq(uid), scaOssConfig.Provider.Eq(provider)).First() if err != nil { return nil, err } diff --git a/app/auth/api/internal/logic/storage/query_location_detail_list_logic.go b/app/auth/api/internal/logic/storage/query_location_detail_list_logic.go index f9d744d..8ff6df8 100644 --- a/app/auth/api/internal/logic/storage/query_location_detail_list_logic.go +++ b/app/auth/api/internal/logic/storage/query_location_detail_list_logic.go @@ -2,6 +2,18 @@ package storage import ( "context" + "encoding/json" + "errors" + "fmt" + "github.com/redis/go-redis/v9" + "math/rand" + "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" + "sync" + "time" "schisandra-album-cloud-microservices/app/auth/api/internal/svc" "schisandra-album-cloud-microservices/app/auth/api/internal/types" @@ -24,7 +36,171 @@ func NewQueryLocationDetailListLogic(ctx context.Context, svcCtx *svc.ServiceCon } func (l *QueryLocationDetailListLogic) QueryLocationDetailList(req *types.LocationDetailListRequest) (resp *types.LocationDetailListResponse, err error) { - // todo: add your logic here and delete this line + uid, ok := l.ctx.Value("user_id").(string) + if !ok { + return nil, errors.New("user_id not found") + } + // 缓存获取数据 v1.0.0 + cacheKey := fmt.Sprintf("%s%s:%s:%s:%v", constant.ImageListPrefix, uid, req.Provider, req.Bucket, req.ID) + // 尝试从缓存获取 + cachedResult, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result() + if err == nil { + var cachedResponse types.LocationDetailListResponse + 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") + } else if !errors.Is(err, redis.Nil) { + logx.Error("Redis error:", err) + return nil, errors.New("get cached image list failed") + } - return + // 缓存未命中,从数据库中查询 + storageInfo := l.svcCtx.DB.ScaStorageInfo + // 数据库查询文件信息列表 + var storageInfoQuery query.IScaStorageInfoDo + + storageInfoQuery = storageInfo.Where( + storageInfo.UserID.Eq(uid), + storageInfo.Provider.Eq(req.Provider), + storageInfo.Bucket.Eq(req.Bucket), + storageInfo.LocationID.Eq(req.ID)). + Order(storageInfo.CreatedAt.Desc()) + storageInfoList, err := storageInfoQuery.Find() + if err != nil { + return nil, err + } + if len(storageInfoList) == 0 { + return &types.LocationDetailListResponse{}, 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") + } + + // 按日期进行分组 + var wg sync.WaitGroup + groupedImages := sync.Map{} + + for _, dbFileInfo := range storageInfoList { + wg.Add(1) + go func(dbFileInfo *model.ScaStorageInfo) { + defer wg.Done() + weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()] + date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday) + 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) + + images = append(images, types.ImageMeta{ + ID: dbFileInfo.ID, + FileName: dbFileInfo.FileName, + URL: url, + Width: dbFileInfo.Width, + Height: dbFileInfo.Height, + CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"), + }) + + // 重新存储更新后的图像列表 + groupedImages.Store(date, images) + }(dbFileInfo) + } + wg.Wait() + var imageList []types.AllImageDetail + groupedImages.Range(func(key, value interface{}) bool { + imageList = append(imageList, types.AllImageDetail{ + Date: key.(string), + List: value.([]types.ImageMeta), + }) + return true + }) + resp = &types.LocationDetailListResponse{ + Records: imageList, + } + + // 缓存结果 + if data, err := json.Marshal(resp); err == nil { + expireTime := 7*24*time.Hour - time.Duration(rand.Intn(60))*time.Minute + 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 *QueryLocationDetailListLogic) 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 *QueryLocationDetailListLogic) 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/query_location_image_list_logic.go b/app/auth/api/internal/logic/storage/query_location_image_list_logic.go index 3119eda..6dd39a9 100644 --- a/app/auth/api/internal/logic/storage/query_location_image_list_logic.go +++ b/app/auth/api/internal/logic/storage/query_location_image_list_logic.go @@ -2,10 +2,17 @@ package storage import ( "context" + "encoding/json" "errors" "fmt" + "github.com/redis/go-redis/v9" "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/common/constant" + "schisandra-album-cloud-microservices/common/encrypt" + storageConfig "schisandra-album-cloud-microservices/common/storage/config" + "time" "github.com/zeromicro/go-zero/core/logx" ) @@ -24,7 +31,7 @@ func NewQueryLocationImageListLogic(ctx context.Context, svcCtx *svc.ServiceCont } } -func (l *QueryLocationImageListLogic) QueryLocationImageList() (resp *types.LocationListResponse, err error) { +func (l *QueryLocationImageListLogic) QueryLocationImageList(req *types.LocationListRequest) (resp *types.LocationListResponse, err error) { uid, ok := l.ctx.Value("user_id").(string) if !ok { return nil, errors.New("user_id not found") @@ -36,11 +43,26 @@ func (l *QueryLocationImageListLogic) QueryLocationImageList() (resp *types.Loca storageLocation.Country, storageLocation.City, storageLocation.Province, - storageLocation.Total).Where(storageLocation.UserID.Eq(uid)). + storageLocation.CoverImage, + storageLocation.Total).Where(storageLocation.UserID.Eq(uid), + storageLocation.Provider.Eq(req.Provider), + storageLocation.Bucket.Eq(req.Bucket)). Order(storageLocation.CreatedAt.Desc()).Find() if err != nil { return nil, err } + + // 加载用户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") + } locationMap := make(map[string][]types.LocationMeta) for _, loc := range locations { @@ -55,10 +77,15 @@ func (l *QueryLocationImageListLogic) QueryLocationImageList() (resp *types.Loca if city == "" { city = loc.Country } + url, err := service.PresignedURL(l.ctx, req.Bucket, loc.CoverImage, 7*24*time.Hour) + if err != nil { + return nil, errors.New("get presigned url failed") + } locationMeta := types.LocationMeta{ - ID: loc.ID, - City: city, - Total: loc.Total, + ID: loc.ID, + City: city, + Total: loc.Total, + CoverImage: url, } locationMap[locationKey] = append(locationMap[locationKey], locationMeta) } @@ -74,3 +101,63 @@ func (l *QueryLocationImageListLogic) QueryLocationImageList() (resp *types.Loca return &types.LocationListResponse{Records: locationListData}, nil } + +// 提取解密操作为函数 +func (l *QueryLocationImageListLogic) 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 *QueryLocationImageListLogic) 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/query_thing_detail_list_logic.go b/app/auth/api/internal/logic/storage/query_thing_detail_list_logic.go index 6090525..efa199d 100644 --- a/app/auth/api/internal/logic/storage/query_thing_detail_list_logic.go +++ b/app/auth/api/internal/logic/storage/query_thing_detail_list_logic.go @@ -2,6 +2,18 @@ package storage import ( "context" + "encoding/json" + "errors" + "fmt" + "github.com/redis/go-redis/v9" + "math/rand" + "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" + "sync" + "time" "schisandra-album-cloud-microservices/app/auth/api/internal/svc" "schisandra-album-cloud-microservices/app/auth/api/internal/types" @@ -24,7 +36,171 @@ func NewQueryThingDetailListLogic(ctx context.Context, svcCtx *svc.ServiceContex } func (l *QueryThingDetailListLogic) QueryThingDetailList(req *types.ThingDetailListRequest) (resp *types.ThingDetailListResponse, err error) { - // todo: add your logic here and delete this line + uid, ok := l.ctx.Value("user_id").(string) + if !ok { + return nil, errors.New("user_id not found") + } + // 缓存获取数据 v1.0.0 + cacheKey := fmt.Sprintf("%s%s:%s:%s:%v", constant.ImageListPrefix, uid, req.Provider, req.Bucket, req.TagName) + // 尝试从缓存获取 + cachedResult, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result() + if err == nil { + var cachedResponse types.ThingDetailListResponse + 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") + } else if !errors.Is(err, redis.Nil) { + logx.Error("Redis error:", err) + return nil, errors.New("get cached image list failed") + } - return + // 缓存未命中,从数据库中查询 + storageInfo := l.svcCtx.DB.ScaStorageInfo + // 数据库查询文件信息列表 + var storageInfoQuery query.IScaStorageInfoDo + + storageInfoQuery = storageInfo.Where( + storageInfo.UserID.Eq(uid), + storageInfo.Provider.Eq(req.Provider), + storageInfo.Bucket.Eq(req.Bucket), + storageInfo.Tag.Eq(req.TagName)). + Order(storageInfo.CreatedAt.Desc()) + storageInfoList, err := storageInfoQuery.Find() + if err != nil { + return nil, err + } + if len(storageInfoList) == 0 { + return &types.ThingDetailListResponse{}, 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") + } + + // 按日期进行分组 + var wg sync.WaitGroup + groupedImages := sync.Map{} + + for _, dbFileInfo := range storageInfoList { + wg.Add(1) + go func(dbFileInfo *model.ScaStorageInfo) { + defer wg.Done() + weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()] + date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday) + 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) + + images = append(images, types.ImageMeta{ + ID: dbFileInfo.ID, + FileName: dbFileInfo.FileName, + URL: url, + Width: dbFileInfo.Width, + Height: dbFileInfo.Height, + CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"), + }) + + // 重新存储更新后的图像列表 + groupedImages.Store(date, images) + }(dbFileInfo) + } + wg.Wait() + var imageList []types.AllImageDetail + groupedImages.Range(func(key, value interface{}) bool { + imageList = append(imageList, types.AllImageDetail{ + Date: key.(string), + List: value.([]types.ImageMeta), + }) + return true + }) + resp = &types.ThingDetailListResponse{ + Records: imageList, + } + + // 缓存结果 + if data, err := json.Marshal(resp); err == nil { + expireTime := 7*24*time.Hour - time.Duration(rand.Intn(60))*time.Minute + 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 *QueryThingDetailListLogic) 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 *QueryThingDetailListLogic) 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/query_thing_image_list_logic.go b/app/auth/api/internal/logic/storage/query_thing_image_list_logic.go index 12e9f4e..1800895 100644 --- a/app/auth/api/internal/logic/storage/query_thing_image_list_logic.go +++ b/app/auth/api/internal/logic/storage/query_thing_image_list_logic.go @@ -2,8 +2,15 @@ package storage import ( "context" + "encoding/json" "errors" + "github.com/redis/go-redis/v9" + "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" "sync" + "time" "schisandra-album-cloud-microservices/app/auth/api/internal/svc" "schisandra-album-cloud-microservices/app/auth/api/internal/types" @@ -25,7 +32,7 @@ func NewQueryThingImageListLogic(ctx context.Context, svcCtx *svc.ServiceContext } } -func (l *QueryThingImageListLogic) QueryThingImageList() (resp *types.ThingListResponse, err error) { +func (l *QueryThingImageListLogic) QueryThingImageList(req *types.ThingListRequest) (resp *types.ThingListResponse, err error) { uid, ok := l.ctx.Value("user_id").(string) if !ok { return nil, errors.New("user_id not found") @@ -34,33 +41,60 @@ func (l *QueryThingImageListLogic) QueryThingImageList() (resp *types.ThingListR storageInfos, err := storageInfo.Select( storageInfo.ID, storageInfo.Category, - storageInfo.Tags, + storageInfo.Tag, + storageInfo.Path, storageInfo.CreatedAt). Where(storageInfo.UserID.Eq(uid), + storageInfo.Provider.Eq(req.Provider), + storageInfo.Bucket.Eq(req.Bucket), storageInfo.Category.IsNotNull(), - storageInfo.Tags.IsNotNull()). + storageInfo.Tag.IsNotNull(), + storageInfo.Category.Length().Gt(0), + storageInfo.Tag.Length().Gte(0)). Order(storageInfo.CreatedAt.Desc()). Find() if err != nil { return nil, err } + // 加载用户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") + } + categoryMap := sync.Map{} tagCountMap := sync.Map{} + tagCoverMap := sync.Map{} // 用于存储每个 Tag 的封面图片路径 for _, info := range storageInfos { - tagKey := info.Category + "::" + info.Tags + tagKey := info.Category + "::" + info.Tag if _, exists := tagCountMap.Load(tagKey); !exists { tagCountMap.Store(tagKey, int64(0)) categoryEntry, _ := categoryMap.LoadOrStore(info.Category, &sync.Map{}) tagMap := categoryEntry.(*sync.Map) - tagMap.Store(info.Tags, types.ThingMeta{ - TagName: info.Tags, + tagMap.Store(info.Tag, types.ThingMeta{ + TagName: info.Tag, CreatedAt: info.CreatedAt.Format("2006-01-02 15:04:05"), }) } tagCount, _ := tagCountMap.Load(tagKey) tagCountMap.Store(tagKey, tagCount.(int64)+1) + + // 为每个 Tag 存储封面图片路径 + if _, exists := tagCoverMap.Load(tagKey); !exists { + // 使用服务生成预签名 URL + coverImageURL, err := service.PresignedURL(l.ctx, req.Bucket, info.Path, 7*24*time.Hour) + if err == nil { + tagCoverMap.Store(tagKey, coverImageURL) + } + } } var thingListData []types.ThingListData @@ -71,6 +105,11 @@ func (l *QueryThingImageListLogic) QueryThingImageList() (resp *types.ThingListR tagCount, _ := tagCountMap.Load(tagKey) meta := item.(types.ThingMeta) meta.TagCount = tagCount.(int64) + + // 获取封面图片 URL + if coverImageURL, ok := tagCoverMap.Load(tagKey); ok { + meta.CoverImage = coverImageURL.(string) + } metas = append(metas, meta) return true }) @@ -83,3 +122,63 @@ func (l *QueryThingImageListLogic) QueryThingImageList() (resp *types.ThingListR return &types.ThingListResponse{Records: thingListData}, nil } + +// 提取解密操作为函数 +func (l *QueryThingImageListLogic) 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 *QueryThingImageListLogic) 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/set_storage_config_logic.go b/app/auth/api/internal/logic/storage/set_storage_config_logic.go index da7bab5..9cef2b7 100644 --- a/app/auth/api/internal/logic/storage/set_storage_config_logic.go +++ b/app/auth/api/internal/logic/storage/set_storage_config_logic.go @@ -40,7 +40,7 @@ func (l *SetStorageConfigLogic) SetStorageConfig(req *types.StorageConfigRequest } ossConfig := &model.ScaStorageConfig{ UserID: uid, - Type: req.Type, + Provider: req.Type, Endpoint: req.Endpoint, Bucket: req.Bucket, AccessKey: accessKey, 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 5d7b124..6223e81 100644 --- a/app/auth/api/internal/logic/storage/upload_file_logic.go +++ b/app/auth/api/internal/logic/storage/upload_file_logic.go @@ -83,29 +83,8 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) { } } - // 解析 EXIF 信息 - exif, err := l.parseExifData(result.Exif) - if err != nil { - return "", err - } - - // 提取拍摄时间 - originalDateTime, err := l.extractOriginalDateTime(exif) - if err != nil { - return "", err - } - - // 提取 GPS 信息 - latitude, longitude := l.extractGPSCoordinates(exif) - // 根据 GPS 信息获取地理位置信息 - country, province, city, err := l.getGeoLocation(latitude, longitude) - if err != nil { - return "", err - } - - // 将地址信息保存到数据库 - locationId, err := l.saveFileLocationInfoToDB(uid, latitude, longitude, country, province, city) + country, province, city, err := l.getGeoLocation(result.Latitude, result.Longitude) if err != nil { return "", err } @@ -120,8 +99,14 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) { return "", err } + // 将地址信息保存到数据库 + locationId, err := l.saveFileLocationInfoToDB(uid, result.Provider, result.Bucket, result.Latitude, result.Longitude, country, province, city, filePath) + if err != nil { + return "", err + } + // 将 EXIF 和文件信息存入数据库 - id, err := l.saveFileInfoToDB(uid, bucket, provider, header, result, originalDateTime, locationId, exif, faceId, filePath) + id, err := l.saveFileInfoToDB(uid, bucket, provider, header, result, locationId, faceId, filePath) if err != nil { return "", err } @@ -175,23 +160,6 @@ func (l *UploadFileLogic) parseAIRecognitionResult(r *http.Request) (types.File, return result, nil } -// 解析 EXIF 数据 -func (l *UploadFileLogic) parseExifData(exifData interface{}) (map[string]interface{}, error) { - if exifData == "" { - return nil, nil - } - marshaledExif, err := json.Marshal(exifData) - if err != nil { - return nil, errors.New("invalid exif") - } - - var exif map[string]interface{} - if err = json.Unmarshal(marshaledExif, &exif); err != nil { - return nil, errors.New("invalid exif") - } - return exif, nil -} - // 提取拍摄时间 func (l *UploadFileLogic) extractOriginalDateTime(exif map[string]interface{}) (string, error) { if dateTimeOriginal, ok := exif["DateTimeOriginal"].(string); ok { @@ -203,18 +171,6 @@ func (l *UploadFileLogic) extractOriginalDateTime(exif map[string]interface{}) ( return "", nil } -// 提取 GPS 信息 -func (l *UploadFileLogic) extractGPSCoordinates(exif map[string]interface{}) (float64, float64) { - var latitude, longitude float64 - if lat, ok := exif["latitude"].(float64); ok { - latitude = lat - } - if long, ok := exif["longitude"].(float64); ok { - longitude = long - } - return latitude, longitude -} - // 根据 GPS 信息获取地理位置信息 func (l *UploadFileLogic) getGeoLocation(latitude, longitude float64) (string, string, string, error) { if latitude == 0.000000 || longitude == 0.000000 { @@ -284,7 +240,7 @@ func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHead return ossConfig.BucketName, ossConfig.Provider, objectKey, url, nil } -func (l *UploadFileLogic) saveFileLocationInfoToDB(uid string, latitude float64, longitude float64, country string, province string, city string) (int64, error) { +func (l *UploadFileLogic) saveFileLocationInfoToDB(uid string, provider string, bucket string, latitude float64, longitude float64, country string, province string, city string, filePath string) (int64, error) { if latitude == 0.000000 || longitude == 0.000000 { return 0, nil } @@ -295,13 +251,16 @@ func (l *UploadFileLogic) saveFileLocationInfoToDB(uid string, latitude float64, } if storageLocations == nil { locationInfo := model.ScaStorageLocation{ - UserID: uid, - Country: country, - City: city, - Province: province, - Latitude: fmt.Sprintf("%f", latitude), - Longitude: fmt.Sprintf("%f", longitude), - Total: 1, + Provider: provider, + Bucket: bucket, + UserID: uid, + Country: country, + City: city, + Province: province, + Latitude: fmt.Sprintf("%f", latitude), + Longitude: fmt.Sprintf("%f", longitude), + Total: 1, + CoverImage: filePath, } err = locationDB.Create(&locationInfo) if err != nil { @@ -309,7 +268,7 @@ func (l *UploadFileLogic) saveFileLocationInfoToDB(uid string, latitude float64, } return locationInfo.ID, nil } else { - info, err := locationDB.Where(locationDB.ID.Eq(storageLocations.ID), locationDB.UserID.Eq(uid)).Update(locationDB.Total, locationDB.Total.Add(1)) + info, err := locationDB.Where(locationDB.ID.Eq(storageLocations.ID), locationDB.UserID.Eq(uid)).UpdateColumnSimple(locationDB.Total.Add(1), locationDB.CoverImage.Value(filePath)) if err != nil { return 0, err } @@ -321,35 +280,29 @@ func (l *UploadFileLogic) saveFileLocationInfoToDB(uid string, latitude float64, } // 将 EXIF 和文件信息存入数据库 -func (l *UploadFileLogic) saveFileInfoToDB(uid, bucket, provider string, header *multipart.FileHeader, result types.File, originalDateTime string, locationId int64, exif map[string]interface{}, faceId int64, filePath string) (int64, error) { - exifJSON, err := json.Marshal(exif) - if err != nil { - return 0, errors.New("marshal exif failed") - } +func (l *UploadFileLogic) saveFileInfoToDB(uid, bucket, provider string, header *multipart.FileHeader, result types.File, locationId, faceId int64, filePath string) (int64, error) { + typeName := l.classifyFile(result.FileType, result.IsScreenshot) scaStorageInfo := &model.ScaStorageInfo{ - UserID: uid, - Provider: provider, - Bucket: bucket, - FileName: header.Filename, - FileSize: strconv.FormatInt(header.Size, 10), - FileType: result.FileType, - Path: filePath, - Landscape: result.Landscape, - Tags: result.TagName, - Anime: strconv.FormatBool(result.IsAnime), - Category: result.TopCategory, - Screenshot: strconv.FormatBool(result.IsScreenshot), - OriginalTime: originalDateTime, - LocationID: locationId, - Exif: string(exifJSON), - FaceID: faceId, - Type: typeName, - Width: result.Width, - Height: result.Height, + UserID: uid, + Provider: provider, + Bucket: bucket, + FileName: header.Filename, + FileSize: strconv.FormatInt(header.Size, 10), + FileType: result.FileType, + Path: filePath, + Landscape: result.Landscape, + Tag: result.TagName, + IsAnime: strconv.FormatBool(result.IsAnime), + Category: result.TopCategory, + LocationID: locationId, + FaceID: faceId, + Type: typeName, + Width: result.Width, + Height: result.Height, } - err = l.svcCtx.DB.ScaStorageInfo.Create(scaStorageInfo) + err := l.svcCtx.DB.ScaStorageInfo.Create(scaStorageInfo) if err != nil { return 0, errors.New("create storage info failed") } @@ -367,7 +320,7 @@ func (l *UploadFileLogic) decryptConfig(dbConfig *model.ScaStorageConfig) (*conf return nil, errors.New("decrypt secret key failed") } return &config.StorageConfig{ - Provider: dbConfig.Type, + Provider: dbConfig.Provider, Endpoint: dbConfig.Endpoint, AccessKey: accessKey, SecretKey: secretKey, @@ -394,7 +347,7 @@ func (l *UploadFileLogic) getOssConfigFromCacheOrDb(cacheKey, uid, provider stri // 缓存未命中,从数据库中加载 scaOssConfig := l.svcCtx.DB.ScaStorageConfig - dbOssConfig, err := scaOssConfig.Where(scaOssConfig.UserID.Eq(uid), scaOssConfig.Type.Eq(provider)).First() + dbOssConfig, err := scaOssConfig.Where(scaOssConfig.UserID.Eq(uid), scaOssConfig.Provider.Eq(provider)).First() if err != nil { return nil, err } diff --git a/app/auth/api/internal/types/file.go b/app/auth/api/internal/types/file.go index a2c9497..3b5c2a4 100644 --- a/app/auth/api/internal/types/file.go +++ b/app/auth/api/internal/types/file.go @@ -10,7 +10,8 @@ type File struct { Landscape string `json:"landscape"` TopCategory string `json:"topCategory"` IsScreenshot bool `json:"isScreenshot"` - Exif any `json:"exif"` Width float64 `json:"width"` Height float64 `json:"height"` + Longitude float64 `json:"longitude"` + Latitude float64 `json:"latitude"` } diff --git a/app/auth/api/internal/types/types.go b/app/auth/api/internal/types/types.go index 6e8bb93..72a84d2 100644 --- a/app/auth/api/internal/types/types.go +++ b/app/auth/api/internal/types/types.go @@ -32,7 +32,13 @@ type AlbumDeleteRequest struct { } type AlbumDetailListRequest struct { - ID int64 `json:"id"` + ID int64 `json:"id"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` +} + +type AlbumDetailListResponse struct { + Records []AllImageDetail `json:"records"` } type AlbumListRequest struct { @@ -142,7 +148,13 @@ type CommentResponse struct { } type FaceDetailListRequest struct { - FaceID int64 `json:"face_id"` + FaceID int64 `json:"face_id"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` +} + +type FaceDetailListResponse struct { + Records []AllImageDetail `json:"records"` } type FaceSampleLibrary struct { @@ -169,7 +181,9 @@ type ImageMeta struct { } type LocationDetailListRequest struct { - ID int64 `json:"id"` + ID int64 `json:"id"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` } type LocationDetailListResponse struct { @@ -181,14 +195,20 @@ type LocationListData struct { List []LocationMeta `json:"list"` // 图片列表 } +type LocationListRequest struct { + Provider string `json:"provider"` + Bucket string `json:"bucket"` +} + type LocationListResponse struct { Records []LocationListData `json:"records"` } type LocationMeta struct { - ID int64 `json:"id"` - City string `json:"city"` - Total int64 `json:"total"` + ID int64 `json:"id"` + City string `json:"city"` + Total int64 `json:"total"` + CoverImage string `json:"cover_image"` } type LoginResponse struct { @@ -311,7 +331,9 @@ type StorageConfigRequest struct { } type ThingDetailListRequest struct { - ID int64 `json:"id"` + TagName string `json:"tag_name"` + Provider string `json:"provider"` + Bucket string `json:"bucket"` } type ThingDetailListResponse struct { @@ -323,14 +345,20 @@ type ThingListData struct { List []ThingMeta `json:"list"` // 图片列表 } +type ThingListRequest struct { + Provider string `json:"provider"` + Bucket string `json:"bucket"` +} + type ThingListResponse struct { Records []ThingListData `json:"records"` } type ThingMeta struct { - TagName string `json:"tag_name"` - CreatedAt string `json:"created_at"` - TagCount int64 `json:"tag_count"` + TagName string `json:"tag_name"` + CreatedAt string `json:"created_at"` + TagCount int64 `json:"tag_count"` + CoverImage string `json:"cover_image"` } type UploadRequest struct { diff --git a/app/auth/model/mysql/model/sca_storage_config.gen.go b/app/auth/model/mysql/model/sca_storage_config.gen.go index 771742c..74d8203 100644 --- a/app/auth/model/mysql/model/sca_storage_config.gen.go +++ b/app/auth/model/mysql/model/sca_storage_config.gen.go @@ -16,7 +16,7 @@ const TableNameScaStorageConfig = "sca_storage_config" type ScaStorageConfig 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 - Type string `gorm:"column:type;type:varchar(50);comment:类型" json:"type"` // 类型 + Provider string `gorm:"column:provider;type:varchar(50);comment:供应商" json:"provider"` // 供应商 Endpoint string `gorm:"column:endpoint;type:varchar(50);comment:地址" json:"endpoint"` // 地址 AccessKey string `gorm:"column:access_key;type:varchar(100);comment:密钥key" json:"access_key"` // 密钥key SecretKey string `gorm:"column:secret_key;type:varchar(100);comment:密钥" json:"secret_key"` // 密钥 diff --git a/app/auth/model/mysql/model/sca_storage_info.gen.go b/app/auth/model/mysql/model/sca_storage_info.gen.go index e0025ee..47dce2b 100644 --- a/app/auth/model/mysql/model/sca_storage_info.gen.go +++ b/app/auth/model/mysql/model/sca_storage_info.gen.go @@ -14,32 +14,29 @@ const TableNameScaStorageInfo = "sca_storage_info" // ScaStorageInfo mapped from table type ScaStorageInfo 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 - Provider string `gorm:"column:provider;type:varchar(50);comment:供应商" json:"provider"` // 供应商 - Bucket string `gorm:"column:bucket;type:varchar(50);comment:存储桶" json:"bucket"` // 存储桶 - Path string `gorm:"column:path;type:text;comment:路径" json:"path"` // 路径 - FileName string `gorm:"column:file_name;type:varchar(100);comment:文件名称" json:"file_name"` // 文件名称 - FileSize string `gorm:"column:file_size;type:varchar(50);comment:文件大小" json:"file_size"` // 文件大小 - FileType string `gorm:"column:file_type;type:varchar(50);comment:文件类型" json:"file_type"` // 文件类型 - Width float64 `gorm:"column:width;type:double;comment:宽" json:"width"` // 宽 - Height float64 `gorm:"column:height;type:double;comment:高" json:"height"` // 高 - Category string `gorm:"column:category;type:varchar(50);comment:分类" json:"category"` // 分类 - Tags string `gorm:"column:tags;type:varchar(255);comment:标签" json:"tags"` // 标签 - Type string `gorm:"column:type;type:varchar(50);comment:类型" json:"type"` // 类型 - LocationID int64 `gorm:"column:location_id;type:bigint(20);comment:地址ID" json:"location_id"` // 地址ID - Hash string `gorm:"column:hash;type:varchar(255);comment:哈希值" json:"hash"` // 哈希值 - Anime string `gorm:"column:anime;type:varchar(50);comment:是否是动漫图片" json:"anime"` // 是否是动漫图片 - FaceID int64 `gorm:"column:face_id;type:bigint(20);comment:人像ID" json:"face_id"` // 人像ID - Landscape string `gorm:"column:landscape;type:varchar(50);comment:风景类型" json:"landscape"` // 风景类型 - OriginalTime string `gorm:"column:original_time;type:varchar(50);comment:拍摄时间" json:"original_time"` // 拍摄时间 - Screenshot string `gorm:"column:screenshot;type:varchar(50);comment:是否是截图" json:"screenshot"` // 是否是截图 - Exif string `gorm:"column:exif;type:json;comment:exif 信息" json:"exif"` // exif 信息 - ImgShow int64 `gorm:"column:img_show;type:tinyint(4);comment:是否隐藏(0 不隐藏 1 隐藏)" json:"img_show"` // 是否隐藏(0 不隐藏 1 隐藏) - AlbumID int64 `gorm:"column:album_id;type:bigint(20);comment:相册ID" json:"album_id"` // 相册ID - 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"` // 删除时间 + 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 + Provider string `gorm:"column:provider;type:varchar(50);comment:供应商" json:"provider"` // 供应商 + Bucket string `gorm:"column:bucket;type:varchar(50);comment:存储桶" json:"bucket"` // 存储桶 + Path string `gorm:"column:path;type:text;comment:路径" json:"path"` // 路径 + FileName string `gorm:"column:file_name;type:varchar(100);comment:文件名称" json:"file_name"` // 文件名称 + FileSize string `gorm:"column:file_size;type:varchar(50);comment:文件大小" json:"file_size"` // 文件大小 + FileType string `gorm:"column:file_type;type:varchar(50);comment:文件类型" json:"file_type"` // 文件类型 + Width float64 `gorm:"column:width;type:double;comment:宽" json:"width"` // 宽 + Height float64 `gorm:"column:height;type:double;comment:高" json:"height"` // 高 + Category string `gorm:"column:category;type:varchar(50);comment:分类" json:"category"` // 分类 + Tag string `gorm:"column:tag;type:varchar(255);comment:标签" json:"tag"` // 标签 + Type string `gorm:"column:type;type:varchar(50);comment:类型" json:"type"` // 类型 + LocationID int64 `gorm:"column:location_id;type:bigint(20);comment:地址ID" json:"location_id"` // 地址ID + Hash string `gorm:"column:hash;type:varchar(255);comment:哈希值" json:"hash"` // 哈希值 + IsAnime string `gorm:"column:is_anime;type:varchar(50);comment:是否是动漫图片" json:"is_anime"` // 是否是动漫图片 + FaceID int64 `gorm:"column:face_id;type:bigint(20);comment:人像ID" json:"face_id"` // 人像ID + Landscape string `gorm:"column:landscape;type:varchar(50);comment:风景类型" json:"landscape"` // 风景类型 + IsDisplayed int64 `gorm:"column:is_displayed;type:tinyint(4);comment:是否隐藏(0 不隐藏 1 隐藏)" json:"is_displayed"` // 是否隐藏(0 不隐藏 1 隐藏) + AlbumID int64 `gorm:"column:album_id;type:bigint(20);comment:相册ID" json:"album_id"` // 相册ID + 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 ScaStorageInfo's table name diff --git a/app/auth/model/mysql/model/sca_storage_location.gen.go b/app/auth/model/mysql/model/sca_storage_location.gen.go index f171cb4..79e98a0 100644 --- a/app/auth/model/mysql/model/sca_storage_location.gen.go +++ b/app/auth/model/mysql/model/sca_storage_location.gen.go @@ -15,18 +15,21 @@ const TableNameScaStorageLocation = "sca_storage_location" // ScaStorageLocation mapped from table type ScaStorageLocation 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 - Country string `gorm:"column:country;type:varchar(100);comment:国家" json:"country"` // 国家 - Province string `gorm:"column:province;type:varchar(100);comment:省" json:"province"` // 省 - City string `gorm:"column:city;type:varchar(100);comment:城市" json:"city"` // 城市 - Latitude string `gorm:"column:latitude;type:varchar(50);comment:纬度" json:"latitude"` // 纬度 - Longitude string `gorm:"column:longitude;type:varchar(50);comment:经度" json:"longitude"` // 经度 - Total int64 `gorm:"column:total;type:bigint(20);comment:数量" json:"total"` // 数量 - 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"` // 删除时间 + 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 + Provider string `gorm:"column:provider;type:varchar(50);comment:供应商" json:"provider"` // 供应商 + Bucket string `gorm:"column:bucket;type:varchar(50);comment:存储桶" json:"bucket"` // 存储桶 + Country string `gorm:"column:country;type:varchar(100);comment:国家" json:"country"` // 国家 + Province string `gorm:"column:province;type:varchar(100);comment:省" json:"province"` // 省 + City string `gorm:"column:city;type:varchar(100);comment:城市" json:"city"` // 城市 + Latitude string `gorm:"column:latitude;type:varchar(50);comment:纬度" json:"latitude"` // 纬度 + Longitude string `gorm:"column:longitude;type:varchar(50);comment:经度" json:"longitude"` // 经度 + Total int64 `gorm:"column:total;type:bigint(20);comment:数量" json:"total"` // 数量 + CoverImage string `gorm:"column:cover_image;type:text;comment:封面图片" json:"cover_image"` // 封面图片 + 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 ScaStorageLocation's table name diff --git a/app/auth/model/mysql/query/sca_storage_config.gen.go b/app/auth/model/mysql/query/sca_storage_config.gen.go index c707e6c..6cec2f4 100644 --- a/app/auth/model/mysql/query/sca_storage_config.gen.go +++ b/app/auth/model/mysql/query/sca_storage_config.gen.go @@ -29,7 +29,7 @@ func newScaStorageConfig(db *gorm.DB, opts ...gen.DOOption) scaStorageConfig { _scaStorageConfig.ALL = field.NewAsterisk(tableName) _scaStorageConfig.ID = field.NewInt64(tableName, "id") _scaStorageConfig.UserID = field.NewString(tableName, "user_id") - _scaStorageConfig.Type = field.NewString(tableName, "type") + _scaStorageConfig.Provider = field.NewString(tableName, "provider") _scaStorageConfig.Endpoint = field.NewString(tableName, "endpoint") _scaStorageConfig.AccessKey = field.NewString(tableName, "access_key") _scaStorageConfig.SecretKey = field.NewString(tableName, "secret_key") @@ -50,7 +50,7 @@ type scaStorageConfig struct { ALL field.Asterisk ID field.Int64 // 主键 UserID field.String // 用户ID - Type field.String // 类型 + Provider field.String // 供应商 Endpoint field.String // 地址 AccessKey field.String // 密钥key SecretKey field.String // 密钥 @@ -77,7 +77,7 @@ func (s *scaStorageConfig) updateTableName(table string) *scaStorageConfig { s.ALL = field.NewAsterisk(table) s.ID = field.NewInt64(table, "id") s.UserID = field.NewString(table, "user_id") - s.Type = field.NewString(table, "type") + s.Provider = field.NewString(table, "provider") s.Endpoint = field.NewString(table, "endpoint") s.AccessKey = field.NewString(table, "access_key") s.SecretKey = field.NewString(table, "secret_key") @@ -105,7 +105,7 @@ func (s *scaStorageConfig) fillFieldMap() { s.fieldMap = make(map[string]field.Expr, 11) s.fieldMap["id"] = s.ID s.fieldMap["user_id"] = s.UserID - s.fieldMap["type"] = s.Type + s.fieldMap["provider"] = s.Provider s.fieldMap["endpoint"] = s.Endpoint s.fieldMap["access_key"] = s.AccessKey s.fieldMap["secret_key"] = s.SecretKey diff --git a/app/auth/model/mysql/query/sca_storage_info.gen.go b/app/auth/model/mysql/query/sca_storage_info.gen.go index 914411a..604e2b7 100644 --- a/app/auth/model/mysql/query/sca_storage_info.gen.go +++ b/app/auth/model/mysql/query/sca_storage_info.gen.go @@ -38,17 +38,14 @@ func newScaStorageInfo(db *gorm.DB, opts ...gen.DOOption) scaStorageInfo { _scaStorageInfo.Width = field.NewFloat64(tableName, "width") _scaStorageInfo.Height = field.NewFloat64(tableName, "height") _scaStorageInfo.Category = field.NewString(tableName, "category") - _scaStorageInfo.Tags = field.NewString(tableName, "tags") + _scaStorageInfo.Tag = field.NewString(tableName, "tag") _scaStorageInfo.Type = field.NewString(tableName, "type") _scaStorageInfo.LocationID = field.NewInt64(tableName, "location_id") _scaStorageInfo.Hash = field.NewString(tableName, "hash") - _scaStorageInfo.Anime = field.NewString(tableName, "anime") + _scaStorageInfo.IsAnime = field.NewString(tableName, "is_anime") _scaStorageInfo.FaceID = field.NewInt64(tableName, "face_id") _scaStorageInfo.Landscape = field.NewString(tableName, "landscape") - _scaStorageInfo.OriginalTime = field.NewString(tableName, "original_time") - _scaStorageInfo.Screenshot = field.NewString(tableName, "screenshot") - _scaStorageInfo.Exif = field.NewString(tableName, "exif") - _scaStorageInfo.ImgShow = field.NewInt64(tableName, "img_show") + _scaStorageInfo.IsDisplayed = field.NewInt64(tableName, "is_displayed") _scaStorageInfo.AlbumID = field.NewInt64(tableName, "album_id") _scaStorageInfo.CreatedAt = field.NewTime(tableName, "created_at") _scaStorageInfo.UpdatedAt = field.NewTime(tableName, "updated_at") @@ -62,33 +59,30 @@ func newScaStorageInfo(db *gorm.DB, opts ...gen.DOOption) scaStorageInfo { type scaStorageInfo struct { scaStorageInfoDo - ALL field.Asterisk - ID field.Int64 // 主键 - UserID field.String // 用户ID - Provider field.String // 供应商 - Bucket field.String // 存储桶 - Path field.String // 路径 - FileName field.String // 文件名称 - FileSize field.String // 文件大小 - FileType field.String // 文件类型 - Width field.Float64 // 宽 - Height field.Float64 // 高 - Category field.String // 分类 - Tags field.String // 标签 - Type field.String // 类型 - LocationID field.Int64 // 地址ID - Hash field.String // 哈希值 - Anime field.String // 是否是动漫图片 - FaceID field.Int64 // 人像ID - Landscape field.String // 风景类型 - OriginalTime field.String // 拍摄时间 - Screenshot field.String // 是否是截图 - Exif field.String // exif 信息 - ImgShow field.Int64 // 是否隐藏(0 不隐藏 1 隐藏) - AlbumID field.Int64 // 相册ID - CreatedAt field.Time // 创建时间 - UpdatedAt field.Time // 更新时间 - DeletedAt field.Field // 删除时间 + ALL field.Asterisk + ID field.Int64 // 主键 + UserID field.String // 用户ID + Provider field.String // 供应商 + Bucket field.String // 存储桶 + Path field.String // 路径 + FileName field.String // 文件名称 + FileSize field.String // 文件大小 + FileType field.String // 文件类型 + Width field.Float64 // 宽 + Height field.Float64 // 高 + Category field.String // 分类 + Tag field.String // 标签 + Type field.String // 类型 + LocationID field.Int64 // 地址ID + Hash field.String // 哈希值 + IsAnime field.String // 是否是动漫图片 + FaceID field.Int64 // 人像ID + Landscape field.String // 风景类型 + IsDisplayed field.Int64 // 是否隐藏(0 不隐藏 1 隐藏) + AlbumID field.Int64 // 相册ID + CreatedAt field.Time // 创建时间 + UpdatedAt field.Time // 更新时间 + DeletedAt field.Field // 删除时间 fieldMap map[string]field.Expr } @@ -116,17 +110,14 @@ func (s *scaStorageInfo) updateTableName(table string) *scaStorageInfo { s.Width = field.NewFloat64(table, "width") s.Height = field.NewFloat64(table, "height") s.Category = field.NewString(table, "category") - s.Tags = field.NewString(table, "tags") + s.Tag = field.NewString(table, "tag") s.Type = field.NewString(table, "type") s.LocationID = field.NewInt64(table, "location_id") s.Hash = field.NewString(table, "hash") - s.Anime = field.NewString(table, "anime") + s.IsAnime = field.NewString(table, "is_anime") s.FaceID = field.NewInt64(table, "face_id") s.Landscape = field.NewString(table, "landscape") - s.OriginalTime = field.NewString(table, "original_time") - s.Screenshot = field.NewString(table, "screenshot") - s.Exif = field.NewString(table, "exif") - s.ImgShow = field.NewInt64(table, "img_show") + s.IsDisplayed = field.NewInt64(table, "is_displayed") s.AlbumID = field.NewInt64(table, "album_id") s.CreatedAt = field.NewTime(table, "created_at") s.UpdatedAt = field.NewTime(table, "updated_at") @@ -147,7 +138,7 @@ func (s *scaStorageInfo) GetFieldByName(fieldName string) (field.OrderExpr, bool } func (s *scaStorageInfo) fillFieldMap() { - s.fieldMap = make(map[string]field.Expr, 26) + s.fieldMap = make(map[string]field.Expr, 23) s.fieldMap["id"] = s.ID s.fieldMap["user_id"] = s.UserID s.fieldMap["provider"] = s.Provider @@ -159,17 +150,14 @@ func (s *scaStorageInfo) fillFieldMap() { s.fieldMap["width"] = s.Width s.fieldMap["height"] = s.Height s.fieldMap["category"] = s.Category - s.fieldMap["tags"] = s.Tags + s.fieldMap["tag"] = s.Tag s.fieldMap["type"] = s.Type s.fieldMap["location_id"] = s.LocationID s.fieldMap["hash"] = s.Hash - s.fieldMap["anime"] = s.Anime + s.fieldMap["is_anime"] = s.IsAnime s.fieldMap["face_id"] = s.FaceID s.fieldMap["landscape"] = s.Landscape - s.fieldMap["original_time"] = s.OriginalTime - s.fieldMap["screenshot"] = s.Screenshot - s.fieldMap["exif"] = s.Exif - s.fieldMap["img_show"] = s.ImgShow + s.fieldMap["is_displayed"] = s.IsDisplayed s.fieldMap["album_id"] = s.AlbumID s.fieldMap["created_at"] = s.CreatedAt s.fieldMap["updated_at"] = s.UpdatedAt diff --git a/app/auth/model/mysql/query/sca_storage_location.gen.go b/app/auth/model/mysql/query/sca_storage_location.gen.go index f5db700..73c1d29 100644 --- a/app/auth/model/mysql/query/sca_storage_location.gen.go +++ b/app/auth/model/mysql/query/sca_storage_location.gen.go @@ -29,12 +29,15 @@ func newScaStorageLocation(db *gorm.DB, opts ...gen.DOOption) scaStorageLocation _scaStorageLocation.ALL = field.NewAsterisk(tableName) _scaStorageLocation.ID = field.NewInt64(tableName, "id") _scaStorageLocation.UserID = field.NewString(tableName, "user_id") + _scaStorageLocation.Provider = field.NewString(tableName, "provider") + _scaStorageLocation.Bucket = field.NewString(tableName, "bucket") _scaStorageLocation.Country = field.NewString(tableName, "country") _scaStorageLocation.Province = field.NewString(tableName, "province") _scaStorageLocation.City = field.NewString(tableName, "city") _scaStorageLocation.Latitude = field.NewString(tableName, "latitude") _scaStorageLocation.Longitude = field.NewString(tableName, "longitude") _scaStorageLocation.Total = field.NewInt64(tableName, "total") + _scaStorageLocation.CoverImage = field.NewString(tableName, "cover_image") _scaStorageLocation.Version = field.NewField(tableName, "version") _scaStorageLocation.CreatedAt = field.NewTime(tableName, "created_at") _scaStorageLocation.UpdatedAt = field.NewTime(tableName, "updated_at") @@ -48,19 +51,22 @@ func newScaStorageLocation(db *gorm.DB, opts ...gen.DOOption) scaStorageLocation type scaStorageLocation struct { scaStorageLocationDo - ALL field.Asterisk - ID field.Int64 // 主键 - UserID field.String // 用户id - Country field.String // 国家 - Province field.String // 省 - City field.String // 城市 - Latitude field.String // 纬度 - Longitude field.String // 经度 - Total field.Int64 // 数量 - Version field.Field // 版本 - CreatedAt field.Time // 创建时间 - UpdatedAt field.Time // 更新时间 - DeletedAt field.Field // 删除时间 + ALL field.Asterisk + ID field.Int64 // 主键 + UserID field.String // 用户id + Provider field.String // 供应商 + Bucket field.String // 存储桶 + Country field.String // 国家 + Province field.String // 省 + City field.String // 城市 + Latitude field.String // 纬度 + Longitude field.String // 经度 + Total field.Int64 // 数量 + CoverImage field.String // 封面图片 + Version field.Field // 版本 + CreatedAt field.Time // 创建时间 + UpdatedAt field.Time // 更新时间 + DeletedAt field.Field // 删除时间 fieldMap map[string]field.Expr } @@ -79,12 +85,15 @@ func (s *scaStorageLocation) updateTableName(table string) *scaStorageLocation { s.ALL = field.NewAsterisk(table) s.ID = field.NewInt64(table, "id") s.UserID = field.NewString(table, "user_id") + s.Provider = field.NewString(table, "provider") + s.Bucket = field.NewString(table, "bucket") s.Country = field.NewString(table, "country") s.Province = field.NewString(table, "province") s.City = field.NewString(table, "city") s.Latitude = field.NewString(table, "latitude") s.Longitude = field.NewString(table, "longitude") s.Total = field.NewInt64(table, "total") + s.CoverImage = field.NewString(table, "cover_image") s.Version = field.NewField(table, "version") s.CreatedAt = field.NewTime(table, "created_at") s.UpdatedAt = field.NewTime(table, "updated_at") @@ -105,15 +114,18 @@ func (s *scaStorageLocation) GetFieldByName(fieldName string) (field.OrderExpr, } func (s *scaStorageLocation) fillFieldMap() { - s.fieldMap = make(map[string]field.Expr, 12) + s.fieldMap = make(map[string]field.Expr, 15) s.fieldMap["id"] = s.ID s.fieldMap["user_id"] = s.UserID + s.fieldMap["provider"] = s.Provider + s.fieldMap["bucket"] = s.Bucket s.fieldMap["country"] = s.Country s.fieldMap["province"] = s.Province s.fieldMap["city"] = s.City s.fieldMap["latitude"] = s.Latitude s.fieldMap["longitude"] = s.Longitude s.fieldMap["total"] = s.Total + s.fieldMap["cover_image"] = s.CoverImage s.fieldMap["version"] = s.Version s.fieldMap["created_at"] = s.CreatedAt s.fieldMap["updated_at"] = s.UpdatedAt diff --git a/common/constant/redis_prefix.go b/common/constant/redis_prefix.go index 885c958..6f3991c 100644 --- a/common/constant/redis_prefix.go +++ b/common/constant/redis_prefix.go @@ -24,4 +24,5 @@ const ( ImageListPrefix = "image:list:" ImageListMetaPrefix = "image:meta:" ImageRecentPrefix = "image:recent:" + ImageFaceListPrefix = "image:face:list:" )