diff --git a/constants/render.go b/constants/render.go index fe8b6a6f..97684193 100644 --- a/constants/render.go +++ b/constants/render.go @@ -1,6 +1,6 @@ package constants -// 渲染要用到的面片模板 +// 渲染要用到的面片模板(以前的模板) const RENDER_FACE_SLICE_TEMPLATE_JSON = `[ { "id": "", diff --git a/model/gmodel/fs_cloud_render_log_gen.go b/model/gmodel/fs_cloud_render_log_gen.go index a1d8aeae..2e851c6d 100644 --- a/model/gmodel/fs_cloud_render_log_gen.go +++ b/model/gmodel/fs_cloud_render_log_gen.go @@ -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"` // diff --git a/model/gmodel/fs_resource_gen.go b/model/gmodel/fs_resource_gen.go index af635081..400dd9e4 100644 --- a/model/gmodel/fs_resource_gen.go +++ b/model/gmodel/fs_resource_gen.go @@ -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 diff --git a/server/render/consumer/assemble_render_data.go b/server/render/consumer/assemble_render_data.go index dd36cc7b..d5f79cbf 100644 --- a/server/render/consumer/assemble_render_data.go +++ b/server/render/consumer/assemble_render_data.go @@ -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, diff --git a/server/render/internal/logic/rendernotifylogic.go b/server/render/internal/logic/rendernotifylogic.go index af298155..b3466aaa 100644 --- a/server/render/internal/logic/rendernotifylogic.go +++ b/server/render/internal/logic/rendernotifylogic.go @@ -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, diff --git a/server/render/internal/types/types.go b/server/render/internal/types/types.go index 91d3407f..959af2ce 100644 --- a/server/render/internal/types/types.go +++ b/server/render/internal/types/types.go @@ -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 { diff --git a/server/resource/etc/resource.yaml b/server/resource/etc/resource.yaml new file mode 100644 index 00000000..b57c5f1c --- /dev/null +++ b/server/resource/etc/resource.yaml @@ -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" \ No newline at end of file diff --git a/server/resource/internal/config/config.go b/server/resource/internal/config/config.go new file mode 100644 index 00000000..51aad1c7 --- /dev/null +++ b/server/resource/internal/config/config.go @@ -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 + } + } +} diff --git a/server/resource/internal/handler/logocombinehandler.go b/server/resource/internal/handler/logocombinehandler.go new file mode 100644 index 00000000..23c1589c --- /dev/null +++ b/server/resource/internal/handler/logocombinehandler.go @@ -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) + } + } +} diff --git a/server/resource/internal/handler/resourceinfohandler.go b/server/resource/internal/handler/resourceinfohandler.go new file mode 100644 index 00000000..14b39d98 --- /dev/null +++ b/server/resource/internal/handler/resourceinfohandler.go @@ -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) + } + } +} diff --git a/server/resource/internal/handler/routes.go b/server/resource/internal/handler/routes.go new file mode 100644 index 00000000..581fdcb1 --- /dev/null +++ b/server/resource/internal/handler/routes.go @@ -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), + }, + }, + ) +} diff --git a/server/resource/internal/logic/logocombinelogic.go b/server/resource/internal/logic/logocombinelogic.go new file mode 100644 index 00000000..71fca87d --- /dev/null +++ b/server/resource/internal/logic/logocombinelogic.go @@ -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": "", + }) +} diff --git a/server/resource/internal/logic/resourceinfologic.go b/server/resource/internal/logic/resourceinfologic.go new file mode 100644 index 00000000..599bede4 --- /dev/null +++ b/server/resource/internal/logic/resourceinfologic.go @@ -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, + }) +} diff --git a/server/resource/internal/svc/servicecontext.go b/server/resource/internal/svc/servicecontext.go new file mode 100644 index 00000000..f4b7039e --- /dev/null +++ b/server/resource/internal/svc/servicecontext.go @@ -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)) +} diff --git a/server/resource/internal/types/types.go b/server/resource/internal/types/types.go new file mode 100644 index 00000000..47e38155 --- /dev/null +++ b/server/resource/internal/types/types.go @@ -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 +} diff --git a/server/resource/resource.go b/server/resource/resource.go new file mode 100644 index 00000000..6a09918b --- /dev/null +++ b/server/resource/resource.go @@ -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() +} diff --git a/server/resource/resource_test.go b/server/resource/resource_test.go new file mode 100644 index 00000000..d035c13a --- /dev/null +++ b/server/resource/resource_test.go @@ -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() +} diff --git a/server/upload/internal/handler/uploadfilebasehandler.go b/server/upload/internal/handler/uploadfilebasehandler.go index db910fcd..72452b27 100644 --- a/server/upload/internal/handler/uploadfilebasehandler.go +++ b/server/upload/internal/handler/uploadfilebasehandler.go @@ -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 { diff --git a/server/upload/internal/logic/uploadlogologic.go b/server/upload/internal/logic/uploadlogologic.go index 0b81695d..28551b04 100644 --- a/server/upload/internal/logic/uploadlogologic.go +++ b/server/upload/internal/logic/uploadlogologic.go @@ -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") diff --git a/server/websocket/internal/logic/datatransferlogic.go b/server/websocket/internal/logic/datatransferlogic.go index 79ba3855..56ad1faa 100644 --- a/server/websocket/internal/logic/datatransferlogic.go +++ b/server/websocket/internal/logic/datatransferlogic.go @@ -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() diff --git a/server/websocket/internal/logic/ws_render_image_logic.go b/server/websocket/internal/logic/ws_render_image_logic.go index fc887a0e..2fa1389e 100644 --- a/server/websocket/internal/logic/ws_render_image_logic.go +++ b/server/websocket/internal/logic/ws_render_image_logic.go @@ -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{ diff --git a/server_api/render.api b/server_api/render.api index 8dd06347..1fdfa90a 100644 --- a/server_api/render.api +++ b/server_api/render.api @@ -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"` } \ No newline at end of file diff --git a/server_api/resource.api b/server_api/resource.api new file mode 100644 index 00000000..0a37f93f --- /dev/null +++ b/server_api/resource.api @@ -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"` // 合图参数 + } +) \ No newline at end of file diff --git a/utils/basic/basic.go b/utils/basic/basic.go index 28f90a5d..8ccf7d45 100644 --- a/utils/basic/basic.go +++ b/utils/basic/basic.go @@ -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 { diff --git a/utils/basic/request_parse.go b/utils/basic/request_parse.go index 5c58877d..71443c83 100644 --- a/utils/basic/request_parse.go +++ b/utils/basic/request_parse.go @@ -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 diff --git a/utils/file/upload.go b/utils/file/upload.go new file mode 100644 index 00000000..38b0e826 --- /dev/null +++ b/utils/file/upload.go @@ -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:%+v,desc:%+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:%+v,desc:%+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:%+v,desc:%+v", err, "fail.upload.resourceInfoAdd.mysql") + return nil, err + } + } + } + return &uploadBaseRes, err +} diff --git a/utils/websocket_data/render_data.go b/utils/websocket_data/render_data.go index 5ec90785..18d272ed 100644 --- a/utils/websocket_data/render_data.go +++ b/utils/websocket_data/render_data.go @@ -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发送渲染完的数据