🚧 developing...
This commit is contained in:
@@ -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 (
|
||||
|
@@ -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",
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@@ -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()
|
||||
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
51
app/auth/api/internal/logic/storage/search_album_logic.go
Normal file
51
app/auth/api/internal/logic/storage/search_album_logic.go
Normal 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
|
||||
}
|
@@ -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"`
|
||||
|
Reference in New Issue
Block a user