Merge branch 'develop' of https://gitee.com/fusenpack/fusenapi into feature/auth

This commit is contained in:
eson 2023-08-09 14:21:22 +08:00
commit 05e8def85a
27 changed files with 830 additions and 56 deletions

View File

@ -1,6 +1,6 @@
package constants
// 渲染要用到的面片模板
// 渲染要用到的面片模板(以前的模板)
const RENDER_FACE_SLICE_TEMPLATE_JSON = `[
{
"id": "",

View File

@ -8,6 +8,7 @@ import (
type FsCloudRenderLog struct {
Id int64 `gorm:"primary_key;default:0;auto_increment;" json:"id"` // ID
UserId *int64 `gorm:"default:0;" json:"user_id"` // 用户id
GuestId *int64 `gorm:"default:0;" json:"guest_id"` // 游客id
PostData *string `gorm:"default:'';" json:"post_data"` //
PostUrl *string `gorm:"default:'';" json:"post_url"` //
Title *string `gorm:"index;default:'';" json:"title"` //

View File

@ -17,7 +17,7 @@ type FsResource struct {
Metadata *string `gorm:"default:'';" json:"metadata"` // 元数据,json格式,存储图像分率
MetaKey1 *string `gorm:"index;default:'';" json:"meta_key1"` // 需要关键信息查询的自定义属性1,可以动态增加
ApiType *int64 `gorm:"default:1;" json:"api_type"` // 调用类型1=对外2=对内
BucketName *string `gorm:"default:'';" json:"bucket_name"` // 存储桶名
BucketName *string `gorm:"default:'';" json:"bucket_name"` // 存储桶名: 1=持久 2=缓存
}
type FsResourceModel struct {
db *gorm.DB

View File

@ -1,7 +1,6 @@
package consumer
import (
"bytes"
"context"
"encoding/json"
"errors"
@ -15,6 +14,7 @@ import (
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
)
@ -64,11 +64,12 @@ func (m *MqConsumerRenderAssemble) Run(ctx context.Context, data []byte) error {
title := "1-组装模板数据"
//云渲染日志
err = allmodels.FsCloudRenderLog.Create(ctx, &gmodel.FsCloudRenderLog{
UserId: &parseInfo.RenderData.UserId,
Title: &title,
Time: &renderLogTime,
Tag: &parseInfo.RenderId,
Ctime: &now,
UserId: &parseInfo.RenderData.UserId,
GuestId: &parseInfo.RenderData.GuestId,
Title: &title,
Time: &renderLogTime,
Tag: &parseInfo.RenderId,
Ctime: &now,
})
if err != nil {
logx.Error(err)
@ -80,9 +81,10 @@ func (m *MqConsumerRenderAssemble) Run(ctx context.Context, data []byte) error {
"data": parseInfo.RenderData.Data,
}
pyPostBytes, _ := json.Marshal(pythonPostData)
//post 数据结构 `{"tids":[128,431],"data":[{"id":"9d35ac5a-81a0-3caf-3246-cdbea9f2ddfe","tag":"MainColor","title":"\u8d34\u56fe2","type":"color","text":"","fill":"#c028b9","fontSize":20,"fontFamily":"Aqum2SmallCaps3","ifBr":false,"ifShow":true,"ifGroup":false,"maxNum":50,"rotation":0,"align":"center","verticalAlign":"middle","material":"","width":1024,"height":1024,"x":0,"y":0,"opacity":1,"optionalColor":[{"color":"#000000","name":"Black","default":true}],"zIndex":2,"svgPath":"","follow":{"fill":"","ifShow":"","content":""},"group":[],"cameraStand":{"x":0,"y":0,"z":0},"proportion":60,"materialName":"","materialTime":""},{"id":"c9be653f-dfc1-5659-1eb8-7ab128abe3d5","tag":"Logo","title":"\u8d34\u56fe4","type":"image","text":"","fill":"#c028b9","fontSize":65,"fontFamily":"MontserratBold3","ifBr":true,"ifShow":true,"ifGroup":false,"maxNum":50,"rotation":0,"align":"center","verticalAlign":"middle","material":"","width":312,"height":144.8044172010362,"x":99,"y":406.49999999999875,"opacity":1,"optionalColor":[{"color":"#000000","name":"Black","default":false},{"color":"#FFFFFF","name":"White","default":false},{"name":"MainColor","color":"#c028b9","default":true}],"zIndex":3,"svgPath":"","follow":{"fill":"","ifShow":"","content":""},"group":[],"cameraStand":{"x":0,"y":0,"z":0},"proportion":60,"materialTime":"","materialName":""},{"id":"e3269e77-b8c2-baec-bb9b-8399915a711c","tag":"Slogan","title":"\u8d34\u56fe5","type":"text","text":"","fill":"","fontSize":16,"fontFamily":"MontserratBold3","ifBr":false,"ifShow":true,"ifGroup":false,"maxNum":50,"rotation":0,"align":"center","verticalAlign":"middle","material":"","width":312,"height":18.999939381668568,"x":99,"y":605.0000538829611,"opacity":1,"optionalColor":[{"color":"#000000","name":"Black","default":true}],"zIndex":4,"svgPath":"","follow":{"fill":"bfc2b5a3-10af-c95b-fbf1-3016540fffad","ifShow":"","content":""},"group":[],"cameraStand":{"x":0,"y":0,"z":36},"proportion":60,"materialName":"","materialTime":""},{"id":"bfc2b5a3-10af-c95b-fbf1-3016540fffad","tag":"SecondaryColor","title":"\u8d34\u56fe9","type":"color","text":"","fill":"#FFFFFF","fontSize":20,"fontFamily":"Aqum2SmallCaps3","ifBr":false,"ifShow":true,"ifGroup":false,"maxNum":50,"rotation":0,"align":"center","verticalAlign":"middle","material":"","width":1024,"height":1024,"x":0,"y":0,"opacity":1,"optionalColor":[{"color":"#000000","name":"Black","default":true}],"zIndex":1,"svgPath":"","follow":{"fill":"","ifShow":"","content":""},"group":[],"cameraStand":{"x":0,"y":0,"z":0},"proportion":60}]}`
//post 数据结构
a := `{"tids":[128,431],"data":[{"id":"9d35ac5a-81a0-3caf-3246-cdbea9f2ddfe","tag":"MainColor","title":"\u8d34\u56fe2","type":"color","text":"","fill":"#c028b9","fontSize":20,"fontFamily":"Aqum2SmallCaps3","ifBr":false,"ifShow":true,"ifGroup":false,"maxNum":50,"rotation":0,"align":"center","verticalAlign":"middle","material":"","width":1024,"height":1024,"x":0,"y":0,"opacity":1,"optionalColor":[{"color":"#000000","name":"Black","default":true}],"zIndex":2,"svgPath":"","follow":{"fill":"","ifShow":"","content":""},"group":[],"cameraStand":{"x":0,"y":0,"z":0},"proportion":60,"materialName":"","materialTime":""},{"id":"c9be653f-dfc1-5659-1eb8-7ab128abe3d5","tag":"Logo","title":"\u8d34\u56fe4","type":"image","text":"","fill":"#c028b9","fontSize":65,"fontFamily":"MontserratBold3","ifBr":true,"ifShow":true,"ifGroup":false,"maxNum":50,"rotation":0,"align":"center","verticalAlign":"middle","material":"","width":312,"height":144.8044172010362,"x":99,"y":406.49999999999875,"opacity":1,"optionalColor":[{"color":"#000000","name":"Black","default":false},{"color":"#FFFFFF","name":"White","default":false},{"name":"MainColor","color":"#c028b9","default":true}],"zIndex":3,"svgPath":"","follow":{"fill":"","ifShow":"","content":""},"group":[],"cameraStand":{"x":0,"y":0,"z":0},"proportion":60,"materialTime":"","materialName":""},{"id":"e3269e77-b8c2-baec-bb9b-8399915a711c","tag":"Slogan","title":"\u8d34\u56fe5","type":"text","text":"","fill":"","fontSize":16,"fontFamily":"MontserratBold3","ifBr":false,"ifShow":true,"ifGroup":false,"maxNum":50,"rotation":0,"align":"center","verticalAlign":"middle","material":"","width":312,"height":18.999939381668568,"x":99,"y":605.0000538829611,"opacity":1,"optionalColor":[{"color":"#000000","name":"Black","default":true}],"zIndex":4,"svgPath":"","follow":{"fill":"bfc2b5a3-10af-c95b-fbf1-3016540fffad","ifShow":"","content":""},"group":[],"cameraStand":{"x":0,"y":0,"z":36},"proportion":60,"materialName":"","materialTime":""},{"id":"bfc2b5a3-10af-c95b-fbf1-3016540fffad","tag":"SecondaryColor","title":"\u8d34\u56fe9","type":"color","text":"","fill":"#FFFFFF","fontSize":20,"fontFamily":"Aqum2SmallCaps3","ifBr":false,"ifShow":true,"ifGroup":false,"maxNum":50,"rotation":0,"align":"center","verticalAlign":"middle","material":"","width":1024,"height":1024,"x":0,"y":0,"opacity":1,"optionalColor":[{"color":"#000000","name":"Black","default":true}],"zIndex":1,"svgPath":"","follow":{"fill":"","ifShow":"","content":""},"group":[],"cameraStand":{"x":0,"y":0,"z":0},"proportion":60}]}`
url := "http://110.41.19.98:8867/imgRender"
pyRsp, err := http.Post(url, "application/json;charset=UTF-8", bytes.NewReader(pyPostBytes))
pyRsp, err := http.Post(url, "application/json;charset=UTF-8", strings.NewReader(a))
if err != nil {
logx.Error("request python render api err:", err)
return err
@ -115,11 +117,12 @@ func (m *MqConsumerRenderAssemble) Run(ctx context.Context, data []byte) error {
now = time.Now().Unix()
pyRequestTime := time.Now().UnixMilli() - pyapiBeginTime
err = allmodels.FsCloudRenderLog.Create(ctx, &gmodel.FsCloudRenderLog{
UserId: &parseInfo.RenderData.UserId,
Title: &title,
Time: &pyRequestTime,
Tag: &parseInfo.RenderId,
Ctime: &now,
UserId: &parseInfo.RenderData.UserId,
GuestId: &parseInfo.RenderData.GuestId,
Title: &title,
Time: &pyRequestTime,
Tag: &parseInfo.RenderId,
Ctime: &now,
})
if err != nil {
logx.Error(err)
@ -137,6 +140,7 @@ func (m *MqConsumerRenderAssemble) Run(ctx context.Context, data []byte) error {
pyRspStr := string(pyRspBytes)
err = allmodels.FsCloudRenderLog.Create(ctx, &gmodel.FsCloudRenderLog{
UserId: &parseInfo.RenderData.UserId,
GuestId: &parseInfo.RenderData.GuestId,
PostUrl: &url,
PostData: &postData,
Result: &pyRspStr,
@ -224,19 +228,21 @@ func (m *MqConsumerRenderAssemble) Run(ctx context.Context, data []byte) error {
title = "接收到python刀版图 -> 3-组装MQ渲染任务队列"
now = time.Now().Unix()
err = allmodels.FsCloudRenderLog.Create(ctx, &gmodel.FsCloudRenderLog{
UserId: &parseInfo.RenderData.UserId,
Title: &title,
Time: &timePinjie,
Tag: &parseInfo.RenderId,
Ctime: &now,
UserId: &parseInfo.RenderData.UserId,
GuestId: &parseInfo.RenderData.GuestId,
Title: &title,
Time: &timePinjie,
Tag: &parseInfo.RenderId,
Ctime: &now,
})
if err != nil {
logx.Error(err)
}
sendData := map[string]interface{}{
"id": parseInfo.RenderId,
"id": parseInfo.TaskId,
"order_id": 0,
"user_id": parseInfo.RenderData.UserId,
"guest_id": parseInfo.RenderData.GuestId,
"sku_ids": []int64{parseInfo.RenderData.ProductId},
"tids": []string{*element.Title},
"data": result,

View File

@ -47,6 +47,9 @@ func (l *RenderNotifyLogic) RenderNotify(req *types.RenderNotifyReq, userinfo *a
if req.Info.Image == "" {
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid param image")
}
if req.Info.UserId == 0 && req.Info.GuestId == 0 {
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid user_id or guest_id")
}
/* if req.Sign == "" {
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid param sign")
}*/
@ -59,6 +62,9 @@ func (l *RenderNotifyLogic) RenderNotify(req *types.RenderNotifyReq, userinfo *a
if req.Sign != sign {
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid sign")
}*/
//创建/更新资源
//发送消息到对应的rabbitmq
data := websocket_data.RenderImageNotify{
TaskId: req.Info.TaskId,
Image: req.Info.Image,

View File

@ -18,8 +18,10 @@ type RenderNotifyReq struct {
}
type NotifyInfo struct {
TaskId string `json:"task_id"` //任务id
Image string `json:"image"`
TaskId string `json:"task_id"` //任务id
UserId int64 `json:"user_id"`
GuestId int64 `json:"guest_id"`
Image string `json:"image"`
}
type Request struct {

View File

@ -0,0 +1,19 @@
Name: resource
Host: 0.0.0.0
Port: 9916
Timeout: 15000 #服务超时时间(毫秒)
SourceMysql: fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest
Auth:
AccessSecret: fusen2023
AccessExpire: 2592000
RefreshAfter: 1592000
SourceRabbitMq: amqp://rabbit001:rabbit001129@110.41.19.98:5672
AWS:
S3:
Credentials:
AccessKeyID: AKIAZB2JKUXDPNRP4YT2
Secret: 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
Token:
BLMService:
LogoCombine:
Url: "http://192.168.1.7:45678/LogoCombine"

View File

@ -0,0 +1,28 @@
package config
import (
"fusenapi/server/resource/internal/types"
"github.com/zeromicro/go-zero/rest"
)
type Config struct {
rest.RestConf
SourceMysql string
Auth types.Auth
SourceRabbitMq string
AWS struct {
S3 struct {
Credentials struct {
AccessKeyID string
Secret string
Token string
}
}
}
BLMService struct {
LogoCombine struct {
Url string
}
}
}

View File

@ -0,0 +1,35 @@
package handler
import (
"net/http"
"reflect"
"fusenapi/utils/basic"
"fusenapi/server/resource/internal/logic"
"fusenapi/server/resource/internal/svc"
"fusenapi/server/resource/internal/types"
)
func LogoCombineHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.LogoCombineReq
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
if err != nil {
return
}
// 创建一个业务逻辑层实例
l := logic.NewLogoCombineLogic(r.Context(), svcCtx)
rl := reflect.ValueOf(l)
basic.BeforeLogic(w, r, rl)
resp := l.LogoCombine(&req, userinfo)
if !basic.AfterLogic(w, r, rl, resp) {
basic.NormalAfterLogic(w, r, resp)
}
}
}

View File

@ -0,0 +1,35 @@
package handler
import (
"net/http"
"reflect"
"fusenapi/utils/basic"
"fusenapi/server/resource/internal/logic"
"fusenapi/server/resource/internal/svc"
"fusenapi/server/resource/internal/types"
)
func ResourceInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ResourceInfoReq
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
if err != nil {
return
}
// 创建一个业务逻辑层实例
l := logic.NewResourceInfoLogic(r.Context(), svcCtx)
rl := reflect.ValueOf(l)
basic.BeforeLogic(w, r, rl)
resp := l.ResourceInfo(&req, userinfo)
if !basic.AfterLogic(w, r, rl, resp) {
basic.NormalAfterLogic(w, r, resp)
}
}
}

View File

@ -0,0 +1,27 @@
// Code generated by goctl. DO NOT EDIT.
package handler
import (
"net/http"
"fusenapi/server/resource/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodPost,
Path: "/api/resource/logo-combine",
Handler: LogoCombineHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/api/resource/info",
Handler: ResourceInfoHandler(serverCtx),
},
},
)
}

View File

@ -0,0 +1,179 @@
package logic
import (
"encoding/json"
"fusenapi/model/gmodel"
"fusenapi/utils/auth"
"fusenapi/utils/basic"
"fusenapi/utils/file"
"fusenapi/utils/hash"
"io"
"net/http"
"strings"
"context"
"fusenapi/server/resource/internal/svc"
"fusenapi/server/resource/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type LogoCombineLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewLogoCombineLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LogoCombineLogic {
return &LogoCombineLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// 处理进入前逻辑w,r
// func (l *LogoCombineLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
// }
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
// func (l *LogoCombineLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
// // httpx.OkJsonCtx(r.Context(), w, resp)
// }
func (l *LogoCombineLogic) LogoCombine(req *types.LogoCombineReq, userinfo *auth.UserInfo) (resp *basic.Response) {
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
// userinfo 传入值时, 一定不为null
if userinfo.IsOnlooker() {
// 如果是,返回未授权的错误码
return resp.SetStatus(basic.CodeUnAuth)
}
var userId int64
var guestId int64
// 检查用户是否是游客
if userinfo.IsGuest() {
// 如果是使用游客ID和游客键名格式
guestId = userinfo.GuestId
} else {
// 否则使用用户ID和用户键名格式
userId = userinfo.UserId
}
// 根据hash 查询数据资源
var resourceId string = hash.JsonHashKey(req.ResourceKey)
resourceModel := gmodel.NewFsResourceModel(l.svcCtx.MysqlConn)
resourceInfo, err := resourceModel.FindOneById(l.ctx, resourceId)
if err == nil && resourceInfo.ResourceId != "" {
return resp.SetStatus(basic.CodeOK, map[string]interface{}{
"resource_id": resourceId,
"resource_url": resourceInfo.ResourceUrl,
"resource_metadata": resourceInfo.Metadata,
})
} else {
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeDbSqlErr, "LogoCombine error")
}
}
// 没有查到先根据模版id 查询模版数据 请求算法数据
productTemplateV2Model := gmodel.NewFsProductTemplateV2Model(l.svcCtx.MysqlConn)
productTemplateV2Info, err := productTemplateV2Model.FindOne(l.ctx, req.TemplateId)
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeDbSqlErr, "LogoCombine error")
}
var groupOptions map[string]interface{}
if productTemplateV2Info.GroupOptions != nil {
err = json.Unmarshal([]byte(*productTemplateV2Info.GroupOptions), &groupOptions)
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeDbSqlErr, "LogoCombine error Unmarshal groupOptions")
}
}
var materialList []interface{}
if productTemplateV2Info.TemplateInfo != nil {
var templateInfo map[string]interface{}
err = json.Unmarshal([]byte(*productTemplateV2Info.TemplateInfo), &templateInfo)
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeDbSqlErr, "LogoCombine error Unmarshal templateInfo")
}
materialList = templateInfo["materialList"].([]interface{})
}
var moduleDataMap = make(map[string]interface{}, 4)
moduleDataMap["id"] = productTemplateV2Info.Id
moduleDataMap["material"] = productTemplateV2Info.MaterialImg
moduleDataMap["groupOptions"] = groupOptions
moduleDataMap["materialList"] = materialList
var combineParam map[string]interface{}
json.Unmarshal([]byte(req.CombineParam), &combineParam)
var postMap = make(map[string]interface{}, 2)
postMap["module_data"] = moduleDataMap
postMap["param_data"] = combineParam
postMapB, _ := json.Marshal(postMap)
result, err := http.Post(l.svcCtx.Config.BLMService.LogoCombine.Url, "application/json", strings.NewReader(string(postMapB)))
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeFileLogoCombineErr, "service post fail")
}
defer result.Body.Close()
b, err := io.ReadAll(result.Body)
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeFileLogoCombineErr, "service read fail")
}
if string(b) == "Internal Server Error" {
return resp.SetStatus(basic.CodeFileLogoCombineErr, "service read fail")
}
var resultData map[string]interface{}
err = json.Unmarshal(b, &resultData)
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeFileLogoCombineErr, "service read fail")
}
// {
// id: "",
// logo_url:"https://s3.amazon.com/xxxx"
// result: "$saa541afaldjaldjasldjsadjsapsaasda"
// }
var fileBase = resultData["result"]
// 上传文件
var upload = file.Upload{
Ctx: l.ctx,
MysqlConn: l.svcCtx.MysqlConn,
AwsSession: l.svcCtx.AwsSession,
}
uploadRes, err := upload.UploadFileByBase64(&file.UploadBaseReq{
FileHash: resourceId,
FileData: fileBase.(string),
UploadBucket: 1,
ApiType: 2,
UserId: userId,
GuestId: guestId,
})
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeFileLogoCombineErr, "LogoCombine error upload file failed")
}
// 返回成功的响应和上传URL
return resp.SetStatus(basic.CodeOK, map[string]interface{}{
"resource_id": resourceId,
"resource_url": uploadRes.ResourceUrl,
"resource_metadata": "",
})
}

View File

@ -0,0 +1,64 @@
package logic
import (
"fusenapi/model/gmodel"
"fusenapi/utils/auth"
"fusenapi/utils/basic"
"fusenapi/utils/hash"
"context"
"fusenapi/server/resource/internal/svc"
"fusenapi/server/resource/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type ResourceInfoLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewResourceInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ResourceInfoLogic {
return &ResourceInfoLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
// 处理进入前逻辑w,r
// func (l *ResourceInfoLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
// }
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
// func (l *ResourceInfoLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
// // httpx.OkJsonCtx(r.Context(), w, resp)
// }
func (l *ResourceInfoLogic) ResourceInfo(req *types.ResourceInfoReq, userinfo *auth.UserInfo) (resp *basic.Response) {
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
// userinfo 传入值时, 一定不为null
var resourceId = req.ResourceId
if req.ResourceKey != "" {
resourceId = hash.JsonHashKey(req.ResourceKey)
}
resourceModel := gmodel.NewFsResourceModel(l.svcCtx.MysqlConn)
resourceInfo, err := resourceModel.FindOneById(l.ctx, resourceId)
var resourceUrl string
var metadata string
if err == nil && resourceInfo.ResourceId != "" {
resourceId = resourceInfo.ResourceId
resourceUrl = *resourceInfo.ResourceUrl
metadata = *resourceInfo.Metadata
}
return resp.SetStatus(basic.CodeOK, map[string]interface{}{
"resource_id": resourceId,
"resource_url": resourceUrl,
"resource_metadata": metadata,
})
}

View File

@ -0,0 +1,71 @@
package svc
import (
"errors"
"fmt"
"fusenapi/server/resource/internal/config"
"net/http"
"fusenapi/initalize"
"fusenapi/model/gmodel"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/golang-jwt/jwt"
"gorm.io/gorm"
)
type ServiceContext struct {
Config config.Config
MysqlConn *gorm.DB
AllModels *gmodel.AllModelsGen
RabbitMq *initalize.RabbitMqHandle
AwsSession *session.Session
}
func NewServiceContext(c config.Config) *ServiceContext {
config := aws.Config{
Credentials: credentials.NewStaticCredentials(c.AWS.S3.Credentials.AccessKeyID, c.AWS.S3.Credentials.Secret, c.AWS.S3.Credentials.Token),
}
initalize.InitRabbitMq(c.SourceRabbitMq, nil)
return &ServiceContext{
Config: c,
MysqlConn: initalize.InitMysql(c.SourceMysql),
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
RabbitMq: initalize.InitRabbitMq(c.SourceRabbitMq, nil),
AwsSession: session.Must(session.NewSession(&config)),
}
}
func (svcCtx *ServiceContext) ParseJwtToken(r *http.Request) (jwt.MapClaims, error) {
AuthKey := r.Header.Get("Authorization")
if AuthKey == "" {
return nil, nil
}
AuthKey = AuthKey[7:]
if len(AuthKey) <= 50 {
return nil, errors.New(fmt.Sprint("Error parsing token, len:", len(AuthKey)))
}
token, err := jwt.Parse(AuthKey, func(token *jwt.Token) (interface{}, error) {
// 检查签名方法是否为 HS256
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// 返回用于验证签名的密钥
return []byte(svcCtx.Config.Auth.AccessSecret), nil
})
if err != nil {
return nil, errors.New(fmt.Sprint("Error parsing token:", err))
}
// 验证成功返回
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New(fmt.Sprint("Invalid token", err))
}

View File

@ -0,0 +1,86 @@
// Code generated by goctl. DO NOT EDIT.
package types
import (
"fusenapi/utils/basic"
)
type ResourceInfoReq struct {
ResourceId string `form:"resource_id,optional"` // 资源ID
ResourceKey string `form:"resource_key,optional"` // 资源唯一标识
}
type LogoCombineReq struct {
ResourceKey string `form:"resource_key"` // 资源唯一标识
CombineParam string `form:"combine_param"` // 合图参数
TemplateId int64 `form:"template_id"` // 合图参数
}
type Request struct {
}
type Response struct {
Code int `json:"code"`
Message string `json:"msg"`
Data interface{} `json:"data"`
}
type Auth struct {
AccessSecret string `json:"accessSecret"`
AccessExpire int64 `json:"accessExpire"`
RefreshAfter int64 `json:"refreshAfter"`
}
type File struct {
Filename string `fsfile:"filename"`
Header map[string][]string `fsfile:"header"`
Size int64 `fsfile:"size"`
Data []byte `fsfile:"data"`
}
type Meta struct {
TotalCount int64 `json:"totalCount"`
PageCount int64 `json:"pageCount"`
CurrentPage int `json:"currentPage"`
PerPage int `json:"perPage"`
}
// Set 设置Response的Code和Message值
func (resp *Response) Set(Code int, Message string) *Response {
return &Response{
Code: Code,
Message: Message,
}
}
// Set 设置整个Response
func (resp *Response) SetWithData(Code int, Message string, Data interface{}) *Response {
return &Response{
Code: Code,
Message: Message,
Data: Data,
}
}
// SetStatus 设置默认StatusResponse(内部自定义) 默认msg, 可以带data, data只使用一个参数
func (resp *Response) SetStatus(sr *basic.StatusResponse, data ...interface{}) *Response {
newResp := &Response{
Code: sr.Code,
}
if len(data) == 1 {
newResp.Data = data[0]
}
return newResp
}
// SetStatusWithMessage 设置默认StatusResponse(内部自定义) 非默认msg, 可以带data, data只使用一个参数
func (resp *Response) SetStatusWithMessage(sr *basic.StatusResponse, msg string, data ...interface{}) *Response {
newResp := &Response{
Code: sr.Code,
Message: msg,
}
if len(data) == 1 {
newResp.Data = data[0]
}
return newResp
}

View File

@ -0,0 +1,36 @@
package main
import (
"flag"
"fmt"
"net/http"
"time"
"fusenapi/utils/auth"
"fusenapi/server/resource/internal/config"
"fusenapi/server/resource/internal/handler"
"fusenapi/server/resource/internal/svc"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
var configFile = flag.String("f", "etc/resource.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
c.Timeout = int64(time.Second * 15)
server := rest.MustNewServer(c.RestConf, rest.WithCustomCors(auth.FsCors, func(w http.ResponseWriter) {
}))
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}

View File

@ -0,0 +1,12 @@
package main
import (
"testing"
)
// var configFile = flag.String("f", "etc/home-user-auth.yaml", "the config file")
func TestMain(t *testing.T) {
// log.Println(model.RawFieldNames[FsCanteenType]())
main()
}

View File

@ -13,7 +13,8 @@ import (
func UploadFileBaseHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// data, _ := io.ReadAll(r.Body)
// log.Println(string(data))
var req types.UploadFileBaseReq
userinfo, err := basic.RequestParse(w, r, svcCtx.SharedState, &req)
if err != nil {

View File

@ -87,8 +87,7 @@ func (l *UploadLogoLogic) UploadLogo(req *types.UploadLogoReq, userinfo *auth.Us
postMap["logo_url"] = req.ResourceUrl
postMapB, _ := json.Marshal(postMap)
contentType := "application/json"
result, err := http.Post(l.svcCtx.Config.BLMService.ImageProcess.Url, contentType, strings.NewReader(string(postMapB)))
result, err := http.Post(l.svcCtx.Config.BLMService.ImageProcess.Url, "application/json", strings.NewReader(string(postMapB)))
if err != nil {
logx.Error(err)
return resp.SetStatus(basic.CodeFileUploadLogoErr, "service fail")

View File

@ -67,13 +67,14 @@ var (
type wsConnectItem struct {
conn *websocket.Conn //websocket的连接
rabbitMq *initalize.RabbitMqHandle
closeChan chan struct{} //ws连接关闭chan
isClose bool //是否已经关闭
uniqueId uint64 //ws连接唯一标识
inChan chan []byte //接受消息缓冲通道
outChan chan []byte //发送回客户端的消息
mutex sync.Mutex //互斥锁
userId int64
closeChan chan struct{} //ws连接关闭chan
isClose bool //是否已经关闭
uniqueId uint64 //ws连接唯一标识
inChan chan []byte //接受消息缓冲通道
outChan chan []byte //发送回客户端的消息
mutex sync.Mutex //互斥锁
userId int64 //用户id
guestId int64 //游客id
renderProperty renderProperty //扩展云渲染属性
}
@ -86,7 +87,7 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp
}
defer conn.Close()
//鉴权不成功10秒后断开
/*var (
var (
userInfo *auth.UserInfo
isAuth bool
)
@ -103,10 +104,10 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp
//发送关闭信息
_ = conn.WriteMessage(websocket.CloseMessage, nil)
return
}*/
}
//测试的目前写死 39
var userInfo auth.UserInfo
userInfo.UserId = 39
/*var userInfo auth.UserInfo
userInfo.UserId = 39*/
//生成连接唯一标识
uniqueId := websocketIdGenerator.Get()
ws := wsConnectItem{
@ -116,16 +117,13 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp
closeChan: make(chan struct{}, 1),
inChan: make(chan []byte, 1000),
outChan: make(chan []byte, 1000),
userId: userInfo.UserId,
guestId: userInfo.GuestId,
renderProperty: renderProperty{
renderImageTask: make(map[string]string),
renderImageTaskCtlChan: make(chan renderImageControlChanItem, 100),
},
}
if userInfo.UserId > 0 {
ws.userId = userInfo.UserId
} else {
ws.userId = userInfo.GuestId
}
//保存连接
mapConnPool.Store(uniqueId, ws)
defer ws.close()

View File

@ -31,7 +31,9 @@ func (w *wsConnectItem) renderImage(data []byte) {
return
}
logx.Info("收到请求云渲染图片数据:", renderImageData)
//用户id赋值
renderImageData.RenderData.UserId = w.userId
renderImageData.RenderData.GuestId = w.guestId
//把需要渲染的图片任务加进去
taskId := hash.JsonHashKey(renderImageData.RenderData)
w.renderProperty.renderImageTaskCtlChan <- renderImageControlChanItem{

View File

@ -31,6 +31,8 @@ type RenderNotifyReq {
Info NotifyInfo `json:"info"`
}
type NotifyInfo {
TaskId string `json:"task_id"` //任务id
Image string `json:"image"`
TaskId string `json:"task_id"` //任务id
UserId int64 `json:"user_id"`
GuestId int64 `json:"guest_id"`
Image string `json:"image"`
}

33
server_api/resource.api Normal file
View File

@ -0,0 +1,33 @@
syntax = "v1"
info (
title: // TODO: add title
desc: // TODO: add description
author: ""
email: ""
)
import "basic.api"
service resource {
@handler LogoCombineHandler
post /api/resource/logo-combine(LogoCombineReq) returns (response);
@handler ResourceInfoHandler
get /api/resource/info(ResourceInfoReq) returns (response);
}
type (
ResourceInfoReq {
ResourceId string `form:"resource_id,optional"` // 资源ID
ResourceKey string `form:"resource_key,optional"` // 资源唯一标识
}
)
type (
LogoCombineReq {
ResourceKey string `form:"resource_key"` // 资源唯一标识
CombineParam string `form:"combine_param"` // 合图参数
TemplateId int64 `form:"template_id"` // 合图参数
}
)

View File

@ -84,8 +84,9 @@ var (
CodeSharedStateErr = &StatusResponse{5201, "shared state server err"} // 状态机错误
CodeEmailConfirmationErr = &StatusResponse{5202, "email confirmation err"} // email 验证错误
CodeFileUploadErr = &StatusResponse{5110, "file upload err"} // 文件上传失败
CodeFileUploadLogoErr = &StatusResponse{5111, "logo upload err"} // 用户上传LOGO失败
CodeFileUploadErr = &StatusResponse{5110, "file upload err"} // 文件上传失败
CodeFileUploadLogoErr = &StatusResponse{5111, "logo upload err"} // 用户上传LOGO失败
CodeFileLogoCombineErr = &StatusResponse{5112, "logo upload err"} // 用户合图失败
)
type Response struct {

View File

@ -52,7 +52,7 @@ func NormalAfterLogic(w http.ResponseWriter, r *http.Request, resp *Response) {
}
func RequestParse(w http.ResponseWriter, r *http.Request, state *fsm.SharedState, LogicRequest any) (*auth.UserInfo, error) {
// log.Println(io.ReadAll(r.Body))
token, info, err := auth.ParseJwtTokenHeader[auth.UserInfo](r) //解析Token头, 和payload信息
if err != nil {
logx.Error(err)
@ -60,16 +60,19 @@ func RequestParse(w http.ResponseWriter, r *http.Request, state *fsm.SharedState
}
var secret uint64 = 0
if info.IsUser() {
us, err := state.GetUserState(info.UserId) //获取缓存的用户状态
if err != nil {
logx.Error(err)
return nil, err
}
secret = us.PwdHash // 获取密码的hash做jwt, 便于重置密码的使用
if info != nil {
} else if info.IsGuest() {
secret = DefaultJwtSecret //获取默认的hash
if info.IsUser() {
us, err := state.GetUserState(info.UserId) //获取缓存的用户状态
if err != nil {
logx.Error(err)
return nil, err
}
secret = us.PwdHash // 获取密码的hash做jwt, 便于重置密码的使用
} else if info.IsGuest() {
secret = DefaultJwtSecret //获取默认的hash
}
}
var userinfo *auth.UserInfo

127
utils/file/upload.go Normal file
View File

@ -0,0 +1,127 @@
package file
import (
"context"
"fusenapi/model/gmodel"
"fusenapi/utils/basic"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gorm"
)
type Upload struct {
Ctx context.Context
MysqlConn *gorm.DB
AwsSession *session.Session
}
type UploadBaseReq struct {
FileHash string
FileData string
Metadata string
UploadBucket int64
ApiType int64
UserId int64
GuestId int64
}
type UploadBaseRes struct {
Key string `json:"key"`
Status int64 `json:"status"`
ApiType int64 `json:"api_type"`
ResourceId string `json:"resource_id"`
ResourceType string `json:"resource_type"`
ResourceUrl string `json:"resource_url"`
}
func (upload *Upload) UploadFileByBase64(req *UploadBaseReq) (*UploadBaseRes, error) {
// 定义存储桶名称
var bucketName *string
var apiType int64 = req.ApiType
// 根据类别选择存储桶
switch req.UploadBucket {
case 2:
bucketName = basic.TempfileBucketName
default:
bucketName = basic.StorageBucketName
}
// 设置AWS会话的区域
upload.AwsSession.Config.Region = aws.String("us-west-1")
// 创建新的S3服务实例
svc := s3.New(upload.AwsSession)
// 定义S3请求和当前时间
var s3req *request.Request
var resourceId string = req.FileHash
var uploadBaseRes = UploadBaseRes{}
resourceModel := gmodel.NewFsResourceModel(upload.MysqlConn)
resourceInfo, err := resourceModel.FindOneById(upload.Ctx, resourceId)
if err == nil && resourceInfo.ResourceId != "" {
uploadBaseRes.Status = 1
uploadBaseRes.ResourceId = resourceId
uploadBaseRes.ResourceUrl = *resourceInfo.ResourceUrl
} else {
dist, contentType, err := FileBase64ToByte(req.FileData)
if err != nil {
logx.Errorf("err:%+vdesc:%+v", err, "fail.upload.resourceInfoGet.mysql")
return nil, err
}
// 创建S3对象存储请求
s3req, _ = svc.PutObjectRequest(
&s3.PutObjectInput{
Bucket: bucketName,
Key: &resourceId,
},
)
// 设置请求体为文件数据
s3req.SetBufferBody(dist)
// 发送请求
err = s3req.Send()
// 检查是否有错误
if err != nil {
logx.Errorf("err:%+vdesc:%+v", err, "fail.upload.s3req")
return nil, err
} else {
var url = s3req.HTTPRequest.URL.String()
// 打印请求URL
logx.Info(url)
uploadBaseRes.Status = 1
uploadBaseRes.ResourceId = resourceId
uploadBaseRes.ResourceUrl = url
var version string = "0.0.1"
var nowTime = time.Now()
_, err = resourceModel.Create(upload.Ctx, &gmodel.FsResource{
ResourceId: resourceId,
UserId: &req.UserId,
GuestId: &req.GuestId,
ResourceType: &contentType,
ResourceUrl: &url,
Version: &version,
UploadedAt: &nowTime,
Metadata: &req.Metadata,
ApiType: &apiType,
BucketName: bucketName,
})
if err != nil {
logx.Errorf("err:%+vdesc:%+v", err, "fail.upload.resourceInfoAdd.mysql")
return nil, err
}
}
}
return &uploadBaseRes, err
}

View File

@ -16,6 +16,7 @@ type RenderData struct {
ProductId int64 `json:"product_id"` //产品id
Data interface{} `json:"data"` //面片数据
UserId int64 `json:"user_id"` //用户id
GuestId int64 `json:"guest_id"` //游客id
}
// websocket发送渲染完的数据