Prechádzať zdrojové kódy

category: 牧场端æ•分类数据同步

Yi 1 rok pred
rodič
commit
e9a5cff8f5

+ 1 - 1
conf/app-test.ini

@@ -42,7 +42,7 @@ Description = tmrgo
 Type = mysql
 User = root
 Password = 123456
-Host = 192.168.1.70:3306
+Host =  :3306
 Name = tmrwatch2
 TablePrefix =
 

+ 2 - 0
conf/setting/setting.go

@@ -110,6 +110,8 @@ func Setup() {
 	} else {
 		CurrentPath = workDir
 	}
+
+	CurrentPath = "./"
 	appEnv = strings.ToLower(os.Getenv("APP_ENVIRONMENT"))
 	switch appEnv {
 	case "test":

+ 27 - 82
http/handle/api/db.go → http/handle/api/ops.go

@@ -16,9 +16,9 @@ import (
 	"strings"
 	"sync"
 	"time"
-
 	"tmr-watch/conf/setting"
 	"tmr-watch/http/handle/restful"
+	"tmr-watch/module"
 	"tmr-watch/pkg/app"
 	"tmr-watch/pkg/e"
 	"tmr-watch/pkg/logging"
@@ -1019,8 +1019,7 @@ func GetAccount(c *gin.Context) {
 	}
 }
 
-// @Summary   根据APISQL表中SQL执行得到数据集
-// @Tags PostDataByName
+// PostDataByName   根据APISQL表中SQL执行得到数据集
 // @Accept json
 // @Produce  json
 // @Param  sqlname  query   string true "sqlname"
@@ -1035,89 +1034,35 @@ func PostDataByName(c *gin.Context) {
 	appG := app.Gin{C: c}
 	dataByte, _ := ioutil.ReadAll(c.Request.Body)
 	fsion := gofasion.NewFasion(string(dataByte))
-	sqlnamestr := fsion.Get("name").ValueStr()
-
-	s_params := make([]interface{}, 0)
-	tx := restful.Engine.NewSession()
-	err := tx.Begin()
-	defer func() {
-		switch {
-		case err != nil:
-
-			if tx != nil {
-				tx.Rollback()
-			}
-		default:
-			if tx != nil {
-				err = tx.Commit()
-			}
-		}
-		if tx != nil {
-			tx.Close()
-		}
-	}()
-	sql, p := restful.GetSqlByNameDBT(sqlnamestr, tx)
-	if sql != "" {
-		if fsion.HasKey("parammaps") {
-			parammaps := fsion.Get("parammaps")
-			logging.Info("PostDataByName ", c.Keys, c.Request.RemoteAddr, sqlnamestr, parammaps.ValueStr())
-			paramslist := strings.Split(p, ",")
-			if len(paramslist) > 0 && p != "" {
-				for _, value := range paramslist {
-					if strings.ToLower(strings.Trim(value, " ")) == "jwt_username" {
-						if tempv, exists := c.Get("jwt_username"); exists {
-							s_params = append(s_params, tempv.(string))
-						} else {
-							s_params = append(s_params, "")
-						}
-					} else if strings.ToLower(strings.Trim(value, " ")) == "snowid" {
-						ids, err := setting.SnowIds.NextId()
-						if err != nil {
-							ids = time.Now().UnixNano()
-							logging.Info("create SnowIds err", err)
-						}
-						s_params = append(s_params, ids)
-					} else {
-						s_params = append(s_params, parammaps.Get(strings.Trim(value, " ")).ValueStr())
-					}
-				}
-			}
-		} else if fsion.HasKey("params") {
-			params := fsion.Get("params").Array()
-			logging.Info("PostDataByName ", c.Keys, c.Request.RemoteAddr, sqlnamestr, params)
-			//ids,err := setting.SnowIds.NextId()
-			//if err !=nil{
-			//	ids = time.Now().UnixNano()
-			//	logging.Info("create SnowIds err",err)
-			//}
-			//s_params = append(s_params, ids)
-			for _, v_params := range params {
-				s_params = append(s_params, v_params.ValueStr())
-			}
-		}
-		queryData, err := execDataBySqlT(sql, s_params, tx)
-		if err != nil {
-			logging.Error("PostDataByName  err: ", err)
-			//appG.Response(http.StatusOK, e.ERROR, err.Error())
 
-			if (sqlnamestr == "copybarfeedremain" || sqlnamestr == "copyFtdry" || sqlnamestr == "copyPennsieve") &&
-				strings.Contains(err.Error(), "Duplicate entry") {
+	var err error
+	queryData, err := module.PostDataByName(c, fsion)
+	if err != nil {
+		appG.Response(http.StatusOK, e.ERROR, err)
+		return
+	}
 
-				appG.Response(http.StatusOK, e.ERROR, "同一栏舍,同一班次不可重复录入")
-			} else if (sqlnamestr == "copybarmilk" || sqlnamestr == "copyBodyscore" || sqlnamestr == "copyDungsieve" || sqlnamestr == "copyDungscore") &&
-				strings.Contains(err.Error(), "Duplicate entry") {
+	var (
+		keyWord string
+	)
+	switch fsion.Get("name").ValueStr() {
+	case "insertFeedclass":
+		keyWord = "feed"
+		err = module.GroupCategorySync(keyWord, fsion)
+	case "insertCowclass":
+		keyWord = "cow"
+		err = module.GroupCategorySync(keyWord, fsion)
+	default:
+	}
+	if err != nil {
+		logging.Error(keyWord, err)
+	}
 
-				appG.Response(http.StatusOK, e.ERROR, "同一栏舍不可重复录入")
-			} else {
-				msg := geterrmsg(err.Error())
-				appG.Response(http.StatusOK, e.ERROR, msg)
-			}
-		} else {
-			appG.Response(http.StatusOK, e.SUCCESS, queryData)
-		}
-	} else {
-		appG.Response(http.StatusOK, e.SUCCESS, nil)
+	if queryData != nil {
+		appG.Response(http.StatusOK, e.SUCCESS, queryData)
+		return
 	}
+	appG.Response(http.StatusOK, e.SUCCESS, nil)
 }
 
 //人脸识别/格润富德接口

+ 0 - 2
http/handle/restful/sql_utils.go

@@ -936,10 +936,8 @@ func ExecQueryT(sqlstr string, params []interface{}, tx *xorm.Session) (interfac
 	if err := sqlCheckParam(sqlstr); err != nil {
 		return 0, err
 	}
-
 	rows, err := tx.SQL(sqlstr, params...).Execute()
 	if err != nil {
-
 		fmt.Println("exe", err)
 		return nil, err
 	}

+ 4 - 3
http/routers/app_api.go

@@ -20,15 +20,16 @@ func AppAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 			opt(s)
 		}
 
+		s.POST("/pasture/account/distribute", group.DistributeAccount) // 账号下发
+
 		s.Static("/static", setting.CurrentPath+"/dist/static")               // 添加资源路径
 		s.StaticFile("/", setting.CurrentPath+"/dist/index.html")             //前端接口
 		s.StaticFile("/favicon.ico", setting.CurrentPath+"/dist/favicon.ico") //前端接口
 
 		s.Static("/file", setting.CurrentPath+"/uploads/file")
 		s.Static("/uploads", setting.CurrentPath+"/uploads")
-		s.POST("/auth", api.Auth)                                      // 获取登录token
-		s.POST("/pasture/account/distribute", group.DistributeAccount) // 账号下发
-		s.POST("/authlogin", api.AuthLogin)                            // 获取登录token
+		s.POST("/auth", api.Auth)           // 获取登录token
+		s.POST("/authlogin", api.AuthLogin) // 获取登录token
 
 		s.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // API 注释
 

+ 174 - 1
migration/v0001_feedtemplet.sql

@@ -29,4 +29,177 @@ ORDER BY  f.sort ASC;
 
 
 ALTER TABLE cowclass ADD COLUMN group_id int(11) unsigned NOT NULL DEFAULT '0' COMMENT '集团端id';
-ALTER TABLE feedclass ADD COLUMN group_id int(11) unsigned NOT NULL DEFAULT '0' COMMENT '集团端id';
+ALTER TABLE feedclass ADD COLUMN group_id int(11) unsigned NOT NULL DEFAULT '0' COMMENT '集团端id';
+
+
+##  SELECT * FROM apisql WHERE sqlname  ='updatesysopt'
+## 需要更新的语句如下sqlstr
+
+UPDATE `sysopt`
+SET inforvalue = CASE inforname
+WHEN 'isGetLastPlan' THEN ?
+WHEN 'isGetNextPlan' THEN ?
+WHEN 'isEnableSupplyFeed' THEN ?
+WHEN 'isEnableRemainFeed' THEN  ?
+WHEN 'reportDigit' THEN  ?
+WHEN 'isLockCount' THEN ?
+WHEN 'remainOpt' THEN ?
+WHEN 'waterOpt' THEN ?
+WHEN 'overweightWarnRate' THEN  ?
+WHEN 'overweightBanRate' THEN  ?
+WHEN 'repertoryWarn' THEN ?
+WHEN 'isfeedstorage' THEN ?
+WHEN 'times' THEN ?
+WHEN 'isEnableContract' THEN  ?
+WHEN 'remainOptDis' THEN  ?
+WHEN 'remainOptRate' THEN round(?/100,2)
+WHEN 'isSmallMaterial' THEN ?
+WHEN 'decimalPlaces' THEN ?
+WHEN 'isDataSync' THEN ?
+WHEN 'anyCar' THEN ?
+WHEN 'decimalRate' THEN ?
+WHEN 'accuracy' THEN ?
+WHEN 'sprinkleFeedTimeAllow' THEN ?
+WHEN 'domain' THEN ?
+END
+WHERE pastureid= ?
+
+## 需要更新的语句如下params
+isGetLastPlan,isGetNextPlan,isEnableSupplyFeed,isEnableRemainFeed,reportDigit,isLockCount,remainOpt,waterOpt,overweightWarnRate,overweightBanRate,repertoryWarn,isfeedstorage,times,isEnableContract,remainOptDis,remainOptRate,isSmallMaterial,decimalPlaces,isDataSync,anyCar,decimalRate,accuracy,sprinkleFeedTimeAllow,domain,pastureid
+
+
+##  setp 1 需要增加的语句
+insert into sprinkleFeedTimeAllow ('pastureid','inforname','inforvalue' )values (1653271339,"sprinkleFeedTimeAllow","10"),(1653271339,"domain","");
+
+##  setp 2  SELECT * FROM apisql WHERE sqlname  ='getysoptList'
+SELECT TRIM(pastureid) pastureid,
+
+       MAX(CASE
+               WHEN inforname = 'isGetLastPlan' THEN
+                   inforvalue
+           END
+           ) AS isGetLastPlan,
+       MAX(CASE
+               WHEN inforname = 'isGetNextPlan' THEN
+                   inforvalue
+           END
+           ) AS isGetNextPlan,
+       MAX(CASE
+               WHEN inforname = 'isEnableSupplyFeed' THEN
+                   inforvalue
+           END
+           ) AS isEnableSupplyFeed,
+       MAX(CASE
+               WHEN inforname = 'isEnableRemainFeed' THEN
+                   inforvalue
+           END
+           ) AS isEnableRemainFeed,
+       MAX(CASE
+               WHEN inforname = 'reportDigit' THEN
+                   inforvalue
+           END
+           ) AS reportDigit,
+       MAX(CASE
+               WHEN inforname = 'isLockCount' THEN
+                   inforvalue
+           END
+           ) AS isLockCount,
+       MAX(CASE
+               WHEN inforname = 'remainOpt' THEN
+                   inforvalue
+           END
+           ) AS remainOpt,
+       MAX(CASE
+               WHEN inforname = 'waterOpt' THEN
+                   inforvalue
+           END
+           ) AS waterOpt,
+       MAX(CASE
+               WHEN inforname = 'overweightWarnRate' THEN
+                   inforvalue
+           END
+           ) AS overweightWarnRate,
+       MAX(CASE
+               WHEN inforname = 'overweightBanRate' THEN
+                   inforvalue
+           END
+           ) AS overweightBanRate,
+       MAX(CASE
+               WHEN inforname = 'repertoryWarn' THEN
+                   inforvalue
+           END
+           ) AS repertoryWarn,
+       MAX(CASE
+               WHEN inforname = 'isfeedstorage' THEN
+                   inforvalue
+           END
+           ) AS  isfeedstorage,
+       MAX(CASE
+               WHEN inforname = 'times' THEN
+                   inforvalue
+           END
+           ) AS times,
+       MAX(CASE
+               WHEN inforname = 'isEnableContract' THEN
+                   inforvalue
+           END
+           ) AS isEnableContract,
+       MAX(CASE
+               WHEN inforname = 'remainOptDis' THEN
+                   inforvalue
+           END
+           ) AS remainOptDis,
+       MAX(CASE
+               WHEN inforname = 'remainOptRate' THEN
+                       inforvalue*100
+           END
+           ) AS remainOptRate,
+       MAX(CASE
+               WHEN inforname = 'isSmallMaterial' THEN
+                   inforvalue
+           END
+           ) AS isSmallMaterial,
+       MAX(CASE
+               WHEN inforname = 'decimalPlaces' THEN
+                   inforvalue
+           END
+           ) AS decimalPlaces,
+       MAX(CASE
+               WHEN inforname = 'isDataSync' THEN
+                   inforvalue
+           END
+           ) AS isDataSync,
+       MAX(CASE
+               WHEN inforname = 'anyCar' THEN
+                   inforvalue
+           END
+           ) as anyCar,
+       MAX(CASE
+               WHEN inforname = 'decimalRate' THEN
+                   inforvalue
+           END
+           ) as decimalRate,
+       MAX(CASE
+               WHEN inforname = 'accuracy' THEN
+                   inforvalue
+           END
+           ) as accuracy
+        ,
+       MAX(CASE
+               WHEN inforname = 'wgSap' THEN
+                   inforvalue
+           END
+           ) as wgSap,
+       MAX(CASE
+              WHEN inforname = 'sprinkleFeedTimeAllow' THEN
+                   inforvalue
+              END
+            ) as sprinkleFeedTimeAllow,
+       MAX(CASE
+               WHEN inforname = 'domain' THEN
+                   inforvalue
+           END
+           ) as domain
+
+FROM `sysopt`
+WHERE pastureid = ?

+ 16 - 2
models/group_data.go

@@ -127,12 +127,26 @@ type AccountDistributionRequest struct {
 }
 
 type CategoryRequest struct {
+	KeyWord    string `json:"key_word"`
 	PastureId  int32  `json:"pasture_id"`
 	ParentId   int32  `json:"parent_id"`
 	ParentName string `json:"parent_name"`
 	Name       string `json:"name"`
-	Id         int32  `json:"id"`
 	Number     string `json:"number"`
 	IsShow     int32  `json:"is_show"`
-	GroupId    int32  `json:"group_id"`
+}
+
+type GroupCateGoryRequest struct {
+	PastureId      int32  `json:"pasture_id"`
+	ParentName     string `json:"parent_name"`
+	ParentId       int32  `json:"parent_id"`
+	IsShow         int32  `json:"is_show"`
+	CategoryName   string `json:"category_name"`
+	CategoryNumber string `json:"category_number"`
+}
+
+type GroupCommonResponse struct {
+	Code int32       `json:"code"`
+	Msg  string      `json:"msg"`
+	Data interface{} `json:"data"`
 }

+ 19 - 0
models/request_data.go

@@ -0,0 +1,19 @@
+package models
+
+type ClassRequest struct {
+	BigFeedClassId   string `json:"bigfeedclassid"`
+	BigFeedClassName string `json:"bigfeedclassname"`
+	Enable           int32  `json:"enable"`
+	FcCode           string `json:"fccode"`
+	FcName           string `json:"fcname"`
+	PastureId        string `json:"pastureid"`
+}
+
+type ClassCowRequest struct {
+	ParentId   string `json:"parentid"`
+	ParentName string `json:"parentname"`
+	PastureId  string `json:"pastureid"`
+	ClassCode  string `json:"classcode"`
+	ClassName  string `json:"classname"`
+	Enable     int32  `json:"enable"`
+}

+ 12 - 0
models/sysopt.go

@@ -0,0 +1,12 @@
+package models
+
+type SysOpt struct {
+	Id         int64  `xorm:"id"`
+	PastureId  int64  `xorm:"pastureid"`
+	InforName  string `xorm:"inforname"`
+	InforValue string `xorm:"inforvalue"`
+}
+
+func (s *SysOpt) TableName() string {
+	return "sysopt"
+}

+ 15 - 0
module/pasture.go

@@ -0,0 +1,15 @@
+package module
+
+import (
+	"tmr-watch/http/handle/restful"
+	"tmr-watch/models"
+)
+
+// GetPastureById 获取牧场详情
+func GetPastureById(pastureID int64) (*models.Pasture, error) {
+	res := &models.Pasture{}
+	if _, err := restful.Engine.Table(new(models.Pasture)).Get(&res); err != nil {
+		return nil, err
+	}
+	return res, nil
+}

+ 232 - 0
module/post_data.go

@@ -0,0 +1,232 @@
+package module
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strconv"
+	"strings"
+	"time"
+	"tmr-watch/conf/setting"
+	"tmr-watch/http/handle/restful"
+	"tmr-watch/models"
+	"tmr-watch/pkg/logging"
+
+	"github.com/Anderson-Lu/gofasion/gofasion"
+	"github.com/gin-gonic/gin"
+	"github.com/xormplus/xorm"
+)
+
+const ClassDataURl = "group/class/sync"
+
+func PostDataByName(c *gin.Context, fashion *gofasion.Fasion) (interface{}, error) {
+	sqlnamestr := fashion.Get("name").ValueStr()
+	s_params := make([]interface{}, 0)
+	tx := restful.Engine.NewSession()
+	defer tx.Close()
+	err := tx.Begin()
+	defer func() {
+		switch {
+		case err != nil:
+
+			if tx != nil {
+				tx.Rollback()
+			}
+		default:
+			if tx != nil {
+				err = tx.Commit()
+			}
+		}
+		if tx != nil {
+			tx.Close()
+		}
+	}()
+	sql, p := restful.GetSqlByNameDBT(sqlnamestr, tx)
+	if sql != "" {
+		if fashion.HasKey("parammaps") {
+			parammaps := fashion.Get("parammaps")
+			logging.Info("PostDataByName ", sqlnamestr, parammaps.ValueStr())
+			paramslist := strings.Split(p, ",")
+			if len(paramslist) > 0 && p != "" {
+				for _, value := range paramslist {
+					if strings.ToLower(strings.Trim(value, " ")) == "jwt_username" {
+						if tempv, exists := c.Get("jwt_username"); exists {
+							s_params = append(s_params, tempv.(string))
+						} else {
+							s_params = append(s_params, "")
+						}
+					} else if strings.ToLower(strings.Trim(value, " ")) == "snowid" {
+						ids, err := setting.SnowIds.NextId()
+						if err != nil {
+							ids = time.Now().UnixNano()
+							logging.Info("create SnowIds err", err)
+						}
+						s_params = append(s_params, ids)
+					} else {
+						s_params = append(s_params, parammaps.Get(strings.Trim(value, " ")).ValueStr())
+					}
+				}
+			}
+		} else if fashion.HasKey("params") {
+			params := fashion.Get("params").Array()
+			logging.Info("PostDataByName ", sqlnamestr, params)
+			for _, v_params := range params {
+				s_params = append(s_params, v_params.ValueStr())
+			}
+		}
+		queryData, err := ExecDataBySqlT(sql, s_params, tx)
+		if err != nil {
+			logging.Error("PostDataByName  err: ", err)
+			//appG.Response(http.StatusOK, e.ERROR, err.Error())
+
+			if (sqlnamestr == "copybarfeedremain" || sqlnamestr == "copyFtdry" || sqlnamestr == "copyPennsieve") &&
+				strings.Contains(err.Error(), "Duplicate entry") {
+
+				return nil, errors.New("同一栏舍,同一班次不可重复录入")
+			} else if (sqlnamestr == "copybarmilk" || sqlnamestr == "copyBodyscore" || sqlnamestr == "copyDungsieve" || sqlnamestr == "copyDungscore") &&
+				strings.Contains(err.Error(), "Duplicate entry") {
+				return nil, errors.New("同一栏舍不可重复录入")
+			} else {
+				return nil, err
+			}
+		} else {
+			return queryData, nil
+		}
+	}
+	return nil, nil
+}
+
+func ParamsMarshal(keyWord string, fashion *gofasion.Fasion) (*models.ClassRequest, error) {
+	params := fashion.Get("parammaps")
+	classRequest := &models.ClassRequest{}
+	if keyWord != "cow" {
+		if err := json.Unmarshal([]byte(params.Json()), classRequest); err != nil {
+			logging.Error("GroupCategorySync", "Unmarshal", err)
+			return nil, err
+		}
+	} else {
+		classCowRequest := &models.ClassCowRequest{}
+		if err := json.Unmarshal([]byte(params.Json()), classCowRequest); err != nil {
+			logging.Error("GroupCategorySync", "Unmarshal", err)
+			return nil, err
+		}
+		classRequest.Enable = classCowRequest.Enable
+		classRequest.PastureId = classCowRequest.PastureId
+		classRequest.BigFeedClassId = classCowRequest.PastureId
+		classRequest.BigFeedClassName = classCowRequest.ParentName
+		classRequest.FcCode = classCowRequest.ClassCode
+		classRequest.FcName = classCowRequest.ClassName
+	}
+
+	return classRequest, nil
+}
+
+func GroupCategorySync(keyWord string, fasion *gofasion.Fasion) error {
+	classRequest, err := ParamsMarshal(keyWord, fasion)
+	if err != nil {
+		return err
+	}
+	parentId, _ := strconv.Atoi(classRequest.BigFeedClassId)
+	pastureId, _ := strconv.Atoi(classRequest.PastureId)
+	body := &models.CategoryRequest{
+		KeyWord:    keyWord,
+		PastureId:  int32(pastureId),
+		ParentId:   int32(parentId),
+		ParentName: classRequest.BigFeedClassName,
+		Name:       classRequest.FcName,
+		Number:     classRequest.FcCode,
+		IsShow:     classRequest.Enable,
+	}
+	URL := fmt.Sprintf("%s/%s", GetGroupDomain(int64(pastureId)), ClassDataURl)
+	result, err := DoPost(URL, body)
+	if err != nil {
+		return err
+	}
+
+	response := &models.GroupCommonResponse{}
+	if err = json.Unmarshal(result, response); err != nil {
+		return err
+	}
+	if response.Code != http.StatusOK {
+		return fmt.Errorf("msg: %s", response.Msg)
+	}
+
+	return nil
+}
+
+func GetGroupDomain(pastureId int64) string {
+	res := &models.SysOpt{}
+	if _, err := restful.Engine.Table(new(models.SysOpt).TableName()).
+		Where("pastureid = ?", pastureId).And("inforname = ?", "domain").Get(res); err != nil {
+		logging.Error("GetGroupDomain", err)
+		return ""
+	}
+	return res.InforValue
+}
+
+func ExecDataBySqlT(sqlstr string, params []interface{}, tx *xorm.Session) (interface{}, error) {
+	queryData, err := restful.ExecQueryT(sqlstr, params, tx)
+	return queryData, err
+}
+
+type GroupClient struct {
+	Detail     *models.Pasture
+	authClient *http.Client
+}
+
+func NewGroupClient(detail *models.Pasture) *GroupClient {
+	return &GroupClient{
+		Detail: detail,
+		authClient: &http.Client{
+			Timeout: time.Duration(5) * time.Second,
+		},
+	}
+}
+
+func doRequest(req *http.Request) ([]byte, error) {
+	groupClient := NewGroupClient(nil)
+	resp, err := groupClient.authClient.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	b, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	if resp.StatusCode != http.StatusOK {
+		if len(b) > 0 {
+			return nil, fmt.Errorf("err:%v,body:%s", err, string(b))
+		} else {
+			return nil, fmt.Errorf("err:%v", err)
+		}
+	}
+	return b, nil
+}
+
+func DoGet(url string) ([]byte, error) {
+	req, err := http.NewRequest(http.MethodGet, url, nil)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Add("Accept", "application/json")
+	req.Header.Add("Content-Type", "application/json")
+	return doRequest(req)
+}
+
+func DoPost(url string, body interface{}) ([]byte, error) {
+	b, err := json.Marshal(body)
+	if err != nil {
+		return nil, err
+	}
+
+	req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(b))
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Add("Accept", "application/json")
+	req.Header.Add("Content-Type", "application/json")
+	return doRequest(req)
+}