🚧 developing...

This commit is contained in:
2025-03-04 01:32:34 +08:00
parent 4d0f628586
commit c0d0d784d6
9 changed files with 343 additions and 4 deletions

View File

@@ -659,6 +659,9 @@ type (
Provider string `json:"provider"`
Bucket string `json:"bucket"`
}
DownloadAlbumResponse {
Records []string `json:"records"`
}
// 搜索图片请求参数
SearchImageRequest {
Type string `json:"type"`
@@ -671,6 +674,21 @@ type (
SearchImageResponse {
Records []AllImageDetail `json:"records"`
}
// 搜索相册请求参数
SearchAlbumRequest {
Keyword string `json:"keyword"`
}
// 搜索相册相应参数
SearchAlbumResponse {
Albums []Album `json:"albums"`
}
// 图片添加到相册请求参数
AddImageToAlbumRequest {
IDS []int64 `json:"ids"`
AlbumID int64 `json:"album_id"`
Provider string `json:"provider"`
Bucket string `json:"bucket"`
}
)
// 文件上传
@@ -780,11 +798,19 @@ service auth {
// 下载相册
@handler downloadAlbum
post /album/download (DownloadAlbumRequest) returns (string)
post /album/download (DownloadAlbumRequest) returns (DownloadAlbumResponse)
// 图片搜索
@handler searchImage
post /image/search (SearchImageRequest) returns (SearchImageResponse)
// 搜索相册
@handler searchAlbum
post /album/search (SearchAlbumRequest) returns (SearchAlbumResponse)
// 添加图片到相册
@handler addImageToAlbum
post /album/add/image (AddImageToAlbumRequest) returns (string)
}
type (

View File

@@ -257,6 +257,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/album/add/image",
Handler: storage.AddImageToAlbumHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/album/create",
@@ -287,6 +292,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/album/rename",
Handler: storage.RenameAlbumHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/album/search",
Handler: storage.SearchAlbumHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/album/share",

View File

@@ -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 AddImageToAlbumHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AddImageToAlbumRequest
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := storage.NewAddImageToAlbumLogic(r.Context(), svcCtx)
resp, err := l.AddImageToAlbum(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -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 SearchAlbumHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.SearchAlbumRequest
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := storage.NewSearchAlbumLogic(r.Context(), svcCtx)
resp, err := l.SearchAlbum(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -3,6 +3,7 @@ package share
import (
"context"
"errors"
"schisandra-album-cloud-microservices/common/constant"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
@@ -65,6 +66,19 @@ func (l *DeleteShareRecordLogic) DeleteShareRecord(req *types.DeleteShareRecordR
tx.Rollback()
return "", errors.New("delete storage info record failed")
}
// delete redis cache
cacheKey := constant.ImageSharePrefix + req.InviteCode
err = l.svcCtx.RedisClient.Del(l.ctx, cacheKey).Err()
if err != nil {
tx.Rollback()
return "", errors.New("delete cache failed")
}
cacheVisitKey := constant.ImageShareVisitPrefix + req.InviteCode
err = l.svcCtx.RedisClient.Del(l.ctx, cacheVisitKey).Err()
if err != nil {
tx.Rollback()
return "", errors.New("delete cache visit failed")
}
err = tx.Commit()
if err != nil {
tx.Rollback()

View File

@@ -0,0 +1,44 @@
package storage
import (
"context"
"errors"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AddImageToAlbumLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAddImageToAlbumLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddImageToAlbumLogic {
return &AddImageToAlbumLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AddImageToAlbumLogic) AddImageToAlbum(req *types.AddImageToAlbumRequest) (resp string, err error) {
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return "", errors.New("user_id not found")
}
storageInfo := l.svcCtx.DB.ScaStorageInfo
update, err := storageInfo.Where(storageInfo.UserID.Eq(uid),
storageInfo.ID.In(req.IDS...),
storageInfo.Provider.Eq(req.Provider),
storageInfo.Bucket.Eq(req.Bucket)).Update(storageInfo.AlbumID, req.AlbumID)
if err != nil {
return "", err
}
if update.RowsAffected == 0 {
return "", errors.New("no image found")
}
return "success", nil
}

View File

@@ -2,6 +2,16 @@ package storage
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/redis/go-redis/v9"
"golang.org/x/sync/errgroup"
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
"schisandra-album-cloud-microservices/common/constant"
"schisandra-album-cloud-microservices/common/encrypt"
storageConfig "schisandra-album-cloud-microservices/common/storage/config"
"time"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
@@ -23,8 +33,115 @@ func NewDownloadAlbumLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Dow
}
}
func (l *DownloadAlbumLogic) DownloadAlbum(req *types.DownloadAlbumRequest) (resp string, err error) {
// todo: download album logic
func (l *DownloadAlbumLogic) DownloadAlbum(req *types.DownloadAlbumRequest) (resp *types.DownloadAlbumResponse, err error) {
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found")
}
storageInfo := l.svcCtx.DB.ScaStorageInfo
storageInfos, err := storageInfo.Where(storageInfo.UserID.Eq(uid), storageInfo.AlbumID.Eq(req.ID),
storageInfo.Provider.Eq(req.Provider), storageInfo.Bucket.Eq(req.Bucket)).Find()
if err != nil {
return nil, err
}
return
// 加载用户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")
}
// 并发生成预签名URL
urls := make([]string, len(storageInfos))
g, ctx := errgroup.WithContext(l.ctx)
for i := range storageInfos {
i := i
file := storageInfos[i]
g.Go(func() error {
select {
case <-ctx.Done():
return ctx.Err()
default:
url, err := service.PresignedURL(ctx, ossConfig.BucketName, file.Path, 5*time.Minute)
if err != nil {
return fmt.Errorf("failed to generate URL for %s: %w", file.Path, err)
}
urls[i] = url
return nil
}
})
}
if err = g.Wait(); err != nil {
return nil, fmt.Errorf("failed to generate URLs: %w", err)
}
return &types.DownloadAlbumResponse{
Records: urls,
}, nil
}
// 提取解密操作为函数
func (l *DownloadAlbumLogic) 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 *DownloadAlbumLogic) 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
}

View File

@@ -0,0 +1,51 @@
package storage
import (
"context"
"errors"
"github.com/zeromicro/go-zero/core/logx"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
)
type SearchAlbumLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSearchAlbumLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SearchAlbumLogic {
return &SearchAlbumLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SearchAlbumLogic) SearchAlbum(req *types.SearchAlbumRequest) (resp *types.SearchAlbumResponse, err error) {
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found")
}
storageAlbum := l.svcCtx.DB.ScaStorageAlbum
storageAlbums, err := storageAlbum.Where(storageAlbum.UserID.Eq(uid), storageAlbum.AlbumName.Like("%"+req.Keyword+"%")).Find()
if err != nil {
return nil, err
}
if len(storageAlbums) == 0 {
return nil, nil
}
var albums []types.Album
for _, album := range storageAlbums {
albums = append(albums, types.Album{
ID: album.ID,
Name: album.AlbumName,
Type: album.AlbumType,
CoverImage: album.CoverImage,
CreatedAt: album.CreatedAt.Format("2006-01-02"),
})
}
return &types.SearchAlbumResponse{
Albums: albums,
}, nil
}

View File

@@ -11,6 +11,13 @@ type AccountLoginRequest struct {
Key string `json:"key"`
}
type AddImageToAlbumRequest struct {
IDS []int64 `json:"ids"`
AlbumID int64 `json:"album_id"`
Provider string `json:"provider"`
Bucket string `json:"bucket"`
}
type Album struct {
ID int64 `json:"id"`
Name string `json:"name"`
@@ -181,6 +188,10 @@ type DownloadAlbumRequest struct {
Bucket string `json:"bucket"`
}
type DownloadAlbumResponse struct {
Records []string `json:"records"`
}
type FaceDetailListRequest struct {
FaceID int64 `json:"face_id"`
Provider string `json:"provider"`
@@ -364,6 +375,14 @@ type RotateCaptchaResponse struct {
Thumb string `json:"thumb"`
}
type SearchAlbumRequest struct {
Keyword string `json:"keyword"`
}
type SearchAlbumResponse struct {
Albums []Album `json:"albums"`
}
type SearchImageRequest struct {
Type string `json:"type"`
Keyword string `json:"keyword"`