diff --git a/model/gmodel/fs_cloud_pick_up_logic.go b/model/gmodel/fs_cloud_pick_up_logic.go index 51202633..c6fd862b 100644 --- a/model/gmodel/fs_cloud_pick_up_logic.go +++ b/model/gmodel/fs_cloud_pick_up_logic.go @@ -2,6 +2,7 @@ package gmodel import ( "context" + "gorm.io/gorm" ) @@ -29,3 +30,8 @@ func (p *FsCloudPickUpModel) SavePickUpWithTransaction(ctx context.Context, pick return nil }) } + +func (p *FsCloudPickUpModel) GetCloudPickUpByIDAndUserID(ctx context.Context, userId int64, RelationID int64) (cloudOrder *FsCloudPickUp, err error) { + + return cloudOrder, err +} diff --git a/model/gmodel/fs_order_logic.go b/model/gmodel/fs_order_logic.go index 90e799e0..bf2d88de 100755 --- a/model/gmodel/fs_order_logic.go +++ b/model/gmodel/fs_order_logic.go @@ -2,6 +2,8 @@ package gmodel import ( "context" + + "gorm.io/gorm" ) func (o *FsOrderModel) FindOneBySn(ctx context.Context, userId int64, sn string) (resp *FsOrder, err error) { @@ -24,3 +26,12 @@ func (o *FsOrderModel) Update(ctx context.Context, data *FsOrder) error { func (o *FsOrderModel) Create(ctx context.Context, data *FsOrder) error { return o.db.WithContext(ctx).Model(&FsOrder{}).Create(&data).Error } + +func (o *FsOrderModel) FindOneAndCreateServiceContact(ctx context.Context, userId int64, OrderId int64) (order *FsOrder, err error) { + err = o.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + err = tx.Model(order).Where("`user_id` = ? and `id` = ?", userId, OrderId).Take(&order).Error + tx.Table("").Model(FsContactService{}) + return err + }) + return order, err +} diff --git a/model/gmodel/fs_product_price_logic.go b/model/gmodel/fs_product_price_logic.go index 6a299b56..88cf1530 100755 --- a/model/gmodel/fs_product_price_logic.go +++ b/model/gmodel/fs_product_price_logic.go @@ -84,7 +84,21 @@ type ProductPrice struct { } func (c *FsProductPriceModel) GetAllSelectBySizeId(ctx context.Context, sizeIds []int64) (prices []*ProductPrice, err error) { - - err = c.db.WithContext(ctx).Model(&ProductPrice{}).Where("size_id IN (?) AND status = ?", sizeIds, 1).Select("id, min_buy_num, step_num, step_price, product_id, size_id, each_box_num").Find(&prices).Error + var pprices []*FsProductPrice + err = c.db.WithContext(ctx).Model(&FsProductPrice{}).Where("size_id IN (?) AND status = ?", sizeIds, 1).Select("id, min_buy_num, step_num, step_price, product_id, size_id, each_box_num").Find(&pprices).Error + if err != nil { + return nil, err + } + for _, p := range pprices { + prices = append(prices, &ProductPrice{ + Id: p.Id, + MinBuyNum: *p.MinBuyNum, + StepNum: *p.StepNum, + StepPrice: *p.StepPrice, + ProductId: *p.ProductId, + SizeId: *p.SizeId, + EachBoxNum: *p.EachBoxNum, + }) + } return prices, err } diff --git a/model/gmodel/fs_product_size_logic.go b/model/gmodel/fs_product_size_logic.go index 032680e6..146554fc 100755 --- a/model/gmodel/fs_product_size_logic.go +++ b/model/gmodel/fs_product_size_logic.go @@ -71,7 +71,7 @@ type CapacityId struct { Capacity string `json:"capacity"` } -func (c *FsProductSizeModel) GetAllSelectIdAndCapacityByIds(ctx context.Context, sizeIds []int64) (sizes []CapacityId, err error) { - err = c.db.WithContext(ctx).Where("id IN ?", sizeIds).Select("id, capacity").Find(&sizes).Error +func (c *FsProductSizeModel) GetAllSelectIdAndCapacityByIds(ctx context.Context, sizeIds []int64) (sizes []FsProductSize, err error) { + err = c.db.WithContext(ctx).Where("id IN ?", sizeIds).Select("id", "capacity").Find(&sizes).Error return sizes, err } diff --git a/model/gmodel/fs_quotation_logic.go b/model/gmodel/fs_quotation_logic.go index 27a9fbdc..83955c41 100644 --- a/model/gmodel/fs_quotation_logic.go +++ b/model/gmodel/fs_quotation_logic.go @@ -5,6 +5,6 @@ import "context" // TODO: 使用model的属性做你想做的 func (m *FsQuotationModel) FindOneOnlyById(ctx context.Context, quotationId int64) (resp FsQuotation, err error) { - err = m.db.WithContext(ctx).Model(&resp).Where("`quotation_id` = ?", quotationId).Take(&resp).Error + err = m.db.WithContext(ctx).Model(&resp).Where("`id` = ?", quotationId).Take(&resp).Error return resp, err } diff --git a/model/gmodel/fs_quotation_product_logic.go b/model/gmodel/fs_quotation_product_logic.go index a230f11f..164b6ca8 100644 --- a/model/gmodel/fs_quotation_product_logic.go +++ b/model/gmodel/fs_quotation_product_logic.go @@ -4,7 +4,7 @@ import "context" // TODO: 使用model的属性做你想做的 -func (c *FsQuotationProductModel) GetAllByQuotationIdOrderAsc(ctx context.Context, quotationId int64) (resp []FsQuotationProduct, err error) { +func (c *FsQuotationProductModel) GetAllByQuotationIdOrderAsc(ctx context.Context, quotationId int64) (resp []*FsQuotationProduct, err error) { err = c.db.WithContext(ctx).Model(&FsQuotationProduct{}).Where("`quotation_id` = ? and `status` = ?", quotationId, 1).Order("sort asc").Find(&resp).Error return resp, err } diff --git a/model/gmodel/fs_quotation_saler_logic.go b/model/gmodel/fs_quotation_saler_logic.go index e68225aa..9198055f 100644 --- a/model/gmodel/fs_quotation_saler_logic.go +++ b/model/gmodel/fs_quotation_saler_logic.go @@ -1,2 +1,10 @@ package gmodel -// TODO: 使用model的属性做你想做的 \ No newline at end of file + +import "context" + +// TODO: 使用model的属性做你想做的 + +func (c *FsQuotationSalerModel) GetOne(ctx context.Context, salerId int64) (resp FsQuotationSaler, err error) { + err = c.db.WithContext(ctx).Model(&resp).Where("`id` = ?", salerId).Take(&resp).Error + return resp, err +} diff --git a/server/backend/internal/logic/init.go b/server/backend/internal/logic/init.go new file mode 100644 index 00000000..04b6be7f --- /dev/null +++ b/server/backend/internal/logic/init.go @@ -0,0 +1,17 @@ +package logic + +import ( + "os" + "regexp" +) + +var templatePath string + +func init() { + d, err := os.Getwd() + if err != nil { + panic(err) + } + + templatePath = regexp.MustCompile(".+fusenapi/server").FindString(d) + "/backend/template" +} diff --git a/server/backend/internal/logic/quotationdetaillogic.go b/server/backend/internal/logic/quotationdetaillogic.go index aaa83fd2..5edcecd9 100644 --- a/server/backend/internal/logic/quotationdetaillogic.go +++ b/server/backend/internal/logic/quotationdetaillogic.go @@ -1,11 +1,16 @@ package logic import ( + "bytes" "encoding/json" + "fusenapi/model/gmodel" "fusenapi/utils/auth" "fusenapi/utils/basic" "fusenapi/utils/collect" + "fusenapi/utils/format" + "log" "strings" + "text/template" "context" @@ -44,6 +49,64 @@ func GetPrice(num int64, stepNum []int64, stepPrice []int64) int64 { return 0 } +// GetDemoHtml 报价单 +func (l *QuotationDetailLogic) GetDemoHtml(quot *gmodel.FsQuotation, quotationProduct []*gmodel.FsQuotationProduct) (htmlcontent string, err error) { + + // htmlContent := "" + priceHtml := "" + // productNum := len(quotationProduct) + // page := 3 + // pageTotal := 3 + productNum + + for _, quotProduct := range quotationProduct { + + var priceInfo []map[string]interface{} + err := json.Unmarshal([]byte(*quotProduct.PriceInfo), &priceInfo) + if err != nil { + return "", err + } + priceHtmlTpl := ` + {{range $i, $parr := .priceInfo}} + {{if lt $i 3}} +
+

${{$parr.price}}

+

{{$parr.num}} Units

+
+ {{else}} +
+

${{$parr.price}}

+

{{$parr.num}} Units

+
+ {{end}} + {{end}}` + + tpl := template.Must(template.New("prCardTpl").Parse(priceHtmlTpl)) + buf := &bytes.Buffer{} + err = tpl.Execute(buf, map[string]interface{}{ + "priceInfo": priceInfo, + }) + if err != nil { + log.Println(err) + return "", err + } + priceHtml = buf.String() + } + + tpl := template.New("quto") + tpl = tpl.Funcs(format.TemplateFuncMap) + + tpl = template.Must(tpl.ParseFiles(templatePath + "/products.tpl")) + buf := &bytes.Buffer{} + + tpl.Execute(buf, map[string]interface{}{ + "products": collect.StructSliceJson2Maps(quotationProduct), + "price_html": priceHtml, + }) + htmlContent := buf.String() + // log.Println(htmlContent) + return htmlContent, nil +} + func (l *QuotationDetailLogic) QuotationDetail(req *types.RequestQuotationId, userinfo *auth.BackendUserInfo) (resp *basic.Response) { // 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data) // userinfo 传入值时, 一定不为null @@ -63,15 +126,15 @@ func (l *QuotationDetailLogic) QuotationDetail(req *types.RequestQuotationId, us return resp.SetStatus(basic.CodeDbSqlErr) } - quotation, err := l.svcCtx.AllModels.FsQuotationProduct.GetAllByQuotationIdOrderAsc(l.ctx, req.QuotationId) + quotationProduct, err := l.svcCtx.AllModels.FsQuotationProduct.GetAllByQuotationIdOrderAsc(l.ctx, req.QuotationId) if err != nil { if err != gorm.ErrRecordNotFound { return resp.SetStatus(basic.CodeDbSqlErr) } } - var target any = quotation - if len(quotation) == 0 { + var target any = quotationProduct + if len(quotationProduct) == 0 { target = CanteenProduct } @@ -83,26 +146,38 @@ func (l *QuotationDetailLogic) QuotationDetail(req *types.RequestQuotationId, us list := gjson.ParseBytes(jdata) - qFlag := len(quotation) > 0 + qFlag := len(quotationProduct) > 0 //获取备注模板 - markList, err := l.svcCtx.AllModels.FsQuotationRemarkTemplate.GetAllSelectContent(l.ctx) + // markList, err := l.svcCtx.AllModels.FsQuotationRemarkTemplate.GetAllSelectContent(l.ctx) var productIds = collect.ArrayColumnTag[int64](CanteenProduct, "product_id") productList, err := l.svcCtx.AllModels.FsProduct.FindAllOnlyByIds(l.ctx, productIds) + if err != nil { + logx.Error(err) + return resp.SetStatus(basic.CodeDbSqlErr) + } productListMap := collect.Array2MapByKeyTag[int64](productList, "id") //获取size信息 var sizeIds = collect.ArrayColumnTag[int64](CanteenProduct, "size_id") sizes, err := l.svcCtx.AllModels.FsProductSize.GetAllSelectIdAndCapacityByIds(l.ctx, sizeIds) + if err != nil { + logx.Error(err) + return resp.SetStatus(basic.CodeDbSqlErr) + } sizesMap := collect.Array2MapByKeyTag[int64](sizes, "id") //获取价格信息 productPrice, err := l.svcCtx.AllModels.FsProductPrice.GetAllSelectBySizeId(l.ctx, sizeIds) + if err != nil { + logx.Error(err) + return resp.SetStatus(basic.CodeDbSqlErr) + } productPriceMap := collect.Array2MapByKeyTag[int64](productPrice, "size_id") // product := []map[string]interface{}{} - product := []map[string]interface{}{} + products := []map[string]interface{}{} for _, parr := range list.Array() { var priceList []map[string]int64 @@ -195,7 +270,7 @@ func (l *QuotationDetailLogic) QuotationDetail(req *types.RequestQuotationId, us num = 1 } - product = append(product, map[string]interface{}{ + products = append(products, map[string]interface{}{ "id": Id, "s_id": sid, "is_gift": isGift, @@ -210,5 +285,38 @@ func (l *QuotationDetailLogic) QuotationDetail(req *types.RequestQuotationId, us } + htmlContent, err := l.GetDemoHtml(", quotationProduct) + if err != nil { + panic(err) + } + log.Println(htmlContent) + + saler, err := l.svcCtx.AllModels.FsQuotationSaler.GetOne(l.ctx, quot.Id) + if err != nil { + logx.Error(err) + if err == gorm.ErrRecordNotFound { + return resp.SetStatus(basic.CodeDbRecordNotFoundErr) + } + return resp.SetStatus(basic.CodeDbSqlErr) + } + + // log.Println(saler) + + tpl := template.New("quto") + tpl = tpl.Funcs(format.TemplateFuncMap) + + tpl = template.Must(tpl.ParseFiles(templatePath + "/return_html.tpl")) + buf := &bytes.Buffer{} + + err = tpl.Execute(buf, map[string]interface{}{ + "demoHtml": htmlContent, + "qutoInfo": collect.StructJson2Map(quot), + "salerInfo": collect.StructJson2Map(saler), + }) + if err != nil { + return resp.SetStatus(basic.CodeApiErr) + } + // returnHtmlContent := buf.String() + return resp.SetStatus(basic.CodeOK) } diff --git a/server/backend/internal/svc/servicecontext.go b/server/backend/internal/svc/servicecontext.go index b1041d34..35489171 100644 --- a/server/backend/internal/svc/servicecontext.go +++ b/server/backend/internal/svc/servicecontext.go @@ -5,6 +5,7 @@ import ( "fmt" "fusenapi/server/backend/internal/config" "net/http" + "time" "fusenapi/initalize" "fusenapi/model/gmodel" @@ -21,11 +22,11 @@ type ServiceContext struct { } func NewServiceContext(c config.Config) *ServiceContext { - + db := initalize.InitMysql(c.SourceMysql).Set("gorm:slow_query_time", time.Second*15) return &ServiceContext{ Config: c, MysqlConn: initalize.InitMysql(c.SourceMysql), - AllModels: gmodel.NewAllModels(initalize.InitMysql(c.SourceMysql)), + AllModels: gmodel.NewAllModels(db), } } diff --git a/server/backend/template/products.tpl b/server/backend/template/products.tpl new file mode 100644 index 00000000..8ebb418a --- /dev/null +++ b/server/backend/template/products.tpl @@ -0,0 +1,129 @@ +{{$product_num := len .products}} +{{$page := 3}} +{{$page_total := add $page $product_num}} +{{range $arr := .products}} +{{$price := json_decode $arr.price_info}} +{{$price_html := .priceHtml}} + +{{$price_html}} + +{{if gt $arr.is_gift 0 }}{{/* 赠品 */}} + +
+
+

{{$arr.name}}

+

{{$arr.size}}

+ +
+
+
+
+
+
+

FREE!

+

{{$arr.num}} Units

+
+
+
Lead Time
+
+ {{$arr.cycle}}Days +
+
+ FREE Design +
+
+ FREE Storage +
+
+ FREE Shipping +
+
+
+
+{{else}}{{/* 非赠品 */}} +{{if $arr.size}} +// 有尺寸的 +
+
+

{{$arr.name}}

+

{{$arr.size}}

+ +
+
+
+
+
+
Unit Price (Tax included)
+
+ Lead Time
+ {{$price_html}} +
+
+ {{$arr.cycle}}Days +
+
+ FREE Design +
+
+ FREE Storage +
+
+ FREE Shipping +
+
+
+
+ +{{else}} +
+
+

+ {{$arr.name}}

+ +
+
+
+
+
+
+
+ Unit Price (Tax included)
+
+ Lead Time
+ {{$price_html}} +
+
+ {{$arr.cycle}}Days +
+
+ FREE Design +
+
+ FREE Storage +
+
+ FREE Shipping +
+
+
+
+{{end}} +{{end}} +
{{$page}}/{{$page_total}}
+{{$page = add $page 1}} +{{end}} \ No newline at end of file diff --git a/server/backend/template/return_html.tpl b/server/backend/template/return_html.tpl new file mode 100644 index 00000000..cf2433a4 --- /dev/null +++ b/server/backend/template/return_html.tpl @@ -0,0 +1,371 @@ +{{$html := .demoHtml}} +{{$saler := .salerInfo}} +{{$info := .qutoInfo}} + + + + + + + + quotation + + + + +
+ +
+
+

+ Custom + Packaging For

+

{{$info.canteen_name}}

+ +
+
+
+
+
+
+

+ Hi, I\'m + Victor from Fusenpack.

+

We + offer simple custom packaging solutions for restaurants with a minimum order quantity that\'s 5X + lower than regular suppliers. Check out our custom designs and prices on the following pages. + Contact me at if you have any questions. Thanks!

+
+
+ +
+

+ {{$saler.phone}}

+
+
+
+ +
+

+ {{$saler.email}}

+
+
+
+ + + +
+
+

+ Why Custom Packaging?

+ +
+
+
+ +
+
+
+ + + + + + + + + + + + {{$html}} + +
+
+ +
+
+
+
+

+ There\'s more! If you don\'t see what you need on these pages, please contact us to inquire about + additional items.

+

{{$saler.name}}

+
+
+ +
+

+ {{$saler.phone}}

+
+
+
+ +
+

+ {{$saler.email}}

+
+
+
+ Our Customers:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+ + \ No newline at end of file diff --git a/server/backend/test/basic.go b/server/backend/test/basic.go new file mode 100644 index 00000000..97e0c1fa --- /dev/null +++ b/server/backend/test/basic.go @@ -0,0 +1,35 @@ +package test + +import ( + "fmt" + "fusenapi/server/backend/internal/config" + "fusenapi/server/backend/internal/handler" + "fusenapi/server/backend/internal/svc" + "log" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/rest" +) + +var testConfigFile = "../etc/backend.yaml" +var cnf config.Config +var gserver *rest.Server + +func init() { + log.SetFlags(log.Llongfile) + gserver = GetTestServer() +} + +func GetTestServer() *rest.Server { + + conf.MustLoad(testConfigFile, &cnf) + + server := rest.MustNewServer(cnf.RestConf) + defer server.Stop() + + ctx := svc.NewServiceContext(cnf) + handler.RegisterHandlers(server, ctx) + + fmt.Printf("Starting server at %s:%d...\n", cnf.Host, cnf.Port) + return server +} diff --git a/server/backend/test/quotationdetaillogic_test.go b/server/backend/test/quotationdetaillogic_test.go new file mode 100644 index 00000000..cb8e293d --- /dev/null +++ b/server/backend/test/quotationdetaillogic_test.go @@ -0,0 +1,29 @@ +package test + +import ( + "fmt" + fstests "fusenapi/utils/tests" + "log" + "testing" +) + +func TestCaseQuotationDetail(t *testing.T) { + ses := fstests.GetBackendSessionWithUserToken(t, gserver, cnf.Host, cnf.Port) + + // 构建新增地址请求体 + // addrReq := types.RequestQuotationId{ + // QuotationId: 1003, + // } + + // 向服务器发送 POST 请求,新增用户地址 + tp := ses.Get(fmt.Sprintf("http://%s:%d//quotation/detail", cnf.Host, cnf.Port)) + tp.QueryParam("id").Set(1003) + resp, err := tp.TestExecute(gserver) + log.Println(resp.Json()) + if err != nil { + t.Error(err) + return + } + + log.Println(resp.Json()) +} diff --git a/server/backend/test/tpl_test.go b/server/backend/test/tpl_test.go new file mode 100644 index 00000000..3a8f2400 --- /dev/null +++ b/server/backend/test/tpl_test.go @@ -0,0 +1,314 @@ +package test + +import ( + "bytes" + "fusenapi/model/gmodel" + "fusenapi/utils/collect" + "fusenapi/utils/format" + "html/template" + "log" + "testing" +) + +func TestCaseTpl(t *testing.T) { + + priceInfo := []map[string]interface{}{ + {"Price": "$10", "Num": 100}, + {"Price": "$20", "Num": 200}, + {"Price": "$30", "Num": 300}, + {"Price": "$40", "Num": 400}, + } + + priceHtml := ` + {{range $i, $parr := .}} + {{if lt $i 3}} +
+

${{$parr.Price}}

+

{{$parr.Num}} Units

+
+ {{else}} +
+

${{$parr.Price}}

+

{{$parr.Num}} Units

+
+ {{end}} + {{end}}` + + tpl := template.Must(template.New("demoHtml").Parse(priceHtml)) + buf := bytes.NewBufferString("") + tpl.Execute(buf, priceInfo) + html := buf.String() + log.Println(html) +} + +func Randomproduct() []*gmodel.FsQuotationProduct { + var quotationId1 int64 = 1 + name1 := "T恤" + size1 := "S,M,L,XL,XXL" + var cycle1 int64 = 7 + var isGift1 int64 = 0 + img1 := "https://xxx.jpg" + var status1 int64 = 1 + var ctime1 int64 = 1623311101 + var sort1 int64 = 1 + sid1 := "abcd1234" + priceInfo1 := `[{"num":500,"price":0.1},{"num":1000,"price":0.06},{"num":1500,"price":0.06}]` + remark1 := "" + var num1 int64 = 100 + + var quotationId2 int64 = 1 + name2 := "帽子" + size2 := "通用" + var cycle2 int64 = 3 + var isGift2 int64 = 1 + img2 := "https://xxx.jpg" + var status2 int64 = 1 + var ctime2 int64 = 1623311102 + var sort2 int64 = 2 + sid2 := "abcd1235" + priceInfo2 := `[{"num":500,"price":0.1},{"num":1000,"price":0.06},{"num":1500,"price":0.06}]` + remark2 := "" + var num2 int64 = 50 + + var quotationId3 int64 = 2 + name3 := "水壶" + size3 := "350ml,500ml,650ml" + var cycle3 int64 = 14 + var isGift3 int64 = 0 + img3 := "https://xxx.jpg" + var status3 int64 = 1 + var ctime3 int64 = 1623311103 + var sort3 int64 = 1 + sid3 := "abcd1236" + priceInfo3 := `[{"num":500,"price":0.1},{"num":1000,"price":0.06},{"num":1500,"price":0.06}]` + remark3 := "" + var num3 int64 = 200 + + var quotationId4 int64 = 2 + name4 := "手机壳" + size4 := "iPhoneX,iPhoneXS,iPhoneXR" + var cycle4 int64 = 5 + var isGift4 int64 = 1 + img4 := "https://xxx.jpg" + var status4 int64 = 1 + var ctime4 int64 = 1623311104 + var sort4 int64 = 2 + sid4 := "abcd1237" + priceInfo4 := `[{"num":500,"price":0.1},{"num":1000,"price":0.06},{"num":1500,"price":0.06}]` + remark4 := "可定制颜料颜色" + var num4 int64 = 150 + + products := []*gmodel.FsQuotationProduct{ + &gmodel.FsQuotationProduct{ + Id: 1, + QuotationId: "ationId1, + Name: &name1, + Size: &size1, + Cycle: &cycle1, + IsGift: &isGift1, + Img: &img1, + Status: &status1, + Ctime: &ctime1, + Sort: &sort1, + Sid: &sid1, + PriceInfo: &priceInfo1, + Remark: &remark1, + Num: &num1, + }, + &gmodel.FsQuotationProduct{ + Id: 2, + QuotationId: "ationId2, + Name: &name2, + Size: &size2, + Cycle: &cycle2, + IsGift: &isGift2, + Img: &img2, + Status: &status2, + Ctime: &ctime2, + Sort: &sort2, + Sid: &sid2, + PriceInfo: &priceInfo2, + Remark: &remark2, + Num: &num2, + }, + &gmodel.FsQuotationProduct{ + Id: 3, + QuotationId: "ationId3, + Name: &name3, + Size: &size3, + Cycle: &cycle3, + IsGift: &isGift3, + Img: &img3, + Status: &status3, + Ctime: &ctime3, + Sort: &sort3, + Sid: &sid3, + PriceInfo: &priceInfo3, + Remark: &remark3, + Num: &num3, + }, + &gmodel.FsQuotationProduct{ + Id: 4, + QuotationId: "ationId4, + Name: &name4, + Size: &size4, + Cycle: &cycle4, + IsGift: &isGift4, + Img: &img4, + Status: &status4, + Ctime: &ctime4, + Sort: &sort4, + Sid: &sid4, + PriceInfo: &priceInfo4, + Remark: &remark4, + Num: &num4, + }, + } + + return products +} + +func TestTpl2(t *testing.T) { + + products := Randomproduct() + + tplcontent := ` + + {{$product_num := len .products}} + {{$page := 3}} + {{$page_total := add $page $product_num}} + {{range $arr := .products}} + {{$price := json_decode $arr.price_info}} + {{$price_html := .priceHtml}} + + {{$price_html}} + + {{if gt $arr.is_gift 0 }}{{/* 赠品 */}} + // 赠品 +
+
+

{{$arr.name}}

+

{{$arr.size}}

+ +
+
+
+
+
+
+

FREE!

+

{{$arr.num}} Units

+
+
+
Lead Time
+
+ {{$arr.cycle}}Days +
+
+ FREE Design +
+
+ FREE Storage +
+
+ FREE Shipping +
+
+
+
+ {{else}}{{/* 非赠品 */}} + {{if $arr.size}} + // 有尺寸的 +
+
+

{{$arr.name}}

+

{{$arr.size}}

+ +
+
+
+
+
+
Unit Price (Tax included)
+
+ Lead Time
+ {{$price_html}} +
+
+ {{$arr.cycle}}Days +
+
+ FREE Design +
+
+ FREE Storage +
+
+ FREE Shipping +
+
+
+
+ + {{else}} +
+
+

+ {{$arr.name}}

+ +
+
+
+
+
+
+
+ Unit Price (Tax included)
+
+ Lead Time
+ {{$price_html}} +
+
+ {{$arr.cycle}}Days +
+
+ FREE Design +
+
+ FREE Storage +
+
+ FREE Shipping +
+
+
+
+ {{end}} + {{end}} +
{{$page}}/{{$page_total}}
+ {{$page = add $page 1}} + {{end}} ` + tpl := template.New("demo") + tpl.Funcs(format.TemplateFuncMap) + tpl = template.Must(tpl.Parse(tplcontent)) + buf := bytes.NewBufferString("") + + tpl.Execute(buf, map[string]interface{}{"products": collect.StructSliceJson2Maps(products)}) + html := buf.String() + log.Println(html) +} diff --git a/server/home-user-auth/internal/handler/routes.go b/server/home-user-auth/internal/handler/routes.go index 0e32f516..d5a3ba1e 100644 --- a/server/home-user-auth/internal/handler/routes.go +++ b/server/home-user-auth/internal/handler/routes.go @@ -57,6 +57,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/user/add-address", Handler: UserAddAddressHandler(serverCtx), }, + { + Method: http.MethodPost, + Path: "/user/contact-service", + Handler: UserContactServiceHandler(serverCtx), + }, { Method: http.MethodPost, Path: "/user/order-delete", diff --git a/server/home-user-auth/internal/handler/usercontactservicehandler.go b/server/home-user-auth/internal/handler/usercontactservicehandler.go new file mode 100644 index 00000000..c6dd536c --- /dev/null +++ b/server/home-user-auth/internal/handler/usercontactservicehandler.go @@ -0,0 +1,78 @@ +package handler + +import ( + "errors" + "net/http" + + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/rest/httpx" + + "fusenapi/utils/auth" + "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 UserContactServiceHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + var ( + // 定义错误变量 + err error + // 定义用户信息变量 + userinfo *auth.UserInfo + ) + // 解析JWT token,并对空用户进行判断 + claims, err := svcCtx.ParseJwtToken(r) + // 如果解析JWT token出错,则返回未授权的JSON响应并记录错误消息 + if err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 401, // 返回401状态码,表示未授权 + Message: "unauthorized", // 返回未授权信息 + }) + logx.Info("unauthorized:", err.Error()) // 记录错误日志 + return + } + + if claims != nil { + // 从token中获取对应的用户信息 + userinfo, err = auth.GetUserInfoFormMapClaims(claims) + // 如果获取用户信息出错,则返回未授权的JSON响应并记录错误消息 + if err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 401, + Message: "unauthorized", + }) + logx.Info("unauthorized:", err.Error()) + return + } + } else { + // 如果claims为nil,则认为用户身份为白板用户 + userinfo = &auth.UserInfo{UserId: 0, GuestId: 0} + } + + var req types.RequestContactService + // 如果端点有请求结构体,则使用httpx.Parse方法从HTTP请求体中解析请求数据 + if err := httpx.Parse(r, &req); err != nil { + httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + Code: 510, + Message: "parameter error", + }) + logx.Info(err) + return + } + // 创建一个业务逻辑层实例 + l := logic.NewUserContactServiceLogic(r.Context(), svcCtx) + resp := l.UserContactService(&req, userinfo) + // 如果响应不为nil,则使用httpx.OkJsonCtx方法返回JSON响应; + if resp != nil { + httpx.OkJsonCtx(r.Context(), w, resp) + } else { + err := errors.New("server logic is error, resp must not be nil") + httpx.ErrorCtx(r.Context(), w, err) + logx.Error(err) + } + } +} diff --git a/server/home-user-auth/internal/logic/usercontactservicelogic.go b/server/home-user-auth/internal/logic/usercontactservicelogic.go new file mode 100644 index 00000000..52afdb46 --- /dev/null +++ b/server/home-user-auth/internal/logic/usercontactservicelogic.go @@ -0,0 +1,66 @@ +package logic + +import ( + "fusenapi/model/gmodel" + "fusenapi/utils/auth" + "fusenapi/utils/basic" + "fusenapi/utils/collect" + + "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 UserContactServiceLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewUserContactServiceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserContactServiceLogic { + return &UserContactServiceLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *UserContactServiceLogic) UserContactService(req *types.RequestContactService, userinfo *auth.UserInfo) (resp *basic.Response) { + // 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data) + // userinfo 传入值时, 一定不为null + + if !userinfo.IsUser() { + return resp.SetStatus(basic.CodeUnAuth) + } + + cs := gmodel.FsContactService{} + + collect.LoadJsonTag(cs, req) + + switch req.Type { + case "order": + _, err := l.svcCtx.AllModels.FsOrder.FindOneAndCreateServiceContact(l.ctx, userinfo.UserId, req.RelationID) + if err != nil { + if err == gorm.ErrRecordNotFound { + return resp.SetStatus(basic.CodeOrderNotFoundErr) + } + return resp.SetStatus(basic.CodeDbSqlErr) + } + case "cloud": + _, err := l.svcCtx.AllModels.FsCloudPickUp.GetCloudPickUpByIDAndUserID(l.ctx, userinfo.UserId, req.RelationID) + if err != nil { + if err == gorm.ErrRecordNotFound { + return resp.SetStatus(basic.CodeCloudOrderNotFoundErr) + } + return resp.SetStatus(basic.CodeDbSqlErr) + } + default: + return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "type is unknown") + } + + return resp.SetStatus(basic.CodeOK) +} diff --git a/server/home-user-auth/internal/types/types.go b/server/home-user-auth/internal/types/types.go index 903b376d..26ad0b3a 100644 --- a/server/home-user-auth/internal/types/types.go +++ b/server/home-user-auth/internal/types/types.go @@ -5,6 +5,15 @@ import ( "fusenapi/utils/basic" ) +type RequestContactService struct { + Type string `json:"type"` // 类型 + RelationID int64 `json:"relation_id"` // 关系id + Name string `json:"name"` // 名字 + Email string `json:"email"` // email + Phone string `json:"phone,optional"` // phone + Remark string `json:"remark,optional"` // remark标记 +} + type RequestBasicInfoForm struct { FirstName string `json:"first_name"` // FirstName LastName string `json:"last_name"` // LastName @@ -123,6 +132,13 @@ type Auth struct { RefreshAfter int64 `json:"refreshAfter"` } +type Pagnation struct { + TotalCount int64 `json:"total_count"` + TotalPage int64 `json:"total_page"` + CurPage int64 `json:"cur_page"` + PageSize int64 `json:"page_size"` +} + // Set 设置Response的Code和Message值 func (resp *Response) Set(Code int, Message string) *Response { return &Response{ diff --git a/server/home-user-auth/test/useraddaddresslogic_test.go b/server/home-user-auth/test/useraddaddresslogic_test.go index 813c73b2..2f33bc2c 100644 --- a/server/home-user-auth/test/useraddaddresslogic_test.go +++ b/server/home-user-auth/test/useraddaddresslogic_test.go @@ -16,7 +16,7 @@ func TestCaseUserAddAddress(t *testing.T) { var result gjson.Result // 获取 session,并携带 JWT token - ses := fstests.GetSesssionWithUserToken(t, gserver, cnf.Host, cnf.Port) + ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port) // 构建新增地址请求体 addrReq := types.RequestAddAddress{ diff --git a/server/home-user-auth/test/useraddresslistlogic_test.go b/server/home-user-auth/test/useraddresslistlogic_test.go index f311d130..163b74e3 100644 --- a/server/home-user-auth/test/useraddresslistlogic_test.go +++ b/server/home-user-auth/test/useraddresslistlogic_test.go @@ -15,7 +15,7 @@ func TestCaseAddressList(t *testing.T) { var result gjson.Result // 获取 session,并携带 JWT token - ses := fstests.GetSesssionWithUserToken(t, gserver, cnf.Host, cnf.Port) + ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port) // 向服务器发送 GET 请求,获取用户地址列表 resp, err = ses.Get(fmt.Sprintf("http://%s:%d/user/address-list", cnf.Host, cnf.Port)).TestExecute(gserver) diff --git a/server/home-user-auth/test/userbasicinfologic_test.go b/server/home-user-auth/test/userbasicinfologic_test.go index 64863bad..818e65a4 100644 --- a/server/home-user-auth/test/userbasicinfologic_test.go +++ b/server/home-user-auth/test/userbasicinfologic_test.go @@ -15,7 +15,7 @@ func TestCaseBasicInfoLogic(t *testing.T) { var result gjson.Result // 获取 session,并携带 JWT token - ses := fstests.GetSesssionWithUserToken(t, gserver, cnf.Host, cnf.Port) + ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port) // 向服务器发送 GET 请求,获取用户基本信息 resp, err = ses.Get(fmt.Sprintf("http://%s:%d/user/basic-info", cnf.Host, cnf.Port)).TestExecute(gserver) diff --git a/server/home-user-auth/test/usercontactservicelogic_test.go b/server/home-user-auth/test/usercontactservicelogic_test.go new file mode 100644 index 00000000..33625fc9 --- /dev/null +++ b/server/home-user-auth/test/usercontactservicelogic_test.go @@ -0,0 +1,107 @@ +package logic + +import ( + "fmt" + "fusenapi/server/home-user-auth/internal/types" + fstests "fusenapi/utils/tests" + "testing" + + "github.com/474420502/requests" + "github.com/tidwall/gjson" +) + +func TestUserContactService(t *testing.T) { + var err error + var resp *requests.Response + var result gjson.Result + + // 获取 session,并携带 JWT token + ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port) + tp := ses.Post(fmt.Sprintf("http://%s:%d/user/contact-service", cnf.Host, cnf.Port)) + req := types.RequestContactService{Type: "order", RelationID: 123, Email: "admin@admin.com", Name: "eson"} + tp.SetBodyJson(req) + // 向服务器发送 GET 请求,获取用户基本信息 + resp, err = tp.TestExecute(gserver) + if err != nil { + t.Error(err) + } + + // 检查返回值中的 code 字段是否存在,并且值是否为 200 + result = resp.Json().Get("code") + if !result.Exists() { + t.Error("code is not exists") + } + if result.Int() != 200 { + t.Error("code != 200") + } + + // 检查返回值中的 msg 字段是否存在,并且值是否为 "success" + result = resp.Json().Get("msg") + if !result.Exists() { + t.Error("msg is not exists") + } + if result.String() != "success" { + t.Error(result.String()) + } + + // 检查返回值中的 data 字段是否存在 + result = resp.Json().Get("data") + if !result.Exists() { + t.Error("data is not exists") + } + + // 检查返回值中的 type 字段是否存在,并且值是否为 0 + result = resp.Json().Get("data.type") + if !result.Exists() { + t.Error("type is not exists") + } + + // 检查返回值中的 is_order_status_email 字段是否存在,并且值是否为 false + result = resp.Json().Get("data.is_order_status_email") + if !result.Exists() { + t.Error("is_order_status_email is not exists") + } + + // 检查返回值中的 is_email_advertisement 字段是否存在,并且值是否为 false + result = resp.Json().Get("data.is_email_advertisement") + if !result.Exists() { + t.Error("is_email_advertisement is not exists") + } + + // 检查返回值中的 is_order_status_phone 字段是否存在,并且值是否为 false + result = resp.Json().Get("data.is_order_status_phone") + if !result.Exists() { + t.Error("is_order_status_phone is not exists") + } + + // 检查返回值中的 is_phone_advertisement 字段是否存在,并且值是否为 false + result = resp.Json().Get("data.is_phone_advertisement") + if !result.Exists() { + t.Error("is_phone_advertisement is not exists") + } + + // 检查返回值中的 is_open_render 字段是否存在,并且值是否为 false + result = resp.Json().Get("data.is_open_render") + if !result.Exists() { + t.Error("is_open_render is not exists") + } + + // 检查返回值中的 is_thousand_face 字段是否存在,并且值是否为 false + result = resp.Json().Get("data.is_thousand_face") + if !result.Exists() { + t.Error("is_thousand_face is not exists") + } + + // 检查返回值中的 is_low_rendering 字段是否存在,并且值是否为 false + result = resp.Json().Get("data.is_low_rendering") + if !result.Exists() { + t.Error("is_low_rendering is not exists") + } + + // 检查返回值中的 is_remove_bg 字段是否存在,并且值是否为 true + result = resp.Json().Get("data.is_remove_bg") + if !result.Exists() { + t.Error("is_remove_bg is not exists") + } + +} diff --git a/server/home-user-auth/test/userfontslogic_test.go b/server/home-user-auth/test/userfontslogic_test.go index ff011b05..cb0e1241 100644 --- a/server/home-user-auth/test/userfontslogic_test.go +++ b/server/home-user-auth/test/userfontslogic_test.go @@ -16,7 +16,7 @@ func TestCaseUserFontsLogic(t *testing.T) { var result gjson.Result // 获取 session,并携带 JWT token - ses := fstests.GetSesssionWithUserToken(t, gserver, cnf.Host, cnf.Port) + ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port) // 向服务器发送 GET 请求,获取字体列表 resp, err = ses.Get(fmt.Sprintf("http://%s:%d/user/fonts", cnf.Host, cnf.Port)).TestExecute(gserver) diff --git a/server/home-user-auth/test/usergettypelogic_test.go b/server/home-user-auth/test/usergettypelogic_test.go index 322e5b84..777912e0 100644 --- a/server/home-user-auth/test/usergettypelogic_test.go +++ b/server/home-user-auth/test/usergettypelogic_test.go @@ -15,7 +15,7 @@ func TestCaseGetTypeLogic(t *testing.T) { var result gjson.Result // 获取 session,并携带 JWT token - ses := fstests.GetSesssionWithUserToken(t, gserver, cnf.Host, cnf.Port) + ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port) // 向服务器发送 GET 请求,获取用户类型信息 resp, err = ses.Get(fmt.Sprintf("http://%s:%d/user/get-type", cnf.Host, cnf.Port)).TestExecute(gserver) diff --git a/server/home-user-auth/test/useroderdeletelogic_test.go b/server/home-user-auth/test/useroderdeletelogic_test.go index 79bf80a8..f1ebbfe3 100644 --- a/server/home-user-auth/test/useroderdeletelogic_test.go +++ b/server/home-user-auth/test/useroderdeletelogic_test.go @@ -16,7 +16,7 @@ func TestCaseLogic(t *testing.T) { var result gjson.Result // 获取 session,并携带 JWT token - ses := fstests.GetSesssionWithUserToken(t, gserver, cnf.Host, cnf.Port) + ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port) // 向服务器发送 GET 请求,获取用户类型信息 tp := ses.Post(fmt.Sprintf("http://%s:%d/user/order-delete", cnf.Host, cnf.Port)) diff --git a/server/home-user-auth/test/usersavebasicinfologic_test.go b/server/home-user-auth/test/usersavebasicinfologic_test.go index 42e3160a..b95f3d19 100644 --- a/server/home-user-auth/test/usersavebasicinfologic_test.go +++ b/server/home-user-auth/test/usersavebasicinfologic_test.go @@ -17,7 +17,7 @@ func TestCaseUserSaveBasicinfoLogic(t *testing.T) { var result gjson.Result // 获取 session,并携带 JWT token - ses := fstests.GetSesssionWithUserToken(t, gserver, cnf.Host, cnf.Port) + ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port) var tp *requests.Temporary tp = ses.Get(fmt.Sprintf("http://%s:%d/user/basic-info", cnf.Host, cnf.Port)) diff --git a/server/home-user-auth/test/userstatusconfiglogic_test.go b/server/home-user-auth/test/userstatusconfiglogic_test.go index e6336d7f..b5436a4b 100644 --- a/server/home-user-auth/test/userstatusconfiglogic_test.go +++ b/server/home-user-auth/test/userstatusconfiglogic_test.go @@ -15,7 +15,7 @@ func TestCaseUserStatusConfigLogic(t *testing.T) { var result gjson.Result // 获取 session,并携带 JWT token - ses := fstests.GetSesssionWithUserToken(t, gserver, cnf.Host, cnf.Port) + ses := fstests.GetSessionWithUserToken(t, gserver, cnf.Host, cnf.Port) // 向服务器发送 GET 请求,获取用户类型信息 resp, err = ses.Post(fmt.Sprintf("http://%s:%d/user/status-config", cnf.Host, cnf.Port)).TestExecute(gserver) diff --git a/server_api/home-user-auth.api b/server_api/home-user-auth.api index c8820ad6..aeef3cae 100644 --- a/server_api/home-user-auth.api +++ b/server_api/home-user-auth.api @@ -37,6 +37,9 @@ service home-user-auth { @handler UserAddAddressHandler post /user/add-address(RequestAddAddress) returns (response); + @handler UserContactServiceHandler + post /user/contact-service (RequestContactService) returns (response); + // @handler UserOderListHandler // get /user/order-list(RequestOrderId) returns (response); @@ -44,6 +47,15 @@ service home-user-auth { post /user/order-delete(RequestOrderId) returns (response); } +type RequestContactService { + Type string `json:"type"` // 类型 + RelationID int64 `json:"relation_id"` // 关系id + Name string `json:"name"` // 名字 + Email string `json:"email"` // email + Phone string `json:"phone,optional"` // phone + Remark string `json:"remark,optional"` // remark标记 +} + type RequestBasicInfoForm { FirstName string `json:"first_name"` // FirstName LastName string `json:"last_name"` // LastName diff --git a/utils/auth/user.go b/utils/auth/user.go index 7eba88ae..9bdb6cb2 100644 --- a/utils/auth/user.go +++ b/utils/auth/user.go @@ -1,6 +1,8 @@ package auth import ( + "crypto/sha256" + "encoding/base64" "errors" "fmt" @@ -181,6 +183,10 @@ func getJwtClaims(AuthKey string, AccessSecret *string) (jwt.MapClaims, error) { return nil, errors.New(fmt.Sprint("Invalid token", err)) } +func PasswordHash(pwd string) string { + return base64.URLEncoding.EncodeToString(sha256.New().Sum([]byte(pwd))) +} + func CheckValueRange[T comparable](v T, rangevalues ...T) bool { for _, rv := range rangevalues { if v == rv { diff --git a/utils/auth/user_test.go b/utils/auth/user_test.go index 19f0df96..c4ae2042 100644 --- a/utils/auth/user_test.go +++ b/utils/auth/user_test.go @@ -1,6 +1,9 @@ package auth import ( + "crypto/sha256" + "encoding/base64" + "log" "testing" "time" ) @@ -29,6 +32,33 @@ func TestGenJwt(t *testing.T) { // log.Println(claims) } +func TestGenBackendJwt(t *testing.T) { + now := time.Now().Unix() + secret := "fusen_backend_2023" + a, err := GenerateBackendJwtToken(&secret, 3600*24*7, now, 1, 1) + if err != nil { + t.Error(err) + } + log.Println(a) + + claims, err := getJwtClaims(a, &secret) + if err != nil { + t.Error(err) + } + userinfo, err := GetBackendUserInfoFormMapClaims(claims) + if err != nil { + t.Error(err) + } + if userinfo.UserId != 1 || userinfo.DepartmentId != 1 { + t.Error(userinfo) + } + // log.Println(claims) +} + +func TestCase1(t *testing.T) { + log.Println(base64.URLEncoding.EncodeToString(sha256.New().Sum([]byte("fusen_backend_2023")))) +} + func TestCheckValueRange(t *testing.T) { v := 1 rv1 := []int{1, 3, 4} diff --git a/utils/basic/basic.go b/utils/basic/basic.go index 23f8c8c0..58a78932 100644 --- a/utils/basic/basic.go +++ b/utils/basic/basic.go @@ -17,10 +17,12 @@ var ( CodeUserIdNotFoundErr = &StatusResponse{5051, "user not found"} // 未找到用户 CodePasswordErr = &StatusResponse{5052, "invalid password"} // 无效密码 - CodeSafeValueRangeErr = &StatusResponse{5040, "value not in range"} // 值不在范围内 + CodeSafeValueRangeErr = &StatusResponse{5040, "value not in range"} // 值不在范围内 + CodeTemplateErr = &StatusResponse{5040, "template parsed error"} // 模板解析错误 - CodeOrderNotFoundErr = &StatusResponse{5030, "order not found"} //未找到订单 - CodeOrderNotCancelledErr = &StatusResponse{5031, "current order cannot be cancelled"} // 当前订单无法取消 + CodeOrderNotFoundErr = &StatusResponse{5030, "order not found"} //未找到订单 + CodeCloudOrderNotFoundErr = &StatusResponse{5031, "cloud order not found"} //未找到云仓订单 + CodeOrderNotCancelledErr = &StatusResponse{5032, "current order cannot be cancelled"} // 当前订单无法取消 CodePayNotFoundErr = &StatusResponse{5020, "pay info not found"} // 支付信息无法查询 CodePayCancelOk = &StatusResponse{5021, "cancellation successful"} // 支付取消成功 diff --git a/utils/collect/collect.go b/utils/collect/collect.go index 1f65c842..796993eb 100644 --- a/utils/collect/collect.go +++ b/utils/collect/collect.go @@ -1,17 +1,30 @@ package collect import ( + "log" "reflect" "strconv" ) func ArrayColumn[R any, T any](arr []T, column string) []R { + defer func() { + if err := recover(); err != nil { + panic(err) + } + }() + var result []R s := reflect.ValueOf(arr) for i := 0; i < s.Len(); i++ { e := s.Index(i) + if e.Kind() == reflect.Ptr { + e = e.Elem() + } k := e.FieldByName(column) + if k.Kind() == reflect.Ptr { + k = k.Elem() + } result = append(result, k.Interface().(R)) } @@ -19,20 +32,36 @@ func ArrayColumn[R any, T any](arr []T, column string) []R { } func ArrayColumnTag[R any, T any](arrSrc []T, tag string) []R { + defer func() { + if err := recover(); err != nil { + panic(err) + } + }() + var result []R arr := reflect.ValueOf(arrSrc) if arr.Len() == 0 { return result } - eleType := arr.Index(0).Elem().Type() + ele := arr.Index(0) + if ele.Kind() == reflect.Ptr { + ele = ele.Elem() + } + eleType := ele.Type() for j := 0; j < eleType.NumField(); j++ { if value, ok := eleType.Field(j).Tag.Lookup("json"); ok && value == tag { for i := 0; i < arr.Len(); i++ { srcv := arr.Index(i) - fv := srcv.Elem().Field(j) + if srcv.Kind() == reflect.Ptr { + srcv = srcv.Elem() + } + fv := srcv.Field(j) + if fv.Kind() == reflect.Ptr { + fv = fv.Elem() + } result = append(result, fv.Interface().(R)) } @@ -44,6 +73,7 @@ func ArrayColumnTag[R any, T any](arrSrc []T, tag string) []R { } func ArrayIndex[T any](arr []T, index int) (result T, ok bool) { + if index < len(arr) { result = arr[index] ok = true @@ -54,6 +84,12 @@ func ArrayIndex[T any](arr []T, index int) (result T, ok bool) { } func ArrayString2Int(arr interface{}) (result []int64) { + defer func() { + if err := recover(); err != nil { + panic(err) + } + }() + for _, a := range arr.([]string) { v, err := strconv.ParseInt(a, 10, 64) if err != nil { @@ -65,12 +101,24 @@ func ArrayString2Int(arr interface{}) (result []int64) { } func Array2MapByKey[KEY comparable, VALUE any](arrSrc []VALUE, fieldName string) (result map[KEY]VALUE) { + defer func() { + if err := recover(); err != nil { + panic(err) + } + }() + result = make(map[KEY]VALUE) arr := reflect.ValueOf(arrSrc) for i := 0; i < arr.Len(); i++ { srcv := arr.Index(i) - fv := srcv.Elem().FieldByName(fieldName) + if srcv.Kind() == reflect.Ptr { + if srcv.IsNil() { + continue + } + srcv = srcv.Elem() + } + fv := srcv.FieldByName(fieldName) k := fv.Interface().(KEY) result[k] = srcv.Interface().(VALUE) } @@ -78,8 +126,15 @@ func Array2MapByKey[KEY comparable, VALUE any](arrSrc []VALUE, fieldName string) return result } +// 一个数组slice转换成 map[tag]slice ,以slice元素的某个tag为map func Array2MapByKeyTag[KEY comparable, VALUE any](arrSrc []VALUE, tag string) (result map[KEY]VALUE) { + defer func() { + if err := recover(); err != nil { + panic(err) + } + }() + arr := reflect.ValueOf(arrSrc) if arr.Len() == 0 { @@ -87,14 +142,30 @@ func Array2MapByKeyTag[KEY comparable, VALUE any](arrSrc []VALUE, tag string) (r } result = make(map[KEY]VALUE) - eleType := arr.Index(0).Elem().Type() + ele := arr.Index(0) + if ele.Kind() == reflect.Ptr { + ele = ele.Elem() + } + eleType := ele.Type() for j := 0; j < eleType.NumField(); j++ { if value, ok := eleType.Field(j).Tag.Lookup("json"); ok && value == tag { for i := 0; i < arr.Len(); i++ { srcv := arr.Index(i) - fv := srcv.Elem().Field(j) + var fv reflect.Value + if srcv.Kind() == reflect.Ptr { + if srcv.IsNil() { + continue + } + fv = srcv.Elem().Field(j) + } else { + fv = srcv.Field(j) + } + + if fv.Kind() == reflect.Ptr { + fv = fv.Elem() + } k := fv.Interface().(KEY) result[k] = srcv.Interface().(VALUE) } @@ -105,3 +176,97 @@ func Array2MapByKeyTag[KEY comparable, VALUE any](arrSrc []VALUE, tag string) (r return result } + +func StructJson2Map(s interface{}) map[string]interface{} { + t := reflect.TypeOf(s) + v := reflect.ValueOf(s) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + var data = make(map[string]interface{}) + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + if tag, ok := field.Tag.Lookup("json"); ok { + val := v.Field(i) + if val.Kind() == reflect.Ptr && !val.IsNil() { + val = val.Elem() + } + data[tag] = val.Interface() + } + } + return data +} + +func StructSliceJson2Maps(s interface{}) []map[string]interface{} { + slice := reflect.ValueOf(s) + var maps []map[string]interface{} + + for i := 0; i < slice.Len(); i++ { + s := slice.Index(i) + + if s.Kind() == reflect.Ptr && !s.IsNil() { + s = s.Elem() + } + structValue := s.Interface() + m := StructJson2Map(structValue) + maps = append(maps, m) + } + return maps +} + +func LoadJsonTag(v interface{}, loaded interface{}) { + vtype := reflect.TypeOf(v) + if vtype.Kind() == reflect.Ptr { + vtype = vtype.Elem() + } + vvalue := reflect.ValueOf(v) + if vvalue.Kind() == reflect.Ptr { + vvalue = vvalue.Elem() + } + + ltype := reflect.TypeOf(loaded) + if ltype.Kind() == reflect.Ptr { + ltype = ltype.Elem() + } + lvalue := reflect.ValueOf(loaded) + if lvalue.Kind() == reflect.Ptr { + lvalue = lvalue.Elem() + } + + for i := 0; i < vtype.NumField(); i++ { + vfield := vtype.Field(i) + + if vtag, ok := vfield.Tag.Lookup("json"); ok { + + for j := 0; j < ltype.NumField(); j++ { + lfield := ltype.Field(j) + if ltag, ok := lfield.Tag.Lookup("json"); ok && vtag == ltag { + vv := vvalue.Field(i) + lv := lvalue.Field(j) + log.Println(vv.Kind(), vv.Type().Elem(), lv.Kind()) + if vv.Kind() == reflect.Ptr { + if lv.Kind() == reflect.Ptr { + vv.Set(lv) + } else { + vv = reflect.New(vv.Type().Elem()) + log.Println(vv.Type().Kind(), vv.Elem().Kind(), lv, reflect.Indirect(vv)) + reflect.Indirect(vv.Addr()).Set(lv.Addr()) + vv = reflect.New(vv.Type().Elem()) + + vv.Set(lv.Addr()) + } + + vv.Set(lv) + } else { + if lv.Kind() != reflect.Ptr { + vv.Set(lv) + } else { + vv.Set(lv.Elem()) + } + } + } + } + } + } +} diff --git a/utils/format/template.go b/utils/format/template.go new file mode 100644 index 00000000..6b914b7b --- /dev/null +++ b/utils/format/template.go @@ -0,0 +1,20 @@ +package format + +import ( + "encoding/json" + "text/template" +) + +var TemplateFuncMap = template.FuncMap{ + "add": func(a, b int) int { + return a + b + }, + "json_decode": func(o interface{}) interface{} { + var r []map[string]interface{} + err := json.Unmarshal([]byte(o.(string)), &r) + if err != nil { + panic(err) + } + return r + }, +} diff --git a/utils/tests/basic.go b/utils/tests/basic.go index b4c2e73b..94180b1c 100644 --- a/utils/tests/basic.go +++ b/utils/tests/basic.go @@ -12,7 +12,7 @@ func GetSesssion() *requests.Session { return ses } -func GetSesssionWithUserToken(t *testing.T, server requests.ITestServer, Host string, Port int) *requests.Session { +func GetSessionWithUserToken(t *testing.T, server requests.ITestServer, Host string, Port int) *requests.Session { ses := requests.NewSession() tp := ses.Post(fmt.Sprintf("http://%s:%d/user/login", Host, Port)) tp.SetBodyJson(map[string]interface{}{ @@ -38,6 +38,32 @@ func GetSesssionWithUserToken(t *testing.T, server requests.ITestServer, Host st return ses } +func GetBackendSessionWithUserToken(t *testing.T, server requests.ITestServer, Host string, Port int) *requests.Session { + ses := requests.NewSession() + tp := ses.Post(fmt.Sprintf("http://%s:%d/backend-user/login", Host, Port)) + tp.SetBodyJson(map[string]interface{}{ + "name": "admin@admin.com", + "pwd": "ZnVzZW5fYmFja2VuZF8yMDIz47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU=", + }) + resp, err := tp.TestExecute(server) + if err != nil { + t.Error(err) + } + result := resp.Json() + code := result.Get("code").Int() + if code != 200 { + t.Error("code is not 200") + } + + token := result.Get("data.token") + if !token.Exists() { + t.Error("data.token is not exists") + } + ses.Header.Add("Authorization", token.String()) + + return ses +} + func GetSesssionWithGuestToken(t *testing.T, server requests.ITestServer, Host string, Port int) *requests.Session { ses := requests.NewSession() tp := ses.Post(fmt.Sprintf("http://%s:%d/accept/cookie", Host, Port))