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 bf008f69..af3b6deb 100755 --- a/model/gmodel/fs_product_size_logic.go +++ b/model/gmodel/fs_product_size_logic.go @@ -51,7 +51,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/handler/quotationdetailhandler.go b/server/backend/internal/handler/quotationdetailhandler.go index 59640c7b..024f8a4c 100644 --- a/server/backend/internal/handler/quotationdetailhandler.go +++ b/server/backend/internal/handler/quotationdetailhandler.go @@ -25,27 +25,30 @@ func QuotationDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { userinfo *auth.BackendUserInfo ) // 解析JWT token,并对空用户进行判断 - claims, err := svcCtx.ParseJwtToken(r) - // 如果解析JWT token出错,则返回未授权的JSON响应并记录错误消息 - if err != nil || claims == nil { - httpx.OkJsonCtx(r.Context(), w, &basic.Response{ - Code: 401, // 返回401状态码,表示未授权 - Message: "unauthorized", // 返回未授权信息 - }) - logx.Info("unauthorized:", err.Error()) // 记录错误日志 - return - } + // claims, err := svcCtx.ParseJwtToken(r) + // // 如果解析JWT token出错,则返回未授权的JSON响应并记录错误消息 + // if err != nil || claims == nil { + // httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + // Code: 401, // 返回401状态码,表示未授权 + // Message: "unauthorized", // 返回未授权信息 + // }) + // logx.Info("unauthorized:", err.Error()) // 记录错误日志 + // return + // } - // 从token中获取对应的用户信息 - userinfo, err = auth.GetBackendUserInfoFormMapClaims(claims) - // 如果获取用户信息出错,则返回未授权的JSON响应并记录错误消息 + // // 从token中获取对应的用户信息 + // userinfo, err = auth.GetBackendUserInfoFormMapClaims(claims) + // // 如果获取用户信息出错,则返回未授权的JSON响应并记录错误消息 + // if err != nil { + // httpx.OkJsonCtx(r.Context(), w, &basic.Response{ + // Code: 401, + // Message: "unauthorized", + // }) + // logx.Info("unauthorized:", err.Error()) + // return + // } if err != nil { - httpx.OkJsonCtx(r.Context(), w, &basic.Response{ - Code: 401, - Message: "unauthorized", - }) - logx.Info("unauthorized:", err.Error()) - return + } var req types.RequestQuotationId diff --git a/server/backend/internal/logic/quotationdetaillogic.go b/server/backend/internal/logic/quotationdetaillogic.go index aaa83fd2..e0deec79 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,200 @@ 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) { + saler, err := l.svcCtx.AllModels.FsQuotationSaler.GetOne(l.ctx, quot.Id) + if err != nil { + + return "", err + } + + log.Println(saler) + + // 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() + } + + 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 = tpl.Funcs(format.TemplateFuncMap) + tpl = template.Must(tpl.Parse(tplcontent)) + buf := bytes.NewBufferString("") + + 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 +262,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 +282,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 +406,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 +421,11 @@ func (l *QuotationDetailLogic) QuotationDetail(req *types.RequestQuotationId, us } + htmlContent, err := l.GetDemoHtml(", quotationProduct) + if err != nil { + panic(err) + } + log.Println(htmlContent) + 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/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..5a828e51 --- /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.GetSesssionWithUserToken(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/template/products.tpl b/template/products.tpl new file mode 100644 index 00000000..8ebb418a --- /dev/null +++ b/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/template/return_html.tpl b/template/return_html.tpl new file mode 100644 index 00000000..44099cab --- /dev/null +++ b/template/return_html.tpl @@ -0,0 +1,368 @@ + + + + + + + + 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/utils/collect/collect.go b/utils/collect/collect.go index 1f65c842..c8da1d2b 100644 --- a/utils/collect/collect.go +++ b/utils/collect/collect.go @@ -6,12 +6,24 @@ import ( ) 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 +31,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 +72,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 +83,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,6 +100,12 @@ 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) @@ -80,6 +121,12 @@ func Array2MapByKey[KEY comparable, VALUE any](arrSrc []VALUE, fieldName string) 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 +134,27 @@ 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 { + 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 +165,40 @@ 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) + + var data = make(map[string]interface{}) + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + if field.Tag == "" { + continue + } + tag := field.Tag.Get("json") + val := v.Field(i) + if val.Kind() == reflect.Ptr { + 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 = s.Elem() + } + structValue := s.Interface() + m := StructJson2Map(structValue) + maps = append(maps, m) + } + return maps +} 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 + }, +}