optimized image list interface

This commit is contained in:
2025-02-17 11:21:38 +08:00
parent ab4e9c4d59
commit b196e50aee
72 changed files with 1676 additions and 343 deletions

View File

@@ -520,6 +520,7 @@ type (
Width float64 `json:"width"`
Height float64 `json:"height"`
CreatedAt string `json:"created_at"`
Thumbnail string `json:"thumbnail"`
}
AllImageDetail {
Date string `json:"date"`
@@ -588,6 +589,10 @@ type (
ThingDetailListResponse {
records []AllImageDetail `json:"records"`
}
// 单张图片请求参数
SingleImageRequest {
ID int64 `json:"id"`
}
)
// 文件上传
@@ -670,5 +675,9 @@ service auth {
// 获取事物详情列表
@handler queryThingDetailList
post /image/thing/detail/list (ThingDetailListRequest) returns (ThingDetailListResponse)
// 获取单张图片连接
@handler getImageUrl
post /image/url/single (SingleImageRequest) returns (string)
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
"schisandra-album-cloud-microservices/app/auth/api/internal/mq"
"schisandra-album-cloud-microservices/common/idgenerator"
"schisandra-album-cloud-microservices/common/middleware"
@@ -31,6 +32,8 @@ func main() {
server.Use(middleware.I18nMiddleware)
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
// start image process consumer
go mq.NewImageProcessConsumer(ctx)
// initialize id generator
idgenerator.NewIDGenerator(0)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)

View File

@@ -171,4 +171,9 @@ Minio:
# Minio 访问密钥
SecretAccessKey: XEHkwExqQdAlEPfpRk36xpc0Sie8hZkcmlhXQJXw
# Minio 使用SSL
UseSSL: false
UseSSL: false
#NSQ配置
NSQ:
# NSQD地址
NSQDHost: 1.95.0.111:4150
LookUpdHost: 1.95.0.111:4161

View File

@@ -73,4 +73,8 @@ type Config struct {
SecretAccessKey string
UseSSL bool
}
NSQ struct {
NSQDHost string
LookUpdHost string
}
}

View File

@@ -268,6 +268,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/image/thing/list",
Handler: storage.QueryThingImageListHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/image/url/single",
Handler: storage.GetImageUrlHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/uploads",

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

View File

@@ -7,6 +7,7 @@ import (
"fmt"
"github.com/redis/go-redis/v9"
"math/rand"
"net/url"
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
"schisandra-album-cloud-microservices/app/auth/model/mysql/query"
"schisandra-album-cloud-microservices/common/constant"
@@ -56,18 +57,28 @@ func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest)
return nil, errors.New("get cached image list failed")
}
// 缓存未命中,从数据库中查询
storageInfo := l.svcCtx.DB.ScaStorageInfo
storageThumb := l.svcCtx.DB.ScaStorageThumb
// 数据库查询文件信息列表
var storageInfoQuery query.IScaStorageInfoDo
var storageInfoList []types.FileInfoResult
storageInfoQuery = storageInfo.Where(
storageInfo.UserID.Eq(uid),
storageInfo.Provider.Eq(req.Provider),
storageInfo.Bucket.Eq(req.Bucket),
storageInfo.AlbumID.Eq(req.ID)).
storageInfoQuery = storageInfo.Select(
storageInfo.ID,
storageInfo.FileName,
storageInfo.CreatedAt,
storageThumb.ThumbPath,
storageThumb.ThumbW,
storageThumb.ThumbH,
storageThumb.ThumbSize).
LeftJoin(storageThumb, storageInfo.ThumbID.EqCol(storageThumb.ID)).
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()
err = storageInfoQuery.Scan(&storageInfoList)
if err != nil {
return nil, err
}
@@ -75,17 +86,17 @@ func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest)
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")
}
//// 加载用户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
@@ -93,11 +104,12 @@ func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest)
for _, dbFileInfo := range storageInfoList {
wg.Add(1)
go func(dbFileInfo *model.ScaStorageInfo) {
go func(dbFileInfo *types.FileInfoResult) {
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)
reqParams := make(url.Values)
presignedUrl, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, dbFileInfo.ThumbPath, time.Hour*24*7, reqParams)
if err != nil {
logx.Error(err)
return
@@ -109,15 +121,15 @@ func (l *GetAlbumDetailLogic) GetAlbumDetail(req *types.AlbumDetailListRequest)
images = append(images, types.ImageMeta{
ID: dbFileInfo.ID,
FileName: dbFileInfo.FileName,
URL: url,
Width: dbFileInfo.Width,
Height: dbFileInfo.Height,
URL: presignedUrl.String(),
Width: dbFileInfo.ThumbW,
Height: dbFileInfo.ThumbH,
CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"),
})
// 重新存储更新后的图像列表
groupedImages.Store(date, images)
}(dbFileInfo)
}(&dbFileInfo)
}
wg.Wait()
var imageList []types.AllImageDetail

View File

@@ -7,8 +7,8 @@ import (
"fmt"
"github.com/redis/go-redis/v9"
"math/rand"
"net/url"
"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"
@@ -58,16 +58,24 @@ func (l *GetFaceDetailListLogic) GetFaceDetailList(req *types.FaceDetailListRequ
// 缓存未命中,从数据库中查询
storageInfo := l.svcCtx.DB.ScaStorageInfo
// 数据库查询文件信息列表
var storageInfoQuery query.IScaStorageInfoDo
storageThumb := l.svcCtx.DB.ScaStorageThumb
var storageInfoList []types.FileInfoResult
storageInfoQuery = storageInfo.Where(
err = storageInfo.Select(
storageInfo.ID,
storageInfo.FileName,
storageInfo.CreatedAt,
storageInfo.Path,
storageThumb.ThumbPath,
storageThumb.ThumbW,
storageThumb.ThumbH,
storageThumb.ThumbSize).
LeftJoin(storageThumb, storageInfo.ThumbID.EqCol(storageThumb.ID)).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()
Order(storageInfo.CreatedAt.Desc()).Scan(&storageInfoList)
if err != nil {
return nil, err
}
@@ -93,15 +101,22 @@ func (l *GetFaceDetailListLogic) GetFaceDetailList(req *types.FaceDetailListRequ
for _, dbFileInfo := range storageInfoList {
wg.Add(1)
go func(dbFileInfo *model.ScaStorageInfo) {
go func(dbFileInfo *types.FileInfoResult) {
defer wg.Done()
weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()]
date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday)
reqParams := make(url.Values)
presignedUrl, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, dbFileInfo.ThumbPath, time.Hour*24*7, reqParams)
if err != nil {
logx.Error(err)
return
}
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)
@@ -109,15 +124,16 @@ func (l *GetFaceDetailListLogic) GetFaceDetailList(req *types.FaceDetailListRequ
images = append(images, types.ImageMeta{
ID: dbFileInfo.ID,
FileName: dbFileInfo.FileName,
Thumbnail: presignedUrl.String(),
URL: url,
Width: dbFileInfo.Width,
Height: dbFileInfo.Height,
Width: dbFileInfo.ThumbW,
Height: dbFileInfo.ThumbH,
CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"),
})
// 重新存储更新后的图像列表
groupedImages.Store(date, images)
}(dbFileInfo)
}(&dbFileInfo)
}
wg.Wait()
var imageList []types.AllImageDetail

View File

@@ -0,0 +1,148 @@
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"
"strconv"
"time"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetImageUrlLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetImageUrlLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetImageUrlLogic {
return &GetImageUrlLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetImageUrlLogic) GetImageUrl(req *types.SingleImageRequest) (resp string, err error) {
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return "", errors.New("user_id not found")
}
// 从redis 获取url
cacheKey := constant.ImageSinglePrefix + uid + ":" + strconv.FormatInt(req.ID, 10)
cacheUrl, err := l.svcCtx.RedisClient.Get(l.ctx, cacheKey).Result()
if err != nil && !errors.Is(err, redis.Nil) {
logx.Info(err)
return "", errors.New("get image url failed")
}
if cacheUrl != "" {
return cacheUrl, nil
}
storageInfo := l.svcCtx.DB.ScaStorageInfo
result, err := storageInfo.Select(
storageInfo.ID,
storageInfo.Provider,
storageInfo.Bucket,
storageInfo.Path).
Where(storageInfo.ID.Eq(req.ID), storageInfo.UserID.Eq(uid)).
First()
if err != nil {
logx.Info(err)
return "", errors.New("get storage info failed")
}
if result == nil {
return "", errors.New("get storage info failed")
}
// 加载用户oss配置信息
cacheOssConfigKey := constant.UserOssConfigPrefix + uid + ":" + result.Provider
ossConfig, err := l.getOssConfigFromCacheOrDb(cacheOssConfigKey, uid, result.Provider)
if err != nil {
return "", err
}
service, err := l.svcCtx.StorageManager.GetStorage(uid, ossConfig)
if err != nil {
return "", errors.New("get storage failed")
}
url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, result.Path, 7*24*time.Hour)
if err != nil {
return "", errors.New("get presigned url failed")
}
// 缓存url
err = l.svcCtx.RedisClient.Set(l.ctx, cacheKey, url, 7*24*time.Hour).Err()
if err != nil {
logx.Info(err)
}
return url, nil
}
// 提取解密操作为函数
func (l *GetImageUrlLogic) 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 *GetImageUrlLogic) 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

