diff --git a/fs_package_docker_image.sh b/fs_package_docker_image.sh index bb9fe4e5..a2b5e68c 100755 --- a/fs_package_docker_image.sh +++ b/fs_package_docker_image.sh @@ -2,14 +2,22 @@ name=${1%%\\*} #进入对应服务目录 cd server/$name +#把https加密密钥对复制进来 +cp /opt/env.yaml ./ +cp /opt/server.fusen.3718.cn.pem ./ +cp /opt/server.fusen.3718.cn.key ./ #构建二进制文件 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o ./bin/api-$name-srv ./$name.go -#删除之前旧的镜像 -docker rmi -f api-$name-srv:latest -docker rmi -f registry.cn-hangzhou.aliyuncs.com/fusen-test/fusen_docker_hub:latest #打包docker镜像 docker build -t api-$name-srv:latest . +#删除临时复制进来的文件 +rm ./env.yaml +rm ./server.fusen.3718.cn.pem +rm ./server.fusen.3718.cn.key #打tag(测试环境,正式把命名空间fusentest改成fusen) docker tag api-$name-srv:latest registry.cn-hangzhou.aliyuncs.com/fusen-test/$name:latest #推送到阿里云镜像库(测试环境,正式把命名空间fusentest改成fusen) docker push registry.cn-hangzhou.aliyuncs.com/fusen-test/$name:latest +#上传了的镜像 +docker rmi -f api-$name-srv:latest +docker rmi -f registry.cn-hangzhou.aliyuncs.com/fusen-test/$name:latest diff --git a/model/gmodel/fs_address_logic.go b/model/gmodel/fs_address_logic.go index ff3b16dc..24a2a845 100755 --- a/model/gmodel/fs_address_logic.go +++ b/model/gmodel/fs_address_logic.go @@ -14,6 +14,7 @@ func (a *FsAddressModel) GetOne(ctx context.Context, addressId int64, userId int } func (a *FsAddressModel) GetUserAllAddress(ctx context.Context, userId int64) (resp []*FsAddressWithDefault, err error) { + resp = make([]*FsAddressWithDefault, 0) var dbresp []*FsAddress err = a.db.WithContext(ctx).Model(&FsAddress{}).Where("`user_id` = ? and `status` = 1", userId).Order("`ltime` DESC").Find(&dbresp).Error diff --git a/proxyserver/main.go b/proxyserver/main.go index 1b0b9f9c..46d17f42 100644 --- a/proxyserver/main.go +++ b/proxyserver/main.go @@ -91,23 +91,20 @@ func main() { routes...)) } + Backends = append(Backends, NewBackend(mux, + fmt.Sprintf("http://%s:%d", "localhost", 9501), + "/api/v1/namespaces/kubernetes-dashboard")) + // 定义用于服务Vue dist文件夹的静态文件服务器 fs := http.FileServer(http.Dir(vueBuild)) indexHtmlPath := vueBuild + "/index.html" mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/api/") { - r.ParseMultipartForm(100 << 20) - // if err != nil { - // logx.Error(err) - // } - // 对/api开头的请求进行反向代理 proxy := httputil.NewSingleHostReverseProxy(apiURL) proxy.ServeHTTP(w, r) - return - } else { // 根据请求路径判断是服务静态文件或者是返回index.html idx := strings.Index(r.URL.Path[1:], "/") diff --git a/run_all_server.sh b/run_all_server.sh index 12b22d1d..ce566905 100755 --- a/run_all_server.sh +++ b/run_all_server.sh @@ -48,7 +48,7 @@ run_server() { # 循环检查screen进程是否存在 - [ -f .gitignore ] || echo $server_name > .gitignore + [ -f .gitignore ] || (echo "$server_name" > .gitignore && echo "main" >> .gitignore) # 使用 screen 运行 go run .go echo "Running $server_name" diff --git a/server/auth/.gitignore b/server/auth/.gitignore index 5fa1d814..e058e303 100644 --- a/server/auth/.gitignore +++ b/server/auth/.gitignore @@ -1 +1,2 @@ auth +main diff --git a/server/auth/Dockerfile b/server/auth/Dockerfile index 55d617cc..9dec07a2 100755 --- a/server/auth/Dockerfile +++ b/server/auth/Dockerfile @@ -1,6 +1,8 @@ FROM alpine WORKDIR /www/fusenapi/ -COPY ./bin/api-assistant-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc -CMD ["/www/fusenapi/api-assistant-srv"] +COPY ./bin/api-auth-srv /www/fusenapi/ +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ +CMD ["/www/fusenapi/api-auth-srv"] diff --git a/server/auth/etc/auth.yaml b/server/auth/etc/auth.yaml index 1713f5c4..76fecec0 100644 --- a/server/auth/etc/auth.yaml +++ b/server/auth/etc/auth.yaml @@ -2,6 +2,7 @@ Name: auth Host: localhost Port: 9980 ReplicaId: 10 +HomePage: "http://www.fusen.3718.cn" MainAddress: "https://server.fusen.3718.cn:9900" WebsocketAddr: "https://server.fusen.3718.cn:9914" SourceMysql: "fsreaderwriter:XErSYmLELKMnf3Dh@tcp(fusen.cdmigcvz3rle.us-east-2.rds.amazonaws.com:3306)/fusen" diff --git a/server/auth/internal/config/config.go b/server/auth/internal/config/config.go index c8b3c6d1..55576d81 100644 --- a/server/auth/internal/config/config.go +++ b/server/auth/internal/config/config.go @@ -12,6 +12,7 @@ type Config struct { Auth types.Auth ReplicaId uint64 + HomePage string MainAddress string WebsocketAddr string diff --git a/server/auth/internal/logic/email_manager.go b/server/auth/internal/logic/email_manager.go index 4d1bd84a..368b8fb1 100644 --- a/server/auth/internal/logic/email_manager.go +++ b/server/auth/internal/logic/email_manager.go @@ -28,7 +28,7 @@ func init() { log.Fatal(err) } - TimeLimit = check.NewTimelimit[string](EmailTaskResendTime) + TimeLimit = check.NewTimeLimit[string](EmailTaskResendTime) // Initialize the email manager EmailManager = &EmailSender{ @@ -53,16 +53,17 @@ func init() { } type EmailFormat struct { - TemplateName string // 模板名字 - UniqueKey string // 用于处理唯一的任务,重发都会被利用到 - TargetEmail string // 发送的目标email - CompanyName string // fs公司名 - ConfirmationLink string // fs确认连接 - SenderName string // 发送人 - SenderTitle string // 发送标题 - Extend map[string]string + TemplateName string // 模板名字 + UniqueKey string // 用于处理唯一的任务,重发都会被利用到 + TargetEmail string // 发送的目标email + CompanyName string // fs公司名 + ConfirmationLink string // fs确认连接 + SenderName string // 发送人 + SenderTitle string // 发送标题 + Extend map[string]string // 扩展参数 } +// 验证邮件格式是否符合要求 func (eformat *EmailFormat) Vaild() error { // 1. 检查模板名称 @@ -123,39 +124,47 @@ type EmailTask struct { SendTime time.Time // 处理的任务时间 } +// ProcessEmailTasks 是 EmailSender 结构体的方法,用于处理邮件任务。 func (m *EmailSender) ProcessEmailTasks() { for { + // 从 EmailTasks 通道中接收邮件任务 emailformat, ok := <-m.EmailTasks if !ok { log.Println("Email task channel closed") break } + // 验证邮件格式是否合法 err := emailformat.Vaild() if err != nil { logx.Error(err) continue } + // 加锁,避免并发修改 emailSending 字典 m.lock.Lock() + + // 检查 emailSending 字典中是否已存在相同的任务 _, isSending := m.emailSending[emailformat.UniqueKey] if isSending { m.lock.Unlock() continue } + // 将邮件任务添加到 emailSending 字典中 m.emailSending[emailformat.UniqueKey] = &EmailTask{ Email: emailformat, SendTime: time.Now().UTC(), } m.lock.Unlock() - // Acquire a token + // 获取一个信号量,限制同时发送的邮件任务数量 m.semaphore <- struct{}{} go func() { - defer func() { <-m.semaphore }() // Release a token + defer func() { <-m.semaphore }() // 释放一个信号量 + // 渲染邮件模板内容 content := RenderEmailTemplate( emailformat.TemplateName, emailformat.CompanyName, @@ -164,34 +173,41 @@ func (m *EmailSender) ProcessEmailTasks() { emailformat.SenderTitle, emailformat.Extend, ) + + // 发送邮件 err := smtp.SendMail("smtp.gmail.com:587", m.Auth, m.FromEmail, []string{emailformat.TargetEmail}, content) if err != nil { log.Printf("Failed to send email to %s: %v\n", emailformat, err) + // 重新发送邮件 m.Resend(emailformat.UniqueKey, content) } }() } } -// Resend 重发邮件 +// Resend 是 EmailSender 结构体的方法,用于重发邮件。 func (m *EmailSender) Resend(uniqueKey string, content []byte) { + // 等待一段时间后重发邮件,避免频繁重发 time.Sleep(m.ResendTimeLimit) m.lock.Lock() defer m.lock.Unlock() - // Check if the email task still exists and has not been sent successfully + + // 检查邮件任务是否仍存在并且未成功发送 if task, ok := m.emailSending[uniqueKey]; ok && task.SendTime.Add(m.ResendTimeLimit).After(time.Now().UTC()) { err := smtp.SendMail(task.Email.TargetEmail, m.Auth, m.FromEmail, []string{task.Email.TargetEmail}, content) if err != nil { log.Printf("Failed to resend email to %s: %v\n", task.Email.TargetEmail, err) } else { + // 从 emailSending 字典中删除已成功发送的邮件任务 delete(m.emailSending, uniqueKey) } } } -// ClearExpiredTasks 清除过期的邮件任务 +// ClearExpiredTasks 是 EmailSender 结构体的方法,用于清除过期的邮件任务。 func (m *EmailSender) ClearExpiredTasks() { + // 每分钟触发一次清除操作 ticker := time.NewTicker(time.Minute) defer ticker.Stop() @@ -199,6 +215,7 @@ func (m *EmailSender) ClearExpiredTasks() { <-ticker.C m.lock.Lock() + // 遍历 emailSending 字典,删除发送时间超过一定限制的过期任务 for email, task := range m.emailSending { if task.SendTime.Add(m.ResendTimeLimit).Before(time.Now().UTC()) { delete(m.emailSending, email) @@ -208,8 +225,19 @@ func (m *EmailSender) ClearExpiredTasks() { } } -func RenderEmailTemplate(templateName, companyName, confirmationLink, senderName, senderTitle string, extend map[string]string) []byte { +// RenderEmailTemplate 是一个渲染邮件模板的函数,根据给定的参数生成邮件内容。 +// 参数: +// - templateName: 邮件模板的名称 +// - companyName: 公司名称 +// - confirmationLink: 确认链接 +// - senderName: 发件人姓名 +// - senderTitle: 发件人职务 +// - extend: 扩展字段,包含其他自定义键值对的映射 +// 返回值: +// - []byte: 渲染后的邮件内容(以字节切片形式返回) +func RenderEmailTemplate(templateName, companyName, confirmationLink, senderName, senderTitle string, extend map[string]string) []byte { + // 创建一个包含邮件模板所需数据的映射 data := map[string]string{ "CompanyName": companyName, "ConfirmationLink": confirmationLink, @@ -217,16 +245,20 @@ func RenderEmailTemplate(templateName, companyName, confirmationLink, senderName "SenderTitle": senderTitle, } + // 将扩展字段中的键值对添加到数据映射中 for k, v := range extend { data[k] = v } + // 创建一个字节缓冲区,用于存储渲染后的邮件内容 var result bytes.Buffer - // result.Write([]byte("MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n")) + + // 使用给定的数据映射执行邮件模板渲染 err := tpls.ExecuteTemplate(&result, templateName, data) if err != nil { log.Fatal(err) } + // 将渲染后的邮件内容转换为字节切片并返回 return result.Bytes() } diff --git a/server/auth/internal/logic/useremailconfirmationlogic.go b/server/auth/internal/logic/useremailconfirmationlogic.go index 3cb86bc8..94330ab3 100644 --- a/server/auth/internal/logic/useremailconfirmationlogic.go +++ b/server/auth/internal/logic/useremailconfirmationlogic.go @@ -14,7 +14,6 @@ import ( "fusenapi/server/auth/internal/svc" "fusenapi/server/auth/internal/types" - "github.com/474420502/requests" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/rest/httpx" "gorm.io/gorm" @@ -57,7 +56,7 @@ func FinishRegister(svcCtx *svc.ServiceContext, user *gmodel.FsUser, token *auth event.Data = wevent.DataEmailRegister{ JwtToken: jwtToken, } - err = CommonNotify(svcCtx.Config.WebsocketAddr, token.Wid, event) + err = wevent.CommonNotify(svcCtx.Config.WebsocketAddr, token.Wid, event) if err != nil { // logx.Error(err, token.TraceId) return err @@ -66,34 +65,6 @@ func FinishRegister(svcCtx *svc.ServiceContext, user *gmodel.FsUser, token *auth return nil } -func CommonNotify(WebsocketAddr, wid string, event *wevent.WebsocketEvent) error { - - reqWebsocketAddr := fmt.Sprintf("%s/api/websocket/common_notify", WebsocketAddr) - tp := requests.Post(reqWebsocketAddr) - tp.SetBodyJson(requests.M{ - "wid": wid, - "data": event, - }) - - wresp, err := tp.Execute() - if err != nil { - // logx.Error(err, token.TraceId) - return err - } - - result := wresp.Json() - - if !result.Get("code").Exists() { - return fmt.Errorf("send %s is error", reqWebsocketAddr) - } - - if result.Get("code").Int() != 200 { - return fmt.Errorf("%s", result.String()) - } - - return nil -} - func (l *UserEmailConfirmationLogic) UserEmailConfirmation(req *types.RequestEmailConfirmation, userinfo *auth.UserInfo) (resp *basic.Response) { // 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data) // userinfo 传入值时, 一定不为null @@ -176,7 +147,7 @@ func (l *UserEmailConfirmationLogic) UserEmailConfirmation(req *types.RequestEma } event := wevent.NewWebsocketEventSuccess(wevent.UserResetToken, rt.TraceId) - err = CommonNotify(l.svcCtx.Config.MainAddress, rt.Wid, event) + err = wevent.CommonNotify(l.svcCtx.Config.MainAddress, rt.Wid, event) if err != nil { logx.Error(err, rt.TraceId) return resp.SetStatus(basic.CodeResetPasswordErr, err.Error()) diff --git a/server/auth/internal/logic/userresetpasswordhtmllogic.go b/server/auth/internal/logic/userresetpasswordhtmllogic.go index 50496ebe..a56d40c7 100644 --- a/server/auth/internal/logic/userresetpasswordhtmllogic.go +++ b/server/auth/internal/logic/userresetpasswordhtmllogic.go @@ -50,7 +50,7 @@ func (l *UserResetPasswordHtmlLogic) UserResetPasswordHtml(req *types.RequestUse func (l *UserResetPasswordHtmlLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) { err := tpls.ExecuteTemplate(w, "reset_confirm.tpl", map[string]string{ - "HomePage": "http://www.fusen.3718.cn", + "HomePage": l.svcCtx.Config.HomePage, "ResetToken": l.ResetToken, "ResetPasswordLink": l.svcCtx.Config.MainAddress + "/api/auth/reset/password", }) diff --git a/server/auth/internal/logic/userresetpasswordlogic.go b/server/auth/internal/logic/userresetpasswordlogic.go index 7f0c943c..aa9ab591 100644 --- a/server/auth/internal/logic/userresetpasswordlogic.go +++ b/server/auth/internal/logic/userresetpasswordlogic.go @@ -51,7 +51,7 @@ func (l *UserResetPasswordLogic) UserResetPassword(req *types.RequestUserResetPa } if time.Since(rt.CreateAt) > 30*time.Minute { - return resp.SetStatusWithMessage(basic.CodeOAuthConfirmationTimeoutErr, "Verification links expire after 30 minute.") + return resp.SetStatusWithMessage(basic.CodeOAuthConfirmationTimeoutErr, "verification links expire after 30 minute.") } err = l.svcCtx.AllModels.FsUser.Transaction(l.ctx, func(tx *gorm.DB) error { @@ -67,7 +67,7 @@ func (l *UserResetPasswordLogic) UserResetPassword(req *types.RequestUserResetPa } if *user.PasswordHash == req.NewPassword { - return fmt.Errorf("the password is the same as the old one. It needs to be changed") + return fmt.Errorf("the password is the same as the old one. it needs to be changed") } return tx.Where("id = ?", rt.UserId).Update("PasswordHash", req.NewPassword).Error @@ -79,7 +79,7 @@ func (l *UserResetPasswordLogic) UserResetPassword(req *types.RequestUserResetPa } event := wevent.NewWebsocketEventSuccess(wevent.UserResetToken, rt.TraceId) - err = CommonNotify(l.svcCtx.Config.WebsocketAddr, rt.Wid, event) + err = wevent.CommonNotify(l.svcCtx.Config.WebsocketAddr, rt.Wid, event) if err != nil { logx.Error(err, rt.TraceId) return resp.SetStatusWithMessage(basic.CodeResetPasswordErr, err.Error()) diff --git a/server/base/.gitignore b/server/base/.gitignore index df967b96..87bfddd1 100644 --- a/server/base/.gitignore +++ b/server/base/.gitignore @@ -1 +1,2 @@ base +main diff --git a/server/base/Dockerfile b/server/base/Dockerfile new file mode 100755 index 00000000..22591e88 --- /dev/null +++ b/server/base/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine + +WORKDIR /www/fusenapi/ +COPY ./bin/api-base-srv /www/fusenapi/ +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ +CMD ["/www/fusenapi/api-base-srv"] diff --git a/server/canteen/.gitignore b/server/canteen/.gitignore index a62359de..9b5d09aa 100644 --- a/server/canteen/.gitignore +++ b/server/canteen/.gitignore @@ -1 +1,2 @@ canteen +main diff --git a/server/canteen/Dockerfile b/server/canteen/Dockerfile index a268222d..025f6e60 100755 --- a/server/canteen/Dockerfile +++ b/server/canteen/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-canteen-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-canteen-srv"] diff --git a/server/collection/.gitignore b/server/collection/.gitignore new file mode 100644 index 00000000..baa9c637 --- /dev/null +++ b/server/collection/.gitignore @@ -0,0 +1,2 @@ +collection +main diff --git a/server/collection/Dockerfile b/server/collection/Dockerfile new file mode 100755 index 00000000..a97329a9 --- /dev/null +++ b/server/collection/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine + +WORKDIR /www/fusenapi/ +COPY ./bin/api-collection-srv /www/fusenapi/ +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ +CMD ["/www/fusenapi/api-collection-srv"] diff --git a/server/collection/internal/handler/routes.go b/server/collection/internal/handler/routes.go index 1d8b59a5..ac4eade4 100644 --- a/server/collection/internal/handler/routes.go +++ b/server/collection/internal/handler/routes.go @@ -27,6 +27,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/api/collection/get_collect_product_list", Handler: GetCollectProductListHandler(serverCtx), }, + { + Method: http.MethodGet, + Path: "/api/collection/test_ai", + Handler: TestAiHandler(serverCtx), + }, }, ) } diff --git a/server/collection/internal/handler/testaihandler.go b/server/collection/internal/handler/testaihandler.go new file mode 100644 index 00000000..7682a6a8 --- /dev/null +++ b/server/collection/internal/handler/testaihandler.go @@ -0,0 +1,35 @@ +package handler + +import ( + "net/http" + "reflect" + + "fusenapi/utils/basic" + + "fusenapi/server/collection/internal/logic" + "fusenapi/server/collection/internal/svc" + "fusenapi/server/collection/internal/types" +) + +func TestAiHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var req types.TestAiReq + userinfo, err := basic.RequestParse(w, r, svcCtx, &req) + if err != nil { + return + } + + // 创建一个业务逻辑层实例 + l := logic.NewTestAiLogic(r.Context(), svcCtx) + + rl := reflect.ValueOf(l) + basic.BeforeLogic(w, r, rl) + + resp := l.TestAi(&req, userinfo) + + if !basic.AfterLogic(w, r, rl, resp) { + basic.NormalAfterLogic(w, r, resp) + } + } +} diff --git a/server/collection/internal/logic/testailogic.go b/server/collection/internal/logic/testailogic.go new file mode 100644 index 00000000..aeaa29a6 --- /dev/null +++ b/server/collection/internal/logic/testailogic.go @@ -0,0 +1,96 @@ +package logic + +import ( + "encoding/json" + "fmt" + "fusenapi/constants" + "fusenapi/utils/auth" + "fusenapi/utils/basic" + "fusenapi/utils/curl" + "github.com/google/uuid" + "strings" + "sync" + "time" + + "context" + + "fusenapi/server/collection/internal/svc" + "fusenapi/server/collection/internal/types" + + "github.com/zeromicro/go-zero/core/logx" +) + +type TestAiLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewTestAiLogic(ctx context.Context, svcCtx *svc.ServiceContext) *TestAiLogic { + return &TestAiLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +// 处理进入前逻辑w,r +// func (l *TestAiLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) { +// } +var data = `{"module_data":{"groupOptions":{"fill_1":{"list":[{"id":"442c4e24-53ae-2a76-8e6b-7550e2747369","tag":""},{"id":"646004b2-ad82-761f-c939-b85f96f97f9a","tag":""},{"id":"b6e4ab68-b36a-9534-d53f-722c93d54657","tag":""},{"id":"280ced37-6725-562f-4272-79bacfcadb12","tag":""},{"id":"a6aac5c1-8e54-c0f1-5749-9847f0fe497e","tag":""},{"id":"2b2eac02-37ae-0b1e-898e-d9e3166dfc9a","tag":""},{"id":"88181235-0317-fe9e-b202-cc7938acd449","tag":""},{"id":"93aba661-9260-8420-4adc-feef1657f4e3","tag":""},{"id":"50dec384-7501-dfdd-9c65-ddf3142540af","tag":""},{"id":"d8337893-bfab-0e43-2ef2-2606d1612fec","tag":""},{"id":"508859ae-cc8f-0b19-3554-c7632838d3fc","tag":""},{"id":"d5d3a1cb-2b13-2345-088d-b9163edf8aa6","tag":"","type":"color"}],"tag":"","title":"主色","value":"#E71F18"},"fill_2":{"list":[],"tag":"","title":"副色","value":"#000"},"fill_3":{"list":[],"tag":"","title":"备选色","value":"#000"},"fill_4":{"list":[{"id":"dbc50336-de0c-4b4a-f0aa-1a6444a3f615","tag":"","type":"color"},{"id":"cd0bf3b0-761f-e5b4-d7b6-f63fc54baa77","tag":"","type":"color"}],"tag":"","title":"固定色A","value":"#FFFFFF"},"material_1":{"id":"material_1","list":[{"id":"9baa3b65-c4f0-ebd8-866f-7e8d8e26e462","tag":"Logo"},{"id":"d9634ebf-78e2-3ead-30c5-cd545c3bfdaa","tag":"Logo"}],"tag":"Logo","title":"LOGO分组","value":""},"text_1":{"id":"text_1","list":[],"tag":"Slogan","title":"Slogan分组","value":"The ultimate experience"},"text_2":{"id":"text_2","list":[],"tag":"QRcode","title":"二维码分组","value":"www.fusen.cn"}},"id":1108,"material":"https://s3.us-west-1.amazonaws.com/storage.fusenpack.com/a0914c5b48319ebeed923ae780a5f1a00736056e0b5406a60095cfd4010b31d8","materialList":[{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":1024,"id":"cf7caf81-1470-613a-7691-9c5eac710a0e","ifGroupNoBr":false,"lineHeight":1,"material":"https://s3.us-west-1.amazonaws.com/storage.fusenpack.com/c83b49a900c3bffff4fbf0d5b8e7d494033b3b793b660ccd1bf98b2558573adb","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"","systemShow":true,"tag":"","text":"","title":"底图","type":"background","verticalAlign":"middle","visible":true,"width":1024,"x":0,"y":0,"zIndex":0},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"0991b333-4baf-8fcc-a8bb-aa5546811815","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"","systemShow":true,"tag":"","text":"","title":"SVG元素","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":1},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":["b3ce7508-232a-89aa-72fd-dbbacc32f938","37ffcea7-0134-e6ee-014a-f0dddc466cf0","869c16d3-4b82-b052-6def-9162e126c57a"],"height":100,"id":"f9b30c55-6aa1-0d82-9a85-945fd6b061d7","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"","systemShow":true,"tag":"","text":"","title":"合图组","type":"combine","verticalAlign":"middle","visible":false,"width":100,"x":0,"y":0,"zIndex":2},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":"f9b30c55-6aa1-0d82-9a85-945fd6b061d7","group":[],"height":100,"id":"b3ce7508-232a-89aa-72fd-dbbacc32f938","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"","systemShow":false,"tag":"Website","text":"www.fusen.cn","title":"网址","type":"text","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":3},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":"f9b30c55-6aa1-0d82-9a85-945fd6b061d7","group":[],"height":100,"id":"37ffcea7-0134-e6ee-014a-f0dddc466cf0","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"","systemShow":false,"tag":"Address","text":"S Halsted St, Chicago, IL 60608 U.S.","title":"地址","type":"text","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":4},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":"f9b30c55-6aa1-0d82-9a85-945fd6b061d7","group":[],"height":100,"id":"869c16d3-4b82-b052-6def-9162e126c57a","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"","systemShow":false,"tag":"Phone","text":"070-6666666","title":"电话","type":"text","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":5},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_4","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"cd0bf3b0-761f-e5b4-d7b6-f63fc54baa77","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M511.2,513.5H24.7V378.9h486.6V513.5z","systemShow":true,"tag":"","text":"","title":"SVG元素_11","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":6},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_4","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"dbc50336-de0c-4b4a-f0aa-1a6444a3f615","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M508.8,593.6l0,138.8l-51-11.2H78l-51,11.2l0-138.8l3.2-1.1v-16.9h475.4l0,16.9L508.8,593.6z","systemShow":true,"tag":"","text":"","title":"SVG元素_12","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":7},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"d5d3a1cb-2b13-2345-088d-b9163edf8aa6","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M508.3,512.2v12.3h-0.5l-2.2,3c0.1,12.4,0.2,24.8,0.3,37.2c-1.2,0.8-2.4,1.6-3.6,2.3c0.1,8.5,0.2,17.1,0.3,25.6c-15.3-5.5-30.7-11-46-16.5c-18.6-0.2-37.2-0.3-55.8-0.5c0-1.5,0-2.9,0.1-4.4c-17.2,0.1-34.4,0.2-51.6,0.2c0,1.3,0,2.7,0,4c-55.4,0.1-110.8,0.1-166.2,0.2c-0.1-1.5-0.1-3-0.2-4.5c-16.5,0.1-33.1,0.1-49.6,0.2c-0.1,1.4-0.1,2.8-0.2,4.2c-18.4,0.2-36.8,0.3-55.2,0.5v-51.8h0v51.8h0v0.4l-45.9,15.7c0.1-8.2,0.1-16.4,0.2-24.6c-1.6-0.9-3.3-1.8-4.9-2.7l-0.2-37.4l-2.2-2.9h-0.3v-12.3H508.3z","systemShow":true,"tag":"","text":"","title":"SVG元素_13","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":8},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"442c4e24-53ae-2a76-8e6b-7550e2747369","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M78,714.4h379.7V942H78V714.4z","systemShow":true,"tag":"","text":"","title":"SVG元素_14","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":9},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"646004b2-ad82-761f-c939-b85f96f97f9a","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M440.6,145.7h70.6V379l-70.6,0V145.7z","systemShow":true,"tag":"","text":"","title":"SVG元素_15","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":10},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"b6e4ab68-b36a-9534-d53f-722c93d54657","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M83.3,85.2c-0.2-21.2-0.4-42.4-0.7-63.7c17.6-0.2,35.1-0.3,52.7-0.5c0.2-2.1,0.4-4.1,0.7-6.2c15.3-0.1,30.6-0.1,45.8-0.2c0.6,2.2,1.1,4.4,1.7,6.7c55.4-0.2,110.9-0.4,166.3-0.7c0.6-1.9,1.1-3.8,1.7-5.7c15.1-0.1,30.1-0.1,45.2-0.2c0.6,2.1,1.2,4.1,1.8,6.2c17.4,0.1,34.8,0.2,52.2,0.3c-0.1,20.9-0.1,41.8-0.2,62.7c-57.4,0.4-114.8,0.7-172.2,1.1c0.2-1.2,0.6-5-1.7-8.8c-0.6-0.9-3.8-5.8-9.1-5.8c-5,0.1-8.4,4.5-9.7,7.7c-1.1,2.9-0.9,5.5-0.6,6.9C199.3,85.1,141.3,85.2,83.3,85.2z","systemShow":true,"tag":"","text":"","title":"SVG元素_16","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":11},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"280ced37-6725-562f-4272-79bacfcadb12","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M83.3,145.8c0-20.4-0.1-40.8-0.1-61.2c58,0.2,116,0.4,174.1,0.6c0.1,1.2,0.5,4.6,3.1,7.6c0.9,1.1,3.4,3.9,7.3,4c4.2,0.1,6.9-2.8,7.6-3.6c2.9-3.2,3-6.9,3-7.9c57.4-0.4,114.8-0.7,172.2-1.1c0,20.6,0,41.3-0.1,61.9C328,145.9,205.7,145.8,83.3,145.8z","systemShow":true,"tag":"","text":"","title":"SVG元素_17","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":12},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"a6aac5c1-8e54-c0f1-5749-9847f0fe497e","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M452.4,942l-17.3,59.9H100.4L82,942c57.6,0,115.2-0.1,172.8-0.1c-0.1,0.6-1.3,6.2,2.7,10.7c0.5,0.6,3.7,4,8.7,3.8c4.1-0.1,7.2-2.5,8.8-4.8c2.8-3.9,2-8.5,2.2-9.7C278.1,935.1,325.5,933.6,452.4,942z","systemShow":true,"tag":"","text":"","title":"SVG元素_18","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":13},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"2b2eac02-37ae-0b1e-898e-d9e3166dfc9a","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M78,379c0.1-78,0.3-155.9,0.4-233.9c20.8,0.2,41.7,0.4,62.5,0.6v2.9h37.4v-2.9h177.5v2.9h37.4v-2.9c21.5,0,43,0,64.5,0c0.9,77.8,1.9,155.5,2.8,233.3C333.1,379,205.6,379,78,379z","systemShow":true,"tag":"","text":"","title":"SVG元素_19","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":14},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"88181235-0317-fe9e-b202-cc7938acd449","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M451.6,145.6c0.1-21,0.1-41.9,0.2-62.9c3.7-19.3,7.4-38.6,11.2-57.9c14.5,0,29,0.1,43.4,0.1c0,40.2,0,80.4,0,120.6C488.2,145.5,469.9,145.5,451.6,145.6z","systemShow":true,"tag":"","text":"","title":"SVG元素_20","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":15},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"93aba661-9260-8420-4adc-feef1657f4e3","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M79,379l-54.4,0V145.7H79V379z","systemShow":true,"tag":"","text":"","title":"SVG元素_21","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":16},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"50dec384-7501-dfdd-9c65-ddf3142540af","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M26.2,145.6c0-39.9,0-79.8,0-119.7c15,0,30-0.1,45-0.1c3.7,19.5,7.4,39,11.2,58.5c-0.1,20.5-0.2,40.9-0.4,61.4C63.4,145.7,44.8,145.7,26.2,145.6z","systemShow":true,"tag":"","text":"","title":"SVG元素_22","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":17},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"d8337893-bfab-0e43-2ef2-2606d1612fec","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M450.3,714.4l58.5,10.7c0.2,66.5,0.3,133,0.5,199.5c-19.7,6.3-39.3,12.7-59,19V714.4z","systemShow":true,"tag":"","text":"","title":"SVG元素_23","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":18},{"QRcodeType":1,"algorithm":{"isLogoSplit":false,"list":[]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"fill_1","material":"","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":100,"id":"508859ae-cc8f-0b19-3554-c7632838d3fc","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"M82.4,943.8c-18.4-6.1-36.7-12.3-55.1-18.4C27.2,858.5,27.1,791.8,27,725l57.3-10.6C83.7,790.8,83.1,867.3,82.4,943.8z","systemShow":true,"tag":"","text":"","title":"SVG元素_24","type":"color","verticalAlign":"middle","visible":true,"width":100,"x":0,"y":0,"zIndex":19},{"QRcodeType":1,"algorithm":{"isLogoSplit":true,"list":[{"id":1,"list":[],"type":0},{"id":5,"list":[],"type":2}]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"","material":"material_1","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":83.3582,"id":"9baa3b65-c4f0-ebd8-866f-7e8d8e26e462","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"","systemShow":true,"tag":"Logo","text":"","title":"logo","type":"image","verticalAlign":"middle","visible":true,"width":158.1738,"x":114.9929,"y":604.1777,"zIndex":20},{"QRcodeType":1,"algorithm":{"isLogoSplit":true,"list":[{"id":2,"list":[],"type":0},{"id":7,"list":[],"type":2}]},"align":"center","cameraStand":{"x":0,"y":0,"z":0},"fill":"#000000","follow":{"fill":"","material":"material_1","text":"","visible":""},"fontFamily":"Aqum2SmallCaps3","fontSize":20,"fromCombineId":-1,"group":[],"height":99.896,"id":"d9634ebf-78e2-3ead-30c5-cd545c3bfdaa","ifGroupNoBr":false,"lineHeight":1,"material":"","maxNum":50,"opacity":1,"paddingNum":0,"proportion":60,"rotation":0,"svgPath":"","systemShow":true,"tag":"Logo","text":"","title":"logo_25","type":"image","verticalAlign":"middle","visible":true,"width":228.5687,"x":191.3252,"y":809.4954,"zIndex":21}]},"param_data":{"address":"","is_crop":true,"line":"thick","logo_url":"https://s3.us-west-1.amazonaws.com/storage.fusenpack.com/c94100bd48992ea8f88c0bc65571547b2e4bd499a5b15bf0f7ba938e70693614` + "?id={{rand}}" + `","other":"","other1":"","phone":"","qrcode":"","ratio":1,"resolution":"512","shape":"other","slogan":"","template_tag":{"B1":[["#C44137"],["#235C9C"]],"B4":[["#C44137","#235C9C"]],"C1":[["#C44137"],["#235C9C"]],"C2":[["#C44137"],["#235C9C"]],"C3":[["#C44137"],["#235C9C"]],"C5":[["#C44137"],["#235C9C"]],"C6":[["#C44137"],["#235C9C"]]},"template_tag_id":["B1","C1","C2","C3","C5","C6","B4"],"template_tag_selected":{"color":[["#C44137"],["#235C9C"]],"index":0,"template_tag":"C1"},"template_tagid":"C1","version":"2","website":""},"tag_data":[{"fixed":0,"name":"主色","tag":"fill_1","value":""},{"fixed":0,"name":"副色","tag":"fill_2","value":""},{"fixed":0,"name":"备选色","tag":"fill_3","value":""},{"fixed":1,"name":"固定色A","tag":"fill_4","value":"rgba(255,255 ,255,1)"}]}` + +func (l *TestAiLogic) TestAi(req *types.TestAiReq, userinfo *auth.UserInfo) (resp *basic.Response) { + if req.Num > 100 { + return resp.SetStatusWithMessage(basic.CodeServiceErr, "num can`t not large than 100") + } + defer func() { + if err := recover(); err != nil { + logx.Error("测试异常:", err) + } + }() + begin := time.Now().UTC().UnixMilli() + errChan := make(chan string) + w := sync.WaitGroup{} + for i := 0; i < req.Num; i++ { + w.Add(1) + go func() { + defer func() { + if err := recover(); err != nil { + logx.Error("测试异常2:", err) + } + }() + defer w.Done() + data = strings.ReplaceAll(data, "{{rand}}", uuid.New().String()) + var postMap map[string]interface{} + if err := json.Unmarshal([]byte(data), &postMap); err != nil { + errChan <- err.Error() + return + } + var resultBLM constants.BLMServiceUrlResult + err := curl.NewClient(l.ctx, &curl.Config{ + BaseUrl: "http://ai.fusen.3718.cn:8999", + Url: constants.BLMServiceUrlLogoCombine, + RequireTimeout: time.Second * 15, + }).PostJson(postMap, &resultBLM) + if err != nil { + errChan <- err.Error() + return + } + }() + } + go func() { + w.Wait() + close(errChan) + }() + for v := range errChan { + return resp.SetStatusWithMessage(basic.CodeServiceErr, v) + } + end := time.Now().UTC().UnixMilli() - begin + avg := end / int64(req.Num) + return resp.SetStatus(basic.CodeOK, fmt.Sprintf("总耗时:%d ms,平均耗时%d ms", end, avg)) +} + +// 处理逻辑后 w,r 如:重定向, resp 必须重新处理 +// func (l *TestAiLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) { +// // httpx.OkJsonCtx(r.Context(), w, resp) +// } diff --git a/server/collection/internal/types/types.go b/server/collection/internal/types/types.go index 62d68266..f3707230 100644 --- a/server/collection/internal/types/types.go +++ b/server/collection/internal/types/types.go @@ -40,6 +40,10 @@ type GetCollectProductListRspItem struct { IsDeleted int64 `json:"is_deleted"` } +type TestAiReq struct { + Num int `form:"num"` +} + type Request struct { } diff --git a/server/data-transfer/.gitignore b/server/data-transfer/.gitignore index 46f1adc6..ebeb0ed4 100644 --- a/server/data-transfer/.gitignore +++ b/server/data-transfer/.gitignore @@ -1 +1,2 @@ data-transfer +main diff --git a/server/data-transfer/Dockerfile b/server/data-transfer/Dockerfile index 82090c4b..fed55f76 100755 --- a/server/data-transfer/Dockerfile +++ b/server/data-transfer/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-data-transfer-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-data-transfer-srv"] diff --git a/server/home-user-auth/.gitignore b/server/home-user-auth/.gitignore index 33431957..d0899ccd 100644 --- a/server/home-user-auth/.gitignore +++ b/server/home-user-auth/.gitignore @@ -1 +1,2 @@ home-user-auth +main diff --git a/server/home-user-auth/Dockerfile b/server/home-user-auth/Dockerfile index 3c7a0da5..91734fe2 100755 --- a/server/home-user-auth/Dockerfile +++ b/server/home-user-auth/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-home-user-auth-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-home-user-auth-srv"] diff --git a/server/info/.gitignore b/server/info/.gitignore index 55021245..14aa2da5 100644 --- a/server/info/.gitignore +++ b/server/info/.gitignore @@ -1 +1,2 @@ info +main diff --git a/server/info/Dockerfile b/server/info/Dockerfile new file mode 100755 index 00000000..54194853 --- /dev/null +++ b/server/info/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine + +WORKDIR /www/fusenapi/ +COPY ./bin/api-info-srv /www/fusenapi/ +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ +CMD ["/www/fusenapi/api-info-srv"] diff --git a/server/map-library/.gitignore b/server/map-library/.gitignore index c90c25e4..178d6e01 100644 --- a/server/map-library/.gitignore +++ b/server/map-library/.gitignore @@ -1 +1,2 @@ map-library +main diff --git a/server/map-library/Dockerfile b/server/map-library/Dockerfile index 460cbfd5..2cfa5e86 100755 --- a/server/map-library/Dockerfile +++ b/server/map-library/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-map-library-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-map-library-srv"] diff --git a/server/order/.gitignore b/server/order/.gitignore index 76f31992..6139e512 100644 --- a/server/order/.gitignore +++ b/server/order/.gitignore @@ -1 +1,2 @@ order +main diff --git a/server/order/Dockerfile b/server/order/Dockerfile new file mode 100755 index 00000000..a4796361 --- /dev/null +++ b/server/order/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine + +WORKDIR /www/fusenapi/ +COPY ./bin/api-order-srv /www/fusenapi/ +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ +CMD ["/www/fusenapi/api-order-srv"] diff --git a/server/pay/.gitignore b/server/pay/.gitignore index 65847d8a..ca97d3a9 100644 --- a/server/pay/.gitignore +++ b/server/pay/.gitignore @@ -1 +1,2 @@ pay +main diff --git a/server/pay/Dockerfile b/server/pay/Dockerfile index df5dab5b..83609c84 100755 --- a/server/pay/Dockerfile +++ b/server/pay/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-pay-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-pay-srv"] diff --git a/server/product-model/.gitignore b/server/product-model/.gitignore index d4af764a..5f9dc70e 100644 --- a/server/product-model/.gitignore +++ b/server/product-model/.gitignore @@ -1 +1,2 @@ product-model +main diff --git a/server/product-model/Dockerfile b/server/product-model/Dockerfile index e207b762..b13ef0b3 100755 --- a/server/product-model/Dockerfile +++ b/server/product-model/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-product-model-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-product-model-srv"] diff --git a/server/product-template-tag/.gitignore b/server/product-template-tag/.gitignore index 5e23e081..26e81105 100644 --- a/server/product-template-tag/.gitignore +++ b/server/product-template-tag/.gitignore @@ -1 +1,2 @@ product-template-tag +main diff --git a/server/product-template-tag/Dockerfile b/server/product-template-tag/Dockerfile index abc15189..9c4000b0 100755 --- a/server/product-template-tag/Dockerfile +++ b/server/product-template-tag/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-product-template-tag-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-product-template-tag-srv"] diff --git a/server/product-template/.gitignore b/server/product-template/.gitignore index 636f2b18..db010145 100644 --- a/server/product-template/.gitignore +++ b/server/product-template/.gitignore @@ -1 +1,2 @@ product-template +main diff --git a/server/product-template/Dockerfile b/server/product-template/Dockerfile index 2012a6d9..479efdad 100755 --- a/server/product-template/Dockerfile +++ b/server/product-template/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-product-template-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-product-template-srv"] diff --git a/server/product/.gitignore b/server/product/.gitignore index 8954caac..9182117d 100644 --- a/server/product/.gitignore +++ b/server/product/.gitignore @@ -1 +1,2 @@ product +main diff --git a/server/product/Dockerfile b/server/product/Dockerfile index 4ec40fcf..4e67a467 100755 --- a/server/product/Dockerfile +++ b/server/product/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-product-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-product-srv"] diff --git a/server/product/internal/logic/gettagproductlistlogic.go b/server/product/internal/logic/gettagproductlistlogic.go index a3861e39..afb6c5aa 100644 --- a/server/product/internal/logic/gettagproductlistlogic.go +++ b/server/product/internal/logic/gettagproductlistlogic.go @@ -49,7 +49,6 @@ func (l *GetTagProductListLogic) GetTagProductList(req *types.GetTagProductListR tStatus := int64(1) tReq := gmodel.GetAllTagByParamsReq{ Status: &tStatus, - OrderBy: "`sort` DESC", WithChild: true, //需要子集 Category: 1, //前台网站用的 Level: 2, //等级是2的 @@ -425,20 +424,6 @@ func (l *GetTagProductListLogic) getTagProducts(req getTagProductsReq) (productL if _, ok = req.MapTagProp[productInfo.Id]; ok { item.CoverDefault = req.MapTagProp[productInfo.Id] } - //千人千面处理 - /*r := image.ThousandFaceImageFormatReq{ - Size: int(req.Size), - IsThousandFace: 0, - Cover: *productInfo.Cover, - CoverImg: *productInfo.CoverImg, - ProductId: productInfo.Id, - UserId: req.User.Id, - } - if req.User.Id != 0 { - r.IsThousandFace = int(*req.User.IsThousandFace) - } - image.ThousandFaceImageFormat(&r) - item.Cover = r.Cover*/ //加入分类产品切片 productListRsp = append(productListRsp, item) } diff --git a/server/resource/.gitignore b/server/resource/.gitignore index 91e75c67..daa90f66 100644 --- a/server/resource/.gitignore +++ b/server/resource/.gitignore @@ -1 +1,2 @@ resource +main diff --git a/server/resource/Dockerfile b/server/resource/Dockerfile new file mode 100755 index 00000000..87ed592a --- /dev/null +++ b/server/resource/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine + +WORKDIR /www/fusenapi/ +COPY ./bin/api-resource-srv /www/fusenapi/ +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ +CMD ["/www/fusenapi/api-resource-srv"] diff --git a/server/shopping-cart/.gitignore b/server/shopping-cart/.gitignore index 2b0ca663..594c3ef6 100644 --- a/server/shopping-cart/.gitignore +++ b/server/shopping-cart/.gitignore @@ -1 +1,2 @@ shopping-cart +main diff --git a/server/shopping-cart/Dockerfile b/server/shopping-cart/Dockerfile index a77012e3..71eda70f 100755 --- a/server/shopping-cart/Dockerfile +++ b/server/shopping-cart/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-shopping-cart-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-shopping-cart-srv"] diff --git a/server/upload/.gitignore b/server/upload/.gitignore index fb68e0cf..b7f44dd7 100644 --- a/server/upload/.gitignore +++ b/server/upload/.gitignore @@ -1 +1,2 @@ upload +main diff --git a/server/upload/Dockerfile b/server/upload/Dockerfile index b3ee358e..1a4d0f34 100755 --- a/server/upload/Dockerfile +++ b/server/upload/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-upload-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-upload-srv"] diff --git a/server/webset/.gitignore b/server/webset/.gitignore index b868fc0c..4a5f33a2 100644 --- a/server/webset/.gitignore +++ b/server/webset/.gitignore @@ -1 +1,2 @@ webset +main diff --git a/server/webset/Dockerfile b/server/webset/Dockerfile index 7cbcc7eb..566081b6 100755 --- a/server/webset/Dockerfile +++ b/server/webset/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-webset-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-webset-srv"] diff --git a/server/websocket/.gitignore b/server/websocket/.gitignore index ca4cb0cf..93238c67 100644 --- a/server/websocket/.gitignore +++ b/server/websocket/.gitignore @@ -1 +1,2 @@ websocket +main diff --git a/server/websocket/Dockerfile b/server/websocket/Dockerfile index e183ac2b..8659875c 100755 --- a/server/websocket/Dockerfile +++ b/server/websocket/Dockerfile @@ -2,5 +2,7 @@ FROM alpine WORKDIR /www/fusenapi/ COPY ./bin/api-websocket-srv /www/fusenapi/ -COPY ./etc /www/fusenapi/etc +COPY ./env.yaml /opt/ +COPY ./server.fusen.3718.cn.pem /opt/ +COPY ./server.fusen.3718.cn.key /opt/ CMD ["/www/fusenapi/api-websocket-srv"] diff --git a/server/websocket/internal/logic/ws_render_image.go b/server/websocket/internal/logic/ws_render_image.go index 47472ee2..441bc393 100644 --- a/server/websocket/internal/logic/ws_render_image.go +++ b/server/websocket/internal/logic/ws_render_image.go @@ -115,7 +115,7 @@ func (w *wsConnectItem) consumeRenderImageData() { }() select { case <-w.extendRenderProperty.renderCtx.Done(): - panic("=========渲染取消旧的上下文=======") + panic("=========检测到模板标签/颜色变化,渲染取消旧的任务=======") case <-tmpChan: return } @@ -277,6 +277,10 @@ func (w *wsConnectItem) getProductRelateionInfo(renderImageData *websocket_data. w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取对应开启云渲染模板失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, 0, 0, 0, 0) return nil, nil, nil, errors.New("获取对应开启云渲染模板失败") } + if productTemplate.TemplateInfo == nil || *productTemplate.TemplateInfo == "" { + w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "模板设计信息是空的", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, 0, 0, 0) + return nil, nil, nil, errors.New("模板设计信息是空的") + } //根据模板找到模型 model3d, err = w.logic.svcCtx.AllModels.FsProductModel3d.FindOne(w.logic.ctx, *productTemplate.ModelId) if err != nil { @@ -288,6 +292,10 @@ func (w *wsConnectItem) getProductRelateionInfo(renderImageData *websocket_data. w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "获取对应模型失败", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, 0, 0, 0) return nil, nil, nil, errors.New("获取对应模型失败") } + if model3d.ModelInfo == nil || *model3d.ModelInfo == "" { + w.renderErrResponse(renderImageData.RenderId, renderImageData.RenderData.TemplateTag, "", "模型设计信息是空的", renderImageData.RenderData.ProductId, w.userId, w.guestId, productTemplate.Id, model3d.Id, 0, 0) + return nil, nil, nil, errors.New("模型设计信息是空的") + } //根据模型id获取尺寸信息 productSize, err = w.logic.svcCtx.AllModels.FsProductSize.FindOne(w.logic.ctx, *model3d.SizeId) if err != nil { diff --git a/server_api/collection.api b/server_api/collection.api index 37bcf004..866a8d1a 100644 --- a/server_api/collection.api +++ b/server_api/collection.api @@ -18,6 +18,9 @@ service collection { //获取收藏列表 @handler GetCollectProductListHandler get /api/collection/get_collect_product_list(GetCollectProductListReq) returns (response); + //测试 + @handler TestAiHandler + get /api/collection/test_ai(TestAiReq) returns (response); } //收藏产品 @@ -52,4 +55,8 @@ type GetCollectProductListRspItem { MinPrice string `json:"min_price"` IsShelf int64 `json:"is_shelf"` IsDeleted int64 `json:"is_deleted"` +} +//测试 +type TestAiReq { + Num int `form:"num"` } \ No newline at end of file diff --git a/utils/basic/basic.go b/utils/basic/basic.go index 2af8be79..8ce30032 100644 --- a/utils/basic/basic.go +++ b/utils/basic/basic.go @@ -56,7 +56,7 @@ var ( CodeUserIdNotFoundErr = &StatusResponse{5051, "user not found"} // 未找到用户 CodePasswordErr = &StatusResponse{5052, "invalid password"} // 无效密码 CodeEmailExistsErr = &StatusResponse{5053, "email exists"} // email存在 - CodeEmailTimeShortErr = &StatusResponse{5053, "email with the time of resend is too short"} // email存在 + CodeEmailTimeShortErr = &StatusResponse{5053, "email with the time of resend is too short"} // email重发的时间太短 CodeResetPasswordErr = &StatusResponse{5054, "reset password error"} // 无效密码 CodeSafeValueRangeErr = &StatusResponse{5040, "value not in range"} // 值不在范围内 diff --git a/utils/check/limit.go b/utils/check/limit.go index fdf3503e..f5e2cb3e 100644 --- a/utils/check/limit.go +++ b/utils/check/limit.go @@ -15,9 +15,9 @@ type TimeLimit[T comparable] struct { dur time.Duration } -// NewTimelimit构造函数,接收限频的时间间隔 +// NewTimeLimit构造函数,接收限频的时间间隔 // 并初始化内部字典和间隔字段 -func NewTimelimit[T comparable](dur time.Duration) *TimeLimit[T] { +func NewTimeLimit[T comparable](dur time.Duration) *TimeLimit[T] { return &TimeLimit[T]{ dict: make(map[T]struct{}), dur: dur, diff --git a/utils/wevent/base_event.go b/utils/wevent/base_event.go index 8ce58131..dd443475 100644 --- a/utils/wevent/base_event.go +++ b/utils/wevent/base_event.go @@ -1,7 +1,10 @@ package wevent import ( + "fmt" "time" + + "github.com/474420502/requests" ) // 和前端交流的事件机制 @@ -22,6 +25,34 @@ type WebsocketEvent struct { Data any `json:"data"` // 关注的数据 } +func CommonNotify(WebsocketAddr, wid string, event *WebsocketEvent) error { + + reqWebsocketAddr := fmt.Sprintf("%s/api/websocket/common_notify", WebsocketAddr) + tp := requests.Post(reqWebsocketAddr) + tp.SetBodyJson(requests.M{ + "wid": wid, + "data": event, + }) + + wresp, err := tp.Execute() + if err != nil { + // logx.Error(err, token.TraceId) + return err + } + + result := wresp.Json() + + if !result.Get("code").Exists() { + return fmt.Errorf("send %s is error", reqWebsocketAddr) + } + + if result.Get("code").Int() != 200 { + return fmt.Errorf("%s", result.String()) + } + + return nil +} + // NewWebsocketEvent 创建一个Websocket事件 func NewWebsocketEvent(etype EventType, TraceId string) *WebsocketEvent { return &WebsocketEvent{