⬆️ upgrade dependencies
This commit is contained in:
@@ -86,7 +86,7 @@ func (l *FaceRecognitionLogic) FaceRecognition(in *pb.FaceRecognitionRequest) (*
|
||||
l.svcCtx.FaceRecognizer.SetSamples(samples, ids)
|
||||
|
||||
// 人脸分类
|
||||
classify := l.svcCtx.FaceRecognizer.ClassifyThreshold(faceFeatures.Descriptor, 0.3)
|
||||
classify := l.svcCtx.FaceRecognizer.ClassifyThreshold(faceFeatures.Descriptor, 0.2)
|
||||
if classify > 0 {
|
||||
return &pb.FaceRecognitionResponse{
|
||||
FaceId: int64(classify),
|
||||
@@ -242,10 +242,10 @@ func (l *FaceRecognitionLogic) saveCroppedFaceToLocal(faceImage []byte, rect ima
|
||||
// 增加边距(比如 20 像素)
|
||||
margin := 20
|
||||
extendedRect := image.Rect(
|
||||
max(rect.Min.X-margin, imgBounds.Min.X), // 确保不超出左边界
|
||||
max(rect.Min.Y-margin, imgBounds.Min.Y), // 确保不超出上边界
|
||||
min(rect.Max.X+margin, imgBounds.Max.X), // 确保不超出右边界
|
||||
min(rect.Max.Y+margin, imgBounds.Max.Y), // 确保不超出下边界
|
||||
max(rect.Min.X-margin, imgBounds.Min.X),
|
||||
max(rect.Min.Y-margin, imgBounds.Min.Y),
|
||||
min(rect.Max.X+margin, imgBounds.Max.X),
|
||||
min(rect.Max.Y+margin, imgBounds.Max.Y),
|
||||
)
|
||||
// 裁剪图像
|
||||
croppedImage := img.(interface {
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package aiservicelogic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"image"
|
||||
|
||||
"schisandra-album-cloud-microservices/app/aisvc/rpc/internal/svc"
|
||||
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
|
||||
@@ -25,12 +27,15 @@ func NewImageClarityLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Imag
|
||||
|
||||
// ImageClarity 图像清晰度检测
|
||||
func (l *ImageClarityLogic) ImageClarity(in *pb.ImageClarityRequest) (*pb.ImageClarityResponse, error) {
|
||||
blurred, confidence, err := l.svcCtx.Clarity.Detect(in.Image)
|
||||
img, _, err := image.Decode(bytes.NewReader(in.Image))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
check, err := l.svcCtx.ClarityDetector.ClarityCheck(img)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.ImageClarityResponse{
|
||||
IsBlurred: blurred,
|
||||
Confidence: float32(confidence),
|
||||
IsBlurred: check,
|
||||
}, nil
|
||||
}
|
||||
|
@@ -17,16 +17,16 @@ import (
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
FaceRecognizer *face.Recognizer
|
||||
DB *query.Query
|
||||
RedisClient *redis.Client
|
||||
TfNet *gocv.Net
|
||||
TfDesc []string
|
||||
CaffeNet *gocv.Net
|
||||
CaffeDesc []string
|
||||
MinioClient *minio.Client
|
||||
Clarity *clarity.Detector
|
||||
Config config.Config
|
||||
FaceRecognizer *face.Recognizer
|
||||
DB *query.Query
|
||||
RedisClient *redis.Client
|
||||
TfNet *gocv.Net
|
||||
TfDesc []string
|
||||
CaffeNet *gocv.Net
|
||||
CaffeDesc []string
|
||||
MinioClient *minio.Client
|
||||
ClarityDetector *clarity.ConcurrentDetector
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
@@ -44,6 +44,8 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
CaffeNet: caffeClassifier,
|
||||
CaffeDesc: caffeDesc,
|
||||
MinioClient: miniox.NewMinio(c.Minio.Endpoint, c.Minio.AccessKeyID, c.Minio.SecretAccessKey, c.Minio.UseSSL),
|
||||
Clarity: clarity.NewDetector(clarity.WithConcurrency(8), clarity.WithBaseThreshold(90), clarity.WithEdgeBoost(1.2), clarity.WithSampleScale(1)),
|
||||
ClarityDetector: clarity.NewConcurrentDetector(clarity.WithMeanThreshold(13.0), // 提高均值阈值
|
||||
clarity.WithLaplaceStdThreshold(25.0), // 提高标准差阈值
|
||||
clarity.WithMaxWorkers(8)),
|
||||
}
|
||||
}
|
||||
|
@@ -365,7 +365,7 @@ type (
|
||||
timeout: 10s // 超时时间
|
||||
maxBytes: 10485760 // 最大请求大小
|
||||
signature: false // 是否开启签名验证
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware // 注册中间件
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware,AuthMiddleware // 注册中间件
|
||||
MaxConns: true // 是否开启最大连接数限制
|
||||
Recover: true // 是否开启自动恢复
|
||||
jwt: Auth // 是否开启jwt验证
|
||||
@@ -727,6 +727,20 @@ type (
|
||||
ShareRecentInfoResponse {
|
||||
Records []ShareRecentMeta `json:"records"`
|
||||
}
|
||||
PrivateImageListRequest {
|
||||
Provider string `json:"provider"`
|
||||
Bucket string `json:"bucket"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
CoordinateMeta {
|
||||
ID int64 `json:"id"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
ImageCount int64 `json:"image_count"`
|
||||
}
|
||||
CoordinateListResponse {
|
||||
Records []CoordinateMeta `json:"records"`
|
||||
}
|
||||
)
|
||||
|
||||
// 文件上传
|
||||
@@ -736,7 +750,7 @@ type (
|
||||
timeout: 20s // 超时时间
|
||||
maxBytes: 104857600 // 最大请求大小
|
||||
signature: false // 是否开启签名验证
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware // 注册中间件
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware,AuthMiddleware // 注册中间件
|
||||
MaxConns: true // 是否开启最大连接数限制
|
||||
Recover: true // 是否开启自动恢复
|
||||
jwt: Auth // 是否开启jwt验证
|
||||
@@ -865,6 +879,14 @@ service auth {
|
||||
// 获取分享最近的数据统计
|
||||
@handler getShareRecentInfo
|
||||
post /share/recent/info returns (ShareRecentInfoResponse)
|
||||
|
||||
// 获取隐私加密图片
|
||||
@handler getPrivateImageList
|
||||
post /image/private/list (PrivateImageListRequest) returns (AllImageListResponse)
|
||||
|
||||
// 获取图像经纬度列表
|
||||
@handler getCoordinateList
|
||||
post /coordinate/list returns (CoordinateListResponse)
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -951,7 +973,7 @@ type (
|
||||
timeout: 10s // 超时时间
|
||||
maxBytes: 104857600 // 最大请求大小
|
||||
signature: false // 是否开启签名验证
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware // 注册中间件
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware,AuthMiddleware // 注册中间件
|
||||
MaxConns: true // 是否开启最大连接数限制
|
||||
Recover: true // 是否开启自动恢复
|
||||
jwt: Auth // 是否开启jwt验证
|
||||
@@ -993,14 +1015,14 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
// 分享服务
|
||||
// 用户服务
|
||||
@server (
|
||||
group: auth // 微服务分组
|
||||
prefix: /api/auth/user // 微服务前缀
|
||||
timeout: 10s // 超时时间
|
||||
maxBytes: 104857600 // 最大请求大小
|
||||
signature: false // 是否开启签名验证
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware // 注册中间件
|
||||
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware,AuthMiddleware // 注册中间件
|
||||
MaxConns: true // 是否开启最大连接数限制
|
||||
Recover: true // 是否开启自动恢复
|
||||
jwt: Auth // 是否开启jwt验证
|
||||
@@ -1009,5 +1031,9 @@ service auth {
|
||||
// 判断用户安全设置
|
||||
@handler checkUserSecuritySetting
|
||||
post /check/security/setting returns (UserSecuritySettingResponse)
|
||||
|
||||
// 退出登录
|
||||
@handler logout
|
||||
post /logout returns (string)
|
||||
}
|
||||
|
||||
|
@@ -90,8 +90,6 @@ Signature:
|
||||
Encrypt:
|
||||
# 密钥(32)
|
||||
Key: p3380puliiep184buh8d5dvujeerqtem
|
||||
# 向量 (16)
|
||||
IV: spb7er04k2vz3dtk
|
||||
# Redis 配置
|
||||
Redis:
|
||||
# Redis 地址
|
||||
|
@@ -16,7 +16,6 @@ type Config struct {
|
||||
}
|
||||
Encrypt struct {
|
||||
Key string
|
||||
IV string
|
||||
}
|
||||
Mysql struct {
|
||||
DataSource string
|
||||
|
21
app/auth/api/internal/handler/auth/logout_handler.go
Normal file
21
app/auth/api/internal/handler/auth/logout_handler.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"schisandra-album-cloud-microservices/app/auth/api/internal/logic/auth"
|
||||
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
|
||||
"schisandra-album-cloud-microservices/common/xhttp"
|
||||
)
|
||||
|
||||
func LogoutHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := auth.NewLogoutLogic(r.Context(), svcCtx)
|
||||
resp, err := l.Logout()
|
||||
if err != nil {
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
|
||||
} else {
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
@@ -27,13 +27,18 @@ import (
|
||||
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware},
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware, serverCtx.AuthMiddleware},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/check/security/setting",
|
||||
Handler: auth.CheckUserSecuritySettingHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/logout",
|
||||
Handler: auth.LogoutHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||
@@ -81,7 +86,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware},
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware, serverCtx.AuthMiddleware},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
@@ -205,7 +210,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware},
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware, serverCtx.AuthMiddleware},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
@@ -273,7 +278,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware},
|
||||
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware, serverCtx.AuthMiddleware},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
@@ -335,6 +340,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
Path: "/config/delete",
|
||||
Handler: storage.DeleteStorageConfigHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/coordinate/list",
|
||||
Handler: storage.GetCoordinateListHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/delete/record",
|
||||
@@ -380,6 +390,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
Path: "/image/location/list",
|
||||
Handler: storage.QueryLocationImageListHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/image/private/list",
|
||||
Handler: storage.GetPrivateImageListHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/image/recent/list",
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"schisandra-album-cloud-microservices/app/auth/api/internal/logic/storage"
|
||||
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
|
||||
"schisandra-album-cloud-microservices/common/xhttp"
|
||||
)
|
||||
|
||||
func GetCoordinateListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := storage.NewGetCoordinateListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetCoordinateList()
|
||||
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 GetPrivateImageListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.PrivateImageListRequest
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
|
||||
l := storage.NewGetPrivateImageListLogic(r.Context(), svcCtx)
|
||||
resp, err := l.GetPrivateImageList(&req)
|
||||
if err != nil {
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
|
||||
} else {
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
38
app/auth/api/internal/logic/auth/logout_logic.go
Normal file
38
app/auth/api/internal/logic/auth/logout_logic.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"schisandra-album-cloud-microservices/common/constant"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
|
||||
)
|
||||
|
||||
type LogoutLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewLogoutLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoutLogic {
|
||||
return &LogoutLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LogoutLogic) Logout() (resp string, err error) {
|
||||
uid, ok := l.ctx.Value("user_id").(string)
|
||||
if !ok {
|
||||
return "", errors.New("user_id not found")
|
||||
}
|
||||
cacheKey := constant.UserTokenPrefix + uid
|
||||
err = l.svcCtx.RedisClient.Del(l.ctx, cacheKey).Err()
|
||||
if err != nil {
|
||||
l.Logger.Error("logout failed")
|
||||
return "", errors.New("logout failed")
|
||||
}
|
||||
return "logout success", nil
|
||||
}
|
@@ -67,6 +67,7 @@ func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest)
|
||||
var queryCondition []gen.Condition
|
||||
queryCondition = append(queryCondition, storageInfo.UserID.Eq(uid))
|
||||
queryCondition = append(queryCondition, storageInfo.AlbumID.Eq(req.ID))
|
||||
queryCondition = append(queryCondition, storageInfo.IsEncrypted.Eq(constant.NoEncrypt))
|
||||
// 类型筛选 1 是分享类型
|
||||
if req.Type != constant.AlbumTypeShared {
|
||||
queryCondition = append(queryCondition, storageInfo.Provider.Eq(req.Provider))
|
||||
|
@@ -0,0 +1,51 @@
|
||||
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 GetCoordinateListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetCoordinateListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCoordinateListLogic {
|
||||
return &GetCoordinateListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetCoordinateListLogic) GetCoordinateList() (resp *types.CoordinateListResponse, err error) {
|
||||
uid, ok := l.ctx.Value("user_id").(string)
|
||||
if !ok {
|
||||
return nil, errors.New("user_id not found")
|
||||
}
|
||||
storageLocation := l.svcCtx.DB.ScaStorageLocation
|
||||
storageInfo := l.svcCtx.DB.ScaStorageInfo
|
||||
var records []types.CoordinateMeta
|
||||
err = storageLocation.Select(
|
||||
storageLocation.ID,
|
||||
storageLocation.Longitude,
|
||||
storageLocation.Latitude,
|
||||
storageInfo.ID.Count().As("image_count"),
|
||||
).Join(
|
||||
storageInfo,
|
||||
storageLocation.ID.EqCol(storageInfo.LocationID),
|
||||
).Where(storageLocation.UserID.Eq(uid),
|
||||
storageInfo.UserID.Eq(uid),
|
||||
).Scan(&records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.CoordinateListResponse{
|
||||
Records: records,
|
||||
}, nil
|
||||
}
|
@@ -74,7 +74,8 @@ func (l *GetFaceDetailListLogic) GetFaceDetailList(req *types.FaceDetailListRequ
|
||||
storageInfo.UserID.Eq(uid),
|
||||
storageInfo.Provider.Eq(req.Provider),
|
||||
storageInfo.Bucket.Eq(req.Bucket),
|
||||
storageInfo.FaceID.Eq(req.FaceID)).
|
||||
storageInfo.FaceID.Eq(req.FaceID),
|
||||
storageInfo.IsEncrypted.Eq(constant.NoEncrypt)).
|
||||
Order(storageInfo.CreatedAt.Desc()).Scan(&storageInfoList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -0,0 +1,289 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sync/semaphore"
|
||||
"gorm.io/gen"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
|
||||
"schisandra-album-cloud-microservices/common/constant"
|
||||
"schisandra-album-cloud-microservices/common/encrypt"
|
||||
storageConfig "schisandra-album-cloud-microservices/common/storage/config"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"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 GetPrivateImageListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
RestyClient *resty.Client
|
||||
}
|
||||
|
||||
func NewGetPrivateImageListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPrivateImageListLogic {
|
||||
return &GetPrivateImageListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
RestyClient: resty.New().
|
||||
SetTimeout(30 * time.Second). // 总超时时间
|
||||
SetRetryCount(3). // 重试次数
|
||||
SetRetryWaitTime(5 * time.Second). // 重试等待时间
|
||||
SetRetryMaxWaitTime(30 * time.Second). // 最大重试等待
|
||||
AddRetryCondition(func(r *resty.Response, err error) bool {
|
||||
return r.StatusCode() == http.StatusTooManyRequests ||
|
||||
err != nil ||
|
||||
r.StatusCode() >= 500
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetPrivateImageListLogic) GetPrivateImageList(req *types.PrivateImageListRequest) (resp *types.AllImageListResponse, 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
|
||||
conditions := []gen.Condition{
|
||||
storageInfo.UserID.Eq(uid),
|
||||
storageInfo.Provider.Eq(req.Provider),
|
||||
storageInfo.Bucket.Eq(req.Bucket),
|
||||
storageInfo.Type.Neq(constant.ImageTypeShared),
|
||||
storageInfo.IsDisplayed.Eq(0),
|
||||
storageInfo.IsEncrypted.Eq(constant.Encrypt),
|
||||
}
|
||||
var storageInfoList []types.FileInfoResult
|
||||
err = storageInfo.Select(
|
||||
storageInfo.ID,
|
||||
storageInfo.FileName,
|
||||
storageInfo.CreatedAt,
|
||||
storageInfo.Path).
|
||||
Where(conditions...).
|
||||
Order(storageInfo.CreatedAt.Desc()).Scan(&storageInfoList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(storageInfoList) == 0 {
|
||||
return &types.AllImageListResponse{}, 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")
|
||||
}
|
||||
|
||||
// 按日期进行分组
|
||||
g, ctx := errgroup.WithContext(l.ctx)
|
||||
sem := semaphore.NewWeighted(10) // 限制并发数为 10
|
||||
groupedImages := sync.Map{}
|
||||
|
||||
for _, dbFileInfo := range storageInfoList {
|
||||
dbFileInfo := dbFileInfo // 创建局部变量以避免闭包问题
|
||||
if err := sem.Acquire(ctx, 1); err != nil {
|
||||
logx.Error("Failed to acquire semaphore:", err)
|
||||
continue
|
||||
}
|
||||
g.Go(func() error {
|
||||
defer sem.Release(1)
|
||||
|
||||
// 生成单条缓存键(包含文件唯一标识)
|
||||
imageCacheKey := fmt.Sprintf("%s%s:%s:%s:%s:%v",
|
||||
constant.ImageCachePrefix,
|
||||
uid,
|
||||
"list",
|
||||
req.Provider,
|
||||
req.Bucket,
|
||||
dbFileInfo.ID)
|
||||
// 尝试获取单条缓存
|
||||
if cached, err := l.svcCtx.RedisClient.Get(l.ctx, imageCacheKey).Result(); err == nil {
|
||||
var meta types.ImageMeta
|
||||
if err := json.Unmarshal([]byte(cached), &meta); err == nil {
|
||||
parse, err := time.Parse("2006-01-02 15:04:05", meta.CreatedAt)
|
||||
if err == nil {
|
||||
logx.Error("Parse Time Error:", err)
|
||||
return nil
|
||||
}
|
||||
date := parse.Format("2006年1月2日 星期") + WeekdayMap[parse.Weekday()]
|
||||
value, _ := groupedImages.LoadOrStore(date, []types.ImageMeta{})
|
||||
images := value.([]types.ImageMeta)
|
||||
images = append(images, meta)
|
||||
groupedImages.Store(date, images)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()]
|
||||
date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday)
|
||||
url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, dbFileInfo.Path, time.Minute*30)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return err
|
||||
}
|
||||
imageBytes, err := l.DownloadAndDecrypt(l.ctx, url, uid)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return err
|
||||
}
|
||||
imageData, err := l.svcCtx.XCipher.Decrypt(imageBytes, []byte(uid))
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return err
|
||||
}
|
||||
// 使用 Load 或 Store 确保原子操作
|
||||
value, _ := groupedImages.LoadOrStore(date, []types.ImageMeta{})
|
||||
images := value.([]types.ImageMeta)
|
||||
|
||||
images = append(images, types.ImageMeta{
|
||||
ID: dbFileInfo.ID,
|
||||
FileName: dbFileInfo.FileName,
|
||||
URL: base64.StdEncoding.EncodeToString(imageData),
|
||||
Width: dbFileInfo.ThumbW,
|
||||
Height: dbFileInfo.ThumbH,
|
||||
CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
|
||||
// 重新存储更新后的图像列表
|
||||
groupedImages.Store(date, images)
|
||||
|
||||
// 缓存单条数据(24小时基础缓存 + 随机防雪崩)
|
||||
if data, err := json.Marshal(images); err == nil {
|
||||
expire := 24*time.Hour + time.Duration(rand.Intn(3600))*time.Second
|
||||
if err := l.svcCtx.RedisClient.Set(l.ctx, imageCacheKey, data, expire).Err(); err != nil {
|
||||
logx.Error("Failed to cache image meta:", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
// 等待所有 goroutine 完成
|
||||
if err = g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
})
|
||||
sort.Slice(imageList, func(i, j int) bool {
|
||||
if len(imageList[i].List) == 0 || len(imageList[j].List) == 0 {
|
||||
return false // 空列表不参与排序
|
||||
}
|
||||
createdAtI, _ := time.Parse("2006-01-02 15:04:05", imageList[i].List[0].CreatedAt)
|
||||
createdAtJ, _ := time.Parse("2006-01-02 15:04:05", imageList[j].List[0].CreatedAt)
|
||||
return createdAtI.After(createdAtJ) // 降序排序
|
||||
})
|
||||
resp = &types.AllImageListResponse{
|
||||
Records: imageList,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// 提取解密操作为函数
|
||||
func (l *GetPrivateImageListLogic) 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 *GetPrivateImageListLogic) 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
|
||||
}
|
||||
|
||||
func (l *GetPrivateImageListLogic) DownloadAndDecrypt(ctx context.Context, url string, uid string) ([]byte, error) {
|
||||
resp, err := l.RestyClient.R().
|
||||
SetContext(ctx).
|
||||
SetDoNotParseResponse(true). // 保持原始响应流
|
||||
Get(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.RawBody().Close()
|
||||
|
||||
if resp.StatusCode() != http.StatusOK {
|
||||
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode(), resp.Status())
|
||||
}
|
||||
|
||||
// 使用缓冲区分块读取
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err := io.Copy(buf, resp.RawBody()); err != nil {
|
||||
return nil, fmt.Errorf("read response body failed: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
@@ -79,6 +79,7 @@ func (l *QueryAllImageListLogic) QueryAllImageList(req *types.AllImageListReques
|
||||
storageInfo.Bucket.Eq(req.Bucket),
|
||||
storageInfo.Type.Neq(constant.ImageTypeShared),
|
||||
storageInfo.IsDisplayed.Eq(0),
|
||||
storageInfo.IsEncrypted.Eq(constant.NoEncrypt),
|
||||
}
|
||||
queryCondition = append(queryCondition, conditions...)
|
||||
if req.Type != "all" {
|
||||
|
@@ -79,7 +79,8 @@ func (l *QueryLocationDetailListLogic) QueryLocationDetailList(req *types.Locati
|
||||
storageInfo.UserID.Eq(uid),
|
||||
storageInfo.Provider.Eq(req.Provider),
|
||||
storageInfo.Bucket.Eq(req.Bucket),
|
||||
storageLocation.ID.Eq(req.ID)).
|
||||
storageLocation.ID.Eq(req.ID),
|
||||
storageInfo.IsEncrypted.Eq(constant.NoEncrypt)).
|
||||
Order(storageInfo.CreatedAt.Desc())
|
||||
err = storageInfoQuery.Scan(&storageInfoList)
|
||||
if err != nil {
|
||||
|
@@ -82,7 +82,8 @@ func (l *QueryRecentImageListLogic) QueryRecentImageList(req *types.RecentListRe
|
||||
storageInfo.Provider.Eq(req.Provider),
|
||||
storageInfo.Bucket.Eq(req.Bucket),
|
||||
storageInfo.Type.Neq(constant.ImageTypeShared),
|
||||
storageInfo.CreatedAt.Gt(thirtyDaysAgo)).
|
||||
storageInfo.CreatedAt.Gt(thirtyDaysAgo),
|
||||
storageInfo.IsEncrypted.Eq(constant.NoEncrypt)).
|
||||
Order(storageInfo.CreatedAt.Desc()).Scan(&storageInfoList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -79,7 +79,8 @@ func (l *QueryThingDetailListLogic) QueryThingDetailList(req *types.ThingDetailL
|
||||
storageInfo.UserID.Eq(uid),
|
||||
storageInfo.Provider.Eq(req.Provider),
|
||||
storageInfo.Bucket.Eq(req.Bucket),
|
||||
storageExtra.Tag.Eq(req.TagName)).
|
||||
storageExtra.Tag.Eq(req.TagName),
|
||||
storageInfo.IsEncrypted.Eq(constant.NoEncrypt)).
|
||||
Order(storageInfo.CreatedAt.Desc())
|
||||
err = storageInfoQuery.Scan(&storageInfoList)
|
||||
if err != nil {
|
||||
|
@@ -110,6 +110,15 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
var imageBytes []byte
|
||||
if settingResult.Encrypt {
|
||||
encryptedData, err := l.svcCtx.XCipher.Encrypt(data, []byte(uid))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
imageBytes = encryptedData
|
||||
}
|
||||
|
||||
// 上传文件到 OSS
|
||||
g.Go(func() error {
|
||||
if err := sem.Acquire(ctx, 1); err != nil {
|
||||
@@ -117,16 +126,7 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) {
|
||||
}
|
||||
defer sem.Release(1)
|
||||
|
||||
// 重新创建 `multipart.File` 兼容的 `Reader`
|
||||
fileReader := struct {
|
||||
*bytes.Reader
|
||||
io.Closer
|
||||
}{
|
||||
Reader: bytes.NewReader(data),
|
||||
Closer: io.NopCloser(nil),
|
||||
}
|
||||
|
||||
fileUrl, thumbUrl, err := l.uploadFileToOSS(uid, header, fileReader, thumbnail, result)
|
||||
fileUrl, thumbUrl, err := l.uploadFileToOSS(uid, header, bytes.NewReader(imageBytes), thumbnail, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -148,6 +148,7 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) {
|
||||
FileSize: header.Size,
|
||||
FilePath: filePath,
|
||||
ThumbPath: thumbPath,
|
||||
Setting: settingResult,
|
||||
}
|
||||
// 转换为 JSON
|
||||
messageData, err := json.Marshal(fileUploadMessage)
|
||||
@@ -209,7 +210,7 @@ func (l *UploadFileLogic) parseUploadSettingResult(r *http.Request) (types.Uploa
|
||||
}
|
||||
|
||||
// 上传文件到 OSS
|
||||
func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHeader, file multipart.File, thumbnail multipart.File, result types.File) (string, string, error) {
|
||||
func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHeader, file io.Reader, thumbnail io.Reader, result types.File) (string, string, error) {
|
||||
cacheKey := constant.UserOssConfigPrefix + uid + ":" + result.Provider
|
||||
ossConfig, err := l.getOssConfigFromCacheOrDb(cacheKey, uid, result.Provider)
|
||||
if err != nil {
|
||||
|
43
app/auth/api/internal/middleware/auth_middleware.go
Normal file
43
app/auth/api/internal/middleware/auth_middleware.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"net/http"
|
||||
"schisandra-album-cloud-microservices/common/constant"
|
||||
"schisandra-album-cloud-microservices/common/errors"
|
||||
"schisandra-album-cloud-microservices/common/xhttp"
|
||||
)
|
||||
|
||||
type AuthMiddleware struct {
|
||||
RedisClient *redis.Client
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func NewAuthMiddleware(redisClient *redis.Client) *AuthMiddleware {
|
||||
return &AuthMiddleware{
|
||||
RedisClient: redisClient,
|
||||
ctx: context.Background(),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userId := r.Header.Get(constant.UID_HEADER_KEY)
|
||||
if userId == "" {
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusNotFound, "user id not found in header"))
|
||||
return
|
||||
}
|
||||
cacheToken := constant.UserTokenPrefix + userId
|
||||
result, err := m.RedisClient.Get(m.ctx, cacheToken).Result()
|
||||
if err != nil {
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusInternalServerError, err.Error()))
|
||||
return
|
||||
}
|
||||
if result == "" {
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusForbidden, "access denied"))
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
}
|
||||
}
|
@@ -21,9 +21,13 @@ func NewCasbinVerifyMiddleware(casbin *casbin.SyncedCachedEnforcer) *CasbinVerif
|
||||
func (m *CasbinVerifyMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
userId := r.Header.Get(constant.UID_HEADER_KEY)
|
||||
if userId == "" {
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusNotFound, "user id not found in header"))
|
||||
return
|
||||
}
|
||||
correct, err := m.casbin.Enforce(userId, r.URL.Path, r.Method)
|
||||
if err != nil || !correct {
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusNotFound, "not found"))
|
||||
xhttp.JsonBaseResponseCtx(r.Context(), w, errors.New(http.StatusNotFound, "casbin verify failed"))
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
|
@@ -61,7 +61,7 @@ func (c *NsqImageProcessConsumer) HandleMessage(msg *nsq.Message) error {
|
||||
}
|
||||
|
||||
// 将文件信息存入数据库
|
||||
storageId, err := c.saveFileInfoToDB(message.UID, message.Result.Bucket, message.Result.Provider, message.FileName, message.FileSize, message.Result, message.FaceID, message.FilePath, locationId, message.Result.AlbumId)
|
||||
storageId, err := c.saveFileInfoToDB(message.UID, message.Result.Bucket, message.Result.Provider, message.FileName, message.FileSize, message.Result, message.FaceID, message.FilePath, locationId, message.Result.AlbumId, message.Setting.Encrypt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -266,7 +266,7 @@ func (c *NsqImageProcessConsumer) saveFileThumbnailInfoToDB(uid string, filePath
|
||||
}
|
||||
|
||||
// 将 EXIF 和文件信息存入数据库
|
||||
func (c *NsqImageProcessConsumer) saveFileInfoToDB(uid, bucket, provider string, fileName string, fileSize int64, result types.File, faceId int64, filePath string, locationID, albumId int64) (int64, error) {
|
||||
func (c *NsqImageProcessConsumer) saveFileInfoToDB(uid, bucket, provider string, fileName string, fileSize int64, result types.File, faceId int64, filePath string, locationID, albumId int64, encrypt bool) (int64, error) {
|
||||
tx := c.svcCtx.DB.Begin()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@@ -274,21 +274,26 @@ func (c *NsqImageProcessConsumer) saveFileInfoToDB(uid, bucket, provider string,
|
||||
logx.Errorf("transaction rollback: %v", r)
|
||||
}
|
||||
}()
|
||||
var isEncrypted int64 = 0
|
||||
if encrypt {
|
||||
isEncrypted = 1
|
||||
}
|
||||
typeName := c.classifyFile(result.FileType, result.IsScreenshot)
|
||||
scaStorageInfo := &model.ScaStorageInfo{
|
||||
UserID: uid,
|
||||
Provider: provider,
|
||||
Bucket: bucket,
|
||||
FileName: fileName,
|
||||
FileSize: fileSize,
|
||||
FileType: result.FileType,
|
||||
Path: filePath,
|
||||
FaceID: faceId,
|
||||
Type: typeName,
|
||||
Width: result.Width,
|
||||
Height: result.Height,
|
||||
LocationID: locationID,
|
||||
AlbumID: albumId,
|
||||
UserID: uid,
|
||||
Provider: provider,
|
||||
Bucket: bucket,
|
||||
FileName: fileName,
|
||||
FileSize: fileSize,
|
||||
FileType: result.FileType,
|
||||
Path: filePath,
|
||||
FaceID: faceId,
|
||||
Type: typeName,
|
||||
Width: result.Width,
|
||||
Height: result.Height,
|
||||
LocationID: locationID,
|
||||
AlbumID: albumId,
|
||||
IsEncrypted: isEncrypted,
|
||||
}
|
||||
err := tx.ScaStorageInfo.Create(scaStorageInfo)
|
||||
if err != nil {
|
||||
|
@@ -3,6 +3,7 @@ package svc
|
||||
import (
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/landaiqing/go-xcipher"
|
||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/nsqio/go-nsq"
|
||||
@@ -37,6 +38,7 @@ type ServiceContext struct {
|
||||
SecurityHeadersMiddleware rest.Middleware
|
||||
CasbinVerifyMiddleware rest.Middleware
|
||||
NonceMiddleware rest.Middleware
|
||||
AuthMiddleware rest.Middleware
|
||||
DB *query.Query
|
||||
RedisClient *redis.Client
|
||||
Ip2Region *xdb.Searcher
|
||||
@@ -50,6 +52,7 @@ type ServiceContext struct {
|
||||
GeoRegionData *geo_json.RegionData
|
||||
NSQProducer *nsq.Producer
|
||||
ZincClient *zincx.ZincClient
|
||||
XCipher *xcipher.XCipher
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
@@ -61,6 +64,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
SecurityHeadersMiddleware: middleware.NewSecurityHeadersMiddleware().Handle,
|
||||
CasbinVerifyMiddleware: middleware.NewCasbinVerifyMiddleware(casbinEnforcer).Handle,
|
||||
NonceMiddleware: middleware.NewNonceMiddleware(redisClient).Handle,
|
||||
AuthMiddleware: middleware.NewAuthMiddleware(redisClient).Handle,
|
||||
DB: queryDB,
|
||||
RedisClient: redisClient,
|
||||
Ip2Region: ip2region.NewIP2Region(),
|
||||
@@ -75,6 +79,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
GeoRegionData: geo_json.NewGeoJSON(),
|
||||
NSQProducer: nsqx.NewNsqProducer(c.NSQ.NSQDHost),
|
||||
ZincClient: zincx.NewZincClient(c.Zinc.URL, c.Zinc.Username, c.Zinc.Password),
|
||||
XCipher: xcipher.NewXCipher([]byte(c.Encrypt.Key)),
|
||||
}
|
||||
return serviceContext
|
||||
}
|
||||
|
@@ -34,16 +34,18 @@ type UploadSetting struct {
|
||||
TargetDetection bool `json:"target_detection"`
|
||||
QrcodeDetection bool `json:"qrcode_detection"`
|
||||
FaceDetection bool `json:"face_detection"`
|
||||
Encrypt bool `json:"encrypt"`
|
||||
}
|
||||
|
||||
type FileUploadMessage struct {
|
||||
FaceID int64 `json:"face_id"`
|
||||
FileName string `json:"file_name"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
Result File `json:"result"`
|
||||
UID string `json:"uid"`
|
||||
FilePath string `json:"file_path"`
|
||||
ThumbPath string `json:"thumb_path"`
|
||||
FaceID int64 `json:"face_id"`
|
||||
FileName string `json:"file_name"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
Result File `json:"result"`
|
||||
UID string `json:"uid"`
|
||||
FilePath string `json:"file_path"`
|
||||
ThumbPath string `json:"thumb_path"`
|
||||
Setting UploadSetting `json:"setting"`
|
||||
}
|
||||
|
||||
type FileInfoResult struct {
|
||||
|
@@ -166,6 +166,17 @@ type CommentResponse struct {
|
||||
ReplyTo int64 `json:"reply_to,optional"`
|
||||
}
|
||||
|
||||
type CoordinateListResponse struct {
|
||||
Records []CoordinateMeta `json:"records"`
|
||||
}
|
||||
|
||||
type CoordinateMeta struct {
|
||||
ID int64 `json:"id"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
ImageCount int64 `json:"image_count"`
|
||||
}
|
||||
|
||||
type DeleteImageRequest struct {
|
||||
IDS []int64 `json:"ids"`
|
||||
Provider string `json:"provider"`
|
||||
@@ -310,6 +321,12 @@ type PhoneLoginRequest struct {
|
||||
AutoLogin bool `json:"auto_login"`
|
||||
}
|
||||
|
||||
type PrivateImageListRequest struct {
|
||||
Provider string `json:"provider"`
|
||||
Bucket string `json:"bucket"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type QueryDeleteRecordRequest struct {
|
||||
Provider string `json:"provider"`
|
||||
Bucket string `json:"bucket"`
|
||||
|
@@ -29,6 +29,7 @@ type ScaStorageInfo struct {
|
||||
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
|
||||
LocationID int64 `gorm:"column:location_id;type:bigint(20);comment:地址ID" json:"location_id"` // 地址ID
|
||||
IsEncrypted int64 `gorm:"column:is_encrypted;type:tinyint(4);comment:是否加密(0 否 1 是)" json:"is_encrypted"` // 是否加密(0 否 1 是)
|
||||
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"` // 删除时间
|
||||
|
@@ -42,6 +42,7 @@ func newScaStorageInfo(db *gorm.DB, opts ...gen.DOOption) scaStorageInfo {
|
||||
_scaStorageInfo.IsDisplayed = field.NewInt64(tableName, "is_displayed")
|
||||
_scaStorageInfo.AlbumID = field.NewInt64(tableName, "album_id")
|
||||
_scaStorageInfo.LocationID = field.NewInt64(tableName, "location_id")
|
||||
_scaStorageInfo.IsEncrypted = field.NewInt64(tableName, "is_encrypted")
|
||||
_scaStorageInfo.CreatedAt = field.NewTime(tableName, "created_at")
|
||||
_scaStorageInfo.UpdatedAt = field.NewTime(tableName, "updated_at")
|
||||
_scaStorageInfo.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
@@ -71,6 +72,7 @@ type scaStorageInfo struct {
|
||||
IsDisplayed field.Int64 // 是否隐藏(0 不隐藏 1 隐藏)
|
||||
AlbumID field.Int64 // 相册ID
|
||||
LocationID field.Int64 // 地址ID
|
||||
IsEncrypted field.Int64 // 是否加密(0 否 1 是)
|
||||
CreatedAt field.Time // 创建时间
|
||||
UpdatedAt field.Time // 更新时间
|
||||
DeletedAt field.Field // 删除时间
|
||||
@@ -105,6 +107,7 @@ func (s *scaStorageInfo) updateTableName(table string) *scaStorageInfo {
|
||||
s.IsDisplayed = field.NewInt64(table, "is_displayed")
|
||||
s.AlbumID = field.NewInt64(table, "album_id")
|
||||
s.LocationID = field.NewInt64(table, "location_id")
|
||||
s.IsEncrypted = field.NewInt64(table, "is_encrypted")
|
||||
s.CreatedAt = field.NewTime(table, "created_at")
|
||||
s.UpdatedAt = field.NewTime(table, "updated_at")
|
||||
s.DeletedAt = field.NewField(table, "deleted_at")
|
||||
@@ -124,7 +127,7 @@ func (s *scaStorageInfo) GetFieldByName(fieldName string) (field.OrderExpr, bool
|
||||
}
|
||||
|
||||
func (s *scaStorageInfo) fillFieldMap() {
|
||||
s.fieldMap = make(map[string]field.Expr, 18)
|
||||
s.fieldMap = make(map[string]field.Expr, 19)
|
||||
s.fieldMap["id"] = s.ID
|
||||
s.fieldMap["user_id"] = s.UserID
|
||||
s.fieldMap["provider"] = s.Provider
|
||||
@@ -140,6 +143,7 @@ func (s *scaStorageInfo) fillFieldMap() {
|
||||
s.fieldMap["is_displayed"] = s.IsDisplayed
|
||||
s.fieldMap["album_id"] = s.AlbumID
|
||||
s.fieldMap["location_id"] = s.LocationID
|
||||
s.fieldMap["is_encrypted"] = s.IsEncrypted
|
||||
s.fieldMap["created_at"] = s.CreatedAt
|
||||
s.fieldMap["updated_at"] = s.UpdatedAt
|
||||
s.fieldMap["deleted_at"] = s.DeletedAt
|
||||
|
Reference in New Issue
Block a user