@@ -8,10 +8,10 @@ import (
"github.com/redis/go-redis/v9"
"github.com/zeromicro/go-zero/core/logx"
"math/rand"
"net/url"
"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/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"
@@ -66,25 +66,44 @@ func (l *QueryAllImageListLogic) QueryAllImageList(req *types.AllImageListReques
// 缓存未命中,从数据库中查询
storageInfo := l.svcCtx.DB.ScaStorageInfo
// 数据库查询文件信息列表
var storageInfoQuery query.IScaStorageInfoDo
storageThumb := l.svcCtx.DB.ScaStorageThumb
var storageInfoList []types.FileInfoResult
if req.Sort {
storageInfoQuery = storageInfo.Where(
storageInfo.UserID.Eq(uid),
storageInfo.Provider.Eq(req.Provider),
storageInfo.Bucket.Eq(req.Bucket),
storageInfo.Type.Eq(req.Type),
storageInfo.AlbumID.IsNull()).
Order(storageInfo.CreatedAt.Desc())
err = storageInfo.Select(
storageInfo.ID,
storageInfo.FileName,
storageInfo.CreatedAt,
storageInfo.Path,
storageThumb.ThumbPath,
storageThumb.ThumbW,
storageThumb.ThumbH,
storageThumb.ThumbSize).
LeftJoin(storageThumb, storageInfo.ThumbID.EqCol(storageThumb.ID)).
Where(
storageInfo.UserID.Eq(uid),
storageInfo.Provider.Eq(req.Provider),
storageInfo.Bucket.Eq(req.Bucket),
storageInfo.Type.Eq(req.Type),
storageInfo.AlbumID.IsNull()).
Order(storageInfo.CreatedAt.Desc()).Scan(&storageInfoList)
} else {
storageInfoQuery = storageInfo.Where(
storageInfo.UserID.Eq(uid),
storageInfo.Provider.Eq(req.Provider),
storageInfo.Bucket.Eq(req.Bucket),
storageInfo.Type.Eq(req.Type)).
Order(storageInfo.CreatedAt.Desc())
err = storageInfo.Select(
storageInfo.ID,
storageInfo.FileName,
storageInfo.CreatedAt,
storageThumb.ThumbPath,
storageInfo.Path,
storageThumb.ThumbW,
storageThumb.ThumbH,
storageThumb.ThumbSize).
LeftJoin(storageThumb, storageInfo.ThumbID.EqCol(storageThumb.ID)).
Where(
storageInfo.UserID.Eq(uid),
storageInfo.Provider.Eq(req.Provider),
storageInfo.Bucket.Eq(req.Bucket),
storageInfo.Type.Eq(req.Type)).
Order(storageInfo.CreatedAt.Desc()).Scan(&storageInfoList)
}
storageInfoList, err := storageInfoQuery.Find()
if err != nil {
return nil, err
}
@@ -110,10 +129,16 @@ func (l *QueryAllImageListLogic) QueryAllImageList(req *types.AllImageListReques
for _, dbFileInfo := range storageInfoList {
wg.Add(1)
go func(dbFileInfo *model.ScaStorageInfo) {
go func(dbFileInfo *types.FileInfoResult) {
defer wg.Done()
weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()]
date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday)
reqParams := make(url.Values)
presignedUrl, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, dbFileInfo.ThumbPath, time.Hour*24*7, reqParams)
if err != nil {
logx.Error(err)
return
}
url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, dbFileInfo.Path, time.Hour*24*7)
if err != nil {
logx.Error(err)
@@ -126,15 +151,16 @@ func (l *QueryAllImageListLogic) QueryAllImageList(req *types.AllImageListReques
images = append(images, types.ImageMeta{
ID: dbFileInfo.ID,
FileName: dbFileInfo.FileName,
Thumbnail: presignedUrl.String(),
URL: url,
Width: dbFileInfo.Width,
Height: dbFileInfo.Height,
Width: dbFileInfo.ThumbW,
Height: dbFileInfo.ThumbH,
CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"),
})
// 重新存储更新后的图像列表
groupedImages.Store(date, images)
}(dbFileInfo)
}(&dbFileInfo)
}
wg.Wait()
var imageList []types.AllImageDetail

View File

@@ -7,6 +7,7 @@ import (
"fmt"
"github.com/redis/go-redis/v9"
"math/rand"
"net/url"
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
"schisandra-album-cloud-microservices/app/auth/model/mysql/query"
"schisandra-album-cloud-microservices/common/constant"
@@ -56,18 +57,28 @@ func (l *QueryLocationDetailListLogic) QueryLocationDetailList(req *types.Locati
return nil, errors.New("get cached image list failed")
}
// 缓存未命中,从数据库中查询
storageInfo := l.svcCtx.DB.ScaStorageInfo
storageThumb := l.svcCtx.DB.ScaStorageThumb
// 数据库查询文件信息列表
var storageInfoQuery query.IScaStorageInfoDo
var storageInfoList []types.FileInfoResult
storageInfoQuery = storageInfo.Where(
storageInfoQuery = storageInfo.Select(
storageInfo.ID,
storageInfo.FileName,
storageInfo.CreatedAt,
storageThumb.ThumbPath,
storageInfo.Path,
storageThumb.ThumbW,
storageThumb.ThumbH,
storageThumb.ThumbSize).
LeftJoin(storageThumb, storageInfo.ThumbID.EqCol(storageThumb.ID)).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()
err = storageInfoQuery.Scan(&storageInfoList)
if err != nil {
return nil, err
}
@@ -93,10 +104,16 @@ func (l *QueryLocationDetailListLogic) QueryLocationDetailList(req *types.Locati
for _, dbFileInfo := range storageInfoList {
wg.Add(1)
go func(dbFileInfo *model.ScaStorageInfo) {
go func(dbFileInfo *types.FileInfoResult) {
defer wg.Done()
weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()]
date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday)
reqParams := make(url.Values)
presignedUrl, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, dbFileInfo.ThumbPath, time.Hour*24*7, reqParams)
if err != nil {
logx.Error(err)
return
}
url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, dbFileInfo.Path, time.Hour*24*7)
if err != nil {
logx.Error(err)
@@ -109,15 +126,16 @@ func (l *QueryLocationDetailListLogic) QueryLocationDetailList(req *types.Locati
images = append(images, types.ImageMeta{
ID: dbFileInfo.ID,
FileName: dbFileInfo.FileName,
Thumbnail: presignedUrl.String(),
URL: url,
Width: dbFileInfo.Width,
Height: dbFileInfo.Height,
Width: dbFileInfo.ThumbW,
Height: dbFileInfo.ThumbH,
CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"),
})
// 重新存储更新后的图像列表
groupedImages.Store(date, images)
}(dbFileInfo)
}(&dbFileInfo)
}
wg.Wait()
var imageList []types.AllImageDetail

View File

@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"github.com/redis/go-redis/v9"
"net/url"
"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"
@@ -53,16 +54,16 @@ func (l *QueryLocationImageListLogic) QueryLocationImageList(req *types.Location
}
// 加载用户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")
}
//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 {
@@ -77,7 +78,8 @@ func (l *QueryLocationImageListLogic) QueryLocationImageList(req *types.Location
if city == "" {
city = loc.Country
}
url, err := service.PresignedURL(l.ctx, req.Bucket, loc.CoverImage, 7*24*time.Hour)
reqParams := make(url.Values)
presignedUrl, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, loc.CoverImage, 7*24*time.Hour, reqParams)
if err != nil {
return nil, errors.New("get presigned url failed")
}
@@ -85,7 +87,7 @@ func (l *QueryLocationImageListLogic) QueryLocationImageList(req *types.Location
ID: loc.ID,
City: city,
Total: loc.Total,
CoverImage: url,
CoverImage: presignedUrl.String(),
}
locationMap[locationKey] = append(locationMap[locationKey], locationMeta)
}

View File

