Merge branch 'develop' of https://gitee.com/fusenpack/fusenapi into feature/auth
This commit is contained in:
commit
8bea89754b
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -4,7 +4,7 @@
|
|||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
bin/
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
|
|
|
@ -126,7 +126,8 @@ type Day int64
|
|||
|
||||
// 订单取消时间
|
||||
const (
|
||||
CANCLE_ORDER_EXPIRE Day = 48 * 3600
|
||||
CANCLE_ORDER_EXPIRE Day = 48 * 3600
|
||||
CANCLE_ORDER_EXPIRE_DAY Day = 2 // 2天
|
||||
)
|
||||
|
||||
// 订单时间配置
|
||||
|
|
|
@ -9,9 +9,3 @@ const (
|
|||
//渲染结果数据队列
|
||||
RABBIT_MQ_RENDER_RESULT_DATA RABBIT_MQ = "RABBIT_MQ_RENDER_RESULT_DATA"
|
||||
)
|
||||
|
||||
// 队列列表
|
||||
var MQ_QUEUE_ARR = []RABBIT_MQ{
|
||||
RABBIT_MQ_ASSEMBLE_RENDER_DATA,
|
||||
RABBIT_MQ_RENDER_RESULT_DATA,
|
||||
}
|
||||
|
|
203
constants/render.go
Normal file
203
constants/render.go
Normal file
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
`
|
|
@ -8,6 +8,8 @@ const (
|
|||
WEBSOCKET_UNAUTH = "WEBSOCKET_UNAUTH"
|
||||
//ws连接成功
|
||||
WEBSOCKET_CONNECT_SUCCESS = "WEBSOCKET_CONNECT_SUCCESS"
|
||||
//渲染前数据组装
|
||||
WEBSOCKET_RENDER_IMAGE_ASSEMBLE = "WEBSOCKET_RENDER_IMAGE_ASSEMBLE"
|
||||
//图片渲染
|
||||
WEBSOCKET_RENDER_IMAGE = "WEBSOCKET_RENDER_IMAGE"
|
||||
//数据格式错误
|
||||
|
|
15
fs_package_docker_image.sh
Executable file
15
fs_package_docker_image.sh
Executable file
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash
|
||||
name=${1%%\\*}
|
||||
#进入对应服务目录
|
||||
cd server/$name
|
||||
#构建二进制文件
|
||||
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 .
|
||||
#打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
|
|
@ -1,6 +1,7 @@
|
|||
Name: {{.serviceName}}
|
||||
Host: {{.host}}
|
||||
Port: {{.port}}
|
||||
Timeout: 15000 #服务超时时间(毫秒)
|
||||
SourceMysql: fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest
|
||||
SourceRabbitMq: amqp://rabbit001:rabbit001129@110.41.19.98:5672
|
||||
Auth:
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package initalize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fusenapi/constants"
|
||||
"fusenapi/utils/mq_consumer_factory"
|
||||
"github.com/streadway/amqp"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type RabbitMqHandle struct {
|
||||
|
@ -18,6 +21,12 @@ type queueItem struct {
|
|||
queue amqp.Queue
|
||||
}
|
||||
|
||||
// 队列列表
|
||||
var mqQueueArr = []constants.RABBIT_MQ{
|
||||
constants.RABBIT_MQ_ASSEMBLE_RENDER_DATA,
|
||||
constants.RABBIT_MQ_RENDER_RESULT_DATA,
|
||||
}
|
||||
|
||||
// 存储连接
|
||||
var mapMq = make(map[constants.RABBIT_MQ]queueItem)
|
||||
|
||||
|
@ -35,7 +44,7 @@ func InitRabbitMq(url string, config *tls.Config) *RabbitMqHandle {
|
|||
log.Fatalf("Failed to open a channel: %v", err)
|
||||
}
|
||||
//声明队列
|
||||
for _, queueName := range constants.MQ_QUEUE_ARR {
|
||||
for _, queueName := range mqQueueArr {
|
||||
q, err := ch.QueueDeclare(
|
||||
string(queueName), // 队列名
|
||||
true, // 是否持久化
|
||||
|
@ -75,11 +84,17 @@ func (h *RabbitMqHandle) SendMsg(queueName constants.RABBIT_MQ, message []byte)
|
|||
}
|
||||
|
||||
// 消费消息
|
||||
func (h *RabbitMqHandle) Consume(queueName constants.RABBIT_MQ, handleFunc func(data []byte) error) error {
|
||||
func (h *RabbitMqHandle) Consume(ctx context.Context, queueName constants.RABBIT_MQ, handle mq_consumer_factory.MqHandle) {
|
||||
object, ok := mapMq[queueName]
|
||||
if !ok {
|
||||
return errors.New("unknown queue")
|
||||
panic("unknown queue")
|
||||
}
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
panic("err ctx deadline")
|
||||
}
|
||||
}()
|
||||
msgs, err := object.ch.Consume(
|
||||
object.queue.Name, // 队列名
|
||||
object.queue.Name, // 消费者名,如果为空,则是随机生成一个
|
||||
|
@ -94,18 +109,21 @@ func (h *RabbitMqHandle) Consume(queueName constants.RABBIT_MQ, handleFunc func(
|
|||
}
|
||||
//允许20的并发
|
||||
limit := make(chan struct{}, 20)
|
||||
wait := sync.WaitGroup{}
|
||||
defer close(limit)
|
||||
// 消费消息
|
||||
for msg := range msgs {
|
||||
limit <- struct{}{}
|
||||
wait.Add(1)
|
||||
go func(m amqp.Delivery) {
|
||||
if err := recover(); err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
defer func() {
|
||||
<-limit
|
||||
wait.Done()
|
||||
}()
|
||||
if err = handleFunc(m.Body); err != nil {
|
||||
if err = handle.Run(ctx, m.Body); err != nil {
|
||||
logx.Error("failed to deal with MQ message:", string(m.Body))
|
||||
return
|
||||
}
|
||||
|
@ -116,5 +134,5 @@ func (h *RabbitMqHandle) Consume(queueName constants.RABBIT_MQ, handleFunc func(
|
|||
}
|
||||
}(msg)
|
||||
}
|
||||
return nil
|
||||
wait.Wait()
|
||||
}
|
||||
|
|
|
@ -92,5 +92,5 @@ func (c *FsCartModel) DeleteCartsByIds(ctx context.Context, ids []int64) ( err e
|
|||
if len(ids) == 0 {
|
||||
return
|
||||
}
|
||||
return c.db.WithContext(ctx).Model(&FsCart{}).Where("`id` in (?)", ids).Delete(&FsCart{}).Error
|
||||
return c.db.Table(c.name).WithContext(ctx).Model(&FsCart{}).Where("`id` in (?)", ids).Update("status", 0).Error
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
package gmodel
|
||||
|
||||
import "context"
|
||||
|
||||
// TODO: 使用model的属性做你想做的
|
||||
|
||||
func (r *FsCloudRenderLogModel) Create(ctx context.Context, data *FsCloudRenderLog) error {
|
||||
return r.db.WithContext(ctx).Model(&FsCloudRenderLog{}).Create(data).Error
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"reflect"
|
||||
"time"
|
||||
|
||||
"fusenapi/utils/handler"
|
||||
"fusenapi/utils/handlers"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
@ -60,7 +60,7 @@ func (o *FsOrderModel) FindPageListByPage(ctx context.Context, rowBuilder *gorm.
|
|||
var resp []*FsOrderRel
|
||||
// 过滤
|
||||
if filterMap != nil {
|
||||
rowBuilder = rowBuilder.Scopes(handler.FilterData(filterMap))
|
||||
rowBuilder = rowBuilder.Scopes(handlers.FilterData(filterMap))
|
||||
}
|
||||
|
||||
// 排序
|
||||
|
@ -70,11 +70,11 @@ func (o *FsOrderModel) FindPageListByPage(ctx context.Context, rowBuilder *gorm.
|
|||
for i := 0; i < s.NumField(); i++ {
|
||||
fieldsMap[s.Field(i).Tag.Get("json")] = struct{}{}
|
||||
}
|
||||
rowBuilder = rowBuilder.Scopes(handler.OrderCheck(orderBy, fieldsMap))
|
||||
rowBuilder = rowBuilder.Scopes(handlers.OrderCheck(orderBy, fieldsMap))
|
||||
}
|
||||
|
||||
// 分页
|
||||
rowBuilder = rowBuilder.Scopes(handler.Paginate(page, pageSize))
|
||||
rowBuilder = rowBuilder.Scopes(handlers.Paginate(page, pageSize))
|
||||
|
||||
// 结果
|
||||
result := rowBuilder.Debug().WithContext(ctx).Find(&resp)
|
||||
|
@ -125,7 +125,7 @@ func (m *FsOrderModel) FindCount(ctx context.Context, countBuilder *gorm.DB, fil
|
|||
|
||||
// 过滤
|
||||
if filterMap != nil {
|
||||
countBuilder = countBuilder.Scopes(handler.FilterData(filterMap))
|
||||
countBuilder = countBuilder.Scopes(handlers.FilterData(filterMap))
|
||||
}
|
||||
|
||||
result := countBuilder.WithContext(ctx).Limit(1).Count(&count)
|
||||
|
@ -140,7 +140,7 @@ func (m *FsOrderModel) FindOneByQuery(ctx context.Context, rowBuilder *gorm.DB,
|
|||
var resp FsOrderRel
|
||||
|
||||
if filterMap != nil {
|
||||
rowBuilder = rowBuilder.Scopes(handler.FilterData(filterMap))
|
||||
rowBuilder = rowBuilder.Scopes(handlers.FilterData(filterMap))
|
||||
}
|
||||
|
||||
result := rowBuilder.WithContext(ctx).Limit(1).Find(&resp)
|
||||
|
|
|
@ -1,2 +1,15 @@
|
|||
package gmodel
|
||||
// TODO: 使用model的属性做你想做的
|
||||
|
||||
import "context"
|
||||
|
||||
// TODO: 使用model的属性做你想做的
|
||||
|
||||
func (p *FsPayEventModel) CreateOrUpdate(ctx context.Context, req *FsPayEvent) (resp *FsPayEvent, err error) {
|
||||
rowBuilder := p.db.Table(p.name).WithContext(ctx)
|
||||
if req.Id > 0 {
|
||||
err = rowBuilder.Save(req).Error
|
||||
} else {
|
||||
err = rowBuilder.Create(req).Error
|
||||
}
|
||||
return req, err
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@ package gmodel
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fusenapi/utils/handler"
|
||||
"fusenapi/utils/handlers"
|
||||
"reflect"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
@ -54,7 +55,7 @@ func (m *FsPayModel) FindCount(ctx context.Context, countBuilder *gorm.DB, filte
|
|||
|
||||
// 过滤
|
||||
if filterMap != nil {
|
||||
countBuilder = countBuilder.Scopes(handler.FilterData(filterMap))
|
||||
countBuilder = countBuilder.Scopes(handlers.FilterData(filterMap))
|
||||
}
|
||||
|
||||
result := countBuilder.WithContext(ctx).Limit(1).Count(&count)
|
||||
|
@ -69,7 +70,7 @@ func (m *FsPayModel) FindOneByQuery(ctx context.Context, rowBuilder *gorm.DB, fi
|
|||
var resp FsPay
|
||||
|
||||
if filterMap != nil {
|
||||
rowBuilder = rowBuilder.Scopes(handler.FilterData(filterMap))
|
||||
rowBuilder = rowBuilder.Scopes(handlers.FilterData(filterMap))
|
||||
}
|
||||
|
||||
result := rowBuilder.WithContext(ctx).Limit(1).Find(&resp)
|
||||
|
@ -80,6 +81,31 @@ func (m *FsPayModel) FindOneByQuery(ctx context.Context, rowBuilder *gorm.DB, fi
|
|||
}
|
||||
}
|
||||
|
||||
func (m *FsPayModel) FindAll(ctx context.Context, rowBuilder *gorm.DB, filterMap map[string]string, orderBy string) ([]*FsPay, error) {
|
||||
var resp []*FsPay
|
||||
// 过滤
|
||||
if filterMap != nil {
|
||||
rowBuilder = rowBuilder.Scopes(handlers.FilterData(filterMap))
|
||||
}
|
||||
|
||||
// 排序
|
||||
if orderBy != "" {
|
||||
var fieldsMap = make(map[string]struct{})
|
||||
s := reflect.TypeOf(&FsOrder{}).Elem() //通过反射获取type定义
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
fieldsMap[s.Field(i).Tag.Get("json")] = struct{}{}
|
||||
}
|
||||
rowBuilder = rowBuilder.Scopes(handlers.OrderCheck(orderBy, fieldsMap))
|
||||
}
|
||||
|
||||
result := rowBuilder.WithContext(ctx).Find(&resp)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
} else {
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 事务
|
||||
func (m *FsPayModel) Trans(ctx context.Context, fn func(ctx context.Context, connGorm *gorm.DB) error) error {
|
||||
tx := m.db.Table(m.name).WithContext(ctx).Begin()
|
||||
|
|
|
@ -93,3 +93,11 @@ func (d *FsProductModel3dModel) GetGroupPartListByProductIds(ctx context.Context
|
|||
Group("product_id").Find(&resp).Error
|
||||
return resp, err
|
||||
}
|
||||
func (d *FsProductModel3dModel) FindOneJoinSize(ctx context.Context, productId int64) (resp FsProductModel3d, err error) {
|
||||
err = d.db.WithContext(ctx).Table(d.name+"as m").Joins("left join fs_product_size as s on m.size_id = s.id").
|
||||
Select("m.*").
|
||||
Where("m.product_id = ?", productId).
|
||||
Where("(s.status= ? and m.tag = ?)", 1, 1).
|
||||
Order("s.sort ASC").Take(&resp).Error
|
||||
return resp, err
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ type FsProductTemplateV2 struct {
|
|||
Ctime *int64 `gorm:"default:0;" json:"ctime"` // 添加时间
|
||||
Tag *string `gorm:"default:'';" json:"tag"` // 标签(用户自填)
|
||||
IsDel *int64 `gorm:"default:0;" json:"is_del"` // 是否删除 1删除
|
||||
GroupOptions *string `gorm:"default:'';" json:"group_options"` // 颜色分组
|
||||
Version *int64 `gorm:"default:0;" json:"version"` //
|
||||
}
|
||||
type FsProductTemplateV2Model struct {
|
||||
db *gorm.DB
|
||||
|
|
|
@ -106,3 +106,16 @@ func (t *FsProductTemplateV2Model) GetProductTemplateListByParams(ctx context.Co
|
|||
err = db.Find(&resp).Error
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// 获取第一个尺寸下的模板
|
||||
func (t *FsProductTemplateV2Model) FindOneByProductIdTagIdWithSizeTable(ctx context.Context, productId int64, tagId string) (resp *FsProductTemplateV2, err error) {
|
||||
err = t.db.WithContext(ctx).Table(t.name+" as t").
|
||||
Joins("left join fs_product_size as s on t.product_id = s.product_id").
|
||||
Select("t.*").
|
||||
Where("t.product_id = ? and t.tag = ? ", productId, tagId).
|
||||
Where("t.status = ? and t.is_del = ?", 1, 0).
|
||||
Where("s.status = ?", 1).
|
||||
Order("s.sort ASC").
|
||||
Take(&resp).Error
|
||||
return resp, err
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package gmodel
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fusenapi/utils/handlers"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
@ -24,3 +25,43 @@ func (m *FsRefundReasonModel) Update(ctx context.Context, obj *FsRefundReason) e
|
|||
func (m *FsRefundReasonModel) UpdateByRefundReasonId(ctx context.Context, obj *FsRefundReason) error {
|
||||
return m.db.WithContext(ctx).Model(obj).Where("`refund_reason_id` = ?", obj.RefundReasonId).Updates(obj).Error
|
||||
}
|
||||
|
||||
func (m *FsRefundReasonModel) CreateOrUpdate(ctx context.Context, req *FsRefundReason) (resp *FsRefundReason, err error) {
|
||||
rowBuilder := m.db.Table(m.name).WithContext(ctx)
|
||||
if req.Id > 0 {
|
||||
err = rowBuilder.Save(req).Error
|
||||
} else {
|
||||
err = rowBuilder.Create(req).Error
|
||||
}
|
||||
return req, err
|
||||
}
|
||||
|
||||
func (m *FsRefundReasonModel) FindOneByQuery(ctx context.Context, rowBuilder *gorm.DB, filterMap map[string]string) (*FsRefundReason, error) {
|
||||
var resp FsRefundReason
|
||||
|
||||
if filterMap != nil {
|
||||
rowBuilder = rowBuilder.Scopes(handlers.FilterData(filterMap))
|
||||
}
|
||||
|
||||
result := rowBuilder.WithContext(ctx).Limit(1).Find(&resp)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
} else {
|
||||
return &resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *FsRefundReasonModel) RowSelectBuilder(selectData []string) *gorm.DB {
|
||||
var rowBuilder = m.db.Table(m.name)
|
||||
|
||||
if selectData != nil {
|
||||
rowBuilder = rowBuilder.Select(selectData)
|
||||
} else {
|
||||
rowBuilder = rowBuilder.Select("*")
|
||||
}
|
||||
return rowBuilder
|
||||
}
|
||||
|
||||
func (m *FsRefundReasonModel) TableName() string {
|
||||
return m.name
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ type FsResource struct {
|
|||
UploadedAt *time.Time `gorm:"index;default:'0000-00-00 00:00:00';" json:"uploaded_at"` // 上传时间
|
||||
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"` // 存储桶名
|
||||
}
|
||||
type FsResourceModel struct {
|
||||
db *gorm.DB
|
||||
|
|
|
@ -1 +1,84 @@
|
|||
package gmodel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fusenapi/utils/handlers"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TODO: 使用model的属性做你想做的
|
||||
|
||||
func (p *FsResourceModel) FindOneById(ctx context.Context, resourceId string) (*FsResource, error) {
|
||||
var resp FsResource
|
||||
result := p.db.Table(p.name).WithContext(ctx).Where("resource_id =?", resourceId).Take(&resp)
|
||||
if result.Error != nil {
|
||||
// 检查 ErrRecordNotFound 错误
|
||||
if !errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, result.Error
|
||||
}
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (p *FsResourceModel) Create(ctx context.Context, req *FsResource) (resp *FsResource, err error) {
|
||||
err = p.db.Table(p.name).WithContext(ctx).Create(req).Error
|
||||
return req, err
|
||||
}
|
||||
|
||||
func (p *FsResourceModel) Update(ctx context.Context, req *FsResource) (resp *FsResource, err error) {
|
||||
err = p.db.Table(p.name).WithContext(ctx).Where("resource_id =?", req.ResourceId).Save(req).Error
|
||||
return req, err
|
||||
}
|
||||
|
||||
func (m *FsResourceModel) FindOneByQuery(ctx context.Context, rowBuilder *gorm.DB, filterMap map[string]string) (*FsResource, error) {
|
||||
var resp FsResource
|
||||
|
||||
if filterMap != nil {
|
||||
rowBuilder = rowBuilder.Scopes(handlers.FilterData(filterMap))
|
||||
}
|
||||
|
||||
result := rowBuilder.WithContext(ctx).Limit(1).Find(&resp)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
} else {
|
||||
return &resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *FsResourceModel) RowSelectBuilder(selectData []string) *gorm.DB {
|
||||
var rowBuilder = m.db.Table(m.name)
|
||||
|
||||
if selectData != nil {
|
||||
rowBuilder = rowBuilder.Select(selectData)
|
||||
} else {
|
||||
rowBuilder = rowBuilder.Select("*")
|
||||
}
|
||||
return rowBuilder
|
||||
}
|
||||
|
||||
// 事务
|
||||
func (m *FsResourceModel) Trans(ctx context.Context, fn func(ctx context.Context, connGorm *gorm.DB) error) error {
|
||||
tx := m.db.Table(m.name).WithContext(ctx).Begin()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
}
|
||||
}()
|
||||
|
||||
if err := tx.Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := fn(ctx, tx); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit().Error
|
||||
}
|
||||
|
||||
func (m *FsResourceModel) TableName() string {
|
||||
return m.name
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ type FsTags struct {
|
|||
Description *string `gorm:"default:'';" json:"description"` // 介绍 Seo
|
||||
RecommendProduct *string `gorm:"default:'';" json:"recommend_product"` //
|
||||
RecommendProductSort *string `gorm:"default:'';" json:"recommend_product_sort"` //
|
||||
Category *int64 `gorm:"default:1;" json:"category"` // 分类:1前台用的 2后台用的
|
||||
}
|
||||
type FsTagsModel struct {
|
||||
db *gorm.DB
|
||||
|
|
|
@ -36,6 +36,7 @@ type GetAllTagByParamsReq struct {
|
|||
OrderBy string
|
||||
LevelPrefixLeftLike string //右模糊
|
||||
WithChild bool //是否包含子层级
|
||||
Category int64
|
||||
}
|
||||
|
||||
func (t *FsTagsModel) GetAllTagByParams(ctx context.Context, req GetAllTagByParamsReq) (resp []FsTags, err error) {
|
||||
|
@ -46,6 +47,9 @@ func (t *FsTagsModel) GetAllTagByParams(ctx context.Context, req GetAllTagByPara
|
|||
if req.Status != nil {
|
||||
db = db.Where("`status` = ?", *req.Status)
|
||||
}
|
||||
if req.Category != 0 {
|
||||
db = db.Where("`category` = ?", req.Category)
|
||||
}
|
||||
if req.LevelPrefixLeftLike != "" {
|
||||
//查询子集
|
||||
if req.WithChild {
|
||||
|
|
25
model/gmodel/fs_user_material_gen.go
Normal file
25
model/gmodel/fs_user_material_gen.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package gmodel
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// fs_user_material 用户素材表
|
||||
type FsUserMaterial struct {
|
||||
Id int64 `gorm:"primary_key;default:0;auto_increment;" json:"id"` // 用户 ID
|
||||
Module *string `gorm:"default:'';" json:"module"` // 所属模块:logo
|
||||
UserId *int64 `gorm:"index;default:0;" json:"user_id"` // 用户 ID
|
||||
GuestId *int64 `gorm:"index;default:0;" json:"guest_id"` // 游客 ID
|
||||
ResourceId *string `gorm:"default:'';" json:"resource_id"` // 资源ID
|
||||
ResourceUrl *string `gorm:"default:'';" json:"resource_url"` // 资源 URL
|
||||
Metadata *string `gorm:"default:'';" json:"metadata"` // 元数据,json格式,存储图像分率
|
||||
CreateAt *int64 `gorm:"default:0;" json:"create_at"` // 上传时间
|
||||
}
|
||||
type FsUserMaterialModel struct {
|
||||
db *gorm.DB
|
||||
name string
|
||||
}
|
||||
|
||||
func NewFsUserMaterialModel(db *gorm.DB) *FsUserMaterialModel {
|
||||
return &FsUserMaterialModel{db: db, name: "fs_user_material"}
|
||||
}
|
72
model/gmodel/fs_user_material_logic.go
Normal file
72
model/gmodel/fs_user_material_logic.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package gmodel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fusenapi/utils/handlers"
|
||||
"reflect"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TODO: 使用model的属性做你想做的
|
||||
|
||||
func (p *FsUserMaterialModel) CreateOrUpdate(ctx context.Context, req *FsUserMaterial) (resp *FsUserMaterial, err error) {
|
||||
rowBuilder := p.db.Table(p.name).WithContext(ctx)
|
||||
if req.Id > 0 {
|
||||
err = rowBuilder.Save(req).Error
|
||||
} else {
|
||||
err = rowBuilder.Create(req).Error
|
||||
}
|
||||
return req, err
|
||||
}
|
||||
|
||||
func (m *FsUserMaterialModel) FindAll(ctx context.Context, rowBuilder *gorm.DB, filterMap map[string]string, orderBy string) ([]*FsUserMaterial, error) {
|
||||
var resp []*FsUserMaterial
|
||||
// 过滤
|
||||
if filterMap != nil {
|
||||
rowBuilder = rowBuilder.Scopes(handlers.FilterData(filterMap))
|
||||
}
|
||||
|
||||
// 排序
|
||||
if orderBy != "" {
|
||||
var fieldsMap = make(map[string]struct{})
|
||||
s := reflect.TypeOf(&FsUserMaterial{}).Elem() //通过反射获取type定义
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
fieldsMap[s.Field(i).Tag.Get("json")] = struct{}{}
|
||||
}
|
||||
rowBuilder = rowBuilder.Scopes(handlers.OrderCheck(orderBy, fieldsMap))
|
||||
}
|
||||
|
||||
result := rowBuilder.WithContext(ctx).Find(&resp)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
} else {
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *FsUserMaterialModel) RowSelectBuilder(selectData []string) *gorm.DB {
|
||||
var rowBuilder = m.db.Table(m.name)
|
||||
|
||||
if selectData != nil {
|
||||
rowBuilder = rowBuilder.Select(selectData)
|
||||
} else {
|
||||
rowBuilder = rowBuilder.Select("*")
|
||||
}
|
||||
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
|
||||
}
|
|
@ -89,6 +89,7 @@ type AllModelsGen struct {
|
|||
FsTrade *FsTradeModel // fs_trade
|
||||
FsUser *FsUserModel // fs_user 用户表
|
||||
FsUserDesign *FsUserDesignModel // fs_user_design 废弃表
|
||||
FsUserMaterial *FsUserMaterialModel // fs_user_material 用户素材表
|
||||
FsUserStock *FsUserStockModel // fs_user_stock 用户云仓库存
|
||||
FsWebSet *FsWebSetModel // fs_web_set 网站配置表
|
||||
|
||||
|
@ -181,6 +182,7 @@ func NewAllModels(gdb *gorm.DB) *AllModelsGen {
|
|||
FsTrade: NewFsTradeModel(gdb),
|
||||
FsUser: NewFsUserModel(gdb),
|
||||
FsUserDesign: NewFsUserDesignModel(gdb),
|
||||
FsUserMaterial: NewFsUserMaterialModel(gdb),
|
||||
FsUserStock: NewFsUserStockModel(gdb),
|
||||
FsWebSet: NewFsWebSetModel(gdb),
|
||||
}
|
||||
|
|
6
server/auth/Dockerfile
Executable file
6
server/auth/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-assistant-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-assistant-srv"]
|
6
server/backend/Dockerfile
Executable file
6
server/backend/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-backend-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-backend-srv"]
|
6
server/canteen/Dockerfile
Executable file
6
server/canteen/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-canteen-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-canteen-srv"]
|
6
server/data-transfer/Dockerfile
Executable file
6
server/data-transfer/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-data-transfer-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-data-transfer-srv"]
|
6
server/home-user-auth/Dockerfile
Executable file
6
server/home-user-auth/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-home-user-auth-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-home-user-auth-srv"]
|
|
@ -12,3 +12,10 @@ Auth:
|
|||
|
||||
Stripe:
|
||||
SK: "sk_test_51IisojHygnIJZeghPVSBhkwySfcyDV4SoAduIxu3J7bvSJ9cZMD96LY1LO6SpdbYquLJX5oKvgEBB67KT9pecfCy00iEC4pp9y"
|
||||
|
||||
PayConfig:
|
||||
Stripe:
|
||||
Key: "sk_test_51IisojHygnIJZeghPVSBhkwySfcyDV4SoAduIxu3J7bvSJ9cZMD96LY1LO6SpdbYquLJX5oKvgEBB67KT9pecfCy00iEC4pp9y"
|
||||
EndpointSecret: "whsec_f5f9a121d43af3789db7459352f08cf523eb9e0fbf3381f91ba6c97c324c174d"
|
||||
SuccessURL: "http://www.baidu.com"
|
||||
CancelURL: "http://www.baidu.com"
|
||||
|
|
|
@ -17,4 +17,13 @@ type Config struct {
|
|||
Stripe struct {
|
||||
SK string
|
||||
}
|
||||
|
||||
PayConfig struct {
|
||||
Stripe struct {
|
||||
EndpointSecret string
|
||||
Key string
|
||||
CancelURL string
|
||||
SuccessURL string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,16 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||
Path: "/api/user/order-cancel",
|
||||
Handler: UserOrderCancelHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/user/logo-list",
|
||||
Handler: UserLogoListHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/user/one-more-order",
|
||||
Handler: UserAgainOrderHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,27 +6,27 @@ import (
|
|||
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"fusenapi/server/websocket/internal/logic"
|
||||
"fusenapi/server/websocket/internal/svc"
|
||||
"fusenapi/server/websocket/internal/types"
|
||||
"fusenapi/server/home-user-auth/internal/logic"
|
||||
"fusenapi/server/home-user-auth/internal/svc"
|
||||
"fusenapi/server/home-user-auth/internal/types"
|
||||
)
|
||||
|
||||
func ThirdPartyLoginNotifyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
func UserAgainOrderHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var req types.ThirdPartyLoginNotifyReq
|
||||
var req types.UserAgainOrderReq
|
||||
userinfo, err := basic.RequestParse(w, r, svcCtx.SharedState, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewThirdPartyLoginNotifyLogic(r.Context(), svcCtx)
|
||||
l := logic.NewUserAgainOrderLogic(r.Context(), svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.ThirdPartyLoginNotify(&req, userinfo)
|
||||
resp := l.UserAgainOrder(&req, userinfo)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
|
@ -0,0 +1,35 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"fusenapi/server/home-user-auth/internal/logic"
|
||||
"fusenapi/server/home-user-auth/internal/svc"
|
||||
"fusenapi/server/home-user-auth/internal/types"
|
||||
)
|
||||
|
||||
func UserLogoListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var req types.UserLogoListReq
|
||||
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewUserLogoListLogic(r.Context(), svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.UserLogoList(&req, userinfo)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
||||
}
|
||||
}
|
||||
}
|
144
server/home-user-auth/internal/logic/useragainorderlogic.go
Normal file
144
server/home-user-auth/internal/logic/useragainorderlogic.go
Normal file
|
@ -0,0 +1,144 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fusenapi/model/gmodel"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/home-user-auth/internal/svc"
|
||||
"fusenapi/server/home-user-auth/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserAgainOrderLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUserAgainOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserAgainOrderLogic {
|
||||
return &UserAgainOrderLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *UserAgainOrderLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
|
||||
// func (l *UserAgainOrderLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
func (l *UserAgainOrderLogic) UserAgainOrder(req *types.UserAgainOrderReq, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
if userinfo == nil || userinfo.UserId == 0 {
|
||||
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "order not found")
|
||||
}
|
||||
|
||||
// 查询订单数据
|
||||
orderModel := gmodel.NewFsOrderModel(l.svcCtx.MysqlConn)
|
||||
orderDetailTemplateModel := gmodel.NewFsOrderDetailTemplateModel(l.svcCtx.MysqlConn)
|
||||
fsOrderDetailModel := gmodel.NewFsOrderDetailModel(l.svcCtx.MysqlConn)
|
||||
fsProductDesignModel := gmodel.NewFsProductDesignModel(l.svcCtx.MysqlConn)
|
||||
|
||||
rsbOrder := orderModel.RowSelectBuilder(nil)
|
||||
rsbOrder = rsbOrder.Where("sn =?", req.Sn).Preload("FsOrderDetails")
|
||||
rsbOrder = rsbOrder.Preload("FsOrderDetails", func(dbPreload *gorm.DB) *gorm.DB {
|
||||
return dbPreload.Table(fsOrderDetailModel.TableName()).Preload("FsOrderDetailTemplateInfo", func(dbPreload *gorm.DB) *gorm.DB {
|
||||
return dbPreload.Table(orderDetailTemplateModel.TableName()).Preload("FsProductDesignInfo", func(dbPreload *gorm.DB) *gorm.DB {
|
||||
return dbPreload.Table(fsProductDesignModel.TableName())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
fsOrderRelInfo, err := orderModel.FindOneByQuery(l.ctx, rsbOrder, nil)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "order not found")
|
||||
}
|
||||
logx.Error(err)
|
||||
return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get order info")
|
||||
}
|
||||
|
||||
if len(fsOrderRelInfo.FsOrderDetails) > 0 {
|
||||
for _, fsOrderDetail := range fsOrderRelInfo.FsOrderDetails {
|
||||
var isCheck int64 = 1
|
||||
productDesignInfo := fsOrderDetail.FsOrderDetailTemplateInfo.FsProductDesignInfo
|
||||
if productDesignInfo.Id != 0 {
|
||||
|
||||
// 查找是否有此材质、产品、大小id的阶梯价格
|
||||
productPriceModel := gmodel.NewFsProductPriceModel(l.svcCtx.MysqlConn)
|
||||
priceStatus := int64(1)
|
||||
priceReq := gmodel.FindOneProductPriceByParamsReq{
|
||||
ProductId: productDesignInfo.ProductId,
|
||||
MaterialId: productDesignInfo.MaterialId,
|
||||
SizeId: productDesignInfo.SizeId,
|
||||
Status: &priceStatus,
|
||||
}
|
||||
productPriceInfo, err := productPriceModel.FindOneProductPriceByParams(l.ctx, priceReq)
|
||||
if err == nil && productPriceInfo.Id != 0 && *productPriceInfo.EachBoxNum > 0 {
|
||||
|
||||
// 买的数量和每箱数量取余为0 且 份数大于等于最小购买数量才算满足条件
|
||||
if *fsOrderDetail.BuyNum%*productPriceInfo.EachBoxNum == 0 && int64(float64(*fsOrderDetail.BuyNum)/float64(*productPriceInfo.EachBoxNum)) >= *productPriceInfo.MinBuyNum {
|
||||
|
||||
// 查询购物车
|
||||
cartModel := gmodel.NewFsCartModel(l.svcCtx.MysqlConn)
|
||||
cartStatus := int64(1)
|
||||
cartReq := gmodel.FindOneCartByParamsReq{
|
||||
UserId: &userinfo.UserId,
|
||||
ProductId: productDesignInfo.ProductId,
|
||||
TemplateId: productDesignInfo.TemplateId,
|
||||
PriceId: &productPriceInfo.Id,
|
||||
DesignId: &productDesignInfo.Id,
|
||||
MaterialId: productDesignInfo.MaterialId,
|
||||
Status: &cartStatus,
|
||||
}
|
||||
cartInfo, err := cartModel.FindOneCartByParams(l.ctx, cartReq)
|
||||
if err == nil && (err != nil && errors.Is(err, gorm.ErrRecordNotFound)) {
|
||||
now := time.Now().Unix()
|
||||
nowTime := time.Now()
|
||||
data := gmodel.FsCart{
|
||||
UserId: &userinfo.UserId,
|
||||
ProductId: productPriceInfo.ProductId,
|
||||
TemplateId: productDesignInfo.TemplateId,
|
||||
PriceId: &productPriceInfo.Id,
|
||||
MaterialId: productDesignInfo.MaterialId,
|
||||
SizeId: productDesignInfo.SizeId,
|
||||
BuyNum: fsOrderDetail.BuyNum,
|
||||
Cover: productDesignInfo.Cover,
|
||||
DesignId: &productDesignInfo.Id,
|
||||
Ctime: &now,
|
||||
Status: &cartStatus,
|
||||
OptionalId: productDesignInfo.OptionalId,
|
||||
IsCheck: &isCheck,
|
||||
TsTime: &nowTime,
|
||||
}
|
||||
if cartInfo == nil {
|
||||
err = cartModel.Create(l.ctx, data)
|
||||
} else {
|
||||
err = cartModel.Update(l.ctx, cartInfo.Id, data)
|
||||
}
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to add to cart")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
71
server/home-user-auth/internal/logic/userlogolistlogic.go
Normal file
71
server/home-user-auth/internal/logic/userlogolistlogic.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fusenapi/model/gmodel"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/home-user-auth/internal/svc"
|
||||
"fusenapi/server/home-user-auth/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserLogoListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUserLogoListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLogoListLogic {
|
||||
return &UserLogoListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *UserLogoListLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
|
||||
// func (l *UserLogoListLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
func (l *UserLogoListLogic) UserLogoList(req *types.UserLogoListReq, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
// 定义用户ID
|
||||
var userId int64
|
||||
var guestId int64
|
||||
|
||||
// 检查用户是否是游客
|
||||
if userinfo.IsGuest() {
|
||||
// 如果是,使用游客ID和游客键名格式
|
||||
guestId = userinfo.GuestId
|
||||
} else {
|
||||
// 否则,使用用户ID和用户键名格式
|
||||
userId = userinfo.UserId
|
||||
}
|
||||
|
||||
userMaterialModel := gmodel.NewFsUserMaterialModel(l.svcCtx.MysqlConn)
|
||||
userMaterialRSB := userMaterialModel.RowSelectBuilder(nil).
|
||||
Where("module = ?", "logo").Where("user_id = ?", userId).Where("guest_id = ?", guestId).Order("id desc")
|
||||
list, err := userMaterialModel.FindAll(l.ctx, userMaterialRSB, nil, "")
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "data not found")
|
||||
}
|
||||
logx.Error(err)
|
||||
return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get data list")
|
||||
}
|
||||
return resp.SetStatus(basic.CodeOK, map[string]interface{}{
|
||||
"list": list,
|
||||
})
|
||||
}
|
|
@ -6,6 +6,8 @@ import (
|
|||
"fusenapi/model/gmodel"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
"fusenapi/utils/handlers"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
|
@ -50,11 +52,76 @@ func (l *UserOrderCancelLogic) UserOrderCancel(req *types.UserOrderCancelReq, us
|
|||
}
|
||||
|
||||
// 判断订单状态
|
||||
if *orderInfo.Status == int64(constants.STATUS_NEW_NOT_PAY) {
|
||||
|
||||
} else {
|
||||
var notCancelStatusMap = make(map[int64]struct{}, 3)
|
||||
notCancelStatusMap[int64(constants.STATUS_NEW_NOT_PAY)] = struct{}{}
|
||||
notCancelStatusMap[int64(constants.STATUS_NEW_PART_PAY)] = struct{}{}
|
||||
notCancelStatusMap[int64(constants.STATUS_NEW_PAY_COMPLETED)] = struct{}{}
|
||||
_, ok := notCancelStatusMap[int64(*orderInfo.Status)]
|
||||
if !ok {
|
||||
return resp.SetStatusWithMessage(basic.CodeOrderNotCancelledErr, "the order status not cancle")
|
||||
}
|
||||
|
||||
var cancelTime int64 = time.Now().Unix() - (*orderInfo.Ctime + int64(constants.CANCLE_ORDER_EXPIRE))
|
||||
// 第一次支付成功后48小时后不能进行取消操作
|
||||
if *orderInfo.IsPayCompleted == 1 && cancelTime > 0 {
|
||||
return resp.SetStatusWithMessage(basic.CodeOrderNotCancelledErr, "The current order cannot be cancelled")
|
||||
}
|
||||
|
||||
// 修改订单--取消状态和取消原因
|
||||
*orderInfo.Status = int64(constants.STATUS_NEW_CANCEL)
|
||||
*orderInfo.IsCancel = 1
|
||||
orderInfo.RefundReasonId = &req.RefundReasonId
|
||||
orderInfo.RefundReason = &req.RefundReason
|
||||
|
||||
var nowTime = time.Now().Unix()
|
||||
var payList []handlers.PayInfo
|
||||
// 事务处理
|
||||
err = orderModel.Trans(l.ctx, func(ctx context.Context, connGorm *gorm.DB) (err error) {
|
||||
// 修改订单信息
|
||||
orderModelTS := gmodel.NewFsOrderModel(connGorm)
|
||||
err = orderModelTS.Update(ctx, orderInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 新增退款记录
|
||||
var isRefund int64 = 0
|
||||
refundReasonModelTS := gmodel.NewFsRefundReasonModel(connGorm)
|
||||
refundReasonModelTS.CreateOrUpdate(ctx, &gmodel.FsRefundReason{
|
||||
IsRefund: &isRefund,
|
||||
RefundReasonId: &req.RefundReasonId,
|
||||
RefundReason: &req.RefundReason,
|
||||
OrderId: &orderInfo.Id,
|
||||
CreatedAt: &nowTime,
|
||||
})
|
||||
// 退款申请
|
||||
// 退款申请--查询支付信息
|
||||
fsPayModelTS := gmodel.NewFsPayModel(connGorm)
|
||||
rbFsPay := fsPayModelTS.RowSelectBuilder(nil).Where("order_number = ?", orderInfo.Sn).Where("pay_status =?", constants.PAYSTATUS_SUCCESS).Where("is_refund =?", 0)
|
||||
payInfoList, err := fsPayModelTS.FindAll(ctx, rbFsPay, nil, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, payInfo := range payInfoList {
|
||||
var key string
|
||||
if *payInfo.PaymentMethod == int64(constants.PAYMETHOD_STRIPE) {
|
||||
key = l.svcCtx.Config.PayConfig.Stripe.Key
|
||||
}
|
||||
payList = append(payList, handlers.PayInfo{
|
||||
TradeNo: *payInfo.TradeNo,
|
||||
PaymentMethod: *payInfo.PaymentMethod,
|
||||
Key: key,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// 退款申请--调取第三方接口发起退款
|
||||
handlers.PayRefundHandler(&handlers.PayRefundHandlerReq{
|
||||
PayInfoList: payList,
|
||||
})
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatusWithMessage(basic.CodeOrderCancelledNotOk, "the order cancle failed")
|
||||
}
|
||||
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,19 @@ import (
|
|||
"fusenapi/utils/basic"
|
||||
)
|
||||
|
||||
type UserAgainOrderReq struct {
|
||||
Sn string `form:"sn"` // 订单编号
|
||||
}
|
||||
|
||||
type UserAgainOrderRes struct {
|
||||
}
|
||||
|
||||
type UserLogoListReq struct {
|
||||
}
|
||||
|
||||
type UserLogoListRes struct {
|
||||
}
|
||||
|
||||
type UserOrderDeleteReq struct {
|
||||
ID int64 `form:"id"` //订单id
|
||||
}
|
||||
|
|
6
server/inventory/Dockerfile
Executable file
6
server/inventory/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-inventory-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-inventory-srv"]
|
|
@ -1,7 +1,6 @@
|
|||
Name: inventory
|
||||
Host: localhost
|
||||
Port: 9905
|
||||
ReplicaId: 30
|
||||
SourceMysql: fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest
|
||||
Auth:
|
||||
AccessSecret: fusen2023
|
||||
|
|
|
@ -18,7 +18,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||
Handler: TakeHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/inventory/list",
|
||||
Handler: GetCloudListHandler(serverCtx),
|
||||
},
|
||||
|
|
|
@ -16,9 +16,9 @@ type TakeForm struct {
|
|||
}
|
||||
|
||||
type GetCloudListReq struct {
|
||||
Page int `form:"page"`
|
||||
PageSize int `form:"page_size"`
|
||||
Size int64 `form:"size"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
type GetCloudListRsp struct {
|
||||
|
|
12
server/inventory/inventory_test.go
Normal file
12
server/inventory/inventory_test.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// var configFile = flag.String("f", "etc/home-user-auth.yaml", "the config file")
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
// log.Println(model.RawFieldNames[FsCanteenType]())
|
||||
main()
|
||||
}
|
6
server/map-library/Dockerfile
Executable file
6
server/map-library/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-map-library-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-map-library-srv"]
|
6
server/orders/Dockerfile
Executable file
6
server/orders/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-order-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-order-srv"]
|
6
server/pay/Dockerfile
Executable file
6
server/pay/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-pay-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-pay-srv"]
|
|
@ -1,8 +1,6 @@
|
|||
Name: pay
|
||||
Host: 0.0.0.0
|
||||
Port: 9915
|
||||
ReplicaId: 45
|
||||
Timeout: 15000
|
||||
SourceMysql: fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest
|
||||
Auth:
|
||||
AccessSecret: fusen2023
|
||||
|
|
35
server/pay/internal/handler/orderrefundhandler.go
Normal file
35
server/pay/internal/handler/orderrefundhandler.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"fusenapi/server/pay/internal/logic"
|
||||
"fusenapi/server/pay/internal/svc"
|
||||
"fusenapi/server/pay/internal/types"
|
||||
)
|
||||
|
||||
func OrderRefundHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var req types.OrderRefundReq
|
||||
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewOrderRefundLogic(r.Context(), svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.OrderRefund(&req, userinfo)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||
Path: "/api/pay/payment-intent",
|
||||
Handler: OrderPaymentIntentHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/pay/refund",
|
||||
Handler: OrderRefundHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/pay/stripe-webhook",
|
||||
|
|
|
@ -30,6 +30,15 @@ func StripeWebhookHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
|||
// return
|
||||
// }
|
||||
|
||||
IPAddress := r.Header.Get("X-Real-Ip")
|
||||
if IPAddress == "" {
|
||||
IPAddress = r.Header.Get("X-Forwarded-For")
|
||||
}
|
||||
if IPAddress == "" {
|
||||
IPAddress = r.RemoteAddr
|
||||
}
|
||||
|
||||
req.RemoteAddr = IPAddress
|
||||
req.Payload = payload
|
||||
req.StripeSignature = r.Header.Get("Stripe-Signature")
|
||||
|
||||
|
|
43
server/pay/internal/logic/orderrefundlogic.go
Normal file
43
server/pay/internal/logic/orderrefundlogic.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/pay/internal/svc"
|
||||
"fusenapi/server/pay/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type OrderRefundLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewOrderRefundLogic(ctx context.Context, svcCtx *svc.ServiceContext) *OrderRefundLogic {
|
||||
return &OrderRefundLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *OrderRefundLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
|
||||
// func (l *OrderRefundLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
func (l *OrderRefundLogic) OrderRefund(req *types.OrderRefundReq, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
|
@ -64,6 +64,21 @@ func (l *StripeWebhookLogic) StripeWebhook(req *types.StripeWebhookReq, userinfo
|
|||
return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "Webhook signature verification failed")
|
||||
}
|
||||
|
||||
// 新增支付回调事件日志
|
||||
var payMethod = int64(constants.PAYMETHOD_STRIPE)
|
||||
var nowTime = time.Now().Unix()
|
||||
var eventData = string(event.Data.Raw)
|
||||
var fsPayEvent = &gmodel.FsPayEvent{
|
||||
PayMethod: &payMethod,
|
||||
EventId: &event.ID,
|
||||
EventType: &event.Type,
|
||||
EventData: &eventData,
|
||||
EventCreated: &event.Created,
|
||||
Ip: &req.RemoteAddr,
|
||||
CreatedAt: &nowTime,
|
||||
}
|
||||
l.HandlePayEventCreate(fsPayEvent)
|
||||
|
||||
// Unmarshal the event data into an appropriate struct depending on its Type
|
||||
switch event.Type {
|
||||
case "charge.succeeded":
|
||||
|
@ -91,12 +106,13 @@ func (l *StripeWebhookLogic) StripeWebhook(req *types.StripeWebhookReq, userinfo
|
|||
var paymentIntent stripe.PaymentIntent
|
||||
err := json.Unmarshal(event.Data.Raw, &paymentIntent)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
logx.Errorf("err:%+v,desc:%s", err, "pay notify Unmarshal fail event.Type payment_intent.succeeded")
|
||||
return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail event.Type payment_intent.succeeded")
|
||||
}
|
||||
err = l.handlePaymentIntentSucceeded(&paymentIntent)
|
||||
err = l.HandlePaymentIntentSucceeded(&paymentIntent)
|
||||
if err != nil {
|
||||
return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail event.Type Unhandled")
|
||||
logx.Errorf("err:%+v,desc:%s", err, "pay notify handle payment_intent.succeeded")
|
||||
return resp.SetStatusWithMessage(basic.CodePaybackNotOk, "pay notify handle payment_intent.succeeded")
|
||||
}
|
||||
case "payment_method.attached":
|
||||
var paymentMethod stripe.PaymentMethod
|
||||
|
@ -105,6 +121,19 @@ func (l *StripeWebhookLogic) StripeWebhook(req *types.StripeWebhookReq, userinfo
|
|||
logx.Error(err)
|
||||
return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail event.Type payment_method.attached")
|
||||
}
|
||||
case "charge.refunded":
|
||||
var chargeRefunded stripe.Charge
|
||||
err := json.Unmarshal(event.Data.Raw, &chargeRefunded)
|
||||
if err != nil {
|
||||
logx.Errorf("err:%+v,desc:%s", err, "pay notify Unmarshal fail event.Type charge.refunded")
|
||||
return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify Unmarshal fail event.Type charge.refunded")
|
||||
}
|
||||
err = l.HandleChargeRefunded(&chargeRefunded)
|
||||
if err != nil {
|
||||
logx.Errorf("err:%+v,desc:%s", err, "pay notify handle charge.refunded")
|
||||
return resp.SetStatusWithMessage(basic.CodeAesCbcDecryptionErr, "pay notify handle charge.refunded")
|
||||
}
|
||||
|
||||
// ... handle other event types
|
||||
default:
|
||||
logx.Error("Unhandled event")
|
||||
|
@ -114,6 +143,72 @@ func (l *StripeWebhookLogic) StripeWebhook(req *types.StripeWebhookReq, userinfo
|
|||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
||||
|
||||
// 回调事件日志
|
||||
func (l *StripeWebhookLogic) HandlePayEventCreate(fsPayEvent *gmodel.FsPayEvent) error {
|
||||
_, err := gmodel.NewFsPayEventModel(l.svcCtx.MysqlConn).CreateOrUpdate(l.ctx, fsPayEvent)
|
||||
return err
|
||||
}
|
||||
|
||||
// 退款成功
|
||||
func (l *StripeWebhookLogic) HandleChargeRefunded(chargeRefunded *stripe.Charge) (err error) {
|
||||
// 退款成功
|
||||
if chargeRefunded.Status == "succeeded" {
|
||||
orderModel := gmodel.NewFsOrderModel(l.svcCtx.MysqlConn)
|
||||
err = orderModel.Trans(l.ctx, func(ctx context.Context, connGorm *gorm.DB) (err error) {
|
||||
// 查询支付记录
|
||||
payModelT := gmodel.NewFsPayModel(connGorm)
|
||||
payModelTRSB := payModelT.RowSelectBuilder(nil)
|
||||
payModelTRSB1 := payModelTRSB.Where("trade_no = ?", chargeRefunded.PaymentIntent.ID).Where("pay_status = ?", constants.PAYSTATUS_SUCCESS).Where("is_refund = ?", 0)
|
||||
payInfo, err := payModelT.FindOneByQuery(l.ctx, payModelTRSB1, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 更新支付记录
|
||||
*payInfo.IsRefund = 1
|
||||
_, err = payModelT.CreateOrUpdate(ctx, payInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 获取是否还有未退款的数据
|
||||
payModelTRSB2 := payModelTRSB.Where("order_number = ?", payInfo.OrderNumber).Where("pay_status = ?", constants.PAYSTATUS_SUCCESS).Where("is_refund = ?", 0)
|
||||
count, err := payModelT.FindCount(l.ctx, payModelTRSB2, nil)
|
||||
if count == 0 {
|
||||
// 退款完成更新订单状态
|
||||
orderModelT := gmodel.NewFsOrderModel(connGorm)
|
||||
orderModelTRSB := orderModelT.RowSelectBuilder(nil).Where("sn =?", payInfo.OrderNumber)
|
||||
orderInfoRel, err := orderModelT.FindOneByQuery(ctx, orderModelTRSB, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var isRefunded int64 = 1
|
||||
var isRefunding int64 = 1
|
||||
var orderStatus int64 = int64(constants.STATUS_NEW_REFUNDED)
|
||||
var orderInfo = &gmodel.FsOrder{}
|
||||
orderInfo.Id = orderInfoRel.Id
|
||||
orderInfo.IsRefunded = &isRefunded
|
||||
orderInfo.IsRefunding = &isRefunding
|
||||
orderInfo.Status = &orderStatus
|
||||
orderModelT.Update(ctx, orderInfo)
|
||||
|
||||
// 记录退款原因
|
||||
refundReasonModelT := gmodel.NewFsRefundReasonModel(connGorm)
|
||||
refundReasonModelTRSB := refundReasonModelT.RowSelectBuilder(nil).Where("order_id =?", orderInfoRel.Id)
|
||||
refundReasonInfo, err := refundReasonModelT.FindOneByQuery(ctx, refundReasonModelTRSB, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*refundReasonInfo.IsRefund = 1
|
||||
_, err = refundReasonModelT.CreateOrUpdate(ctx, refundReasonInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// session完成
|
||||
// func (l *StripeWebhookLogic) handlePaymentSessionCompleted(sessionId string, tradeNo string) (err error) {
|
||||
// // 查询支付记录
|
||||
|
@ -136,8 +231,8 @@ func (l *StripeWebhookLogic) StripeWebhook(req *types.StripeWebhookReq, userinfo
|
|||
// return err
|
||||
// }
|
||||
|
||||
// 成功的付款
|
||||
func (l *StripeWebhookLogic) handlePaymentIntentSucceeded(paymentIntent *stripe.PaymentIntent) error {
|
||||
// 付款成功
|
||||
func (l *StripeWebhookLogic) HandlePaymentIntentSucceeded(paymentIntent *stripe.PaymentIntent) error {
|
||||
orderSn, ok := paymentIntent.Metadata["order_sn"]
|
||||
if !ok || orderSn == "" {
|
||||
return errors.New("order_sn not found")
|
||||
|
@ -146,14 +241,12 @@ func (l *StripeWebhookLogic) handlePaymentIntentSucceeded(paymentIntent *stripe.
|
|||
// 查询支付记录
|
||||
payModel := gmodel.NewFsPayModel(l.svcCtx.MysqlConn)
|
||||
rsbPay := payModel.RowSelectBuilder(nil)
|
||||
rsbPay = rsbPay.Where("order_number = ?", orderSn)
|
||||
rsbPay = rsbPay.Where("order_number = ?", orderSn).Where("pay_status = ?", constants.PAYSTATUS_UNSUCCESS)
|
||||
payInfo, err := payModel.FindOneByQuery(l.ctx, rsbPay, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *payInfo.PayStatus == 1 {
|
||||
return errors.New("pay status 1")
|
||||
}
|
||||
|
||||
//订单信息
|
||||
orderDetailTemplateModel := gmodel.NewFsOrderDetailTemplateModel(l.svcCtx.MysqlConn)
|
||||
orderModel := gmodel.NewFsOrderModel(l.svcCtx.MysqlConn)
|
||||
|
@ -209,6 +302,7 @@ func (l *StripeWebhookLogic) handlePaymentIntentSucceeded(paymentIntent *stripe.
|
|||
*payInfo.PayTime = nowTime
|
||||
*payInfo.CardNo = card
|
||||
*payInfo.Brand = brand
|
||||
*payInfo.TradeNo = paymentIntent.ID
|
||||
_, err = payModelT.CreateOrUpdate(ctx, payInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -244,12 +338,12 @@ func (l *StripeWebhookLogic) handlePaymentIntentSucceeded(paymentIntent *stripe.
|
|||
|
||||
// 支付记录是尾款
|
||||
if *payInfo.PayStage == int64(constants.PAYSTAGE_REMAINING) {
|
||||
if *orderInfo.Status < int64(constants.STATUS_NEW_PAY_COMPLETED) {
|
||||
if *fsOrderRelInfo.Status < int64(constants.STATUS_NEW_PAY_COMPLETED) {
|
||||
orderStatus = int64(constants.STATUS_NEW_PAY_COMPLETED)
|
||||
}
|
||||
orderIsPayCompleted = 1
|
||||
orderInfo.IsPayCompleted = &orderIsPayCompleted
|
||||
orderPayedAmount = *orderInfo.PayedAmount + paymentIntent.Amount
|
||||
orderPayedAmount = *fsOrderRelInfo.PayedAmount + paymentIntent.Amount
|
||||
}
|
||||
|
||||
// 更新订单信息
|
||||
|
|
|
@ -5,6 +5,12 @@ import (
|
|||
"fusenapi/utils/basic"
|
||||
)
|
||||
|
||||
type OrderRefundReq struct {
|
||||
}
|
||||
|
||||
type OrderRefundRes struct {
|
||||
}
|
||||
|
||||
type OrderPaymentIntentReq struct {
|
||||
Sn string `form:"sn"` //订单编号
|
||||
DeliveryMethod int64 `form:"delivery_method"` //发货方式
|
||||
|
@ -20,6 +26,7 @@ type OrderPaymentIntentRes struct {
|
|||
type StripeWebhookReq struct {
|
||||
Payload []byte `json:"base_byte_slice,optional"`
|
||||
StripeSignature string `json:"Stripe-Signature"`
|
||||
RemoteAddr string `json:"remote_addr"`
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
|
|
6
server/product-model/Dockerfile
Executable file
6
server/product-model/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-product-model-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-product-model-srv"]
|
6
server/product-template-tag/Dockerfile
Executable file
6
server/product-template-tag/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-product-template-tag-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-product-template-tag-srv"]
|
6
server/product-template/Dockerfile
Executable file
6
server/product-template/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-product-template-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-product-template-srv"]
|
6
server/product/Dockerfile
Executable file
6
server/product/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-product-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-product-srv"]
|
|
@ -50,6 +50,7 @@ func (l *GetTagProductListLogic) GetTagProductList(req *types.GetTagProductListR
|
|||
Status: &tStatus,
|
||||
OrderBy: "`sort` DESC",
|
||||
WithChild: true, //需要子集
|
||||
Category: 1, //前台网站用的
|
||||
}
|
||||
//传入分类id
|
||||
if req.Cid > 0 {
|
||||
|
@ -62,6 +63,9 @@ func (l *GetTagProductListLogic) GetTagProductList(req *types.GetTagProductListR
|
|||
logx.Error(err)
|
||||
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get tag info")
|
||||
}
|
||||
if *tagData.Category != 1 {
|
||||
return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "invalid tag")
|
||||
}
|
||||
tReq.LevelPrefixLeftLike = *tagData.LevelPrefix
|
||||
}
|
||||
tagList, err := l.svcCtx.AllModels.FsTags.GetAllTagByParams(l.ctx, tReq)
|
||||
|
@ -220,7 +224,7 @@ func (l *GetTagProductListLogic) dealWithTagMenuData(req dealWithTagMenuDataReq)
|
|||
*req.MinLevel = lenLevel
|
||||
}
|
||||
tagTem := types.TagItem{
|
||||
TagProductList: nil,
|
||||
TagProductList: []interface{}{},
|
||||
TypeName: *tagInfo.Title,
|
||||
TypeId: tagInfo.Id,
|
||||
Icon: *tagInfo.Icon,
|
||||
|
|
6
server/render/Dockerfile
Executable file
6
server/render/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-render-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-render-srv"]
|
238
server/render/consumer/assemble_render_data.go
Normal file
238
server/render/consumer/assemble_render_data.go
Normal file
|
@ -0,0 +1,238 @@
|
|||
package consumer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"fusenapi/model/gmodel"
|
||||
"fusenapi/utils/websocket_data"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gorm.io/gorm"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 这里请求的py接口返回数据
|
||||
type pythonApiRsp struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data []struct {
|
||||
Tid int64 `json:"tid"`
|
||||
Imgurl string `json:"imgurl"`
|
||||
Costtime int64 `json:"costtime"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// 消费渲染需要组装的数据
|
||||
type MqConsumerRenderAssemble struct {
|
||||
}
|
||||
|
||||
func (m *MqConsumerRenderAssemble) Run(ctx context.Context, 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 //不返回错误就删除消息
|
||||
}
|
||||
val := ctx.Value("allmodels")
|
||||
if val == nil {
|
||||
return errors.New("allmodels is nil")
|
||||
}
|
||||
allmodels, ok := val.(*gmodel.AllModelsGen)
|
||||
if !ok {
|
||||
return errors.New("allmodels is nil!!")
|
||||
}
|
||||
timeSearchBegin := time.Now().UnixMilli()
|
||||
//获取模板
|
||||
templateInfo, err := allmodels.FsProductTemplateV2.FindOneByProductIdTagIdWithSizeTable(ctx, parseInfo.RenderData.ProductId, fmt.Sprintf("%d", parseInfo.RenderData.TemplateTagId))
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logx.Error("template info is not found")
|
||||
return nil
|
||||
}
|
||||
logx.Error("failed to get template info:", err)
|
||||
return err
|
||||
}
|
||||
renderLogTime := time.Now().UnixMilli() - timeSearchBegin
|
||||
now := time.Now().Unix()
|
||||
title := "1-组装模板数据"
|
||||
//云渲染日志
|
||||
err = allmodels.FsCloudRenderLog.Create(ctx, &gmodel.FsCloudRenderLog{
|
||||
UserId: &parseInfo.RenderData.UserId,
|
||||
Title: &title,
|
||||
Time: &renderLogTime,
|
||||
Tag: &parseInfo.RenderId,
|
||||
Ctime: &now,
|
||||
})
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
pyapiBeginTime := time.Now().UnixMilli()
|
||||
//这里curl post请求数据。获取处理好的贴图数据,用于贴model的贴图
|
||||
pythonPostData := map[string]interface{}{
|
||||
"tids": templateInfo.Id,
|
||||
"data": parseInfo.RenderData.Data,
|
||||
}
|
||||
pyPostBytes, _ := json.Marshal(pythonPostData)
|
||||
url := "http://110.41.19.98:8867/imgRender"
|
||||
pyRsp, err := http.Post(url, "application/json;charset=UTF-8", bytes.NewReader(pyPostBytes))
|
||||
if err != nil {
|
||||
logx.Error("request python render api err:", err)
|
||||
return err
|
||||
}
|
||||
defer pyRsp.Body.Close()
|
||||
pyRspBytes, err := ioutil.ReadAll(pyRsp.Body)
|
||||
if err != nil {
|
||||
logx.Error("failed to read python api rsp body,err=", err)
|
||||
return err
|
||||
}
|
||||
var rspInfo pythonApiRsp
|
||||
if err = json.Unmarshal(pyRspBytes, &rspInfo); err != nil {
|
||||
logx.Error("failed to unmarshal python api rsp:", err)
|
||||
return err
|
||||
}
|
||||
if rspInfo.Code != 200 {
|
||||
logx.Error("python api 接口请求错误:", rspInfo.Msg)
|
||||
return err
|
||||
}
|
||||
if len(rspInfo.Data) == 0 {
|
||||
logx.Error("python api 接口没有数据:")
|
||||
return err
|
||||
}
|
||||
//云渲染日志
|
||||
title = "2-请求->接收python合成刀版图接口"
|
||||
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,
|
||||
})
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
incTime := int64(0)
|
||||
mapCurlData := make(map[int64]int)
|
||||
for k, v := range rspInfo.Data {
|
||||
mapCurlData[v.Tid] = k
|
||||
incTime += v.Costtime
|
||||
}
|
||||
//云渲染日志
|
||||
title = "3-python合成刀版图"
|
||||
now = time.Now().Unix()
|
||||
postData := string(pyPostBytes)
|
||||
pyRspStr := string(pyRspBytes)
|
||||
err = allmodels.FsCloudRenderLog.Create(ctx, &gmodel.FsCloudRenderLog{
|
||||
UserId: &parseInfo.RenderData.UserId,
|
||||
PostUrl: &url,
|
||||
PostData: &postData,
|
||||
Result: &pyRspStr,
|
||||
Title: &title,
|
||||
Time: &incTime,
|
||||
Tag: &parseInfo.RenderId,
|
||||
Ctime: &now,
|
||||
})
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
//获取渲染设置信息
|
||||
//element, err := allmodels.FsProductTemplateElement
|
||||
/*
|
||||
|
||||
$element = ProductTemplateElement::find()
|
||||
->andFilterWhere(['in', 'product_template_id', $mids])
|
||||
->asArray()
|
||||
->all();
|
||||
|
||||
$element = array_column($element, null, 'product_template_id');
|
||||
$elementTitles = array_column($element, 'title');
|
||||
|
||||
$result = [];
|
||||
|
||||
$time_pinjie_begin = $render->getMillisecond();
|
||||
foreach ($templates as $key => $val) {
|
||||
if(!isset($element[$val['model_id']]) || !isset($imageData[$val['id']])){
|
||||
continue;
|
||||
}
|
||||
//数据拼装
|
||||
$item = [];
|
||||
|
||||
$item['light'] = $element[$val['model_id']]['light'];
|
||||
$item['refletion'] = $element[$val['model_id']]['refletion'] == '' ? -1 : (int)$element[$val['model_id']]['refletion'];
|
||||
$item['scale'] = $element[$val['model_id']]['scale'];
|
||||
$item['sku_id'] = $val['product_id'];
|
||||
$item['tid'] = $element[$val['model_id']]['title'];
|
||||
$item['rotation'] = $element[$val['model_id']]['rotation'];
|
||||
$item['filePath'] = '';//todo 文件路径,针对千人千面
|
||||
|
||||
//组装data数据
|
||||
$tempData = [];
|
||||
//获取材质模式对应关系
|
||||
$mode = $element[$val['model_id']]['mode'] ? json_decode($element[$val['model_id']]['mode'], true) : [];
|
||||
// $base_img = (new ImageService())->base64EncodeImageNoHeader(\Yii::$app->params['baseurl'].$imageData[$val['id']]['imgurl']);
|
||||
$base_img = \Yii::$app->params['h5Url'].'/storage'.$imageData[$val['id']]['imgurl'];
|
||||
//判断是否包含base数据 即对应建模那边的model
|
||||
if($element[$val['model_id']]['base']){
|
||||
$tempData[] = [
|
||||
'name' => 'model',
|
||||
'data' => '0,'.$base_img.','.$element[$val['model_id']]['base'],
|
||||
'type' => 'other',
|
||||
'layer' => '0',
|
||||
'is_update' => 1,
|
||||
'mode' => $mode['model'],
|
||||
];
|
||||
}
|
||||
if($element[$val['model_id']]['shadow']){
|
||||
$tempData[] = [
|
||||
'name' => 'shadow',
|
||||
'data' => $element[$val['model_id']]['shadow'],
|
||||
'type' => 'other',
|
||||
'layer' => '0',
|
||||
'is_update' => 0,
|
||||
'mode' => $mode['shadow'],
|
||||
];
|
||||
}
|
||||
if($element[$val['model_id']]['model_p']){
|
||||
$tempData[] = [
|
||||
'name' => 'model_P',
|
||||
'data' => '0,'.$element[$val['model_id']]['model_p'],
|
||||
'type' => 'other',
|
||||
'layer' => '0',
|
||||
'is_update' => 0,
|
||||
'mode' => $mode['model_P'],
|
||||
];
|
||||
}
|
||||
$item['data'] = $tempData;
|
||||
$result[] = $item;
|
||||
|
||||
}
|
||||
$log = new CloudRenderLog();
|
||||
$log->title = '接收到python刀版图 -> 3-组装MQ渲染任务队列';
|
||||
$log->time = $render->getMillisecond() - $time_pinjie_begin;
|
||||
$log->user_id = $user_id;
|
||||
$log->post_data = '';
|
||||
$log->post_url = '';
|
||||
$log->result = $res;
|
||||
$log->tag = $inputData['id'];
|
||||
$log->ctime = time();
|
||||
$log->save(false);
|
||||
}
|
||||
|
||||
$sendData = [
|
||||
'id' => $inputData['id'],
|
||||
'order_id' => 0,
|
||||
'user_id' => \Yii::$app->user->id,
|
||||
'sku_ids' => $inputData['sku_ids'],
|
||||
'tids' => $elementTitles,
|
||||
'data' => $result,
|
||||
'is_thousand_face' => 0,
|
||||
'folder' => '',//todo 千人千面需要使用
|
||||
];
|
||||
return $sendData;*/
|
||||
return nil
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
Name: render
|
||||
Host: localhost
|
||||
Port: 8888
|
||||
ReplicaId: 55
|
||||
Host: 0.0.0.0
|
||||
Port: 9919
|
||||
SourceMysql: fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest
|
||||
Auth:
|
||||
AccessSecret: fusen2023
|
||||
AccessExpire: 2592000
|
||||
RefreshAfter: 1592000
|
||||
RefreshAfter: 1592000
|
||||
SourceRabbitMq: amqp://rabbit001:rabbit001129@110.41.19.98:5672
|
|
@ -8,7 +8,8 @@ import (
|
|||
|
||||
type Config struct {
|
||||
rest.RestConf
|
||||
SourceMysql string
|
||||
Auth types.Auth
|
||||
ReplicaId uint64
|
||||
SourceMysql string
|
||||
Auth types.Auth
|
||||
ReplicaId uint64
|
||||
SourceRabbitMq string
|
||||
}
|
||||
|
|
|
@ -11,22 +11,22 @@ import (
|
|||
"fusenapi/server/render/internal/types"
|
||||
)
|
||||
|
||||
func ToUnityHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
func GetFaceSliceHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var req types.RequestToUnity
|
||||
var req types.Request
|
||||
userinfo, err := basic.RequestParse(w, r, svcCtx.SharedState, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewToUnityLogic(r.Context(), svcCtx)
|
||||
l := logic.NewGetFaceSliceLogic(r.Context(), svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.ToUnity(&req, userinfo)
|
||||
resp := l.GetFaceSlice(&req, userinfo)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
|
@ -11,22 +11,22 @@ import (
|
|||
"fusenapi/server/render/internal/types"
|
||||
)
|
||||
|
||||
func ReadImagesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
func RenderNotifyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var req types.RequestReadImages
|
||||
var req types.RenderNotifyReq
|
||||
userinfo, err := basic.RequestParse(w, r, svcCtx.SharedState, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewReadImagesLogic(r.Context(), svcCtx)
|
||||
l := logic.NewRenderNotifyLogic(r.Context(), svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.ReadImages(&req, userinfo)
|
||||
resp := l.RenderNotify(&req, userinfo)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
|
@ -13,14 +13,14 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||
server.AddRoutes(
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/render/to-unity",
|
||||
Handler: ToUnityHandler(serverCtx),
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/render/render_notify",
|
||||
Handler: RenderNotifyHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/render/read-images",
|
||||
Handler: ReadImagesHandler(serverCtx),
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/render/get_face_slice",
|
||||
Handler: GetFaceSliceHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
|
69
server/render/internal/logic/getfaceslicelogic.go
Normal file
69
server/render/internal/logic/getfaceslicelogic.go
Normal file
|
@ -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)
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/render/internal/svc"
|
||||
"fusenapi/server/render/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ReadImagesLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewReadImagesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ReadImagesLogic {
|
||||
return &ReadImagesLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *ReadImagesLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向
|
||||
// func (l *ReadImagesLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// }
|
||||
|
||||
func (l *ReadImagesLogic) ReadImages(req *types.RequestReadImages, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
|
@ -1,14 +1,15 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fusenapi/constants"
|
||||
"fusenapi/utils/basic"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fusenapi/constants"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
"fusenapi/utils/websocket_data"
|
||||
|
||||
"fusenapi/server/websocket/internal/svc"
|
||||
"fusenapi/server/websocket/internal/types"
|
||||
"fusenapi/server/render/internal/svc"
|
||||
"fusenapi/server/render/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
@ -36,50 +37,36 @@ func NewRenderNotifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Rend
|
|||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
func (l *RenderNotifyLogic) RenderNotify(req *types.RenderNotifyReq) (resp *basic.Response) {
|
||||
if time.Now().Unix()-120 > req.Time /*|| req.Time > time.Now().Unix() */ {
|
||||
func (l *RenderNotifyLogic) RenderNotify(req *types.RenderNotifyReq, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
/*if time.Now().Unix()-120 > req.Time || req.Time > time.Now().Unix() {
|
||||
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid param time")
|
||||
}*/
|
||||
if req.Info.TaskId == "" {
|
||||
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid param task_id")
|
||||
}
|
||||
if req.Info.Image == "" {
|
||||
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid param image")
|
||||
}
|
||||
/* if req.Sign == "" {
|
||||
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid param sign")
|
||||
}*/
|
||||
//验证签名 sha256
|
||||
/*notifyByte, _ := json.Marshal(req.Info)
|
||||
h := sha256.New()
|
||||
h.Write([]byte(fmt.Sprintf(constants.RENDER_NOTIFY_SIGN_KEY, string(notifyByte), req.Time)))
|
||||
signHex := h.Sum(nil)
|
||||
sign := hex.EncodeToString(signHex)
|
||||
//fmt.Println(sign)
|
||||
if req.Sign != sign {
|
||||
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid sign")
|
||||
}*/
|
||||
//遍历websocket链接把数据传进去
|
||||
mapConnPool.Range(func(key, value any) bool {
|
||||
//断言连接
|
||||
ws, ok := value.(wsConnectItem)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
//关闭标识
|
||||
if ws.isClose {
|
||||
return true
|
||||
}
|
||||
renderKey := ws.getRenderImageMapKey(req.Info.ProductId, req.Info.TemplateTagId, req.Info.LogoId, req.Info.AlgorithmVersion)
|
||||
//查询有无该渲染任务
|
||||
_, ok = ws.renderProperty.renderImageTask[renderKey]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
b := ws.respondDataFormat(constants.WEBSOCKET_RENDER_IMAGE, types.RenderImageRspMsg{
|
||||
ProductId: req.Info.ProductId,
|
||||
TemplateTagId: req.Info.TemplateTagId,
|
||||
Image: req.Info.Image,
|
||||
})
|
||||
//删除对应的需要渲染的图片map
|
||||
ws.renderProperty.renderImageTaskCtlChan <- renderImageControlChanItem{
|
||||
Option: 0, //0删除 1添加
|
||||
Key: renderKey,
|
||||
}
|
||||
//发送数据到out chan
|
||||
ws.sendToOutChan(b)
|
||||
return true
|
||||
})
|
||||
data := websocket_data.RenderImageNotify{
|
||||
TaskId: req.Info.TaskId,
|
||||
Image: req.Info.Image,
|
||||
}
|
||||
d, _ := json.Marshal(data)
|
||||
if err := l.svcCtx.RabbitMq.SendMsg(constants.RABBIT_MQ_RENDER_RESULT_DATA, d); err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeServiceErr, "failed to send data")
|
||||
}
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/render/internal/svc"
|
||||
"fusenapi/server/render/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ToUnityLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewToUnityLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ToUnityLogic {
|
||||
return &ToUnityLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *ToUnityLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向
|
||||
// func (l *ToUnityLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// }
|
||||
|
||||
func (l *ToUnityLogic) ToUnity(req *types.RequestToUnity, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
|
@ -21,6 +21,7 @@ type ServiceContext struct {
|
|||
|
||||
MysqlConn *gorm.DB
|
||||
AllModels *gmodel.AllModelsGen
|
||||
RabbitMq *initalize.RabbitMqHandle
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
|
@ -32,6 +33,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
|||
MysqlConn: conn,
|
||||
SharedState: StateServer,
|
||||
AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)),
|
||||
RabbitMq: initalize.InitRabbitMq(c.SourceRabbitMq, nil),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,17 @@ type RequestToUnity struct {
|
|||
type RequestReadImages struct {
|
||||
}
|
||||
|
||||
type RenderNotifyReq struct {
|
||||
Sign string `json:"sign"`
|
||||
Time int64 `json:"time"`
|
||||
Info NotifyInfo `json:"info"`
|
||||
}
|
||||
|
||||
type NotifyInfo struct {
|
||||
TaskId string `json:"task_id"` //任务id
|
||||
Image string `json:"image"`
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"fusenapi/constants"
|
||||
"fusenapi/server/render/consumer"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
|
@ -29,8 +32,13 @@ func main() {
|
|||
defer server.Stop()
|
||||
|
||||
ctx := svc.NewServiceContext(c)
|
||||
//消费渲染前组装数据队列
|
||||
ctx1 := context.Background()
|
||||
ctx2, cancel := context.WithCancel(ctx1)
|
||||
ctx2 = context.WithValue(ctx2, "allmodels", ctx.AllModels)
|
||||
defer cancel()
|
||||
go ctx.RabbitMq.Consume(ctx2, constants.RABBIT_MQ_ASSEMBLE_RENDER_DATA, &consumer.MqConsumerRenderAssemble{})
|
||||
handler.RegisterHandlers(server, ctx)
|
||||
|
||||
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
|
||||
server.Start()
|
||||
}
|
||||
|
|
6
server/shopping-cart-confirmation/Dockerfile
Executable file
6
server/shopping-cart-confirmation/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-shopping-cart-confirmation-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-shopping-cart-confirmation-srv"]
|
6
server/upload/Dockerfile
Executable file
6
server/upload/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-upload-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-upload-srv"]
|
|
@ -13,4 +13,7 @@ AWS:
|
|||
Credentials:
|
||||
AccessKeyID: AKIAZB2JKUXDPNRP4YT2
|
||||
Secret: sjCEv0JxATnPCxno2KNLm0X8oDc7srUR+4vkYhvm
|
||||
Token:
|
||||
Token:
|
||||
BLMService:
|
||||
ImageProcess:
|
||||
Url: "http://110.41.19.98:8868/removebg"
|
||||
|
|
|
@ -21,4 +21,9 @@ type Config struct {
|
|||
}
|
||||
}
|
||||
}
|
||||
BLMService struct {
|
||||
ImageProcess struct {
|
||||
Url string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,31 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||
Path: "/api/upload/qrcode",
|
||||
Handler: UploadQrcodeHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/upload/upload-files-backend",
|
||||
Handler: UploadFilesBackendHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/upload/upload-files-frontend",
|
||||
Handler: UploadFilesFrontendHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/upload/upload-callback",
|
||||
Handler: UploadCallbackHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/upload/up-logo",
|
||||
Handler: UploadLogoHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/upload/upload-file-base",
|
||||
Handler: UploadFileBaseHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
35
server/upload/internal/handler/uploadcallbackhandler.go
Normal file
35
server/upload/internal/handler/uploadcallbackhandler.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"fusenapi/server/upload/internal/logic"
|
||||
"fusenapi/server/upload/internal/svc"
|
||||
"fusenapi/server/upload/internal/types"
|
||||
)
|
||||
|
||||
func UploadCallbackHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var req types.UploadCallbackReq
|
||||
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewUploadCallbackLogic(r.Context(), svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.UploadCallback(&req, userinfo)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
||||
}
|
||||
}
|
||||
}
|
35
server/upload/internal/handler/uploadfilebasehandler.go
Normal file
35
server/upload/internal/handler/uploadfilebasehandler.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"fusenapi/server/upload/internal/logic"
|
||||
"fusenapi/server/upload/internal/svc"
|
||||
"fusenapi/server/upload/internal/types"
|
||||
)
|
||||
|
||||
func UploadFileBaseHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var req types.UploadFileBaseReq
|
||||
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewUploadFileBaseLogic(r.Context(), svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.UploadFileBase(&req, userinfo)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
||||
}
|
||||
}
|
||||
}
|
35
server/upload/internal/handler/uploadfilesbackendhandler.go
Normal file
35
server/upload/internal/handler/uploadfilesbackendhandler.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"fusenapi/server/upload/internal/logic"
|
||||
"fusenapi/server/upload/internal/svc"
|
||||
"fusenapi/server/upload/internal/types"
|
||||
)
|
||||
|
||||
func UploadFilesBackendHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var req types.UploadFilesReq
|
||||
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewUploadFilesBackendLogic(r, svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.UploadFilesBackend(&req, userinfo)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
||||
}
|
||||
}
|
||||
}
|
35
server/upload/internal/handler/uploadfilesfrontendhandler.go
Normal file
35
server/upload/internal/handler/uploadfilesfrontendhandler.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"fusenapi/server/upload/internal/logic"
|
||||
"fusenapi/server/upload/internal/svc"
|
||||
"fusenapi/server/upload/internal/types"
|
||||
)
|
||||
|
||||
func UploadFilesFrontendHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var req types.UploadFilesReq
|
||||
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewUploadFilesFrontendLogic(r.Context(), svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.UploadFilesFrontend(&req, userinfo)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
||||
}
|
||||
}
|
||||
}
|
35
server/upload/internal/handler/uploadlogohandler.go
Normal file
35
server/upload/internal/handler/uploadlogohandler.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"fusenapi/server/upload/internal/logic"
|
||||
"fusenapi/server/upload/internal/svc"
|
||||
"fusenapi/server/upload/internal/types"
|
||||
)
|
||||
|
||||
func UploadLogoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var req types.UploadLogoReq
|
||||
userinfo, err := basic.RequestParse(w, r, svcCtx, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewUploadLogoLogic(r.Context(), svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.UploadLogo(&req, userinfo)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
||||
}
|
||||
}
|
||||
}
|
98
server/upload/internal/logic/uploadcallbacklogic.go
Normal file
98
server/upload/internal/logic/uploadcallbacklogic.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fusenapi/model/gmodel"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/upload/internal/svc"
|
||||
"fusenapi/server/upload/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UploadCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUploadCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadCallbackLogic {
|
||||
return &UploadCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *UploadCallbackLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
|
||||
// func (l *UploadCallbackLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
func (l *UploadCallbackLogic) UploadCallback(req *types.UploadCallbackReq, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
|
||||
var userId int64
|
||||
var guestId int64
|
||||
|
||||
// 检查用户是否是游客
|
||||
if userinfo.IsGuest() {
|
||||
// 如果是,使用游客ID和游客键名格式
|
||||
guestId = userinfo.GuestId
|
||||
} else {
|
||||
// 否则,使用用户ID和用户键名格式
|
||||
userId = userinfo.UserId
|
||||
}
|
||||
|
||||
// 定义存储桶名称
|
||||
var bucketName *string
|
||||
|
||||
// 根据类别选择存储桶
|
||||
switch req.UploadBucket {
|
||||
case 2:
|
||||
bucketName = basic.TempfileBucketName
|
||||
default:
|
||||
bucketName = basic.StorageBucketName
|
||||
}
|
||||
|
||||
resourceModel := gmodel.NewFsResourceModel(l.svcCtx.MysqlConn)
|
||||
err := resourceModel.Trans(l.ctx, func(ctx context.Context, connGorm *gorm.DB) (err error) {
|
||||
resourceModelTS := gmodel.NewFsResourceModel(l.svcCtx.MysqlConn)
|
||||
resourceInfo, err := resourceModelTS.FindOneById(ctx, req.ResourceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var version string = "0.0.1"
|
||||
var fsResource = &gmodel.FsResource{}
|
||||
fsResource.UserId = &userId
|
||||
fsResource.GuestId = &guestId
|
||||
fsResource.ResourceId = req.ResourceId
|
||||
fsResource.ResourceType = &req.ResourceType
|
||||
fsResource.ResourceUrl = &req.ResourceUrl
|
||||
fsResource.Metadata = &req.Metadata
|
||||
fsResource.ApiType = &req.ApiType
|
||||
fsResource.BucketName = bucketName
|
||||
fsResource.Version = &version
|
||||
if resourceInfo.ResourceId == "" {
|
||||
_, err = resourceModelTS.Create(ctx, fsResource)
|
||||
} else {
|
||||
_, err = resourceModelTS.Update(ctx, fsResource)
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeFileUploadErr, "file upload err,UploadCallback failed")
|
||||
}
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
154
server/upload/internal/logic/uploadfilebaselogic.go
Normal file
154
server/upload/internal/logic/uploadfilebaselogic.go
Normal file
|
@ -0,0 +1,154 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fusenapi/model/gmodel"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
"fusenapi/utils/file"
|
||||
"fusenapi/utils/hash"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/upload/internal/svc"
|
||||
"fusenapi/server/upload/internal/types"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type UploadFileBaseLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUploadFileBaseLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadFileBaseLogic {
|
||||
return &UploadFileBaseLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *UploadFileBaseLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
|
||||
// func (l *UploadFileBaseLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
func (l *UploadFileBaseLogic) UploadFileBase(req *types.UploadFileBaseReq, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
// 定义用户ID和S3键名格式
|
||||
var userId int64
|
||||
var guestId int64
|
||||
|
||||
// 检查用户是否是游客
|
||||
if userinfo.IsGuest() {
|
||||
// 如果是,使用游客ID和游客键名格式
|
||||
guestId = userinfo.GuestId
|
||||
} else {
|
||||
// 否则,使用用户ID和用户键名格式
|
||||
userId = userinfo.UserId
|
||||
}
|
||||
if guestId == 0 {
|
||||
guestId = req.GuestId
|
||||
}
|
||||
if userId == 0 {
|
||||
userId = req.UserId
|
||||
}
|
||||
|
||||
// 定义存储桶名称
|
||||
var bucketName *string
|
||||
var apiType int64 = req.ApiType
|
||||
|
||||
// 根据类别选择存储桶
|
||||
switch req.UploadBucket {
|
||||
case 2:
|
||||
bucketName = basic.TempfileBucketName
|
||||
default:
|
||||
bucketName = basic.StorageBucketName
|
||||
}
|
||||
|
||||
// 设置AWS会话的区域
|
||||
l.svcCtx.AwsSession.Config.Region = aws.String("us-west-1")
|
||||
|
||||
// 创建新的S3服务实例
|
||||
svc := s3.New(l.svcCtx.AwsSession)
|
||||
|
||||
// 定义S3请求和当前时间
|
||||
var s3req *request.Request
|
||||
|
||||
var resourceId string = hash.JsonHashKey(req.FileKey)
|
||||
|
||||
var uploadUrl = UploadUrl{}
|
||||
resourceModel := gmodel.NewFsResourceModel(l.svcCtx.MysqlConn)
|
||||
resourceInfo, err := resourceModel.FindOneById(l.ctx, resourceId)
|
||||
if err == nil && resourceInfo.ResourceId != "" {
|
||||
uploadUrl.Status = 1
|
||||
uploadUrl.ResourceId = resourceId
|
||||
uploadUrl.ResourceUrl = *resourceInfo.ResourceUrl
|
||||
} else {
|
||||
dist, contentType, err := file.FileBase64ToByte(req.FileData)
|
||||
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeFileUploadErr, "file upload err,base64tobyte error")
|
||||
}
|
||||
|
||||
// 创建S3对象存储请求
|
||||
s3req, _ = svc.PutObjectRequest(
|
||||
&s3.PutObjectInput{
|
||||
Bucket: bucketName,
|
||||
Key: &resourceId,
|
||||
},
|
||||
)
|
||||
|
||||
// 设置请求体为文件数据
|
||||
s3req.SetBufferBody(dist)
|
||||
|
||||
// 发送请求
|
||||
err = s3req.Send()
|
||||
|
||||
// 检查是否有错误
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
uploadUrl.Status = 0
|
||||
} else {
|
||||
var url = s3req.HTTPRequest.URL.String()
|
||||
// 打印请求URL
|
||||
logx.Info(url)
|
||||
uploadUrl.Status = 1
|
||||
uploadUrl.ResourceId = resourceId
|
||||
uploadUrl.ResourceUrl = url
|
||||
var version string = "0.0.1"
|
||||
var nowTime = time.Now()
|
||||
_, err = resourceModel.Create(l.ctx, &gmodel.FsResource{
|
||||
ResourceId: resourceId,
|
||||
UserId: &userId,
|
||||
GuestId: &guestId,
|
||||
ResourceType: &contentType,
|
||||
ResourceUrl: &url,
|
||||
Version: &version,
|
||||
UploadedAt: &nowTime,
|
||||
Metadata: &req.Metadata,
|
||||
ApiType: &apiType,
|
||||
BucketName: bucketName,
|
||||
})
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 返回成功的响应和上传URL
|
||||
return resp.SetStatus(basic.CodeOK, map[string]interface{}{
|
||||
"upload_data": uploadUrl,
|
||||
})
|
||||
}
|
259
server/upload/internal/logic/uploadfilesbackendlogic.go
Normal file
259
server/upload/internal/logic/uploadfilesbackendlogic.go
Normal file
|
@ -0,0 +1,259 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fusenapi/model/gmodel"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
"fusenapi/utils/hash"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/upload/internal/svc"
|
||||
"fusenapi/server/upload/internal/types"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
)
|
||||
|
||||
type UploadFilesBackendLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
r *http.Request
|
||||
}
|
||||
|
||||
func NewUploadFilesBackendLogic(r *http.Request, svcCtx *svc.ServiceContext) *UploadFilesBackendLogic {
|
||||
return &UploadFilesBackendLogic{
|
||||
Logger: logx.WithContext(r.Context()),
|
||||
ctx: r.Context(),
|
||||
svcCtx: svcCtx,
|
||||
r: r,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *UploadFilesBackendLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
|
||||
// func (l *UploadFilesBackendLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
func (l *UploadFilesBackendLogic) UploadFilesBackend(req *types.UploadFilesReq, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
if userinfo.IsOnlooker() {
|
||||
// 如果是,返回未授权的错误码
|
||||
return resp.SetStatus(basic.CodeUnAuth)
|
||||
}
|
||||
|
||||
// 定义用户ID和S3键名格式
|
||||
var userId int64
|
||||
var guestId int64
|
||||
|
||||
// 检查用户是否是游客
|
||||
if userinfo.IsGuest() {
|
||||
// 如果是,使用游客ID和游客键名格式
|
||||
guestId = userinfo.GuestId
|
||||
} else {
|
||||
// 否则,使用用户ID和用户键名格式
|
||||
userId = userinfo.UserId
|
||||
}
|
||||
|
||||
var uploadInfoList []UploadInfo
|
||||
err := json.Unmarshal([]byte(req.UploadInfo), &uploadInfoList)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeFileUploadErr, "file upload err,params Unmarshal failed")
|
||||
}
|
||||
|
||||
var fileLen = len(uploadInfoList)
|
||||
|
||||
if fileLen == 0 {
|
||||
return resp.SetStatus(basic.CodeFileUploadErr, "file upload err,no files")
|
||||
}
|
||||
if req.ApiType == 1 && fileLen > 100 {
|
||||
return resp.SetStatus(basic.CodeFileUploadErr, "file upload err, files count is beyond the maximum")
|
||||
}
|
||||
|
||||
// 定义存储桶名称
|
||||
var bucketName *string
|
||||
|
||||
// 根据类别选择存储桶
|
||||
switch req.UploadBucket {
|
||||
case 2:
|
||||
bucketName = basic.TempfileBucketName
|
||||
default:
|
||||
bucketName = basic.StorageBucketName
|
||||
}
|
||||
|
||||
//设置内存大小
|
||||
l.r.ParseMultipartForm(32 << 20)
|
||||
|
||||
//获取上传的文件组
|
||||
files := l.r.MultipartForm.File["file"]
|
||||
|
||||
// 设置AWS会话的区域
|
||||
l.svcCtx.AwsSession.Config.Region = aws.String("us-west-1")
|
||||
|
||||
// 创建新的S3服务实例
|
||||
svc := s3.New(l.svcCtx.AwsSession)
|
||||
|
||||
// 定义S3请求和当前时间
|
||||
var s3req *request.Request
|
||||
|
||||
resourceModel := gmodel.NewFsResourceModel(l.svcCtx.MysqlConn)
|
||||
result, err := mr.MapReduce(func(source chan<- interface{}) {
|
||||
for i, info := range uploadInfoList {
|
||||
fileType := files[i].Header.Get("Content-Type")
|
||||
// 打开文件
|
||||
file, err := files[i].Open()
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
defer file.Close()
|
||||
// 读取数据流
|
||||
ioData, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
|
||||
// 一系列业务逻辑....验证类型,文件大小
|
||||
|
||||
var hashKey string = hash.JsonHashKey(info.FileKeys)
|
||||
source <- UploadData{
|
||||
FileKey: info.FileKeys,
|
||||
FileType: fileType,
|
||||
Metadata: info.Metadata,
|
||||
FileData: ioData,
|
||||
ApiType: req.ApiType,
|
||||
Bucket: bucketName,
|
||||
HashKey: hashKey,
|
||||
}
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[interface{}], cancel func(error)) {
|
||||
uploadDataInfo := item.(UploadData)
|
||||
|
||||
var uploadUrl = UploadUrl{}
|
||||
uploadUrl.Key = uploadDataInfo.FileKey
|
||||
uploadUrl.ApiType = uploadDataInfo.ApiType
|
||||
uploadUrl.ResourceType = uploadDataInfo.FileType
|
||||
|
||||
var resourceId string = uploadDataInfo.HashKey
|
||||
// 查询数据库
|
||||
resourceInfo, err := resourceModel.FindOneById(l.ctx, resourceId)
|
||||
if err == nil && resourceInfo.ResourceId != "" {
|
||||
uploadUrl.Status = 1
|
||||
uploadUrl.ResourceId = resourceId
|
||||
uploadUrl.ResourceUrl = *resourceInfo.ResourceUrl
|
||||
} else {
|
||||
// 创建S3对象存储请求
|
||||
s3req, _ = svc.PutObjectRequest(
|
||||
&s3.PutObjectInput{
|
||||
Bucket: uploadDataInfo.Bucket,
|
||||
Key: &uploadDataInfo.HashKey,
|
||||
},
|
||||
)
|
||||
|
||||
// 设置请求体为文件数据
|
||||
s3req.SetBufferBody(uploadDataInfo.FileData)
|
||||
|
||||
// 发送请求
|
||||
err = s3req.Send()
|
||||
// 检查是否有错误
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
uploadUrl.Status = 0
|
||||
} else {
|
||||
contentType := http.DetectContentType(uploadDataInfo.FileData)
|
||||
var url = s3req.HTTPRequest.URL.String()
|
||||
// 打印请求URL
|
||||
logx.Info(url)
|
||||
uploadUrl.Status = 1
|
||||
uploadUrl.ResourceId = resourceId
|
||||
uploadUrl.ResourceUrl = url
|
||||
var version string = "0.0.1"
|
||||
var nowTime = time.Now()
|
||||
_, err = resourceModel.Create(l.ctx, &gmodel.FsResource{
|
||||
ResourceId: resourceId,
|
||||
UserId: &userId,
|
||||
GuestId: &guestId,
|
||||
ResourceType: &contentType,
|
||||
ResourceUrl: &url,
|
||||
Version: &version,
|
||||
UploadedAt: &nowTime,
|
||||
Metadata: &uploadDataInfo.Metadata,
|
||||
ApiType: &uploadDataInfo.ApiType,
|
||||
BucketName: bucketName,
|
||||
})
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Notice 这个必须加!
|
||||
writer.Write(uploadUrl)
|
||||
}, func(pipe <-chan interface{}, writer mr.Writer[interface{}], cancel func(error)) {
|
||||
var uploadUrlList = make(map[string][]*UploadUrl)
|
||||
var uploadUrlListFail []*UploadUrl
|
||||
var uploadUrlListSuccess []*UploadUrl
|
||||
for p := range pipe {
|
||||
var uploadUrl = p.(UploadUrl)
|
||||
if uploadUrl.Status == 1 {
|
||||
uploadUrlListSuccess = append(uploadUrlListSuccess, &uploadUrl)
|
||||
} else {
|
||||
uploadUrlListFail = append(uploadUrlListFail, &uploadUrl)
|
||||
}
|
||||
}
|
||||
|
||||
// Notice 这个必须加!
|
||||
uploadUrlList["success"] = uploadUrlListSuccess
|
||||
uploadUrlList["fail"] = uploadUrlListFail
|
||||
writer.Write(uploadUrlList)
|
||||
})
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
|
||||
// 返回成功的响应和上传URL
|
||||
return resp.SetStatus(basic.CodeOK, map[string]interface{}{
|
||||
"upload_data": result,
|
||||
})
|
||||
}
|
||||
|
||||
type UploadInfo struct {
|
||||
FileSize int64 `json:"file_size"` // 上传文件大小
|
||||
FileKeys string `json:"file_keys"` // 上传文件唯一标识
|
||||
FileData *string `json:"file_data"` // 上传文件Base64
|
||||
Metadata string `json:"meta_data"` // 上传文件额外信息
|
||||
|
||||
}
|
||||
|
||||
type UploadData struct {
|
||||
ApiType int64 `json:"api_type"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
FileType string `json:"file_type"`
|
||||
FileKey string `json:"file_key"`
|
||||
Metadata string `json:"metadata"`
|
||||
Bucket *string `json:"bucket"`
|
||||
HashKey string `json:"hash_key"`
|
||||
FileData []byte `fsfile:"data"`
|
||||
}
|
||||
|
||||
type UploadUrl 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"`
|
||||
}
|
149
server/upload/internal/logic/uploadfilesfrontendlogic.go
Normal file
149
server/upload/internal/logic/uploadfilesfrontendlogic.go
Normal file
|
@ -0,0 +1,149 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
"fusenapi/utils/hash"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/upload/internal/svc"
|
||||
"fusenapi/server/upload/internal/types"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
)
|
||||
|
||||
type UploadFilesFrontendLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUploadFilesFrontendLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadFilesFrontendLogic {
|
||||
return &UploadFilesFrontendLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *UploadFilesFrontendLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
|
||||
// func (l *UploadFilesFrontendLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
func (l *UploadFilesFrontendLogic) UploadFilesFrontend(req *types.UploadFilesReq, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
|
||||
var uploadInfoList []UploadInfo
|
||||
err := json.Unmarshal([]byte(req.UploadInfo), &uploadInfoList)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeFileUploadErr, "file upload err,params Unmarshal failed")
|
||||
}
|
||||
|
||||
var fileLen = len(uploadInfoList)
|
||||
|
||||
if fileLen == 0 {
|
||||
return resp.SetStatus(basic.CodeFileUploadErr, "file upload err,no files")
|
||||
}
|
||||
if req.ApiType == 1 && fileLen > 100 {
|
||||
return resp.SetStatus(basic.CodeFileUploadErr, "file upload err, files count is beyond the maximum")
|
||||
}
|
||||
|
||||
// 定义存储桶名称
|
||||
var bucketName *string
|
||||
|
||||
// 根据类别选择存储桶
|
||||
switch req.UploadBucket {
|
||||
case 2:
|
||||
bucketName = basic.TempfileBucketName
|
||||
default:
|
||||
bucketName = basic.StorageBucketName
|
||||
}
|
||||
|
||||
// 设置AWS会话的区域
|
||||
l.svcCtx.AwsSession.Config.Region = aws.String("us-west-1")
|
||||
|
||||
// 创建新的S3服务实例
|
||||
svc := s3.New(l.svcCtx.AwsSession)
|
||||
|
||||
result, err := mr.MapReduce(func(source chan<- interface{}) {
|
||||
for _, info := range uploadInfoList {
|
||||
if info.FileSize <= 1024*1024*500 {
|
||||
// 一系列业务逻辑....验证类型,文件大小
|
||||
var hashKey string = hash.JsonHashKey(info.FileKeys)
|
||||
source <- UploadData{
|
||||
FileKey: info.FileKeys,
|
||||
FileSize: info.FileSize,
|
||||
Bucket: bucketName,
|
||||
HashKey: hashKey,
|
||||
}
|
||||
}
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[interface{}], cancel func(error)) {
|
||||
uploadDataInfo := item.(UploadData)
|
||||
|
||||
var uploadUrl = UploadUrl{}
|
||||
uploadUrl.Key = uploadDataInfo.FileKey
|
||||
uploadUrl.ApiType = uploadDataInfo.ApiType
|
||||
uploadUrl.ResourceType = uploadDataInfo.FileType
|
||||
|
||||
s3req, _ := svc.PutObjectRequest(
|
||||
&s3.PutObjectInput{
|
||||
Bucket: uploadDataInfo.Bucket,
|
||||
Key: &uploadDataInfo.HashKey,
|
||||
ContentLength: aws.Int64(uploadDataInfo.FileSize),
|
||||
},
|
||||
)
|
||||
|
||||
url, err := s3req.Presign(time.Minute * 5)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
uploadUrl.Status = 0
|
||||
} else {
|
||||
// 打印请求URL
|
||||
logx.Info(url)
|
||||
uploadUrl.Status = 1
|
||||
uploadUrl.ResourceUrl = url
|
||||
uploadUrl.ResourceId = uploadDataInfo.HashKey
|
||||
}
|
||||
// Notice 这个必须加!
|
||||
writer.Write(uploadUrl)
|
||||
}, func(pipe <-chan interface{}, writer mr.Writer[interface{}], cancel func(error)) {
|
||||
var uploadUrlList = make(map[string][]*UploadUrl)
|
||||
var uploadUrlListFail []*UploadUrl
|
||||
var uploadUrlListSuccess []*UploadUrl
|
||||
for p := range pipe {
|
||||
var uploadUrl = p.(UploadUrl)
|
||||
if uploadUrl.Status == 1 {
|
||||
uploadUrlListSuccess = append(uploadUrlListSuccess, &uploadUrl)
|
||||
} else {
|
||||
uploadUrlListFail = append(uploadUrlListFail, &uploadUrl)
|
||||
}
|
||||
}
|
||||
|
||||
// Notice 这个必须加!
|
||||
uploadUrlList["success"] = uploadUrlListSuccess
|
||||
uploadUrlList["fail"] = uploadUrlListFail
|
||||
writer.Write(uploadUrlList)
|
||||
})
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
|
||||
// 返回成功的响应和上传URL
|
||||
return resp.SetStatus(basic.CodeOK, map[string]interface{}{
|
||||
"upload_urls": result,
|
||||
})
|
||||
}
|
144
server/upload/internal/logic/uploadlogologic.go
Normal file
144
server/upload/internal/logic/uploadlogologic.go
Normal file
|
@ -0,0 +1,144 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fusenapi/model/gmodel"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/upload/internal/svc"
|
||||
"fusenapi/server/upload/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type UploadLogoLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUploadLogoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadLogoLogic {
|
||||
return &UploadLogoLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *UploadLogoLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
|
||||
// func (l *UploadLogoLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
func (l *UploadLogoLogic) UploadLogo(req *types.UploadLogoReq, 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
|
||||
}
|
||||
|
||||
var logoWidth int64
|
||||
var logoHeight int64
|
||||
// 查看sku是否存在
|
||||
if req.SkuId > 0 {
|
||||
// 查询出产品模板信息
|
||||
productTemplateV2Model := gmodel.NewFsProductTemplateV2Model(l.svcCtx.MysqlConn)
|
||||
productTemplateV2Info, err := productTemplateV2Model.FindOne(l.ctx, req.SkuId)
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeFileUploadLogoErr, "logo upload err,no product template")
|
||||
}
|
||||
|
||||
logoWidth = *productTemplateV2Info.LogoWidth
|
||||
logoHeight = *productTemplateV2Info.LogoHeight
|
||||
}
|
||||
// 设置默认宽高
|
||||
if logoWidth == 0 || logoHeight == 0 {
|
||||
logoWidth = 300
|
||||
logoHeight = 200
|
||||
}
|
||||
var resultStr string
|
||||
var err error
|
||||
|
||||
// apiUrl := l.svcCtx.Config.BLMService.ImageProcess.Url
|
||||
// var onlyScale = true
|
||||
// data := url.Values{}
|
||||
// data.Set("imgurl", req.ResourceUrl)
|
||||
// data.Set("layerwidth", strconv.Itoa(int(logoWidth)))
|
||||
// data.Set("layerheight", strconv.Itoa(int(logoHeight)))
|
||||
// data.Set("is_remove_bg", strconv.Itoa(int(req.IsRemoveBg)))
|
||||
// data.Set("proportion", strconv.Itoa(int(req.Proportion)))
|
||||
// data.Set("only_scale", fmt.Sprintf("%v", onlyScale))
|
||||
|
||||
// u, err := url.ParseRequestURI(apiUrl)
|
||||
// if err != nil {
|
||||
// logx.Error(err)
|
||||
// return resp.SetStatus(basic.CodeFileUploadLogoErr, "service fail")
|
||||
// }
|
||||
// u.RawQuery = data.Encode() // URL encode
|
||||
// fmt.Println(u.String())
|
||||
// result, err := http.Get(u.String())
|
||||
// if err != nil {
|
||||
// logx.Error(err)
|
||||
// return resp.SetStatus(basic.CodeFileUploadLogoErr, "service fail")
|
||||
// }
|
||||
// defer result.Body.Close()
|
||||
// b, err := io.ReadAll(result.Body)
|
||||
// if err != nil {
|
||||
// logx.Error(err)
|
||||
// return resp.SetStatus(basic.CodeFileUploadLogoErr, "service fail")
|
||||
// }
|
||||
// resultStr = string(b)
|
||||
|
||||
// // 上传图片
|
||||
// var reqs types.UploadFileBaseReq
|
||||
// reqs.ApiType = 2
|
||||
// reqs.UploadBucket = 2
|
||||
// reqs.Metadata = ""
|
||||
// reqs.FileData = ""
|
||||
// reqs.FileKey = ""
|
||||
|
||||
// // 创建一个业务逻辑层实例
|
||||
// resUpload := NewUploadFileBaseLogic(l.ctx, l.svcCtx).UploadFileBase(&reqs, userinfo)
|
||||
|
||||
var module = "logo"
|
||||
var nowTime = time.Now().Unix()
|
||||
// 新增记录
|
||||
userMaterialModel := gmodel.NewFsUserMaterialModel(l.svcCtx.MysqlConn)
|
||||
_, err = userMaterialModel.CreateOrUpdate(l.ctx, &gmodel.FsUserMaterial{
|
||||
Module: &module,
|
||||
UserId: &userId,
|
||||
GuestId: &guestId,
|
||||
ResourceId: &req.ResourceId,
|
||||
ResourceUrl: &req.ResourceUrl,
|
||||
Metadata: &resultStr,
|
||||
CreateAt: &nowTime,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
return resp.SetStatus(basic.CodeFileUploadLogoErr, "service fail")
|
||||
}
|
||||
|
||||
return resp.SetStatus(basic.CodeOK)
|
||||
}
|
|
@ -5,6 +5,45 @@ import (
|
|||
"fusenapi/utils/basic"
|
||||
)
|
||||
|
||||
type UploadFileBaseReq struct {
|
||||
ApiType int64 `form:"api_type,options=[1,2],default=1"` // 调用类型:1=对外,2=对内
|
||||
FileKey string `form:"file_key"` // 上传唯一标识信息
|
||||
FileData string `form:"file_data"` // 上传文件额外信息
|
||||
Metadata string `form:"meta_data,optional"` // 上传文件额外信息
|
||||
UserId int64 `form:"user_id,optional"` // 上传文件额外信息
|
||||
GuestId int64 `form:"guest_id,optional"` // 上传文件额外信息
|
||||
UploadBucket int64 `form:"upload_bucket,options=[1,2],default=1"` // 上传桶名:1=缓存,2=持久
|
||||
}
|
||||
|
||||
type UploadLogoReq struct {
|
||||
ResourceId string `form:"resource_id"` // 资源ID
|
||||
ResourceUrl string `form:"resource_url"` // 资源URL
|
||||
IsRemoveBg int64 `form:"is_remove_bg"` // 是否要去掉背景
|
||||
Proportion int64 `form:"proportion,default=60"` // 贴图在模型面板上的比例
|
||||
SkuId int64 `form:"sku_id,default=0"` // 模板ID
|
||||
}
|
||||
|
||||
type UploadInfo struct {
|
||||
FileSize int64 `form:"file_size,optional"` // 上传唯一标识信息
|
||||
FileKeys string `form:"file_keys,optional"` // 上传唯一标识信息
|
||||
Metadata string `form:"meta_data,optional"` // 上传文件额外信息
|
||||
}
|
||||
|
||||
type UploadFilesReq struct {
|
||||
ApiType int64 `form:"api_type,options=[1,2],default=1"` // 调用类型:1=对外,2=对内
|
||||
UploadBucket int64 `form:"upload_bucket,options=[1,2],default=1"` // 上传桶名:1=缓存,2=持久
|
||||
UploadInfo string `form:"upload_info"` // 上传信息 json
|
||||
}
|
||||
|
||||
type UploadCallbackReq struct {
|
||||
UploadBucket int64 `form:"upload_bucket,options=[1,2],default=1"` // 上传桶名:1=缓存,2=持久
|
||||
ResourceId string `form:"resource_id"` // 资源ID
|
||||
ResourceType string `form:"resource_type"` // 资源类型
|
||||
ResourceUrl string `form:"resource_url"` // 资源URL
|
||||
Metadata string `form:"metadata,optional"` // 元数据,json格式,存储图像分率
|
||||
ApiType int64 `form:"api_type,options=[1,2],default=1"` // 调用类型:1=对外,2=对内
|
||||
}
|
||||
|
||||
type RequestUpFile struct {
|
||||
UpFile string `form:"upfile"`
|
||||
IsCut string `form:"is_cut"` // 是否裁剪
|
||||
|
|
6
server/webset/Dockerfile
Executable file
6
server/webset/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-webset-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-webset-srv"]
|
6
server/websocket/Dockerfile
Executable file
6
server/websocket/Dockerfile
Executable file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine
|
||||
|
||||
WORKDIR /www/fusenapi/
|
||||
COPY ./bin/api-websocket-srv /www/fusenapi/
|
||||
COPY ./etc /www/fusenapi/etc
|
||||
CMD ["/www/fusenapi/api-websocket-srv"]
|
|
@ -2,6 +2,7 @@ Name: websocket
|
|||
Host: 0.0.0.0
|
||||
Port: 9914
|
||||
ReplicaId: 75
|
||||
Timeout: 15000 #服务超时时间
|
||||
SourceMysql: fusentest:XErSYmLELKMnf3Dh@tcp(110.41.19.98:3306)/fusentest
|
||||
Auth:
|
||||
AccessSecret: fusen2023
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fusenapi/server/websocket/internal/logic"
|
||||
"fusenapi/server/websocket/internal/svc"
|
||||
"fusenapi/server/websocket/internal/types"
|
||||
"fusenapi/utils/basic"
|
||||
"net/http"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func RenderNotifyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.RenderNotifyReq
|
||||
_, err := basic.RequestParse(w, r, svcCtx.SharedState, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 创建一个业务逻辑层实例
|
||||
l := logic.NewRenderNotifyLogic(r.Context(), svcCtx)
|
||||
|
||||
rl := reflect.ValueOf(l)
|
||||
basic.BeforeLogic(w, r, rl)
|
||||
|
||||
resp := l.RenderNotify(&req)
|
||||
|
||||
if !basic.AfterLogic(w, r, rl, resp) {
|
||||
basic.NormalAfterLogic(w, r, resp)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,16 +17,6 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||
Path: "/api/websocket/data_transfer",
|
||||
Handler: DataTransferHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/websocket/render_notify",
|
||||
Handler: RenderNotifyHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/websocket/third_party_login_notify",
|
||||
Handler: ThirdPartyLoginNotifyHandler(serverCtx),
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,12 +3,11 @@ package logic
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"fusenapi/constants"
|
||||
"fusenapi/initalize"
|
||||
"fusenapi/server/websocket/internal/types"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/id_generator"
|
||||
"fusenapi/utils/websocket_data"
|
||||
"github.com/gorilla/websocket"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
@ -66,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 //扩展云渲染属性
|
||||
}
|
||||
|
||||
|
@ -84,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,
|
||||
}
|
||||
|
@ -97,7 +101,7 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp
|
|||
//发送关闭信息
|
||||
_ = conn.WriteMessage(websocket.CloseMessage, nil)
|
||||
return
|
||||
}*/
|
||||
}
|
||||
//生成连接唯一标识
|
||||
uniqueId := websocketIdGenerator.Get()
|
||||
ws := wsConnectItem{
|
||||
|
@ -108,10 +112,15 @@ func (l *DataTransferLogic) DataTransfer(svcCtx *svc.ServiceContext, w http.Resp
|
|||
inChan: make(chan []byte, 1000),
|
||||
outChan: make(chan []byte, 1000),
|
||||
renderProperty: renderProperty{
|
||||
renderImageTask: make(map[string]struct{}),
|
||||
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()
|
||||
|
@ -146,10 +155,13 @@ func (l *DataTransferLogic) checkAuth(svcCtx *svc.ServiceContext, r *http.Reques
|
|||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
} else {
|
||||
return false, nil
|
||||
//不是登录用户也不是游客
|
||||
if !userInfo.IsUser() && !userInfo.IsGuest() {
|
||||
return false, nil
|
||||
}
|
||||
return true, userInfo
|
||||
}
|
||||
return true, userInfo
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 心跳
|
||||
|
@ -247,14 +259,9 @@ func (w *wsConnectItem) sendToOutChan(data []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
// 获取需要渲染图片的map key
|
||||
func (w *wsConnectItem) getRenderImageMapKey(productId, templateTagId, logoId int64, algorithmVersion string) string {
|
||||
return fmt.Sprintf("%d-%d-%d-%s", productId, templateTagId, logoId, algorithmVersion)
|
||||
}
|
||||
|
||||
// 格式化返回数据
|
||||
func (w *wsConnectItem) respondDataFormat(msgType string, data interface{}) []byte {
|
||||
d := types.DataTransferData{
|
||||
d := websocket_data.DataTransferData{
|
||||
T: msgType,
|
||||
D: data,
|
||||
}
|
||||
|
@ -264,7 +271,7 @@ func (w *wsConnectItem) respondDataFormat(msgType string, data interface{}) []by
|
|||
|
||||
// 处理接受到的数据
|
||||
func (w *wsConnectItem) dealwithReciveData(data []byte) {
|
||||
var parseInfo types.DataTransferData
|
||||
var parseInfo websocket_data.DataTransferData
|
||||
if err := json.Unmarshal(data, &parseInfo); err != nil {
|
||||
logx.Error("invalid format of websocket message")
|
||||
w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket message:"+string(data))
|
||||
|
@ -275,7 +282,7 @@ func (w *wsConnectItem) dealwithReciveData(data []byte) {
|
|||
switch parseInfo.T {
|
||||
//图片渲染
|
||||
case constants.WEBSOCKET_RENDER_IMAGE:
|
||||
w.SendToCloudRender(d)
|
||||
w.renderImage(d)
|
||||
default:
|
||||
|
||||
}
|
||||
|
|
53
server/websocket/internal/logic/mq_consumer.go
Normal file
53
server/websocket/internal/logic/mq_consumer.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fusenapi/constants"
|
||||
"fusenapi/utils/websocket_data"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
// 消费渲染结果数据
|
||||
type MqConsumerRenderResult struct {
|
||||
}
|
||||
|
||||
func (m *MqConsumerRenderResult) Run(ctx context.Context, data []byte) error {
|
||||
logx.Info("接收到MqConsumerRenderResult数据:", string(data))
|
||||
var parseInfo websocket_data.RenderImageNotify
|
||||
if err := json.Unmarshal(data, &parseInfo); err != nil {
|
||||
logx.Error("MqConsumerRenderResult data format err:", err)
|
||||
return nil //不返回错误则就删掉该消息
|
||||
}
|
||||
//遍历websocket链接把数据传进去
|
||||
mapConnPool.Range(func(key, value any) bool {
|
||||
//断言连接
|
||||
ws, ok := value.(wsConnectItem)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
//关闭标识
|
||||
if ws.isClose {
|
||||
return true
|
||||
}
|
||||
//查询有无该渲染任务
|
||||
renderId, ok := ws.renderProperty.renderImageTask[parseInfo.TaskId]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
b := ws.respondDataFormat(constants.WEBSOCKET_RENDER_IMAGE, websocket_data.RenderImageRspMsg{
|
||||
RenderId: renderId,
|
||||
Image: parseInfo.Image,
|
||||
})
|
||||
//删除对应的需要渲染的图片map
|
||||
ws.renderProperty.renderImageTaskCtlChan <- renderImageControlChanItem{
|
||||
Option: 0, //0删除 1添加
|
||||
TaskId: parseInfo.TaskId,
|
||||
RenderId: renderId,
|
||||
}
|
||||
//发送数据到out chan
|
||||
ws.sendToOutChan(b)
|
||||
return true
|
||||
})
|
||||
return nil
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fusenapi/constants"
|
||||
"fusenapi/utils/auth"
|
||||
"fusenapi/utils/basic"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
"fusenapi/server/websocket/internal/svc"
|
||||
"fusenapi/server/websocket/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ThirdPartyLoginNotifyLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewThirdPartyLoginNotifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ThirdPartyLoginNotifyLogic {
|
||||
return &ThirdPartyLoginNotifyLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 处理进入前逻辑w,r
|
||||
// func (l *ThirdPartyLoginNotifyLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
|
||||
// }
|
||||
|
||||
// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
|
||||
// func (l *ThirdPartyLoginNotifyLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
// // httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
// }
|
||||
|
||||
func (l *ThirdPartyLoginNotifyLogic) ThirdPartyLoginNotify(req *types.ThirdPartyLoginNotifyReq, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
if time.Now().Unix()-120 > req.Time /*|| req.Time > time.Now().Unix() */ {
|
||||
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param: time is invalid")
|
||||
}
|
||||
if req.Info.WebsocketId <= 0 {
|
||||
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param:websocket_id is required")
|
||||
}
|
||||
if req.Info.Token == "" {
|
||||
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param:token is required")
|
||||
}
|
||||
//验证签名 sha256
|
||||
/*notifyByte, _ := json.Marshal(req.Info)
|
||||
h := sha256.New()
|
||||
h.Write([]byte(fmt.Sprintf(constants.THIRD_PARTY_LOGIN_NOTIFY_SIGN_KEY, string(notifyByte), req.Time)))
|
||||
signHex := h.Sum(nil)
|
||||
sign := hex.EncodeToString(signHex)
|
||||
//fmt.Println(sign)
|
||||
if req.Sign != sign {
|
||||
return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "invalid sign")
|
||||
}*/
|
||||
//查询对应websocket连接
|
||||
val, ok := mapConnPool.Load(req.Info.WebsocketId)
|
||||
if !ok {
|
||||
return resp.SetStatusWithMessage(basic.CodeOK, "success:websocket connection is not exists")
|
||||
}
|
||||
ws, ok := val.(wsConnectItem)
|
||||
if !ok {
|
||||
return resp.SetStatusWithMessage(basic.CodeServiceErr, "type of websocket connect object is err")
|
||||
}
|
||||
b := ws.respondDataFormat(constants.WEBSOCKET_THIRD_PARTY_LOGIN_NOTIFY, types.ThirdPartyLoginRspMsg{
|
||||
Token: req.Info.Token,
|
||||
})
|
||||
select {
|
||||
case <-ws.closeChan:
|
||||
return resp.SetStatusWithMessage(basic.CodeOK, "websocket connect object is closed")
|
||||
case ws.outChan <- b:
|
||||
return resp.SetStatusWithMessage(basic.CodeOK, "success")
|
||||
}
|
||||
}
|
|
@ -3,20 +3,53 @@ package logic
|
|||
import (
|
||||
"encoding/json"
|
||||
"fusenapi/constants"
|
||||
"fusenapi/server/websocket/internal/types"
|
||||
"fusenapi/utils/hash"
|
||||
"fusenapi/utils/websocket_data"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
// 云渲染属性
|
||||
type renderProperty struct {
|
||||
renderImageTask map[string]struct{} //需要渲染的图片任务
|
||||
renderImageTask map[string]string //需要渲染的图片任务 key是taskId val 是renderId
|
||||
renderImageTaskCtlChan chan renderImageControlChanItem //渲染任务新增移除的控制通道
|
||||
}
|
||||
|
||||
// 渲染任务新增移除的控制通道的数据
|
||||
type renderImageControlChanItem struct {
|
||||
Option int // 0删除 1添加
|
||||
Key string //map的key
|
||||
Option int // 0删除 1添加
|
||||
TaskId string //map的key
|
||||
RenderId string // map的val
|
||||
}
|
||||
|
||||
// 渲染发送到组装数据组装数据
|
||||
func (w *wsConnectItem) renderImage(data []byte) {
|
||||
var renderImageData websocket_data.RenderImageReqMsg
|
||||
if err := json.Unmarshal(data, &renderImageData); err != nil {
|
||||
w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket render image message:"+string(data))
|
||||
logx.Error("invalid format of websocket render image message", err)
|
||||
return
|
||||
}
|
||||
logx.Info("收到请求云渲染图片数据:", renderImageData)
|
||||
renderImageData.RenderData.UserId = w.userId
|
||||
//把需要渲染的图片任务加进去
|
||||
taskId := hash.JsonHashKey(renderImageData.RenderData)
|
||||
w.renderProperty.renderImageTaskCtlChan <- renderImageControlChanItem{
|
||||
Option: 1, //0删除 1添加
|
||||
TaskId: taskId,
|
||||
RenderId: renderImageData.RenderId,
|
||||
}
|
||||
tmpData := websocket_data.AssembleRenderData{
|
||||
TaskId: taskId,
|
||||
RenderId: renderImageData.RenderId,
|
||||
RenderData: renderImageData.RenderData,
|
||||
}
|
||||
d, _ := json.Marshal(tmpData)
|
||||
//发送给对应的流水线组装数据
|
||||
if err := w.rabbitMq.SendMsg(constants.RABBIT_MQ_ASSEMBLE_RENDER_DATA, d); err != nil {
|
||||
logx.Error("发送渲染任务数据到MQ失败:", string(data), "err:", err)
|
||||
return
|
||||
}
|
||||
logx.Info("发送渲染数据到rabbitmq成功:", string(data))
|
||||
}
|
||||
|
||||
// 操作连接中渲染任务的增加/删除
|
||||
|
@ -28,43 +61,10 @@ func (w *wsConnectItem) operationRenderTask() {
|
|||
case data := <-w.renderProperty.renderImageTaskCtlChan:
|
||||
switch data.Option {
|
||||
case 0: //删除任务
|
||||
delete(w.renderProperty.renderImageTask, data.Key)
|
||||
delete(w.renderProperty.renderImageTask, data.TaskId)
|
||||
case 1: //新增任务
|
||||
w.renderProperty.renderImageTask[data.Key] = struct{}{}
|
||||
default:
|
||||
|
||||
w.renderProperty.renderImageTask[data.TaskId] = data.RenderId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染请求数据处理发送云渲染服务处理
|
||||
func (w *wsConnectItem) SendToCloudRender(data []byte) {
|
||||
var renderImageData types.RenderImageReqMsg
|
||||
if err := json.Unmarshal(data, &renderImageData); err != nil {
|
||||
w.outChan <- w.respondDataFormat(constants.WEBSOCKET_ERR_DATA_FORMAT, "invalid format of websocket render image message:"+string(data))
|
||||
logx.Error("invalid format of websocket render image message", err)
|
||||
return
|
||||
}
|
||||
logx.Info("收到请求云渲染图片数据:", renderImageData)
|
||||
//把需要渲染的图片任务加进去
|
||||
for _, productId := range renderImageData.ProductIds {
|
||||
select {
|
||||
case <-w.closeChan: //连接关闭了
|
||||
return
|
||||
default:
|
||||
//加入渲染任务
|
||||
key := w.getRenderImageMapKey(productId, renderImageData.TemplateTagId, renderImageData.LogoId, renderImageData.AlgorithmVersion)
|
||||
w.renderProperty.renderImageTaskCtlChan <- renderImageControlChanItem{
|
||||
Option: 1, //0删除 1添加
|
||||
Key: key,
|
||||
}
|
||||
//发送给对应的流水线组装数据
|
||||
if err := w.rabbitMq.SendMsg(constants.RABBIT_MQ_ASSEMBLE_RENDER_DATA, data); err != nil {
|
||||
logx.Error("发送渲染任务数据到MQ失败:", string(data))
|
||||
continue
|
||||
}
|
||||
logx.Info("发送渲染数据到rabbitmq成功:", string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,55 +5,6 @@ import (
|
|||
"fusenapi/utils/basic"
|
||||
)
|
||||
|
||||
type DataTransferData struct {
|
||||
T string `json:"t"` //消息类型
|
||||
D interface{} `json:"d"` //传递的消息
|
||||
}
|
||||
|
||||
type RenderImageReqMsg struct {
|
||||
ProductIds []int64 `json:"product_ids"` //产品 id
|
||||
TemplateTagId int64 `json:"template_tag_id"` //模板标签id
|
||||
LogoId int64 `json:"logo_id"` //logoid
|
||||
AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本
|
||||
}
|
||||
|
||||
type RenderImageRspMsg struct {
|
||||
ProductId int64 `json:"product_id"` //产品 id
|
||||
TemplateTagId int64 `json:"template_tag_id"` //模板标签id
|
||||
AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本
|
||||
LogoId int64 `json:"logo_id"` //logoid
|
||||
Image string `json:"image"` //渲染后的图片
|
||||
}
|
||||
|
||||
type ThirdPartyLoginRspMsg struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type RenderNotifyReq struct {
|
||||
Sign string `json:"sign"`
|
||||
Time int64 `json:"time"`
|
||||
Info NotifyInfo `json:"info"`
|
||||
}
|
||||
|
||||
type NotifyInfo struct {
|
||||
ProductId int64 `json:"product_id"` //产品id
|
||||
TemplateTagId int64 `json:"template_tag_id"` //模板标签id
|
||||
AlgorithmVersion string `json:"algorithm_version,optional"` //算法版本
|
||||
LogoId int64 `json:"logo_id"` //logoid
|
||||
Image string `json:"image"`
|
||||
}
|
||||
|
||||
type ThirdPartyLoginNotifyReq struct {
|
||||
Sign string `json:"sign"`
|
||||
Time int64 `json:"time"`
|
||||
Info ThirdPartyLoginNotify `json:"info"`
|
||||
}
|
||||
|
||||
type ThirdPartyLoginNotify struct {
|
||||
WebsocketId uint64 `json:"websocket_id"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"fusenapi/constants"
|
||||
"fusenapi/server/websocket/internal/logic"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"fusenapi/utils/auth"
|
||||
|
||||
|
@ -23,14 +25,18 @@ func main() {
|
|||
|
||||
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)
|
||||
//消费渲染结果队列
|
||||
ctx1 := context.Background()
|
||||
ctx2, cancel := context.WithCancel(ctx1)
|
||||
ctx2 = context.WithValue(ctx2, "allmodels", ctx.AllModels)
|
||||
defer cancel()
|
||||
go ctx.RabbitMq.Consume(ctx2, constants.RABBIT_MQ_RENDER_RESULT_DATA, &logic.MqConsumerRenderResult{})
|
||||
handler.RegisterHandlers(server, ctx)
|
||||
|
||||
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
|
||||
server.Start()
|
||||
}
|
||||
|
|
|
@ -56,8 +56,29 @@ service home-user-auth {
|
|||
@handler UserOrderCancelHandler
|
||||
get /api/user/order-cancel (UserOrderCancelReq) returns (response);
|
||||
|
||||
// 用户logo列表
|
||||
@handler UserLogoListHandler
|
||||
get /api/user/logo-list (UserLogoListReq) returns (response);
|
||||
|
||||
// 再来一单
|
||||
@handler UserAgainOrderHandler
|
||||
get /api/user/one-more-order (UserAgainOrderReq) returns (response);
|
||||
}
|
||||
|
||||
type (
|
||||
UserAgainOrderReq {
|
||||
Sn string `form:"sn"` // 订单编号
|
||||
}
|
||||
UserAgainOrderRes struct{}
|
||||
)
|
||||
|
||||
type (
|
||||
UserLogoListReq {
|
||||
}
|
||||
UserLogoListRes {
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
UserOrderDeleteReq {
|
||||
ID int64 `form:"id"` //订单id
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user