diff --git a/constants/render.go b/constants/render.go new file mode 100644 index 00000000..fe8b6a6f --- /dev/null +++ b/constants/render.go @@ -0,0 +1,203 @@ +package constants + +// 渲染要用到的面片模板 +const RENDER_FACE_SLICE_TEMPLATE_JSON = `[ + { + "id": "", + "tag": "MainColor", + "title": "", + "type": "color", + "text": "", + "fill": "{{MainColorFill}}", + "fontSize": 20, + "fontFamily": "Aqum2SmallCaps3", + "ifBr": false, + "ifShow": true, + "ifGroup": false, + "maxNum": 50, + "rotation": 0, + "lineHeight": 1, + "align": "center", + "verticalAlign": "middle", + "material": "", + "materialTime": "", + "materialName": "", + "QRcodeType": "", + "width": 1024, + "height": 1024, + "proportion": 60, + "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 + } + }, + { + "id": "", + "tag": "SecondaryColor", + "title": "贴图3", + "type": "color", + "text": "", + "fill": "{{SecondaryColorFill}}", + "fontSize": 20, + "fontFamily": "Aqum2SmallCaps3", + "ifBr": false, + "ifShow": true, + "ifGroup": false, + "maxNum": 50, + "rotation": 0, + "lineHeight": 1, + "align": "center", + "verticalAlign": "middle", + "material": "", + "materialTime": "", + "materialName": "", + "QRcodeType": "", + "width": 1024, + "height": 1024, + "proportion": 60, + "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 + } + }, + { + "id": "569d7981-25c3-3c03-0e7e-800c14800362", + "tag": "Slogan", + "title": "贴图4", + "type": "text", + "text": "", + "fill": "", + "fontSize": 13, + "fontFamily": "MontserratBold3", + "ifBr": false, + "ifShow": true, + "ifGroup": false, + "maxNum": 50, + "rotation": 0, + "lineHeight": 1, + "align": "center", + "verticalAlign": "middle", + "material": "", + "materialTime": "", + "materialName": "", + "QRcodeType": "", + "width": 309.9999999999993, + "height": 11.999265664648076, + "proportion": 60, + "x": 97.0015259021898, + "y": 575.300725990631, + "opacity": 1, + "optionalColor": [ + { + "color": "#000000", + "name": "Black", + "default": true + } + ], + "zIndex": 3, + "svgPath": "", + "follow": { + "fill": "38c09538-937d-510c-bf32-bfb1ce90cafa", + "ifShow": "", + "content": "" + }, + "group": [], + "cameraStand": { + "x": 0, + "y": 0, + "z": 45 + } + }, + { + "id": "c466e27c-d48f-db86-b85f-3c4c51114046", + "tag": "Logo", + "title": "贴图8", + "type": "image", + "text": "", + "fill": "#0082ca", + "fontSize": 20, + "fontFamily": "Aqum2SmallCaps3", + "ifBr": false, + "ifShow": true, + "ifGroup": false, + "maxNum": 50, + "rotation": 0, + "lineHeight": 1, + "align": "center", + "verticalAlign": "middle", + "material": "{{LogoMaterial}}", + "materialTime": "", + "materialName": "", + "QRcodeType": "", + "width": 282.9999999999999, + "height": 95.99999999999933, + "proportion": 60, + "x": 110.99999999999982, + "y": 438.7192999999991, + "opacity": 1, + "optionalColor": [ + { + "color": "#000000", + "name": "Black", + "default": false + }, + { + "name": "MainColor", + "color": "#0082ca", + "default": true + } + ], + "zIndex": 7, + "svgPath": "", + "follow": { + "fill": "", + "ifShow": "", + "content": "" + }, + "group": [], + "cameraStand": { + "x": 0, + "y": 0, + "z": 0 + } + } +] +` diff --git a/model/gmodel/fs_user_material_logic.go b/model/gmodel/fs_user_material_logic.go index d8ecaaf4..134ced4b 100644 --- a/model/gmodel/fs_user_material_logic.go +++ b/model/gmodel/fs_user_material_logic.go @@ -55,3 +55,18 @@ func (m *FsUserMaterialModel) RowSelectBuilder(selectData []string) *gorm.DB { } return rowBuilder } + +// 获取最新记录 +func (m *FsUserMaterialModel) FindLatestOne(ctx context.Context, userId int64, guestId int64) (resp FsUserMaterial, err error) { + if userId == 0 && guestId == 0 { + return FsUserMaterial{}, nil + } + db := m.db.WithContext(ctx).Model(&FsUserMaterial{}).Order("id DESC") + if userId != 0 { + db = db.Where("`user_id` = ?", userId) + } else { + db = db.Where("`guest_id` = ?", guestId) + } + err = db.Take(&resp).Error + return resp, err +} diff --git a/server/render/consumer/assemble_render_data.go b/server/render/consumer/assemble_render_data.go index dce9488a..6e6cc6a3 100644 --- a/server/render/consumer/assemble_render_data.go +++ b/server/render/consumer/assemble_render_data.go @@ -1,6 +1,8 @@ package consumer import ( + "encoding/json" + "fusenapi/utils/websocket_data" "github.com/zeromicro/go-zero/core/logx" ) @@ -10,5 +12,11 @@ type MqConsumerRenderAssemble struct { func (m *MqConsumerRenderAssemble) Run(data []byte) error { logx.Info("收到需要组装的消息:", string(data)) + var parseInfo websocket_data.AssembleRenderData + if err := json.Unmarshal(data, &parseInfo); err != nil { + logx.Error("MqConsumerRenderAssemble数据格式错误:", err) + return nil //不返回错误就删除消息 + } + return nil } diff --git a/server/render/internal/handler/getfaceslicehandler.go b/server/render/internal/handler/getfaceslicehandler.go new file mode 100644 index 00000000..ac337bb0 --- /dev/null +++ b/server/render/internal/handler/getfaceslicehandler.go @@ -0,0 +1,35 @@ +package handler + +import ( + "net/http" + "reflect" + + "fusenapi/utils/basic" + + "fusenapi/server/render/internal/logic" + "fusenapi/server/render/internal/svc" + "fusenapi/server/render/internal/types" +) + +func GetFaceSliceHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var req types.Request + userinfo, err := basic.RequestParse(w, r, svcCtx, &req) + if err != nil { + return + } + + // 创建一个业务逻辑层实例 + l := logic.NewGetFaceSliceLogic(r.Context(), svcCtx) + + rl := reflect.ValueOf(l) + basic.BeforeLogic(w, r, rl) + + resp := l.GetFaceSlice(&req, userinfo) + + if !basic.AfterLogic(w, r, rl, resp) { + basic.NormalAfterLogic(w, r, resp) + } + } +} diff --git a/server/render/internal/handler/routes.go b/server/render/internal/handler/routes.go index 0bbf0d3e..26ad8b15 100644 --- a/server/render/internal/handler/routes.go +++ b/server/render/internal/handler/routes.go @@ -17,6 +17,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/api/render/render_notify", Handler: RenderNotifyHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/api/render/get_face_slice", + Handler: GetFaceSliceHandler(serverCtx), + }, }, ) } diff --git a/server/render/internal/logic/getfaceslicelogic.go b/server/render/internal/logic/getfaceslicelogic.go new file mode 100644 index 00000000..0afb49fb --- /dev/null +++ b/server/render/internal/logic/getfaceslicelogic.go @@ -0,0 +1,69 @@ +package logic + +import ( + "encoding/json" + "errors" + "fusenapi/constants" + "fusenapi/utils/auth" + "fusenapi/utils/basic" + "gorm.io/gorm" + "strings" + + "context" + + "fusenapi/server/render/internal/svc" + "fusenapi/server/render/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type GetFaceSliceLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewGetFaceSliceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFaceSliceLogic { + return &GetFaceSliceLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +// 处理进入前逻辑w,r +// func (l *GetFaceSliceLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) { +// } + +// 处理逻辑后 w,r 如:重定向, resp 必须重新处理 +// func (l *GetFaceSliceLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) { +// // httpx.OkJsonCtx(r.Context(), w, resp) +// } + +func (l *GetFaceSliceLogic) GetFaceSlice(req *types.Request, userinfo *auth.UserInfo) (resp *basic.Response) { + if !userinfo.IsUser() && !userinfo.IsGuest() { + return resp.SetStatusWithMessage(basic.CodeUnAuth, "please login or access cookie") + } + //获取用户素材信息 + materialInfo, err := l.svcCtx.AllModels.FsUserMaterial.FindLatestOne(l.ctx, userinfo.UserId, userinfo.GuestId) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "user material info is not exists") + } + return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get user material info") + } + if materialInfo.Metadata == nil || *materialInfo.Metadata == "" { + return resp.SetStatusWithMessage(basic.CodeServiceErr, "user material info`Metadata is empty") + } + var info map[string]interface{} + if err = json.Unmarshal([]byte(*materialInfo.Metadata), &info); err != nil { + logx.Error(err) + return resp.SetStatusWithMessage(basic.CodeJsonErr, "invalid json format of metadata") + } + str := strings.ReplaceAll(constants.RENDER_FACE_SLICE_TEMPLATE_JSON, "{{MainColorFill}}", info["main_color_fill"].(string)) + str = strings.ReplaceAll(str, "{{SecondaryColorFill}}", info["secondary_color_fill"].(string)) + str = strings.ReplaceAll(str, "{{LogoMaterial}}", info["logo_material"].(string)) + var rspInfo interface{} + _ = json.Unmarshal([]byte(str), &rspInfo) + return resp.SetStatusWithMessage(basic.CodeOK, "success", rspInfo) +} diff --git a/server/websocket/internal/logic/datatransferlogic.go b/server/websocket/internal/logic/datatransferlogic.go index 66bb21b8..d999d8e3 100644 --- a/server/websocket/internal/logic/datatransferlogic.go +++ b/server/websocket/internal/logic/datatransferlogic.go @@ -65,12 +65,13 @@ 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 //互斥锁 + closeChan chan struct{} //ws连接关闭chan + isClose bool //是否已经关闭 + uniqueId uint64 //ws连接唯一标识 + inChan chan []byte //接受消息缓冲通道 + outChan chan []byte //发送回客户端的消息 + mutex sync.Mutex //互斥锁 + userId int64 renderProperty renderProperty //扩展云渲染属性 } @@ -83,10 +84,14 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp } defer conn.Close() //鉴权不成功10秒后断开 - /*isAuth, _ := l.checkAuth(svcCtx, r) + var ( + userInfo *auth.UserInfo + isAuth bool + ) + isAuth, userInfo = l.checkAuth(svcCtx, r) if !isAuth { time.Sleep(time.Second) //兼容下火狐 - rsp := types.DataTransferData{ + rsp := websocket_data.DataTransferData{ T: constants.WEBSOCKET_UNAUTH, D: nil, } @@ -96,7 +101,7 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp //发送关闭信息 _ = conn.WriteMessage(websocket.CloseMessage, nil) return - }*/ + } //生成连接唯一标识 uniqueId := websocketIdGenerator.Get() ws := wsConnectItem{ @@ -111,6 +116,11 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp 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 0b750cfb..d4974870 100644 --- a/server/websocket/internal/logic/ws_render_image_logic.go +++ b/server/websocket/internal/logic/ws_render_image_logic.go @@ -39,6 +39,7 @@ func (w *wsConnectItem) renderImage(data []byte) { } tmpData := websocket_data.AssembleRenderData{ TaskId: taskId, + UserId: w.userId, RenderData: renderImageData.RenderData, } d, _ := json.Marshal(tmpData) diff --git a/server_api/render.api b/server_api/render.api index f25d6ab3..8dd06347 100644 --- a/server_api/render.api +++ b/server_api/render.api @@ -19,6 +19,9 @@ service render { //云渲染完了通知接口 @handler RenderNotifyHandler post /api/render/render_notify(RenderNotifyReq) returns (response); + //获取面片信息 + @handler GetFaceSliceHandler + post /api/render/get_face_slice(request) returns (response); } //渲染完了通知接口 diff --git a/utils/websocket_data/render_data.go b/utils/websocket_data/render_data.go index 1add9d87..09a203da 100644 --- a/utils/websocket_data/render_data.go +++ b/utils/websocket_data/render_data.go @@ -8,8 +8,14 @@ type DataTransferData struct { // websocket接受要云渲染处理的数据 type RenderImageReqMsg struct { - RenderId string `json:"render_id"` //渲染id - RenderData interface{} `json:"render_data"` //参数数据 + RenderId string `json:"render_id"` //渲染id + RenderData RenderData `json:"render_data"` +} +type RenderData struct { + TemplateTagId int64 `json:"template_tag_id"` //模板标签id + ProductId int64 `json:"product_id"` //产品id + Data interface{} `json:"data"` //面片数据 + UserId int64 `json:"user_id"` //用户id } // websocket发送渲染完的数据 @@ -30,6 +36,7 @@ type ThirdPartyLoginRspMsg struct { // 发送到渲染组装的mq数据 type AssembleRenderData struct { - TaskId string `json:"task_id"` - RenderData interface{} `json:"render_data"` + TaskId string `json:"task_id"` + UserId int64 `json:"user_id"` + RenderData RenderData `json:"render_data"` }