@@ -62,37 +62,34 @@ func (l *QueryRecentImageListLogic) QueryRecentImageList() (resp *types.RecentLi
if cmd == nil {
continue
}
val, ok := cmd.(string)
if !ok {
logx.Error("invalid value type")
return nil, errors.New("invalid value type")
}
var imageMeta types.ImageMeta
err = json.Unmarshal([]byte(val), &imageMeta)
if err != nil {
logx.Error(err)
return nil, errors.New("unmarshal recent file list failed")
}
parse, err := time.Parse("2006-01-02 15:04:05", imageMeta.CreatedAt)
if err != nil {
logx.Error(err)
return nil, errors.New("parse recent file list failed")
}
date := parse.Format("2006年1月2日 星期" + WeekdayMap[parse.Weekday()])
// 使用LoadOrStore来检查并存储或者追加
wg.Add(1)
go func(cmd interface{}) {
go func(date string, imageMeta types.ImageMeta) {
defer wg.Done()
val, ok := cmd.(string)
if !ok {
logx.Error("invalid value type")
return
value, loaded := groupedImages.LoadOrStore(date, []types.ImageMeta{imageMeta})
if loaded {
images := value.([]types.ImageMeta)
images = append(images, imageMeta)
groupedImages.Store(date, images)
}
var imageMeta types.ImageMeta
err = json.Unmarshal([]byte(val), &imageMeta)
if err != nil {
logx.Error(err)
return
}
parse, err := time.Parse("2006-01-02 15:04:05", imageMeta.CreatedAt)
if err != nil {
logx.Error(err)
return
}
date := parse.Format("2006年1月2日 星期" + WeekdayMap[parse.Weekday()])
groupedImages.Range(func(key, value interface{}) bool {
if key == date {
images := value.([]types.ImageMeta)
images = append(images, imageMeta)
groupedImages.Store(date, images)
return false
}
return true
})
groupedImages.Store(date, []types.ImageMeta{imageMeta})
}(cmd)
}(date, imageMeta)
}
wg.Wait()

View File

@@ -7,6 +7,7 @@ import (
"fmt"
"github.com/redis/go-redis/v9"
"math/rand"
"net/url"
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
"schisandra-album-cloud-microservices/app/auth/model/mysql/query"
"schisandra-album-cloud-microservices/common/constant"
@@ -56,18 +57,29 @@ func (l *QueryThingDetailListLogic) QueryThingDetailList(req *types.ThingDetailL
return nil, errors.New("get cached image list failed")
}
// 缓存未命中,从数据库中查询
storageInfo := l.svcCtx.DB.ScaStorageInfo
storageThumb := l.svcCtx.DB.ScaStorageThumb
// 数据库查询文件信息列表
var storageInfoQuery query.IScaStorageInfoDo
var storageInfoList []types.FileInfoResult
storageInfoQuery = storageInfo.Where(
storageInfo.UserID.Eq(uid),
storageInfo.Provider.Eq(req.Provider),
storageInfo.Bucket.Eq(req.Bucket),
storageInfo.Tag.Eq(req.TagName)).
storageInfoQuery = storageInfo.Select(
storageInfo.ID,
storageInfo.FileName,
storageInfo.CreatedAt,
storageThumb.ThumbPath,
storageInfo.Path,
storageThumb.ThumbW,
storageThumb.ThumbH,
storageThumb.ThumbSize).
LeftJoin(storageThumb, storageInfo.ThumbID.EqCol(storageThumb.ID)).
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()
err = storageInfoQuery.Scan(&storageInfoList)
if err != nil {
return nil, err
}
@@ -93,10 +105,16 @@ func (l *QueryThingDetailListLogic) QueryThingDetailList(req *types.ThingDetailL
for _, dbFileInfo := range storageInfoList {
wg.Add(1)
go func(dbFileInfo *model.ScaStorageInfo) {
go func(dbFileInfo *types.FileInfoResult) {
defer wg.Done()
weekday := WeekdayMap[dbFileInfo.CreatedAt.Weekday()]
date := dbFileInfo.CreatedAt.Format("2006年1月2日 星期" + weekday)
reqParams := make(url.Values)
presignedUrl, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, dbFileInfo.ThumbPath, time.Hour*24*7, reqParams)
if err != nil {
logx.Error(err)
return
}
url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, dbFileInfo.Path, time.Hour*24*7)
if err != nil {
logx.Error(err)
@@ -109,15 +127,16 @@ func (l *QueryThingDetailListLogic) QueryThingDetailList(req *types.ThingDetailL
images = append(images, types.ImageMeta{
ID: dbFileInfo.ID,
FileName: dbFileInfo.FileName,
Thumbnail: presignedUrl.String(),
URL: url,
Width: dbFileInfo.Width,
Height: dbFileInfo.Height,
Width: dbFileInfo.ThumbW,
Height: dbFileInfo.ThumbH,
CreatedAt: dbFileInfo.CreatedAt.Format("2006-01-02 15:04:05"),
})
// 重新存储更新后的图像列表
groupedImages.Store(date, images)
}(dbFileInfo)
}(&dbFileInfo)
}
wg.Wait()
var imageList []types.AllImageDetail

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"github.com/redis/go-redis/v9"
"net/url"
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
"schisandra-album-cloud-microservices/common/constant"
"schisandra-album-cloud-microservices/common/encrypt"
@@ -38,12 +39,15 @@ func (l *QueryThingImageListLogic) QueryThingImageList(req *types.ThingListReque
return nil, errors.New("user_id not found")
}
storageInfo := l.svcCtx.DB.ScaStorageInfo
storageInfos, err := storageInfo.Select(
storageThumb := l.svcCtx.DB.ScaStorageThumb
var thingList []types.ThingImageList
err = storageInfo.Select(
storageInfo.ID,
storageInfo.Category,
storageInfo.Tag,
storageInfo.Path,
storageThumb.ThumbPath,
storageInfo.CreatedAt).
LeftJoin(storageThumb, storageInfo.ThumbID.EqCol(storageThumb.ID)).
Where(storageInfo.UserID.Eq(uid),
storageInfo.Provider.Eq(req.Provider),
storageInfo.Bucket.Eq(req.Bucket),
@@ -52,28 +56,28 @@ func (l *QueryThingImageListLogic) QueryThingImageList(req *types.ThingListReque
storageInfo.Category.Length().Gt(0),
storageInfo.Tag.Length().Gte(0)).
Order(storageInfo.CreatedAt.Desc()).
Find()
Scan(&thingList)
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")
}
//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 {
for _, info := range thingList {
tagKey := info.Category + "::" + info.Tag
if _, exists := tagCountMap.Load(tagKey); !exists {
tagCountMap.Store(tagKey, int64(0))
@@ -90,14 +94,15 @@ func (l *QueryThingImageListLogic) QueryThingImageList(req *types.ThingListReque
// 为每个 Tag 存储封面图片路径
if _, exists := tagCoverMap.Load(tagKey); !exists {
// 使用服务生成预签名 URL
coverImageURL, err := service.PresignedURL(l.ctx, req.Bucket, info.Path, 7*24*time.Hour)
reqParams := make(url.Values)
presignedUrl, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, info.ThumbPath, time.Hour*24*7, reqParams)
if err == nil {
tagCoverMap.Store(tagKey, coverImageURL)
tagCoverMap.Store(tagKey, presignedUrl.String())
}
}
}
var thingListData []types.ThingListData
var thingListResponse []types.ThingListData
categoryMap.Range(func(category, tagData interface{}) bool {
var metas []types.ThingMeta
tagData.(*sync.Map).Range(func(tag, item interface{}) bool {
@@ -113,14 +118,14 @@ func (l *QueryThingImageListLogic) QueryThingImageList(req *types.ThingListReque
metas = append(metas, meta)
return true
})
thingListData = append(thingListData, types.ThingListData{
thingListResponse = append(thingListResponse, types.ThingListData{
Category: category.(string),
List: metas,
})
return true
})
return &types.ThingListResponse{Records: thingListData}, nil
return &types.ThingListResponse{Records: thingListResponse}, nil
}
// 提取解密操作为函数

View File

@@ -1,17 +1,23 @@
package storage
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/ccpwcn/kgo"
"github.com/minio/minio-go/v7"
"github.com/redis/go-redis/v9"
"github.com/zeromicro/go-zero/core/logx"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/semaphore"
"gorm.io/gorm"
"io"
"mime/multipart"
"net/http"
"net/url"
"path"
"path/filepath"
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
@@ -56,72 +62,159 @@ func (l *UploadFileLogic) UploadFile(r *http.Request) (resp string, err error) {
if err != nil {
return "", err
}
defer file.Close()
defer func(file multipart.File) {
_ = file.Close()
}(file)
// 解析 AI 识别结果
result, err := l.parseAIRecognitionResult(r)
if err != nil {
return "", err
}
var faceId int64 = 0
bytes, err := io.ReadAll(file)
data, err := io.ReadAll(file)
if err != nil {
return "", err
}
// 人脸识别
if result.FileType == "image/png" || result.FileType == "image/jpeg" {
face, err := l.svcCtx.AiSvcRpc.FaceRecognition(l.ctx, &pb.FaceRecognitionRequest{Face: bytes, UserId: uid})
if err != nil {
return "", err
// 解析上传的缩略图
thumbnail, _, err := l.getUploadedThumbnail(r)
if err != nil {
return "", err
}
defer thumbnail.Close()
// 解析图片信息识别结果
result, err := l.parseImageInfoResult(r)
if err != nil {
return "", err
}
// 使用 `errgroup.Group` 处理并发任务
var (
faceId int64
filePath string
minioFilePath string
presignedURL string
)
g, ctx := errgroup.WithContext(context.Background())
// 创建信号量,限制最大并发上传数(比如最多同时 5 个任务)
sem := semaphore.NewWeighted(5)
// 进行人脸识别
g.Go(func() error {
if result.FileType == "image/png" || result.FileType == "image/jpeg" {
face, err := l.svcCtx.AiSvcRpc.FaceRecognition(l.ctx, &pb.FaceRecognitionRequest{
Face: data,
UserId: uid,
})
if err != nil {
return err
}
if face != nil {
faceId = face.GetFaceId()
}
}
if face != nil {
faceId = face.GetFaceId()
}
}
// 根据 GPS 信息获取地理位置信息
country, province, city, err := l.getGeoLocation(result.Latitude, result.Longitude)
if err != nil {
return "", err
}
return nil
})
// 上传文件到 OSS
// 重新设置文件指针到文件开头
if _, err = file.Seek(0, 0); err != nil {
return "", err
}
bucket, provider, filePath, url, err := l.uploadFileToOSS(uid, header, file, result)
if err != nil {
g.Go(func() error {
if err := sem.Acquire(ctx, 1); err != nil {
return err
}
defer sem.Release(1)
// 重新创建 `multipart.File` 兼容的 `Reader`
fileReader := struct {
*bytes.Reader
io.Closer
}{
Reader: bytes.NewReader(data),
Closer: io.NopCloser(nil),
}
fileUrl, err := l.uploadFileToOSS(uid, header, fileReader, result)
if err != nil {
return err
}
filePath = fileUrl
return nil
})
// 上传缩略图到 MinIO
g.Go(func() error {
if err := sem.Acquire(ctx, 1); err != nil {
return err
}
defer sem.Release(1)
path, url, err := l.uploadFileToMinio(uid, header, thumbnail, result)
if err != nil {
return err
}
minioFilePath = path
presignedURL = url
return nil
})
// 等待所有 goroutine 执行完毕
if err = g.Wait(); err != nil {
return "", err
}
// 将地址信息保存到数据库
locationId, err := l.saveFileLocationInfoToDB(uid, result.Provider, result.Bucket, result.Latitude, result.Longitude, country, province, city, filePath)
fileUploadMessage := &types.FileUploadMessage{
UID: uid,
Data: result,
FaceID: faceId,
FileHeader: header,
FilePath: filePath,
PresignedURL: presignedURL,
ThumbPath: minioFilePath,
}
// 转换为 JSON
messageData, err := json.Marshal(fileUploadMessage)
if err != nil {
return "", err
}
err = l.svcCtx.NSQProducer.Publish(constant.MQTopicImageProcess, messageData)
if err != nil {
return "", errors.New("publish message failed")
}
// 将 EXIF 和文件信息存入数据库
id, err := l.saveFileInfoToDB(uid, bucket, provider, header, result, locationId, faceId, filePath)
if err != nil {
return "", err
}
// 删除缓存
l.afterImageUpload(uid, provider, bucket)
// ------------------------------------------------------------------------
// redis 保存最近7天上传的文件列表
err = l.saveRecentFileList(uid, url, id, result, header.Filename)
if err != nil {
return "", err
}
//// 根据 GPS 信息获取地理位置信息
//country, province, city, err := l.getGeoLocation(result.Latitude, result.Longitude)
//if err != nil {
// 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, locationId, faceId, filePath)
//if err != nil {
// return "", err
//}
//// 删除缓存
//l.afterImageUpload(uid, provider, bucket)
//
//// redis 保存最近7天上传的文件列表
//err = l.saveRecentFileList(uid, url, id, result, header.Filename)
//if err != nil {
// return "", err
//}
return "success", nil
}
// 将 multipart.File 转为 Base64 字符串
func (l *UploadFileLogic) fileToBase64(file multipart.File) (string, error) {
// 读取文件内容
fileBytes, err := io.ReadAll(file)
if err != nil {
return "", err
}
// 将文件内容转为 Base64 编码
return base64.StdEncoding.EncodeToString(fileBytes), nil
}
// 获取用户 ID
func (l *UploadFileLogic) getUserID() (string, error) {
uid, ok := l.ctx.Value("user_id").(string)
@@ -150,8 +243,17 @@ func (l *UploadFileLogic) getUploadedFile(r *http.Request) (multipart.File, *mul
return file, header, nil
}
// 解析 AI 识别结果
func (l *UploadFileLogic) parseAIRecognitionResult(r *http.Request) (types.File, error) {
// 解析上传的文件
func (l *UploadFileLogic) getUploadedThumbnail(r *http.Request) (multipart.File, *multipart.FileHeader, error) {
file, header, err := r.FormFile("thumbnail")
if err != nil {
return nil, nil, errors.New("file not found")
}
return file, header, nil
}
// 解析图片信息结果
func (l *UploadFileLogic) parseImageInfoResult(r *http.Request) (types.File, error) {
formValue := r.PostFormValue("data")
var result types.File
if err := json.Unmarshal([]byte(formValue), &result); err != nil {
@@ -160,47 +262,11 @@ func (l *UploadFileLogic) parseAIRecognitionResult(r *http.Request) (types.File,
return result, nil
}
// 提取拍摄时间
func (l *UploadFileLogic) extractOriginalDateTime(exif map[string]interface{}) (string, error) {
if dateTimeOriginal, ok := exif["DateTimeOriginal"].(string); ok {
parsedTime, err := time.Parse(time.RFC3339, dateTimeOriginal)
if err == nil {
return parsedTime.Format("2006-01-02 15:04:05"), nil
}
}
return "", nil
}
// 根据 GPS 信息获取地理位置信息
func (l *UploadFileLogic) getGeoLocation(latitude, longitude float64) (string, string, string, error) {
if latitude == 0.000000 || longitude == 0.000000 {
return "", "", "", nil
}
//request := gao_map.ReGeoRequest{Location: fmt.Sprintf("%f,%f", latitude, longitude)}
//
//location, err := l.svcCtx.GaoMap.Location.ReGeo(&request)
//if err != nil {
// return nil, errors.New("regeo failed")
//}
//
//addressInfo := map[string]string{}
//if location.ReGeoCode.AddressComponent.Country != "" {
// addressInfo["country"] = location.ReGeoCode.AddressComponent.Country
//}
//if location.ReGeoCode.AddressComponent.Province != "" {
// addressInfo["province"] = location.ReGeoCode.AddressComponent.Province
//}
//if location.ReGeoCode.AddressComponent.City != "" {
// addressInfo["city"] = location.ReGeoCode.AddressComponent.City.(string)
//}
//if location.ReGeoCode.AddressComponent.District != "" {
// addressInfo["district"] = location.ReGeoCode.AddressComponent.District.(string)
//}
//if location.ReGeoCode.AddressComponent.Township != "" {
// addressInfo["township"] = location.ReGeoCode.AddressComponent.Township
//}
country, province, city, err := geo_json.GetAddress(latitude, longitude, l.svcCtx.GeoRegionData)
if err != nil {
return "", "", "", errors.New("get geo location failed")
@@ -210,20 +276,21 @@ func (l *UploadFileLogic) getGeoLocation(latitude, longitude float64) (string, s
}
// 上传文件到 OSS
func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHeader, file multipart.File, result types.File) (string, string, string, string, error) {
func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHeader, file multipart.File, result types.File) (string, error) {
cacheKey := constant.UserOssConfigPrefix + uid + ":" + result.Provider
ossConfig, err := l.getOssConfigFromCacheOrDb(cacheKey, uid, result.Provider)
if err != nil {
return "", "", "", "", errors.New("get oss config failed")
return "", errors.New("get oss config failed")
}
service, err := l.svcCtx.StorageManager.GetStorage(uid, ossConfig)
if err != nil {
return "", "", "", "", errors.New("get storage failed")
return "", errors.New("get storage failed")
}
objectKey := path.Join(
uid,
time.Now().Format("2006/01"), // 按年/月划分目录
l.classifyFile(result.FileType, result.IsScreenshot),
fmt.Sprintf("%s_%s%s", strings.TrimSuffix(header.Filename, filepath.Ext(header.Filename)), kgo.SimpleUuid(), filepath.Ext(header.Filename)),
)
@@ -231,13 +298,50 @@ func (l *UploadFileLogic) uploadFileToOSS(uid string, header *multipart.FileHead
"Content-Type": header.Header.Get("Content-Type"),
})
if err != nil {
return "", "", "", "", errors.New("upload file failed")
return "", errors.New("upload file failed")
}
url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, objectKey, time.Hour*24*7)
//url, err := service.PresignedURL(l.ctx, ossConfig.BucketName, objectKey, time.Hour*24*7)
//if err != nil {
// return "", "", errors.New("presigned url failed")
//}
return objectKey, nil
}
func (l *UploadFileLogic) uploadFileToMinio(uid string, header *multipart.FileHeader, file multipart.File, result types.File) (string, string, error) {
objectKey := path.Join(
uid,
time.Now().Format("2006/01"), // 按年/月划分目录
l.classifyFile(result.FileType, result.IsScreenshot),
fmt.Sprintf("%s_%s%s", strings.TrimSuffix(header.Filename, filepath.Ext(header.Filename)), kgo.SimpleUuid(), filepath.Ext(header.Filename)),
)
exists, err := l.svcCtx.MinioClient.BucketExists(l.ctx, constant.ThumbnailBucketName)
if err != nil || !exists {
err = l.svcCtx.MinioClient.MakeBucket(l.ctx, constant.ThumbnailBucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
if err != nil {
logx.Errorf("Failed to create MinIO bucket: %v", err)
return "", "", err
}
}
// 上传到MinIO
_, err = l.svcCtx.MinioClient.PutObject(
l.ctx,
constant.ThumbnailBucketName,
objectKey,
file,
int64(result.ThumbSize),
minio.PutObjectOptions{
ContentType: result.FileType,
},
)
if err != nil {
return "", "", "", "", errors.New("presigned url failed")
return "", "", err
}
return ossConfig.BucketName, ossConfig.Provider, objectKey, url, nil
reqParams := make(url.Values)
presignedURL, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.ThumbnailBucketName, objectKey, time.Hour*24*7, reqParams)
if err != nil {
return "", "", err
}
return objectKey, presignedURL.String(), nil
}
func (l *UploadFileLogic) saveFileLocationInfoToDB(uid string, provider string, bucket string, latitude float64, longitude float64, country string, province string, city string, filePath string) (int64, error) {

View File

@@ -0,0 +1,305 @@
package mq
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/nsqio/go-nsq"
"github.com/redis/go-redis/v9"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
"mime/multipart"
"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"
"schisandra-album-cloud-microservices/common/geo_json"
"schisandra-album-cloud-microservices/common/nsqx"
"schisandra-album-cloud-microservices/common/storage/config"
"strconv"
"time"
)
type NsqImageProcessConsumer struct {
svcCtx *svc.ServiceContext
ctx context.Context
}
func NewImageProcessConsumer(svcCtx *svc.ServiceContext) {
consumer := nsqx.NewNSQConsumer(constant.MQTopicImageProcess)
consumer.AddHandler(&NsqImageProcessConsumer{
svcCtx: svcCtx,
ctx: context.Background(),
})
err := consumer.ConnectToNSQD(svcCtx.Config.NSQ.NSQDHost)
if err != nil {
panic(err)
}
}
func (c *NsqImageProcessConsumer) HandleMessage(msg *nsq.Message) error {
if len(msg.Body) == 0 {
return errors.New("empty message body")
}
var message types.FileUploadMessage
err := json.Unmarshal(msg.Body, &message)
if err != nil {
return err
}
// 根据 GPS 信息获取地理位置信息
country, province, city, err := c.getGeoLocation(message.Data.Latitude, message.Data.Longitude)
if err != nil {
return err
}
// 将地址信息保存到数据库
locationId, err := c.saveFileLocationInfoToDB(message.UID, message.Data.Provider, message.Data.Bucket, message.Data.Latitude, message.Data.Longitude, country, province, city, message.ThumbPath)
if err != nil {
return err
}
thumbnailId, err := c.saveFileThumbnailInfoToDB(message.UID, message.ThumbPath, message.Data.ThumbW, message.Data.ThumbH, message.Data.ThumbSize)
// 将文件信息存入数据库
id, err := c.saveFileInfoToDB(message.UID, message.Data.Bucket, message.Data.Provider, message.FileHeader, message.Data, locationId, message.FaceID, message.FilePath, thumbnailId)
if err != nil {
return err
}
// 删除缓存
c.afterImageUpload(message.UID, message.Data.Provider, message.Data.Bucket)
// redis 保存最近7天上传的文件列表
err = c.saveRecentFileList(message.UID, message.PresignedURL, id, message.Data, message.FileHeader.Filename)
if err != nil {
return err
}
return nil
}
// 根据 GPS 信息获取地理位置信息
func (c *NsqImageProcessConsumer) getGeoLocation(latitude, longitude float64) (string, string, string, error) {
if latitude == 0.000000 || longitude == 0.000000 {
return "", "", "", nil
}
country, province, city, err := geo_json.GetAddress(latitude, longitude, c.svcCtx.GeoRegionData)
if err != nil {
return "", "", "", errors.New("get geo location failed")
}
return country, province, city, nil
}
// 从缓存或数据库中获取 OSS 配置
func (c *NsqImageProcessConsumer) getOssConfigFromCacheOrDb(cacheKey, uid, provider string) (*config.StorageConfig, error) {
result, err := c.svcCtx.RedisClient.Get(c.ctx, cacheKey).Result()
if err != nil && !errors.Is(err, redis.Nil) {
return nil, errors.New("get oss config failed")
}
var ossConfig *config.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 c.decryptConfig(&redisOssConfig)
}
// 缓存未命中,从数据库中加载
scaOssConfig := c.svcCtx.DB.ScaStorageConfig
dbOssConfig, err := scaOssConfig.Where(scaOssConfig.UserID.Eq(uid), scaOssConfig.Provider.Eq(provider)).First()
if err != nil {
return nil, err
}
// 缓存数据库配置
ossConfig, err = c.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 = c.svcCtx.RedisClient.Set(c.ctx, cacheKey, marshalData, 0).Err()
if err != nil {
return nil, errors.New("set oss config failed")
}
return ossConfig, nil
}
func (c *NsqImageProcessConsumer) classifyFile(mimeType string, isScreenshot bool) string {
// 使用map存储MIME类型及其对应的分类
typeMap := map[string]string{
"image/jpeg": "image",
"image/png": "image",
"image/gif": "gif",
"image/bmp": "image",
"image/tiff": "image",
"image/webp": "image",
"video/mp4": "video",
"video/avi": "video",
"video/mpeg": "video",
"video/quicktime": "video",
"video/x-msvideo": "video",
"video/x-flv": "video",
"video/x-matroska": "video",
}
// 根据MIME类型从map中获取分类
if classification, exists := typeMap[mimeType]; exists {
return classification
}
// 如果isScreenshot为true则返回"screenshot"
if isScreenshot {
return "screenshot"
}
return "unknown"
}
// 保存最近7天上传的文件列表
func (c *NsqImageProcessConsumer) saveRecentFileList(uid, url string, id int64, result types.File, filename string) error {
redisKey := constant.ImageRecentPrefix + uid + ":" + strconv.FormatInt(id, 10)
imageMeta := types.ImageMeta{
ID: id,
URL: url,
FileName: filename,
Width: result.Width,
Height: result.Height,
CreatedAt: time.Now().Format("2006-01-02 15:04:05"),
}
marshal, err := json.Marshal(imageMeta)
if err != nil {
logx.Error(err)
return errors.New("marshal image meta failed")
}
err = c.svcCtx.RedisClient.Set(c.ctx, redisKey, marshal, time.Hour*24*7).Err()
if err != nil {
logx.Error(err)
return errors.New("save recent file list failed")
}
return nil
}
// 提取解密操作为函数
func (c *NsqImageProcessConsumer) decryptConfig(dbConfig *model.ScaStorageConfig) (*config.StorageConfig, error) {
accessKey, err := encrypt.Decrypt(dbConfig.AccessKey, c.svcCtx.Config.Encrypt.Key)
if err != nil {
return nil, errors.New("decrypt access key failed")
}
secretKey, err := encrypt.Decrypt(dbConfig.SecretKey, c.svcCtx.Config.Encrypt.Key)
if err != nil {
return nil, errors.New("decrypt secret key failed")
}
return &config.StorageConfig{
Provider: dbConfig.Provider,
Endpoint: dbConfig.Endpoint,
AccessKey: accessKey,
SecretKey: secretKey,
BucketName: dbConfig.Bucket,
Region: dbConfig.Region,
}, nil
}
func (c *NsqImageProcessConsumer) 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
}
locationDB := c.svcCtx.DB.ScaStorageLocation
storageLocations, err := locationDB.Where(locationDB.UserID.Eq(uid), locationDB.Province.Eq(province), locationDB.City.Eq(city)).First()
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return 0, err
}
if storageLocations == nil {
locationInfo := model.ScaStorageLocation{
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 {
return 0, err
}
return locationInfo.ID, nil
} else {
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
}
if info.RowsAffected == 0 {
return 0, errors.New("update location failed")
}
return storageLocations.ID, nil
}
}
func (c *NsqImageProcessConsumer) saveFileThumbnailInfoToDB(uid string, filePath string, width, height float64, size float64) (int64, error) {
storageThumb := c.svcCtx.DB.ScaStorageThumb
storageThumbInfo := &model.ScaStorageThumb{
UserID: uid,
ThumbPath: filePath,
ThumbW: width,
ThumbH: height,
ThumbSize: size,
}
err := storageThumb.Create(storageThumbInfo)
if err != nil {
logx.Error(err)
return 0, errors.New("create storage thumb failed")
}
return storageThumbInfo.ID, nil
}
// 将 EXIF 和文件信息存入数据库
func (c *NsqImageProcessConsumer) saveFileInfoToDB(uid, bucket, provider string, header *multipart.FileHeader, result types.File, locationId, faceId int64, filePath string, thumbnailId int64) (int64, error) {
typeName := c.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,
Tag: result.TagName,
IsAnime: strconv.FormatBool(result.IsAnime),
Category: result.TopCategory,
LocationID: locationId,
FaceID: faceId,
Type: typeName,
Width: result.Width,
Height: result.Height,
ThumbID: thumbnailId,
}
err := c.svcCtx.DB.ScaStorageInfo.Create(scaStorageInfo)
if err != nil {
return 0, errors.New("create storage info failed")
}
return scaStorageInfo.ID, nil
}
// 在UploadImageLogic或其他需要使缓存失效的逻辑中添加
func (c *NsqImageProcessConsumer) afterImageUpload(uid, provider, bucket string) {
for _, sort := range []bool{true, false} {
key := fmt.Sprintf("%s%s:%s:%s:%v", constant.ImageListPrefix, uid, provider, bucket, sort)
if err := c.svcCtx.RedisClient.Del(c.ctx, key).Err(); err != nil {
logx.Errorf("删除缓存键 %s 失败: %v", key, err)
}
}
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/casbin/casbin/v2"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/minio/minio-go/v7"
"github.com/nsqio/go-nsq"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha/v2/rotate"
"github.com/wenlng/go-captcha/v2/slide"
@@ -21,6 +22,7 @@ import (
"schisandra-album-cloud-microservices/common/geo_json"
"schisandra-album-cloud-microservices/common/ip2region"
"schisandra-album-cloud-microservices/common/miniox"
"schisandra-album-cloud-microservices/common/nsqx"
"schisandra-album-cloud-microservices/common/redisx"
"schisandra-album-cloud-microservices/common/sensitivex"
"schisandra-album-cloud-microservices/common/storage"
@@ -45,13 +47,14 @@ type ServiceContext struct {
StorageManager *manager.Manager
MinioClient *minio.Client
GeoRegionData *geo_json.RegionData
NSQProducer *nsq.Producer
}
func NewServiceContext(c config.Config) *ServiceContext {
redisClient := redisx.NewRedis(c.Redis.Host, c.Redis.Pass, c.Redis.DB)
db, queryDB := mysql.NewMySQL(c.Mysql.DataSource, c.Mysql.MaxOpenConn, c.Mysql.MaxIdleConn, redisClient)
casbinEnforcer := casbinx.NewCasbin(db)
return &ServiceContext{
serviceContext := &ServiceContext{
Config: c,
SecurityHeadersMiddleware: middleware.NewSecurityHeadersMiddleware().Handle,
CasbinVerifyMiddleware: middleware.NewCasbinVerifyMiddleware(casbinEnforcer).Handle,
@@ -68,5 +71,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
AiSvcRpc: aiservice.NewAiService(zrpc.MustNewClient(c.AiSvcRpc)),
MinioClient: miniox.NewMinio(c.Minio.Endpoint, c.Minio.AccessKeyID, c.Minio.SecretAccessKey, c.Minio.UseSSL),
GeoRegionData: geo_json.NewGeoJSON(),
NSQProducer: nsqx.NewNsqProducer(c.NSQ.NSQDHost),
}
return serviceContext
}

View File

@@ -1,17 +0,0 @@
package types
// File represents a file uploaded by the user.
type File struct {
Provider string `json:"provider"`
Bucket string `json:"bucket"`
FileType string `json:"fileType"`
IsAnime bool `json:"isAnime"`
TagName string `json:"tagName"`
Landscape string `json:"landscape"`
TopCategory string `json:"topCategory"`
IsScreenshot bool `json:"isScreenshot"`
Width float64 `json:"width"`
Height float64 `json:"height"`
Longitude float64 `json:"longitude"`
Latitude float64 `json:"latitude"`
}

View File

@@ -0,0 +1,56 @@
package types
import (
"mime/multipart"
"time"
)
// File represents a file uploaded by the user.
type File struct {
Provider string `json:"provider"`
Bucket string `json:"bucket"`
FileType string `json:"fileType"`
IsAnime bool `json:"isAnime"`
TagName string `json:"tagName"`
Landscape string `json:"landscape"`
TopCategory string `json:"topCategory"`
IsScreenshot bool `json:"isScreenshot"`
Width float64 `json:"width"`
Height float64 `json:"height"`
Longitude float64 `json:"longitude"`
Latitude float64 `json:"latitude"`
ThumbW float64 `json:"thumb_w"`
ThumbH float64 `json:"thumb_h"`
ThumbSize float64 `json:"thumb_size"`
}
// FileUploadMessage represents a message sent to the user after a file upload.
type FileUploadMessage struct {
FaceID int64 `json:"face_id"`
FileHeader *multipart.FileHeader `json:"fileHeader"`
Data File `json:"data"`
UID string `json:"uid"`
FilePath string `json:"filePath"`
PresignedURL string `json:"presignedURL"`
ThumbPath string `json:"thumbPath"`
}
type FileInfoResult struct {
ID int64 `json:"id"`
FileName string `json:"file_name"`
ThumbPath string `json:"thumb_path"`
ThumbW float64 `json:"thumb_w"`
ThumbH float64 `json:"thumb_h"`
ThumbSize float64 `json:"thumb_size"`
CreatedAt time.Time `json:"created_at"`
Path string `json:"path"`
}
type ThingImageList struct {
ID int64 `json:"id"`
Category string `json:"category"`
Tag string `json:"tag"`
CreatedAt time.Time `json:"created_at"`
ThumbPath string `json:"thumb_path"`
Path string `json:"path"`
}

View File

@@ -178,6 +178,7 @@ type ImageMeta struct {
Width float64 `json:"width"`
Height float64 `json:"height"`
CreatedAt string `json:"created_at"`
Thumbnail string `json:"thumbnail"`
}
type LocationDetailListRequest struct {
@@ -305,6 +306,10 @@ type RotateCaptchaResponse struct {
Thumb string `json:"thumb"`
}
type SingleImageRequest struct {
ID int64 `json:"id"`
}
type SlideCaptchaResponse struct {
Key string `json:"key"`
Image string `json:"image"`

View File

@@ -117,6 +117,7 @@ func main() {
scaUserMessage := g.GenerateModel("sca_user_message", fieldOpts...)
scaStorageAlbum := g.GenerateModel("sca_storage_album", fieldOpts...)
scaStorageLocation := g.GenerateModel("sca_storage_location", fieldOpts...)
scaStorageThumb := g.GenerateModel("sca_storage_thumb", fieldOpts...)
g.ApplyBasic(
scaAuthMenu,
@@ -137,6 +138,7 @@ func main() {
scaUserMessage,
scaStorageAlbum,
scaStorageLocation,
scaStorageThumb,
)
g.Execute()

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaAuthMenu = "sca_auth_menu"
// ScaAuthMenu mapped from table <sca_auth_menu>
// ScaAuthMenu 菜单表
type ScaAuthMenu struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键ID;primary_key" json:"id,string"` // 主键ID
MenuName string `gorm:"column:menu_name;type:varchar(64);comment:名称" json:"menu_name"` // 名称

View File

@@ -6,7 +6,7 @@ package model
const TableNameScaAuthPermissionRule = "sca_auth_permission_rule"
// ScaAuthPermissionRule mapped from table <sca_auth_permission_rule>
// ScaAuthPermissionRule 接口权限表
type ScaAuthPermissionRule struct {
ID int64 `gorm:"column:id;type:int(11);primaryKey;autoIncrement:true;primary_key" json:"id,string"`
Ptype string `gorm:"column:ptype;type:varchar(100);uniqueIndex:idx_sca_auth_permission_rule,priority:1;index:IDX_sca_auth_permission_rule_ptype,priority:1" json:"ptype"`

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaAuthRole = "sca_auth_role"
// ScaAuthRole mapped from table <sca_auth_role>
// ScaAuthRole 角色表
type ScaAuthRole struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键ID;primary_key" json:"id,string"` // 主键ID
RoleName string `gorm:"column:role_name;type:varchar(32);not null;comment:角色名称" json:"role_name"` // 角色名称

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaAuthUser = "sca_auth_user"
// ScaAuthUser mapped from table <sca_auth_user>
// ScaAuthUser 用户表
type ScaAuthUser struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;uniqueIndex:id,priority:1;comment:自增ID;primary_key" json:"id,string"` // 自增ID
UID string `gorm:"column:uid;type:varchar(50);not null;uniqueIndex:uid,priority:1;comment:唯一ID" json:"uid"` // 唯一ID

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaAuthUserDevice = "sca_auth_user_device"
// ScaAuthUserDevice mapped from table <sca_auth_user_device>
// ScaAuthUserDevice 用户设备表
type ScaAuthUserDevice struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键ID;primary_key" json:"id,string"` // 主键ID
UserID string `gorm:"column:user_id;type:varchar(20);not null;comment:用户ID" json:"user_id"` // 用户ID

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaAuthUserSocial = "sca_auth_user_social"
// ScaAuthUserSocial mapped from table <sca_auth_user_social>
// ScaAuthUserSocial 第三方登录信息表
type ScaAuthUserSocial struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键ID;primary_key" json:"id,string"` // 主键ID
UserID string `gorm:"column:user_id;type:varchar(50);not null;comment:用户ID" json:"user_id"` // 用户ID

View File

@@ -10,7 +10,7 @@ import (
const TableNameScaCommentLike = "sca_comment_likes"
// ScaCommentLike mapped from table <sca_comment_likes>
// ScaCommentLike 评论点赞表
type ScaCommentLike struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键id;primary_key" json:"id,string"` // 主键id
TopicID string `gorm:"column:topic_id;type:varchar(50);not null;comment:话题ID" json:"topic_id"` // 话题ID

View File

@@ -13,7 +13,7 @@ import (
const TableNameScaCommentReply = "sca_comment_reply"
// ScaCommentReply mapped from table <sca_comment_reply>
// ScaCommentReply 评论表
type ScaCommentReply struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;uniqueIndex:id,priority:1;comment:主键id;primary_key" json:"id,string"` // 主键id
UserID string `gorm:"column:user_id;type:varchar(50);not null;comment:评论用户id" json:"user_id"` // 评论用户id

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaMessageReport = "sca_message_report"
// ScaMessageReport mapped from table <sca_message_report>
// ScaMessageReport 评论举报表
type ScaMessageReport 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(20);comment:用户Id" json:"user_id"` // 用户Id

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaStorageAlbum = "sca_storage_album"
// ScaStorageAlbum mapped from table <sca_storage_album>
// ScaStorageAlbum 相册信息表
type ScaStorageAlbum 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

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaStorageConfig = "sca_storage_config"
// ScaStorageConfig mapped from table <sca_storage_config>
// ScaStorageConfig 用户存储配置表
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

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaStorageInfo = "sca_storage_info"
// ScaStorageInfo mapped from table <sca_storage_info>
// ScaStorageInfo 文件存储信息表
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
@@ -24,6 +24,7 @@ type ScaStorageInfo struct {
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"` // 高
ThumbID int64 `gorm:"column:thumb_id;type:bigint(20);comment:缩略图id" json:"thumb_id"` // 缩略图id
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"` // 类型

View File

@@ -13,7 +13,7 @@ import (
const TableNameScaStorageLocation = "sca_storage_location"
// ScaStorageLocation mapped from table <sca_storage_location>
// ScaStorageLocation 文件地理位置信息表
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

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaStorageTag = "sca_storage_tag"
// ScaStorageTag mapped from table <sca_storage_tag>
// ScaStorageTag 标签表
type ScaStorageTag struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键;primary_key" json:"id,string"` // 主键
FileID int64 `gorm:"column:file_id;type:bigint(20);comment:文件ID" json:"file_id"` // 文件ID

View File

@@ -0,0 +1,31 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package model
import (
"time"
"gorm.io/gorm"
)
const TableNameScaStorageThumb = "sca_storage_thumb"
// ScaStorageThumb mapped from table <sca_storage_thumb>
type ScaStorageThumb 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
ThumbPath string `gorm:"column:thumb_path;type:text;comment:缩略图路径" json:"thumb_path"` // 缩略图路径
ThumbW float64 `gorm:"column:thumb_w;type:double;comment:缩略图宽" json:"thumb_w"` // 缩略图宽
ThumbH float64 `gorm:"column:thumb_h;type:double;comment:缩略图高" json:"thumb_h"` // 缩略图高
ThumbSize float64 `gorm:"column:thumb_size;type:double;comment:缩略图大小" json:"thumb_size"` // 缩略图大小
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 ScaStorageThumb's table name
func (*ScaStorageThumb) TableName() string {
return TableNameScaStorageThumb
}

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaUserFollow = "sca_user_follows"
// ScaUserFollow mapped from table <sca_user_follows>
// ScaUserFollow 用户关注表
type ScaUserFollow struct {
FollowerID string `gorm:"column:follower_id;type:varchar(50);not null;comment:关注者" json:"follower_id"` // 关注者
FolloweeID string `gorm:"column:followee_id;type:varchar(50);not null;comment:被关注者" json:"followee_id"` // 被关注者

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaUserLevel = "sca_user_level"
// ScaUserLevel mapped from table <sca_user_level>
// ScaUserLevel 用户等级表
type ScaUserLevel struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;comment:主键;primary_key" json:"id,string"` // 主键
UserID string `gorm:"column:user_id;type:varchar(50);comment:用户Id" json:"user_id"` // 用户Id

View File

@@ -12,7 +12,7 @@ import (
const TableNameScaUserMessage = "sca_user_message"
// ScaUserMessage mapped from table <sca_user_message>
// ScaUserMessage 用户消息表
type ScaUserMessage struct {
ID int64 `gorm:"column:id;type:bigint(20);primaryKey;autoIncrement:true;comment:主键;primary_key" json:"id,string"` // 主键
TopicID string `gorm:"column:topic_id;type:varchar(50);comment:话题Id" json:"topic_id"` // 话题Id

View File

@@ -23,11 +23,11 @@ func NewMySQL(url string, maxOpenConn int, maxIdleConn int, client *redis.Client
Logger: logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second, // 慢sql日志
LogLevel: logger.Error, // 级别
Colorful: true, // 颜色
IgnoreRecordNotFoundError: true, // 忽略RecordNotFoundError
ParameterizedQueries: true, // 格式化SQL语句
SlowThreshold: time.Second, // 慢sql日志
LogLevel: logger.Info, // 级别
Colorful: true, // 颜色
IgnoreRecordNotFoundError: true, // 忽略RecordNotFoundError
ParameterizedQueries: true, // 格式化SQL语句
}),
})
if err != nil {
@@ -41,7 +41,7 @@ func NewMySQL(url string, maxOpenConn int, maxIdleConn int, client *redis.Client
sqlDB.SetMaxIdleConns(maxIdleConn)
useDB := query.Use(db)
// migrate
Migrate(db)
//Migrate(db)
// cache
gormCache, err := cache.NewGorm2Cache(&config.CacheConfig{
CacheLevel: config.CacheLevelAll,

View File

@@ -32,6 +32,7 @@ var (
ScaStorageLocation *scaStorageLocation
ScaStorageTag *scaStorageTag
ScaStorageTagInfo *scaStorageTagInfo
ScaStorageThumb *scaStorageThumb
ScaUserFollow *scaUserFollow
ScaUserLevel *scaUserLevel
ScaUserMessage *scaUserMessage
@@ -54,6 +55,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
ScaStorageLocation = &Q.ScaStorageLocation
ScaStorageTag = &Q.ScaStorageTag
ScaStorageTagInfo = &Q.ScaStorageTagInfo
ScaStorageThumb = &Q.ScaStorageThumb
ScaUserFollow = &Q.ScaUserFollow
ScaUserLevel = &Q.ScaUserLevel
ScaUserMessage = &Q.ScaUserMessage
@@ -77,6 +79,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
ScaStorageLocation: newScaStorageLocation(db, opts...),
ScaStorageTag: newScaStorageTag(db, opts...),
ScaStorageTagInfo: newScaStorageTagInfo(db, opts...),
ScaStorageThumb: newScaStorageThumb(db, opts...),
ScaUserFollow: newScaUserFollow(db, opts...),
ScaUserLevel: newScaUserLevel(db, opts...),
ScaUserMessage: newScaUserMessage(db, opts...),
@@ -101,6 +104,7 @@ type Query struct {
ScaStorageLocation scaStorageLocation
ScaStorageTag scaStorageTag
ScaStorageTagInfo scaStorageTagInfo
ScaStorageThumb scaStorageThumb
ScaUserFollow scaUserFollow
ScaUserLevel scaUserLevel
ScaUserMessage scaUserMessage
@@ -126,6 +130,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
ScaStorageLocation: q.ScaStorageLocation.clone(db),
ScaStorageTag: q.ScaStorageTag.clone(db),
ScaStorageTagInfo: q.ScaStorageTagInfo.clone(db),
ScaStorageThumb: q.ScaStorageThumb.clone(db),
ScaUserFollow: q.ScaUserFollow.clone(db),
ScaUserLevel: q.ScaUserLevel.clone(db),
ScaUserMessage: q.ScaUserMessage.clone(db),
@@ -158,6 +163,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
ScaStorageLocation: q.ScaStorageLocation.replaceDB(db),
ScaStorageTag: q.ScaStorageTag.replaceDB(db),
ScaStorageTagInfo: q.ScaStorageTagInfo.replaceDB(db),
ScaStorageThumb: q.ScaStorageThumb.replaceDB(db),
ScaUserFollow: q.ScaUserFollow.replaceDB(db),
ScaUserLevel: q.ScaUserLevel.replaceDB(db),
ScaUserMessage: q.ScaUserMessage.replaceDB(db),
@@ -180,6 +186,7 @@ type queryCtx struct {
ScaStorageLocation IScaStorageLocationDo
ScaStorageTag IScaStorageTagDo
ScaStorageTagInfo IScaStorageTagInfoDo
ScaStorageThumb IScaStorageThumbDo
ScaUserFollow IScaUserFollowDo
ScaUserLevel IScaUserLevelDo
ScaUserMessage IScaUserMessageDo
@@ -202,6 +209,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
ScaStorageLocation: q.ScaStorageLocation.WithContext(ctx),
ScaStorageTag: q.ScaStorageTag.WithContext(ctx),
ScaStorageTagInfo: q.ScaStorageTagInfo.WithContext(ctx),
ScaStorageThumb: q.ScaStorageThumb.WithContext(ctx),
ScaUserFollow: q.ScaUserFollow.WithContext(ctx),
ScaUserLevel: q.ScaUserLevel.WithContext(ctx),
ScaUserMessage: q.ScaUserMessage.WithContext(ctx),

View File

@@ -46,6 +46,7 @@ func newScaAuthMenu(db *gorm.DB, opts ...gen.DOOption) scaAuthMenu {
return _scaAuthMenu
}
// scaAuthMenu 菜单表
type scaAuthMenu struct {
scaAuthMenuDo

View File

@@ -41,6 +41,7 @@ func newScaAuthPermissionRule(db *gorm.DB, opts ...gen.DOOption) scaAuthPermissi
return _scaAuthPermissionRule
}
// scaAuthPermissionRule 接口权限表
type scaAuthPermissionRule struct {
scaAuthPermissionRuleDo

View File

@@ -39,6 +39,7 @@ func newScaAuthRole(db *gorm.DB, opts ...gen.DOOption) scaAuthRole {
return _scaAuthRole
}
// scaAuthRole 角色表
type scaAuthRole struct {
scaAuthRoleDo

View File

@@ -50,6 +50,7 @@ func newScaAuthUser(db *gorm.DB, opts ...gen.DOOption) scaAuthUser {
return _scaAuthUser
}
// scaAuthUser 用户表
type scaAuthUser struct {
scaAuthUserDo

View File

@@ -50,6 +50,7 @@ func newScaAuthUserDevice(db *gorm.DB, opts ...gen.DOOption) scaAuthUserDevice {
return _scaAuthUserDevice
}
// scaAuthUserDevice 用户设备表
type scaAuthUserDevice struct {
scaAuthUserDeviceDo

View File

@@ -41,6 +41,7 @@ func newScaAuthUserSocial(db *gorm.DB, opts ...gen.DOOption) scaAuthUserSocial {
return _scaAuthUserSocial
}
// scaAuthUserSocial 第三方登录信息表
type scaAuthUserSocial struct {
scaAuthUserSocialDo

View File

@@ -38,6 +38,7 @@ func newScaCommentLike(db *gorm.DB, opts ...gen.DOOption) scaCommentLike {
return _scaCommentLike
}
// scaCommentLike 评论点赞表
type scaCommentLike struct {
scaCommentLikeDo

View File

@@ -55,6 +55,7 @@ func newScaCommentReply(db *gorm.DB, opts ...gen.DOOption) scaCommentReply {
return _scaCommentReply
}
// scaCommentReply 评论表
type scaCommentReply struct {
scaCommentReplyDo

View File

@@ -45,6 +45,7 @@ func newScaMessageReport(db *gorm.DB, opts ...gen.DOOption) scaMessageReport {
return _scaMessageReport
}
// scaMessageReport 评论举报表
type scaMessageReport struct {
scaMessageReportDo

View File

@@ -41,6 +41,7 @@ func newScaStorageAlbum(db *gorm.DB, opts ...gen.DOOption) scaStorageAlbum {
return _scaStorageAlbum
}
// scaStorageAlbum 相册信息表
type scaStorageAlbum struct {
scaStorageAlbumDo

View File

@@ -44,6 +44,7 @@ func newScaStorageConfig(db *gorm.DB, opts ...gen.DOOption) scaStorageConfig {
return _scaStorageConfig
}
// scaStorageConfig 用户存储配置表
type scaStorageConfig struct {
scaStorageConfigDo

View File

@@ -37,6 +37,7 @@ func newScaStorageInfo(db *gorm.DB, opts ...gen.DOOption) scaStorageInfo {
_scaStorageInfo.FileType = field.NewString(tableName, "file_type")
_scaStorageInfo.Width = field.NewFloat64(tableName, "width")
_scaStorageInfo.Height = field.NewFloat64(tableName, "height")
_scaStorageInfo.ThumbID = field.NewInt64(tableName, "thumb_id")
_scaStorageInfo.Category = field.NewString(tableName, "category")
_scaStorageInfo.Tag = field.NewString(tableName, "tag")
_scaStorageInfo.Type = field.NewString(tableName, "type")
@@ -56,6 +57,7 @@ func newScaStorageInfo(db *gorm.DB, opts ...gen.DOOption) scaStorageInfo {
return _scaStorageInfo
}
// scaStorageInfo 文件存储信息表
type scaStorageInfo struct {
scaStorageInfoDo
@@ -70,6 +72,7 @@ type scaStorageInfo struct {
FileType field.String // 文件类型
Width field.Float64 // 宽
Height field.Float64 // 高
ThumbID field.Int64 // 缩略图id
Category field.String // 分类
Tag field.String // 标签
Type field.String // 类型
@@ -109,6 +112,7 @@ func (s *scaStorageInfo) updateTableName(table string) *scaStorageInfo {
s.FileType = field.NewString(table, "file_type")
s.Width = field.NewFloat64(table, "width")
s.Height = field.NewFloat64(table, "height")
s.ThumbID = field.NewInt64(table, "thumb_id")
s.Category = field.NewString(table, "category")
s.Tag = field.NewString(table, "tag")
s.Type = field.NewString(table, "type")
@@ -138,7 +142,7 @@ func (s *scaStorageInfo) GetFieldByName(fieldName string) (field.OrderExpr, bool
}
func (s *scaStorageInfo) fillFieldMap() {
s.fieldMap = make(map[string]field.Expr, 23)
s.fieldMap = make(map[string]field.Expr, 24)
s.fieldMap["id"] = s.ID
s.fieldMap["user_id"] = s.UserID
s.fieldMap["provider"] = s.Provider
@@ -149,6 +153,7 @@ func (s *scaStorageInfo) fillFieldMap() {
s.fieldMap["file_type"] = s.FileType
s.fieldMap["width"] = s.Width
s.fieldMap["height"] = s.Height
s.fieldMap["thumb_id"] = s.ThumbID
s.fieldMap["category"] = s.Category
s.fieldMap["tag"] = s.Tag
s.fieldMap["type"] = s.Type

View File

@@ -48,6 +48,7 @@ func newScaStorageLocation(db *gorm.DB, opts ...gen.DOOption) scaStorageLocation
return _scaStorageLocation
}
// scaStorageLocation 文件地理位置信息表
type scaStorageLocation struct {
scaStorageLocationDo

View File

@@ -39,6 +39,7 @@ func newScaStorageTag(db *gorm.DB, opts ...gen.DOOption) scaStorageTag {
return _scaStorageTag
}
// scaStorageTag 标签表
type scaStorageTag struct {
scaStorageTagDo

View File

@@ -0,0 +1,412 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package query
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
)
func newScaStorageThumb(db *gorm.DB, opts ...gen.DOOption) scaStorageThumb {
_scaStorageThumb := scaStorageThumb{}
_scaStorageThumb.scaStorageThumbDo.UseDB(db, opts...)
_scaStorageThumb.scaStorageThumbDo.UseModel(&model.ScaStorageThumb{})
tableName := _scaStorageThumb.scaStorageThumbDo.TableName()
_scaStorageThumb.ALL = field.NewAsterisk(tableName)
_scaStorageThumb.ID = field.NewInt64(tableName, "id")
_scaStorageThumb.UserID = field.NewString(tableName, "user_id")
_scaStorageThumb.ThumbPath = field.NewString(tableName, "thumb_path")
_scaStorageThumb.ThumbW = field.NewFloat64(tableName, "thumb_w")
_scaStorageThumb.ThumbH = field.NewFloat64(tableName, "thumb_h")
_scaStorageThumb.ThumbSize = field.NewFloat64(tableName, "thumb_size")
_scaStorageThumb.CreatedAt = field.NewTime(tableName, "created_at")
_scaStorageThumb.UpdatedAt = field.NewTime(tableName, "updated_at")
_scaStorageThumb.DeletedAt = field.NewField(tableName, "deleted_at")
_scaStorageThumb.fillFieldMap()
return _scaStorageThumb
}
type scaStorageThumb struct {
scaStorageThumbDo
ALL field.Asterisk
ID field.Int64 // 主键
UserID field.String // 用户ID
ThumbPath field.String // 缩略图路径
ThumbW field.Float64 // 缩略图宽
ThumbH field.Float64 // 缩略图高
ThumbSize field.Float64 // 缩略图大小
CreatedAt field.Time // 创建时间
UpdatedAt field.Time // 更新时间
DeletedAt field.Field // 删除时间
fieldMap map[string]field.Expr
}
func (s scaStorageThumb) Table(newTableName string) *scaStorageThumb {
s.scaStorageThumbDo.UseTable(newTableName)
return s.updateTableName(newTableName)
}
func (s scaStorageThumb) As(alias string) *scaStorageThumb {
s.scaStorageThumbDo.DO = *(s.scaStorageThumbDo.As(alias).(*gen.DO))
return s.updateTableName(alias)
}
func (s *scaStorageThumb) updateTableName(table string) *scaStorageThumb {
s.ALL = field.NewAsterisk(table)
s.ID = field.NewInt64(table, "id")
s.UserID = field.NewString(table, "user_id")
s.ThumbPath = field.NewString(table, "thumb_path")
s.ThumbW = field.NewFloat64(table, "thumb_w")
s.ThumbH = field.NewFloat64(table, "thumb_h")
s.ThumbSize = field.NewFloat64(table, "thumb_size")
s.CreatedAt = field.NewTime(table, "created_at")
s.UpdatedAt = field.NewTime(table, "updated_at")
s.DeletedAt = field.NewField(table, "deleted_at")
s.fillFieldMap()
return s
}
func (s *scaStorageThumb) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := s.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (s *scaStorageThumb) fillFieldMap() {
s.fieldMap = make(map[string]field.Expr, 9)
s.fieldMap["id"] = s.ID
s.fieldMap["user_id"] = s.UserID
s.fieldMap["thumb_path"] = s.ThumbPath
s.fieldMap["thumb_w"] = s.ThumbW
s.fieldMap["thumb_h"] = s.ThumbH
s.fieldMap["thumb_size"] = s.ThumbSize
s.fieldMap["created_at"] = s.CreatedAt
s.fieldMap["updated_at"] = s.UpdatedAt
s.fieldMap["deleted_at"] = s.DeletedAt
}
func (s scaStorageThumb) clone(db *gorm.DB) scaStorageThumb {
s.scaStorageThumbDo.ReplaceConnPool(db.Statement.ConnPool)
return s
}
func (s scaStorageThumb) replaceDB(db *gorm.DB) scaStorageThumb {
s.scaStorageThumbDo.ReplaceDB(db)
return s
}
type scaStorageThumbDo struct{ gen.DO }
type IScaStorageThumbDo interface {
gen.SubQuery
Debug() IScaStorageThumbDo
WithContext(ctx context.Context) IScaStorageThumbDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IScaStorageThumbDo
WriteDB() IScaStorageThumbDo
As(alias string) gen.Dao
Session(config *gorm.Session) IScaStorageThumbDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IScaStorageThumbDo
Not(conds ...gen.Condition) IScaStorageThumbDo
Or(conds ...gen.Condition) IScaStorageThumbDo
Select(conds ...field.Expr) IScaStorageThumbDo
Where(conds ...gen.Condition) IScaStorageThumbDo
Order(conds ...field.Expr) IScaStorageThumbDo
Distinct(cols ...field.Expr) IScaStorageThumbDo
Omit(cols ...field.Expr) IScaStorageThumbDo
Join(table schema.Tabler, on ...field.Expr) IScaStorageThumbDo
LeftJoin(table schema.Tabler, on ...field.Expr) IScaStorageThumbDo
RightJoin(table schema.Tabler, on ...field.Expr) IScaStorageThumbDo
Group(cols ...field.Expr) IScaStorageThumbDo
Having(conds ...gen.Condition) IScaStorageThumbDo
Limit(limit int) IScaStorageThumbDo
Offset(offset int) IScaStorageThumbDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IScaStorageThumbDo
Unscoped() IScaStorageThumbDo
Create(values ...*model.ScaStorageThumb) error
CreateInBatches(values []*model.ScaStorageThumb, batchSize int) error
Save(values ...*model.ScaStorageThumb) error
First() (*model.ScaStorageThumb, error)
Take() (*model.ScaStorageThumb, error)
Last() (*model.ScaStorageThumb, error)
Find() ([]*model.ScaStorageThumb, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.ScaStorageThumb, err error)
FindInBatches(result *[]*model.ScaStorageThumb, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*model.ScaStorageThumb) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IScaStorageThumbDo
Assign(attrs ...field.AssignExpr) IScaStorageThumbDo
Joins(fields ...field.RelationField) IScaStorageThumbDo
Preload(fields ...field.RelationField) IScaStorageThumbDo
FirstOrInit() (*model.ScaStorageThumb, error)
FirstOrCreate() (*model.ScaStorageThumb, error)
FindByPage(offset int, limit int) (result []*model.ScaStorageThumb, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IScaStorageThumbDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (s scaStorageThumbDo) Debug() IScaStorageThumbDo {
return s.withDO(s.DO.Debug())
}
func (s scaStorageThumbDo) WithContext(ctx context.Context) IScaStorageThumbDo {
return s.withDO(s.DO.WithContext(ctx))
}
func (s scaStorageThumbDo) ReadDB() IScaStorageThumbDo {
return s.Clauses(dbresolver.Read)
}
func (s scaStorageThumbDo) WriteDB() IScaStorageThumbDo {
return s.Clauses(dbresolver.Write)
}
func (s scaStorageThumbDo) Session(config *gorm.Session) IScaStorageThumbDo {
return s.withDO(s.DO.Session(config))
}
func (s scaStorageThumbDo) Clauses(conds ...clause.Expression) IScaStorageThumbDo {
return s.withDO(s.DO.Clauses(conds...))
}
func (s scaStorageThumbDo) Returning(value interface{}, columns ...string) IScaStorageThumbDo {
return s.withDO(s.DO.Returning(value, columns...))
}
func (s scaStorageThumbDo) Not(conds ...gen.Condition) IScaStorageThumbDo {
return s.withDO(s.DO.Not(conds...))
}
func (s scaStorageThumbDo) Or(conds ...gen.Condition) IScaStorageThumbDo {
return s.withDO(s.DO.Or(conds...))
}
func (s scaStorageThumbDo) Select(conds ...field.Expr) IScaStorageThumbDo {
return s.withDO(s.DO.Select(conds...))
}
func (s scaStorageThumbDo) Where(conds ...gen.Condition) IScaStorageThumbDo {
return s.withDO(s.DO.Where(conds...))
}
func (s scaStorageThumbDo) Order(conds ...field.Expr) IScaStorageThumbDo {
return s.withDO(s.DO.Order(conds...))
}
func (s scaStorageThumbDo) Distinct(cols ...field.Expr) IScaStorageThumbDo {
return s.withDO(s.DO.Distinct(cols...))
}
func (s scaStorageThumbDo) Omit(cols ...field.Expr) IScaStorageThumbDo {
return s.withDO(s.DO.Omit(cols...))
}
func (s scaStorageThumbDo) Join(table schema.Tabler, on ...field.Expr) IScaStorageThumbDo {
return s.withDO(s.DO.Join(table, on...))
}
func (s scaStorageThumbDo) LeftJoin(table schema.Tabler, on ...field.Expr) IScaStorageThumbDo {
return s.withDO(s.DO.LeftJoin(table, on...))
}
func (s scaStorageThumbDo) RightJoin(table schema.Tabler, on ...field.Expr) IScaStorageThumbDo {
return s.withDO(s.DO.RightJoin(table, on...))
}
func (s scaStorageThumbDo) Group(cols ...field.Expr) IScaStorageThumbDo {
return s.withDO(s.DO.Group(cols...))
}
func (s scaStorageThumbDo) Having(conds ...gen.Condition) IScaStorageThumbDo {
return s.withDO(s.DO.Having(conds...))
}
func (s scaStorageThumbDo) Limit(limit int) IScaStorageThumbDo {
return s.withDO(s.DO.Limit(limit))
}
func (s scaStorageThumbDo) Offset(offset int) IScaStorageThumbDo {
return s.withDO(s.DO.Offset(offset))
}
func (s scaStorageThumbDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IScaStorageThumbDo {
return s.withDO(s.DO.Scopes(funcs...))
}
func (s scaStorageThumbDo) Unscoped() IScaStorageThumbDo {
return s.withDO(s.DO.Unscoped())
}
func (s scaStorageThumbDo) Create(values ...*model.ScaStorageThumb) error {
if len(values) == 0 {
return nil
}
return s.DO.Create(values)
}
func (s scaStorageThumbDo) CreateInBatches(values []*model.ScaStorageThumb, batchSize int) error {
return s.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (s scaStorageThumbDo) Save(values ...*model.ScaStorageThumb) error {
if len(values) == 0 {
return nil
}
return s.DO.Save(values)
}
func (s scaStorageThumbDo) First() (*model.ScaStorageThumb, error) {
if result, err := s.DO.First(); err != nil {
return nil, err
} else {
return result.(*model.ScaStorageThumb), nil
}
}
func (s scaStorageThumbDo) Take() (*model.ScaStorageThumb, error) {
if result, err := s.DO.Take(); err != nil {
return nil, err
} else {
return result.(*model.ScaStorageThumb), nil
}
}
func (s scaStorageThumbDo) Last() (*model.ScaStorageThumb, error) {
if result, err := s.DO.Last(); err != nil {
return nil, err
} else {
return result.(*model.ScaStorageThumb), nil
}
}
func (s scaStorageThumbDo) Find() ([]*model.ScaStorageThumb, error) {
result, err := s.DO.Find()
return result.([]*model.ScaStorageThumb), err
}
func (s scaStorageThumbDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.ScaStorageThumb, err error) {
buf := make([]*model.ScaStorageThumb, 0, batchSize)
err = s.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (s scaStorageThumbDo) FindInBatches(result *[]*model.ScaStorageThumb, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return s.DO.FindInBatches(result, batchSize, fc)
}
func (s scaStorageThumbDo) Attrs(attrs ...field.AssignExpr) IScaStorageThumbDo {
return s.withDO(s.DO.Attrs(attrs...))
}
func (s scaStorageThumbDo) Assign(attrs ...field.AssignExpr) IScaStorageThumbDo {
return s.withDO(s.DO.Assign(attrs...))
}
func (s scaStorageThumbDo) Joins(fields ...field.RelationField) IScaStorageThumbDo {
for _, _f := range fields {
s = *s.withDO(s.DO.Joins(_f))
}
return &s
}
func (s scaStorageThumbDo) Preload(fields ...field.RelationField) IScaStorageThumbDo {
for _, _f := range fields {
s = *s.withDO(s.DO.Preload(_f))
}
return &s
}
func (s scaStorageThumbDo) FirstOrInit() (*model.ScaStorageThumb, error) {
if result, err := s.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*model.ScaStorageThumb), nil
}
}
func (s scaStorageThumbDo) FirstOrCreate() (*model.ScaStorageThumb, error) {
if result, err := s.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*model.ScaStorageThumb), nil
}
}
func (s scaStorageThumbDo) FindByPage(offset int, limit int) (result []*model.ScaStorageThumb, count int64, err error) {
result, err = s.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = s.Offset(-1).Limit(-1).Count()
return
}
func (s scaStorageThumbDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = s.Count()
if err != nil {
return
}
err = s.Offset(offset).Limit(limit).Scan(result)
return
}
func (s scaStorageThumbDo) Scan(result interface{}) (err error) {
return s.DO.Scan(result)
}
func (s scaStorageThumbDo) Delete(models ...*model.ScaStorageThumb) (result gen.ResultInfo, err error) {
return s.DO.Delete(models)
}
func (s *scaStorageThumbDo) withDO(do gen.Dao) *scaStorageThumbDo {
s.DO = *do.(*gen.DO)
return s
}

View File

@@ -40,6 +40,7 @@ func newScaUserFollow(db *gorm.DB, opts ...gen.DOOption) scaUserFollow {
return _scaUserFollow
}
// scaUserFollow 用户关注表
type scaUserFollow struct {
scaUserFollowDo

View File

@@ -44,6 +44,7 @@ func newScaUserLevel(db *gorm.DB, opts ...gen.DOOption) scaUserLevel {
return _scaUserLevel
}
// scaUserLevel 用户等级表
type scaUserLevel struct {
scaUserLevelDo

View File

@@ -42,6 +42,7 @@ func newScaUserMessage(db *gorm.DB, opts ...gen.DOOption) scaUserMessage {
return _scaUserMessage
}
// scaUserMessage 用户消息表
type scaUserMessage struct {
scaUserMessageDo