作者 唐旭辉

Merge branch 'dev' of http://gitlab.fjmaimaimai.com/mmm-go/partnermg into dev

... ... @@ -25,3 +25,4 @@
/*.exe~
/logs
download
\ No newline at end of file
... ...
... ... @@ -7,6 +7,7 @@ require (
github.com/Shopify/sarama v1.23.1
github.com/ajg/form v1.5.1 // indirect
github.com/astaxie/beego v1.12.2
github.com/beego/beego/v2 v2.0.1
github.com/bsm/sarama-cluster v2.1.15+incompatible
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
... ...
... ... @@ -7,14 +7,25 @@ type ListOrderBaseQuery struct {
// 查询限制
Limit int `json:"limit"`
//发货单号
PartnerOrCode string `json:"partnerOrCode"`
CompanyId int64 `json:"companyId"`
//PartnerOrCode string `json:"partnerOrCode"`
//合伙人姓名
PartnerName string `json:"partnerName"`
//订单号
OrderCode string `json:"orderCode"`
//发货单号
DeliveryCode string `json:"deliveryCode"`
//公司id
CompanyId int64 `json:"companyId"`
//订单类型
OrderType int `json:"orderType"`
//合伙人分类
PartnerCategory int `json:"partnerCategory"`
PartnerCategory int `json:"partnerCategory"`
//更新时间开始
UpdateTimeBegin string `json:"updateTimeBegin"`
UpdateTimeEnd string `json:"updateTimeEnd"`
//更新时间截止
UpdateTimeEnd string `json:"updateTimeEnd"`
//创建时间开始
CreateTimeBegin string `json:"createTimeBegin"`
CreateTimeEnd string `json:"createTimeEnd"`
//创建时间截止
CreateTimeEnd string `json:"createTimeEnd"`
}
... ...
... ... @@ -26,7 +26,13 @@ func NewOrderInfoService(option map[string]interface{}) *OrderInfoService {
return newAdminUserService
}
// PageListOrderBase 获取订单列表
/**
* @Author SteveChan
* @Description // 获取订单列表
* @Date 22:05 2021/1/10
* @Param
* @return
**/
func (service OrderInfoService) PageListOrderBase(listOrderQuery query.ListOrderBaseQuery) ([]map[string]interface{}, int, error) {
var err error
transactionContext, err := factory.CreateTransactionContext(nil)
... ... @@ -53,7 +59,9 @@ func (service OrderInfoService) PageListOrderBase(listOrderQuery query.ListOrder
orders, cnt, err = orderDao.OrderListByCondition(
listOrderQuery.CompanyId,
listOrderQuery.OrderType,
listOrderQuery.PartnerOrCode,
listOrderQuery.PartnerName, // 合伙人姓名
listOrderQuery.OrderCode, // 订单号
listOrderQuery.DeliveryCode, // 发货单号
[2]string{listOrderQuery.UpdateTimeBegin, listOrderQuery.UpdateTimeEnd},
[2]string{listOrderQuery.CreateTimeBegin, listOrderQuery.CreateTimeEnd},
listOrderQuery.PartnerCategory,
... ... @@ -865,6 +873,13 @@ func (service OrderInfoService) ListOrderBonusForExcel(listOrderQuery query.List
return resultMaps, column, nil
}
/**
* @Author SteveChan
* @Description // 导出订单数据
* @Date 22:05 2021/1/10
* @Param
* @return
**/
func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrderBaseQuery) ([]map[string]string, [][2]string, error) {
transactionContext, err := factory.CreateTransactionContext(nil)
if err != nil {
... ... @@ -876,6 +891,7 @@ func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrder
defer func() {
transactionContext.RollbackTransaction()
}()
var (
orderBaseDao *dao.OrderBaseDao
)
... ... @@ -887,7 +903,9 @@ func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrder
}
ordersData, err := orderBaseDao.OrderListForExcel(
listOrderQuery.CompanyId,
listOrderQuery.PartnerOrCode,
listOrderQuery.PartnerName, // 合伙人姓名
listOrderQuery.OrderCode, // 订单号
listOrderQuery.DeliveryCode, // 发货单号
[2]string{listOrderQuery.UpdateTimeBegin, listOrderQuery.UpdateTimeEnd},
[2]string{listOrderQuery.CreateTimeBegin, listOrderQuery.CreateTimeEnd},
listOrderQuery.PartnerCategory,
... ... @@ -944,28 +962,29 @@ func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrder
/**
* @Author SteveChan
* @Description // 批量导入创建订单
* @Description //TODO 批量导入创建订单
* @Date 11:00 2021/1/7
* @Param
* @return
**/
func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*command.CreateOrderCommand) ([]interface{}, error) {
func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*command.CreateOrderCommand) ([]*domain.ImportInfo, error) {
// 事务初始化
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
err error
failureDataList []interface{} // 错误数据返回
errorDataList []*domain.ImportInfo // 错误数据返回
)
// 循环校验命令
for _, cmd := range createOrderCommands {
if err = cmd.Valid(); err != nil {
// 返回信息 0: 订单号, 1: 发货单号, 2: 客户名称, 3: 订单区域, 4: 编号, 5: 合伙人, 6: 类型, 7: 业务抽成比例, 8: 产品名称, 9: 数量, 10: 单价, 11: 合伙人分红比例
row := []interface{}{
lib.ThrowError(lib.BUSINESS_ERROR, err.Error()), // 错误信息
cmd.LineNumbers, // 错误影响的行
row := &domain.ImportInfo{
Error: lib.ThrowError(lib.BUSINESS_ERROR, err.Error()), // 错误信息
LineNumbers: cmd.LineNumbers, // 错误影响的行
GoodLine: map[int]interface{}{},
}
failureDataList = append(failureDataList, row)
errorDataList = append(errorDataList, row)
continue
}
}
... ... @@ -987,6 +1006,7 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co
categoryRepository domain.PartnerCategoryRepository
orderBaseDao *dao.OrderBaseDao
)
// 合伙人信息仓储初始化
if PartnerInfoRepository, err = factory.CreatePartnerInfoRepository(map[string]interface{}{
"transactionContext": transactionContext,
... ... @@ -1028,26 +1048,55 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co
var partnerData *domain.PartnerInfo
partnerData, err = PartnerInfoRepository.FindOne(domain.PartnerFindOneQuery{UserId: cmd.PartnerId})
if err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("检索合伙人数据失败"))
row := &domain.ImportInfo{
Error: lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("检索合伙人数据失败")),
LineNumbers: cmd.LineNumbers, // 错误影响的行
GoodLine: map[int]interface{}{},
}
errorDataList = append(errorDataList, row)
continue
}
// 批量校验订单
if ok, err := orderBaseDao.CheckOrderExist(cmd.CompanyId, cmd.OrderCode, cmd.DeliveryCode,
cmd.PartnerCategory, cmd.PartnerId, 0); err != nil {
return nil, lib.ThrowError(lib.TRANSACTION_ERROR, err.Error())
row := &domain.ImportInfo{
Error: lib.ThrowError(lib.TRANSACTION_ERROR, err.Error()),
LineNumbers: cmd.LineNumbers, // 错误影响的行
GoodLine: map[int]interface{}{},
}
errorDataList = append(errorDataList, row)
continue
} else if ok {
return nil, lib.ThrowError(lib.BUSINESS_ERROR, "订单已存在")
row := &domain.ImportInfo{
Error: lib.ThrowError(lib.BUSINESS_ERROR, "订单已存在"),
LineNumbers: cmd.LineNumbers, // 错误影响的行
GoodLine: map[int]interface{}{},
}
errorDataList = append(errorDataList, row)
continue
}
// 批量校验产品
var goodMap = map[string]int{}
goodErrMap := make(map[int]interface{}, 0)
for i := range cmd.Goods {
goodName := cmd.Goods[i].GoodName
if _, ok := goodMap[goodName]; ok {
return nil, lib.ThrowError(lib.BUSINESS_ERROR, "订单中货品重复已存在")
goodErrMap[cmd.Goods[i].LineNumber] = lib.ThrowError(lib.BUSINESS_ERROR, "订单中货品重复已存在")
continue
}
goodMap[goodName] = 1
}
if len(goodErrMap) > 0 {
row := &domain.ImportInfo{
Error: lib.ThrowError(lib.BUSINESS_ERROR, "订单中货品重复已存在"),
LineNumbers: cmd.LineNumbers, // 错误影响的行
GoodLine: goodErrMap, // 错误产品行号记录
}
errorDataList = append(errorDataList, row)
continue
}
newOrder := &domain.OrderBase{
OrderType: cmd.OrderType, OrderCode: cmd.OrderCode,
... ... @@ -1083,12 +1132,19 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co
}
}
if !cmdPartnerCategoryOk {
return nil, lib.ThrowError(lib.BUSINESS_ERROR, "合伙人类型选择错误")
row := &domain.ImportInfo{
Error: lib.ThrowError(lib.BUSINESS_ERROR, "合伙人类型选择错误"),
LineNumbers: cmd.LineNumbers, // 错误影响的行
GoodLine: map[int]interface{}{},
}
errorDataList = append(errorDataList, row)
continue
}
// 订单产品分红核算
var orderGoods []domain.OrderGood
for _, good := range cmd.Goods {
orderGoodErrMap := make(map[int]interface{}, 0)
for i, good := range cmd.Goods {
m := domain.NewOrderGood()
m.OrderId = 0
m.GoodName = good.GoodName
... ... @@ -1097,28 +1153,54 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co
m.PartnerBonusPercent = good.PartnerBonusPercent
m.Remark = good.Remark
m.CompanyId = cmd.CompanyId
err = m.Compute()
if err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中商品的数值失败:%s", err))
orderGoodErrMap[cmd.Goods[i].LineNumber] = lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中商品的数值失败:%s", err))
continue
}
err = m.CurrentBonusStatus.WartPayPartnerBonus(&m)
if err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中商品的分红数值失败:%s", err))
orderGoodErrMap[cmd.Goods[i].LineNumber] = lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中商品的分红数值失败:%s", err))
continue
}
orderGoods = append(orderGoods, m)
}
if len(orderGoodErrMap) > 0 {
row := &domain.ImportInfo{
Error: lib.ThrowError(lib.BUSINESS_ERROR, "核算订单中商品错误"),
LineNumbers: cmd.LineNumbers, // 错误影响的行
GoodLine: orderGoodErrMap, // 错误产品行号记录
}
errorDataList = append(errorDataList, row)
continue
}
newOrder.Goods = orderGoods
err = newOrder.Compute()
if err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中合计的数值失败:%s", err))
row := &domain.ImportInfo{
Error: lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中合计的数值失败:%s", err)),
LineNumbers: cmd.LineNumbers, // 错误影响的行
GoodLine: map[int]interface{}{},
}
errorDataList = append(errorDataList, row)
continue
}
// 保存订单数据
err = orderBaseRepository.Save(newOrder)
if err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("保存订单数据失败:%s", err))
row := &domain.ImportInfo{
Error: lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("保存订单数据失败:%s", err)),
LineNumbers: cmd.LineNumbers, // 错误影响的行
GoodLine: map[int]interface{}{},
}
errorDataList = append(errorDataList, row)
continue
}
for i := range newOrder.Goods {
... ... @@ -1128,22 +1210,27 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co
// 保存订单产品
err = orderGoodRepository.Save(orderGoods)
if err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("保存订单中的商品数据失败:%s", err))
row := &domain.ImportInfo{
Error: lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("保存订单中的商品数据失败:%s", err)),
LineNumbers: cmd.LineNumbers, // 错误影响的行
GoodLine: map[int]interface{}{},
}
errorDataList = append(errorDataList, row)
continue
}
newOrder.Goods = orderGoods
}
if len(failureDataList) == 0 {
if len(errorDataList) == 0 {
// 完成事务
err = transactionContext.CommitTransaction()
if err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
return failureDataList, nil
return errorDataList, nil
}
return failureDataList, nil
return errorDataList, nil
}
/**
... ...
... ... @@ -6,6 +6,7 @@ const SERVICE_NAME = "partnermg"
var LOG_LEVEL = "debug"
var LOG_File = "./logs/partnermg.log"
var IMPORT_EXCEL = "./download/订单数据模板.xlsx"
var Log_PREFIX = "[partnermg_dev]"
var (
UCENTER_HOST = "https://suplus-ucenter-test.fjmaimaimai.com" //统一用户中心地址
... ...
... ... @@ -14,7 +14,7 @@ var KafkaCfg KafkaConfig
func init() {
KafkaCfg = KafkaConfig{
Servers: []string{"106.52.15.41:9092"},
Servers: []string{"127.0.0.1:9092"},
ConsumerId: "partnermg_dev",
}
if os.Getenv("KAFKA_HOST") != "" {
... ...
... ... @@ -314,6 +314,17 @@ type OrderBaseFindQuery struct {
CompanyId int64
}
// 导入错误信息
type ImportInfo struct {
Error error
LineNumbers []int
GoodLine map[int]interface{}
}
// 导入产品错误信息
type GoodErrInfo struct {
}
type OrderBaseRepository interface {
Save(order *OrderBase) error
FindOne(qureyOptions OrderBaseFindOneQuery) (*OrderBase, error)
... ...
... ... @@ -187,7 +187,7 @@ func (dao OrderBaseDao) OrderBonusListForExcel(companyId int64, orderType int, p
//@param partnerCategory 合伙人类型id
//@param updateTime 订单更新时间范围"[开始时间,结束时间]",时间格式"2006-01-02 15:04:05+07"
//@param createTime 订单的创建时间范围"[开始时间,结束时间]" 时间格式"2006-01-02 15:04:05+07"
func (dao OrderBaseDao) OrderListByCondition(companyId int64, orderType int, partnerOrCode string,
func (dao OrderBaseDao) OrderListByCondition(companyId int64, orderType int, partnerName string, orderCode string, deliveryCode string,
updateTime [2]string, createTime [2]string, partnerCategory int, limit, offset int) ([]models.OrderBase, int, error) {
tx := dao.transactionContext.GetDB()
var orders []models.OrderBase
... ... @@ -212,16 +212,25 @@ func (dao OrderBaseDao) OrderListByCondition(companyId int64, orderType int, par
if len(createTime[1]) > 0 {
query = query.Where(`order_base.create_time<=?`, createTime[1])
}
if len(partnerOrCode) > 0 {
if len(partnerName) > 0 {
query = query.Join("LEFT JOIN partner_info as p ON order_base.partner_id=p.id").
WhereGroup(func(q *orm.Query) (*orm.Query, error) {
q = q.WhereOr("order_base.order_code like ? ", "%"+partnerOrCode+"%").
WhereOr("order_base.delivery_code like ? ", "%"+partnerOrCode+"%").
WhereOr("p.partner_name like ? ", "%"+partnerOrCode+"%")
return q, nil
})
Where("p.partner_name like ? ", "%"+partnerName+"%")
}
if len(orderCode) > 0 {
query = query.Where("order_base.order_code like ? ", "%"+orderCode+"%")
}
if len(deliveryCode) > 0 {
query = query.Where("order_base.delivery_code like ? ", "%"+deliveryCode+"%")
}
//if len(partnerOrCode) > 0 {
// query = query.Join("LEFT JOIN partner_info as p ON order_base.partner_id=p.id").
// WhereGroup(func(q *orm.Query) (*orm.Query, error) {
// q = q.WhereOr("order_base.order_code like ? ", "%"+partnerOrCode+"%").
// WhereOr("order_base.delivery_code like ? ", "%"+partnerOrCode+"%").
// WhereOr("p.partner_name like ? ", "%"+partnerOrCode+"%")
// return q, nil
// })
//}
query = query.Order("order_base.create_time DESC").
Offset(offset).
Limit(limit)
... ... @@ -254,7 +263,7 @@ type CustomOrderListForExcel struct {
//@param partnerCategory 合伙人类型id
//@param updateTime 订单更新时间范围"[开始时间,结束时间]",时间格式"2006-01-02 15:04:05+07"
//@param createTime 订单的创建时间范围"[开始时间,结束时间]" 时间格式"2006-01-02 15:04:05+07"
func (dao OrderBaseDao) OrderListForExcel(companyId int64, partnerOrCode string,
func (dao OrderBaseDao) OrderListForExcel(companyId int64, partnerName string, orderCode string, deliveryCode string,
updateTime [2]string, createTime [2]string, partnerCategory int) (
result []CustomOrderListForExcel, err error) {
sqlstr := `
... ... @@ -270,12 +279,26 @@ func (dao OrderBaseDao) OrderListForExcel(companyId int64, partnerOrCode string,
WHERE 1=1 AND t1.order_type = 1 AND t1.company_id=?
`
params := []interface{}{companyId}
if len(partnerOrCode) > 0 {
like := "%" + partnerOrCode + "%"
params = append(params, like, like, like)
sqlstr += " AND (t1.order_code like ? OR t1.delivery_code like ? OR t2.partner_name like ? ) "
//if len(partnerOrCode) > 0 {
// like := "%" + partnerOrCode + "%"
// params = append(params, like, like, like)
// sqlstr += " AND (t1.order_code like ? OR t1.delivery_code like ? OR t2.partner_name like ? ) "
//}
if len(partnerName) > 0 {
like := "%" + partnerName + "%"
params = append(params, like)
sqlstr += ` AND t2.partner_name like ? `
}
if len(orderCode) > 0 {
like := "%" + orderCode + "%"
params = append(params, like)
sqlstr += ` AND t1.order_code like ? `
}
if len(deliveryCode) > 0 {
like := "%" + deliveryCode + "%"
params = append(params, like)
sqlstr += ` AND t1.delivery_code like ? `
}
if partnerCategory > 0 {
params = append(params, partnerCategory)
sqlstr += ` AND t1.partner_category@>'{"id":?}' `
... ...
... ... @@ -3,9 +3,10 @@ package controllers
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"github.com/beego/beego/v2/client/httplib"
"os"
"path"
"regexp"
"strconv"
... ... @@ -70,6 +71,7 @@ func (postData *postPurposeOrderDetail) Valid() error {
}
if postData.PartnerId == 0 {
return lib.ThrowError(lib.ARG_ERROR, "合伙人信息必填")
}
if len(postData.OrderDist) == 0 {
return lib.ThrowError(lib.ARG_ERROR, "订单区域必填")
... ... @@ -149,10 +151,19 @@ func (postData *postOrderPurposeDelivery) Valid() error {
return nil
}
//PageListOrderReal 获取实发订单列表
/**
* @Author SteveChan
* @Description // 获取实发订单列表,修改搜索条件
* @Date 20:23 2021/1/10
* @Param
* @return
**/
func (c *OrderInfoController) PageListOrderReal() {
type Parameter struct {
SearchText string `json:"searchText"`
//SearchText string `json:"searchText"`
PartnerName string `json:"partnerName"` // 合伙人姓名
OrderCode string `json:"orderCode"` // 订单号
DeliveryCode string `json:"deliveryCode"` // 发货单号
PartnerCategory int `json:"PartnerCategory"`
PageSize int `json:"pageSize"`
PageNumber int `json:"pageNumber"`
... ... @@ -230,7 +241,10 @@ func (c *OrderInfoController) PageListOrderReal() {
companyId := c.GetUserCompany()
orderSrv := orderService.NewOrderInfoService(nil)
orderinfos, cnt, err := orderSrv.PageListOrderBase(orderQuery.ListOrderBaseQuery{
PartnerOrCode: param.SearchText,
//PartnerOrCode: param.SearchText,
PartnerName: param.PartnerName,
OrderCode: param.OrderCode,
DeliveryCode: param.DeliveryCode,
OrderType: domain.OrderReal,
Limit: param.PageSize,
Offset: (param.PageNumber - 1) * param.PageSize,
... ... @@ -514,7 +528,10 @@ func (c *OrderInfoController) RemoveOrderReal() {
//ListOrderForExcel excel 导出实际订单的列表
func (c *OrderInfoController) ListOrderForExcel() {
type Parameter struct {
SearchText string `json:"searchText"`
//SearchText string `json:"searchText"`
PartnerName string `json:"partnerName"` // 合伙人姓名
OrderCode string `json:"orderCode"` // 订单号
DeliveryCode string `json:"deliveryCode"` // 发货单号
PartnerCategory int `json:"PartnerCategory"`
UpdateTime []string `json:"updateTime"`
CreateTime []string `json:"createTime"`
... ... @@ -584,7 +601,10 @@ func (c *OrderInfoController) ListOrderForExcel() {
companyId := c.GetUserCompany()
orderSrv := orderService.NewOrderInfoService(nil)
orderinfos, columns, err := orderSrv.ListOrderForExcel(orderQuery.ListOrderBaseQuery{
PartnerOrCode: param.SearchText,
//PartnerOrCode: param.SearchText,
PartnerName: param.PartnerName,
OrderCode: param.OrderCode,
DeliveryCode: param.DeliveryCode,
OrderType: domain.OrderReal,
CompanyId: companyId,
PartnerCategory: param.PartnerCategory,
... ... @@ -614,6 +634,62 @@ func (c *OrderInfoController) ListOrderForExcel() {
/**
* @Author SteveChan
* @Description // 下载导入模板
* @Date 16:48 2021/1/8
* @Param
* @return
**/
func (c *OrderInfoController) DownloadTemplate() {
type Parameter struct {
TYPE string `json:"type"`
}
var (
param Parameter
err error
)
if err = c.BindJsonData(&param); err != nil {
logs.Error(err)
c.ResponseError(errors.New("json数据解析失败"))
return
}
// 校验类型编码
if param.TYPE != "PARTNER_ORDER_FILE" {
c.ResponseError(errors.New("类型编码错误"))
}
// 创建下载文件夹
mkErr := os.Mkdir("download", os.ModePerm)
if mkErr != nil {
fmt.Println(mkErr)
}
// 获取导入模板
req := httplib.Get("http://suplus-file-dev.fjmaimaimai.com/upload/file/2021010803305336443.xlsx")
err = req.ToFile(constant.IMPORT_EXCEL)
if err != nil {
logs.Error("could not save to file: ", err)
}
// 返回字段定义
ret := map[string]interface{}{}
resp, err := req.Response()
if err != nil {
logs.Error("could not get response: ", err)
} else {
logs.Info(resp)
ret = map[string]interface{}{
"url": "http://" + c.Ctx.Request.Host + "/download/订单数据模板.xlsx",
}
c.ResponseData(ret)
}
}
/**
* @Author SteveChan
* @Description //TODO 导入excel订单
* @Date 10:52 2021/1/6
* @Param
... ... @@ -621,17 +697,20 @@ func (c *OrderInfoController) ListOrderForExcel() {
**/
func (c *OrderInfoController) ImportOrderFromExcel() {
// 获取参数
where := c.GetString("where")
typeCode := c.GetString("type")
file, h, _ := c.GetFile("file")
companyId := c.GetUserCompany()
// Json数据解析
jsonMap := make(map[string]interface{})
err := json.Unmarshal([]byte(where), &jsonMap)
if err != nil {
logs.Error(err)
c.ResponseError(errors.New("json数据解析失败"))
//jsonMap := make(map[string]interface{})
//err := json.Unmarshal([]byte(where), &jsonMap)
//if err != nil {
// logs.Error(err)
// c.ResponseError(errors.New("json数据解析失败"))
//}
if typeCode != "PARTNER_ORDER_IMPORT" {
c.ResponseError(errors.New("类型编码错误"))
}
// 返回字段定义
... ... @@ -664,6 +743,9 @@ func (c *OrderInfoController) ImportOrderFromExcel() {
return
}
// 数据行计数
rowCnt := 0
// 空文件校验
if len(rows) < 3 {
c.ResponseError(errors.New("导入的excel文件为空文件,请上传正确的文件"))
... ... @@ -674,11 +756,12 @@ func (c *OrderInfoController) ImportOrderFromExcel() {
nullFlag := false
for i, row := range rows {
if i > 2 && row != nil {
rowCnt++
if len(row) == constant.EXCEL_COLUMN { // 中间空字符校验
var tmpRow = row
var myRow []string
for j, cell := range row {
if j != 8 { // 业务员抽成比例不校验
if j != 8 { // 业务员抽成比例非必填
if cell == "" || cell == " " { // 空字符串填充
tmpRow[j] = "null"
nullFlag = true
... ... @@ -728,11 +811,13 @@ func (c *OrderInfoController) ImportOrderFromExcel() {
var myRow []string
for j, cell := range row {
switch j {
case 0, 1, 2, 3, 4, 5, 8: // 订单号、发货单号、客户名称、订单区域、编号、合伙人、产品名称
case 0, 1, 2, 3, 4, 5, 8: // 订单号、发货单号、客户名称、订单区域、编号、合伙人、产品名称长度校验
{
if len(cell) > 50 {
cellStr := strings.TrimSpace(cell)
lenCellStr := utf8.RuneCountInString(cellStr)
if lenCellStr > 50 {
var tmpRow []string
tmpRow = append(tmpRow, tableHeader[j+2]+"长度超过50,请重新输入") // 错误信息
tmpRow = append(tmpRow, tableHeader[j+2]+"长度超过50,请重新输入") // 错误信息
s := strconv.Itoa(i + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, row...) // 错误行数据
... ... @@ -752,50 +837,76 @@ func (c *OrderInfoController) ImportOrderFromExcel() {
}
case 7: // 业务员抽成比例,非必填,精确到小数点后两位
{
var (
typeErrFlag bool
lenErrFlag bool
ratioErrFlag bool
)
if len(cell) > 0 {
_, err := strconv.ParseFloat(cell, 64)
// 参数类型转换
shareRatio, err := strconv.ParseFloat(cell, 64)
if err != nil {
typeErrFlag = true
}
// 比例不能超过100%
if shareRatio > 100 {
ratioErrFlag = true
}
// 长度校验
regexpStr := `^(100|[1-9]\d|\d)(.\d{1,2})?$`
ok := regexp.MustCompile(regexpStr).MatchString(cell)
if !ok {
lenErrFlag = true
}
if typeErrFlag || lenErrFlag || ratioErrFlag {
var tmpRow []string
tmpRow = append(tmpRow, "业务员抽成比例格式错误,请输入正确的业务员抽成比例比例,保留两位小数") // 错误信息
s := strconv.Itoa(i + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, row...) // 错误行数据
myRow = tmpRow
typeErrFlag = false
lenErrFlag = false
ratioErrFlag = false
}
//if isOk, _ := regexp.MatchString("^(.[0-9]{0,1,2})?$", cell); !isOk { // 最多两位小数
// var tmpRow []string
// tmpRow = append(tmpRow, "业务员员抽成比例格式错误,请输入正确的单价,保留两位小数") // 错误信息
// s := strconv.Itoa(i + 1)
// tmpRow = append(tmpRow, s) // 行号
// tmpRow = append(tmpRow, row...) // 错误行数据
// myRow = tmpRow
//}
}
}
case 9: // 数量不超过16位正整数
{
_, err := strconv.ParseInt(cell, 10, 64)
var (
typeErrFlag bool
lenErrFlag bool
)
//参数类型转换
orderNum, err := strconv.ParseInt(cell, 10, 64)
if err != nil {
var tmpRow []string
tmpRow = append(tmpRow, "数量须为整数,请重新填写") // 错误信息
s := strconv.Itoa(i + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, row...) // 错误行数据
myRow = tmpRow
typeErrFlag = true
}
// 长度校验
if orderNum > 1e16 {
lenErrFlag = true
}
if len(cell) > 16 {
if typeErrFlag || lenErrFlag {
var tmpRow []string
tmpRow = append(tmpRow, "数量长度超过最大限制十六位整数,请重新填写") // 错误信息
s := strconv.Itoa(i + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, row...) // 错误行数据
myRow = tmpRow
typeErrFlag = false
lenErrFlag = false
}
}
case 10: // 单价,精确到小数点后两位,小数点左侧最多可输入16位数字
{
_, err := strconv.ParseFloat(cell, 64)
// 参数类型转换
univalent, err := strconv.ParseFloat(cell, 64)
if err != nil {
var tmpRow []string
tmpRow = append(tmpRow, "单价格式错误,请输入正确的单价,保留两位小数点,小数点前面不能超过十六位数字") // 错误信息
... ... @@ -804,34 +915,53 @@ func (c *OrderInfoController) ImportOrderFromExcel() {
tmpRow = append(tmpRow, row...) // 错误行数据
myRow = tmpRow
}
//if isOk, _ := regexp.MatchString("^d{1,16}(.[0-9]{0,1,2})?$", cell); !isOk { // 非负的16位整数最多两位小数
// var tmpRow []string
// tmpRow = append(tmpRow, "单价格式错误,请输入正确的单价,保留两位小数点,小数点前面不能超过十六位数字") // 错误信息
// s := strconv.Itoa(i + 1)
// tmpRow = append(tmpRow, s) // 行号
// tmpRow = append(tmpRow, row...) // 错误行数据
// myRow = tmpRow
//}
// 长度校验
if univalent >= 1e16 {
var tmpRow []string
tmpRow = append(tmpRow, "单价格式错误,请输入正确的单价,保留两位小数点,小数点前面不能超过十六位数字") // 错误信息
s := strconv.Itoa(i + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, row...) // 错误行数据
myRow = tmpRow
}
}
case 11: // 合伙人分红比例,精确到小数点后两位
{
_, err := strconv.ParseFloat(cell, 64)
var (
typeErrFlag bool
lenErrFlag bool
ratioErrFlag bool
)
//参数类型转换
partnerRatio, err := strconv.ParseFloat(cell, 64)
if err != nil {
typeErrFlag = true
}
// 合伙人分红比例超额
if partnerRatio > 100 {
ratioErrFlag = true
}
// 长度判断
regexpStr := `^(100|[1-9]\d|\d)(.\d{1,2})?$`
ok := regexp.MustCompile(regexpStr).MatchString(cell)
if !ok {
lenErrFlag = true
}
if typeErrFlag || lenErrFlag || ratioErrFlag {
var tmpRow []string
tmpRow = append(tmpRow, "合伙人分红比例格式错误,请输入正确的合伙人分红比例,保留两位小数") // 错误信息
s := strconv.Itoa(i + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, row...) // 错误行数据
myRow = tmpRow
typeErrFlag = false
lenErrFlag = false
ratioErrFlag = false
}
//if isOk, _ := regexp.MatchString("^(.[0-9]{1,2})?$", cell); !isOk { // 最多两位小数
// var tmpRow []string
// tmpRow = append(tmpRow, "合伙人分红比例错误,请输入正确的合伙人分红比例,保留两位小数") // 错误信息
// s := strconv.Itoa(i + 1)
// tmpRow = append(tmpRow, s) // 行号
// tmpRow = append(tmpRow, row...) // 错误行数据
// myRow = tmpRow
//}
}
}
}
... ... @@ -888,11 +1018,12 @@ func (c *OrderInfoController) ImportOrderFromExcel() {
PlanGoodNumber: int(amount),
Price: price,
PartnerBonusPercent: percent,
LineNumber: i,
},
},
CompanyId: companyId,
PartnerCategory: 1,
LineNumbers: []int{i},
LineNumbers: []int{i}, // 记录行号
}
// 获取partnerId
... ... @@ -940,10 +1071,36 @@ func (c *OrderInfoController) ImportOrderFromExcel() {
}
}
// 产品数量校验
productNumberError := make([]interface{}, 0)
// 批量创建订单命令集
var createOrderCommands []*orderCmd.CreateOrderCommand
for _, orderCommand := range orderCommands {
createOrderCommands = append(createOrderCommands, orderCommand)
if len(orderCommand.Goods) > 50 { // 产品数量校验
for _, line := range orderCommand.LineNumbers {
var tmpRow []string
tmpRow = append(tmpRow, "单笔订单产品超过50种") // 错误信息
s := strconv.Itoa(line + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, rows[line]...) // 错误行数据
productNumberError = append(productNumberError, tmpRow)
}
} else {
createOrderCommands = append(createOrderCommands, orderCommand)
}
}
if len(productNumberError) > 0 {
ret = map[string]interface{}{
"successCount": 0,
"fail": map[string]interface{}{
"tableHeader": tableHeader,
"tableData": productNumberError,
},
}
c.ResponseData(ret)
return
}
// 新增失败记录
... ... @@ -952,17 +1109,37 @@ func (c *OrderInfoController) ImportOrderFromExcel() {
// 新增成功记录计数
var successDataCount int64
// 新增错误信息
var createError error
// 批量新增订单
failureDataList, createError = orderSrv.CreateNewOrderByImport(createOrderCommands)
errorDataList, createError := orderSrv.CreateNewOrderByImport(createOrderCommands)
if createError != nil {
c.ResponseError(createError)
return
} else {
if len(failureDataList) > 0 { // 导入失败返回
if len(errorDataList) > 0 { // 导入失败返回
successDataCount = 0
// 错误记录处理
for _, errorData := range errorDataList {
if len(errorData.GoodLine) == 0 { // 订单错误
for _, line := range errorData.LineNumbers {
var tmpRow []string
tmpRow = append(tmpRow, errorData.Error.Error()) // 错误信息
s := strconv.Itoa(line + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, rows[line]...) // 错误行数据
failureDataList = append(failureDataList, tmpRow)
}
} else if len(errorData.GoodLine) > 0 { // 订单产品错误
for line := range errorData.GoodLine {
var tmpRow []string
tmpRow = append(tmpRow, errorData.Error.Error()) // 错误信息
s := strconv.Itoa(line + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, rows[line]...) // 错误行数据
failureDataList = append(failureDataList, tmpRow)
}
}
}
ret = map[string]interface{}{
"successCount": successDataCount,
"fail": map[string]interface{}{
... ... @@ -971,8 +1148,8 @@ func (c *OrderInfoController) ImportOrderFromExcel() {
},
}
} else { // 导入成功返回
successDataCount = int64(len(rows) - 3 - len(failureDataList))
if successDataCount == int64(len(rows)-3) {
successDataCount = int64(rowCnt - len(failureDataList))
if successDataCount == int64(rowCnt) {
ret = map[string]interface{}{
"successCount": successDataCount,
"fail": nil,
... ...
... ... @@ -6,6 +6,10 @@ import (
)
func init() {
// 导入相关
beego.Router("/fileImportTemplate", &controllers.OrderInfoController{}, "POST:DownloadTemplate") // 下载导入模板
beego.Router("/fileImport", &controllers.OrderInfoController{}, "POST:ImportOrderFromExcel") // 导入订单数据
adminRouter := beego.NewNamespace("/v1",
beego.NSNamespace("/auth",
beego.NSRouter("/login", &controllers.AdminLoginController{}, "POST:Login"),
... ... @@ -35,12 +39,11 @@ func init() {
beego.NSRouter("/list/excel", &controllers.OrderDividendController{}, "POST:ListOrderBonusForExcel"),
),
beego.NSNamespace("/order",
beego.NSRouter("/actual/list", &controllers.OrderInfoController{}, "POST:PageListOrderReal"), // 返归订单列表
beego.NSRouter("/actual/list/excel", &controllers.OrderInfoController{}, "POST:ListOrderForExcel"), // 导出excel
beego.NSRouter("/actual/import/excel", &controllers.OrderInfoController{}, "POST:ImportOrderFromExcel"), // 导入订单数据
beego.NSRouter("/actual/detail", &controllers.OrderInfoController{}, "POST:GetOrderReal"), // 查看实际订单详情
beego.NSRouter("/actual/del", &controllers.OrderInfoController{}, "POST:RemoveOrderReal"), // 删除实际订单
beego.NSRouter("/actual/update", &controllers.OrderInfoController{}, "POST:UpdateOrderReal"), // 新增实际订单
beego.NSRouter("/actual/list", &controllers.OrderInfoController{}, "POST:PageListOrderReal"), // 返归订单列表
beego.NSRouter("/actual/list/excel", &controllers.OrderInfoController{}, "POST:ListOrderForExcel"), // 导出订单记录
beego.NSRouter("/actual/detail", &controllers.OrderInfoController{}, "POST:GetOrderReal"), // 查看实际订单详情
beego.NSRouter("/actual/del", &controllers.OrderInfoController{}, "POST:RemoveOrderReal"), // 删除实际订单
beego.NSRouter("/actual/update", &controllers.OrderInfoController{}, "POST:UpdateOrderReal"), // 新增实际订单
beego.NSRouter("/actual/close", &controllers.OrderInfoController{}, "POST:OrderDisable"),
),
beego.NSNamespace("/common",
... ...
... ... @@ -20,4 +20,7 @@ func init() {
http.ServeFile(ctx.ResponseWriter, ctx.Request, constant.LOG_File)
return
})
// 静态文件路径映射
beego.SetStaticPath("/download", "download")
}
... ...
Copyright 2014 astaxie
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
... ...
# httplib
httplib is an libs help you to curl remote url.
# How to use?
## GET
you can use Get to crawl data.
import "github.com/beego/beego/v2/httplib"
str, err := httplib.Get("http://beego.me/").String()
if err != nil {
// error
}
fmt.Println(str)
## POST
POST data to remote url
req := httplib.Post("http://beego.me/")
req.Param("username","astaxie")
req.Param("password","123456")
str, err := req.String()
if err != nil {
// error
}
fmt.Println(str)
## Set timeout
The default timeout is `60` seconds, function prototype:
SetTimeout(connectTimeout, readWriteTimeout time.Duration)
Example:
// GET
httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
// POST
httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second)
## Debug
If you want to debug the request info, set the debug on
httplib.Get("http://beego.me/").Debug(true)
## Set HTTP Basic Auth
str, err := Get("http://beego.me/").SetBasicAuth("user", "passwd").String()
if err != nil {
// error
}
fmt.Println(str)
## Set HTTPS
If request url is https, You can set the client support TSL:
httplib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
More info about the `tls.Config` please visit http://golang.org/pkg/crypto/tls/#Config
## Set HTTP Version
some servers need to specify the protocol version of HTTP
httplib.Get("http://beego.me/").SetProtocolVersion("HTTP/1.1")
## Set Cookie
some http request need setcookie. So set it like this:
cookie := &http.Cookie{}
cookie.Name = "username"
cookie.Value = "astaxie"
httplib.Get("http://beego.me/").SetCookie(cookie)
## Upload file
httplib support mutil file upload, use `req.PostFile()`
req := httplib.Post("http://beego.me/")
req.Param("username","astaxie")
req.PostFile("uploadfile1", "httplib.pdf")
str, err := req.String()
if err != nil {
// error
}
fmt.Println(str)
See godoc for further documentation and examples.
* [godoc.org/github.com/beego/beego/v2/httplib](https://godoc.org/github.com/beego/beego/v2/httplib)
... ...
// Copyright 2020 beego
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package httplib
import (
"context"
"net/http"
)
type FilterChain func(next Filter) Filter
type Filter func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error)
... ...
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package httplib is used as http.Client
// Usage:
//
// import "github.com/beego/beego/v2/httplib"
//
// b := httplib.Post("http://beego.me/")
// b.Param("username","astaxie")
// b.Param("password","123456")
// b.PostFile("uploadfile1", "httplib.pdf")
// b.PostFile("uploadfile2", "httplib.txt")
// str, err := b.String()
// if err != nil {
// t.Fatal(err)
// }
// fmt.Println(str)
//
// more docs http://beego.me/docs/module/httplib.md
package httplib
import (
"bytes"
"compress/gzip"
"context"
"crypto/tls"
"encoding/json"
"encoding/xml"
"io"
"io/ioutil"
"log"
"mime/multipart"
"net"
"net/http"
"net/http/cookiejar"
"net/http/httputil"
"net/url"
"os"
"path"
"strings"
"sync"
"time"
"gopkg.in/yaml.v2"
)
var defaultSetting = BeegoHTTPSettings{
UserAgent: "beegoServer",
ConnectTimeout: 60 * time.Second,
ReadWriteTimeout: 60 * time.Second,
Gzip: true,
DumpBody: true,
}
var defaultCookieJar http.CookieJar
var settingMutex sync.Mutex
// it will be the last filter and execute request.Do
var doRequestFilter = func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error) {
return req.doRequest(ctx)
}
// createDefaultCookie creates a global cookiejar to store cookies.
func createDefaultCookie() {
settingMutex.Lock()
defer settingMutex.Unlock()
defaultCookieJar, _ = cookiejar.New(nil)
}
// SetDefaultSetting overwrites default settings
func SetDefaultSetting(setting BeegoHTTPSettings) {
settingMutex.Lock()
defer settingMutex.Unlock()
defaultSetting = setting
}
// NewBeegoRequest returns *BeegoHttpRequest with specific method
func NewBeegoRequest(rawurl, method string) *BeegoHTTPRequest {
var resp http.Response
u, err := url.Parse(rawurl)
if err != nil {
log.Println("Httplib:", err)
}
req := http.Request{
URL: u,
Method: method,
Header: make(http.Header),
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
}
return &BeegoHTTPRequest{
url: rawurl,
req: &req,
params: map[string][]string{},
files: map[string]string{},
setting: defaultSetting,
resp: &resp,
}
}
// Get returns *BeegoHttpRequest with GET method.
func Get(url string) *BeegoHTTPRequest {
return NewBeegoRequest(url, "GET")
}
// Post returns *BeegoHttpRequest with POST method.
func Post(url string) *BeegoHTTPRequest {
return NewBeegoRequest(url, "POST")
}
// Put returns *BeegoHttpRequest with PUT method.
func Put(url string) *BeegoHTTPRequest {
return NewBeegoRequest(url, "PUT")
}
// Delete returns *BeegoHttpRequest DELETE method.
func Delete(url string) *BeegoHTTPRequest {
return NewBeegoRequest(url, "DELETE")
}
// Head returns *BeegoHttpRequest with HEAD method.
func Head(url string) *BeegoHTTPRequest {
return NewBeegoRequest(url, "HEAD")
}
// BeegoHTTPSettings is the http.Client setting
type BeegoHTTPSettings struct {
ShowDebug bool
UserAgent string
ConnectTimeout time.Duration
ReadWriteTimeout time.Duration
TLSClientConfig *tls.Config
Proxy func(*http.Request) (*url.URL, error)
Transport http.RoundTripper
CheckRedirect func(req *http.Request, via []*http.Request) error
EnableCookie bool
Gzip bool
DumpBody bool
Retries int // if set to -1 means will retry forever
RetryDelay time.Duration
FilterChains []FilterChain
}
// BeegoHTTPRequest provides more useful methods than http.Request for requesting a url.
type BeegoHTTPRequest struct {
url string
req *http.Request
params map[string][]string
files map[string]string
setting BeegoHTTPSettings
resp *http.Response
body []byte
dump []byte
}
// GetRequest returns the request object
func (b *BeegoHTTPRequest) GetRequest() *http.Request {
return b.req
}
// Setting changes request settings
func (b *BeegoHTTPRequest) Setting(setting BeegoHTTPSettings) *BeegoHTTPRequest {
b.setting = setting
return b
}
// SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password.
func (b *BeegoHTTPRequest) SetBasicAuth(username, password string) *BeegoHTTPRequest {
b.req.SetBasicAuth(username, password)
return b
}
// SetEnableCookie sets enable/disable cookiejar
func (b *BeegoHTTPRequest) SetEnableCookie(enable bool) *BeegoHTTPRequest {
b.setting.EnableCookie = enable
return b
}
// SetUserAgent sets User-Agent header field
func (b *BeegoHTTPRequest) SetUserAgent(useragent string) *BeegoHTTPRequest {
b.setting.UserAgent = useragent
return b
}
// Debug sets show debug or not when executing request.
func (b *BeegoHTTPRequest) Debug(isdebug bool) *BeegoHTTPRequest {
b.setting.ShowDebug = isdebug
return b
}
// Retries sets Retries times.
// default is 0 (never retry)
// -1 retry indefinitely (forever)
// Other numbers specify the exact retry amount
func (b *BeegoHTTPRequest) Retries(times int) *BeegoHTTPRequest {
b.setting.Retries = times
return b
}
// RetryDelay sets the time to sleep between reconnection attempts
func (b *BeegoHTTPRequest) RetryDelay(delay time.Duration) *BeegoHTTPRequest {
b.setting.RetryDelay = delay
return b
}
// DumpBody sets the DumbBody field
func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest {
b.setting.DumpBody = isdump
return b
}
// DumpRequest returns the DumpRequest
func (b *BeegoHTTPRequest) DumpRequest() []byte {
return b.dump
}
// SetTimeout sets connect time out and read-write time out for BeegoRequest.
func (b *BeegoHTTPRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHTTPRequest {
b.setting.ConnectTimeout = connectTimeout
b.setting.ReadWriteTimeout = readWriteTimeout
return b
}
// SetTLSClientConfig sets TLS connection configuration if visiting HTTPS url.
func (b *BeegoHTTPRequest) SetTLSClientConfig(config *tls.Config) *BeegoHTTPRequest {
b.setting.TLSClientConfig = config
return b
}
// Header adds header item string in request.
func (b *BeegoHTTPRequest) Header(key, value string) *BeegoHTTPRequest {
b.req.Header.Set(key, value)
return b
}
// SetHost set the request host
func (b *BeegoHTTPRequest) SetHost(host string) *BeegoHTTPRequest {
b.req.Host = host
return b
}
// SetProtocolVersion sets the protocol version for incoming requests.
// Client requests always use HTTP/1.1.
func (b *BeegoHTTPRequest) SetProtocolVersion(vers string) *BeegoHTTPRequest {
if len(vers) == 0 {
vers = "HTTP/1.1"
}
major, minor, ok := http.ParseHTTPVersion(vers)
if ok {
b.req.Proto = vers
b.req.ProtoMajor = major
b.req.ProtoMinor = minor
}
return b
}
// SetCookie adds a cookie to the request.
func (b *BeegoHTTPRequest) SetCookie(cookie *http.Cookie) *BeegoHTTPRequest {
b.req.Header.Add("Cookie", cookie.String())
return b
}
// SetTransport sets the transport field
func (b *BeegoHTTPRequest) SetTransport(transport http.RoundTripper) *BeegoHTTPRequest {
b.setting.Transport = transport
return b
}
// SetProxy sets the HTTP proxy
// example:
//
// func(req *http.Request) (*url.URL, error) {
// u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
// return u, nil
// }
func (b *BeegoHTTPRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHTTPRequest {
b.setting.Proxy = proxy
return b
}
// SetCheckRedirect specifies the policy for handling redirects.
//
// If CheckRedirect is nil, the Client uses its default policy,
// which is to stop after 10 consecutive requests.
func (b *BeegoHTTPRequest) SetCheckRedirect(redirect func(req *http.Request, via []*http.Request) error) *BeegoHTTPRequest {
b.setting.CheckRedirect = redirect
return b
}
// SetFilters will use the filter as the invocation filters
func (b *BeegoHTTPRequest) SetFilters(fcs ...FilterChain) *BeegoHTTPRequest {
b.setting.FilterChains = fcs
return b
}
// AddFilters adds filter
func (b *BeegoHTTPRequest) AddFilters(fcs ...FilterChain) *BeegoHTTPRequest {
b.setting.FilterChains = append(b.setting.FilterChains, fcs...)
return b
}
// Param adds query param in to request.
// params build query string as ?key1=value1&key2=value2...
func (b *BeegoHTTPRequest) Param(key, value string) *BeegoHTTPRequest {
if param, ok := b.params[key]; ok {
b.params[key] = append(param, value)
} else {
b.params[key] = []string{value}
}
return b
}
// PostFile adds a post file to the request
func (b *BeegoHTTPRequest) PostFile(formname, filename string) *BeegoHTTPRequest {
b.files[formname] = filename
return b
}
// Body adds request raw body.
// Supports string and []byte.
func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest {
switch t := data.(type) {
case string:
bf := bytes.NewBufferString(t)
b.req.Body = ioutil.NopCloser(bf)
b.req.ContentLength = int64(len(t))
case []byte:
bf := bytes.NewBuffer(t)
b.req.Body = ioutil.NopCloser(bf)
b.req.ContentLength = int64(len(t))
}
return b
}
// XMLBody adds the request raw body encoded in XML.
func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
if b.req.Body == nil && obj != nil {
byts, err := xml.Marshal(obj)
if err != nil {
return b, err
}
b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
b.req.ContentLength = int64(len(byts))
b.req.Header.Set("Content-Type", "application/xml")
}
return b, nil
}
// YAMLBody adds the request raw body encoded in YAML.
func (b *BeegoHTTPRequest) YAMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
if b.req.Body == nil && obj != nil {
byts, err := yaml.Marshal(obj)
if err != nil {
return b, err
}
b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
b.req.ContentLength = int64(len(byts))
b.req.Header.Set("Content-Type", "application/x+yaml")
}
return b, nil
}
// JSONBody adds the request raw body encoded in JSON.
func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) {
if b.req.Body == nil && obj != nil {
byts, err := json.Marshal(obj)
if err != nil {
return b, err
}
b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
b.req.ContentLength = int64(len(byts))
b.req.Header.Set("Content-Type", "application/json")
}
return b, nil
}
func (b *BeegoHTTPRequest) buildURL(paramBody string) {
// build GET url with query string
if b.req.Method == "GET" && len(paramBody) > 0 {
if strings.Contains(b.url, "?") {
b.url += "&" + paramBody
} else {
b.url = b.url + "?" + paramBody
}
return
}
// build POST/PUT/PATCH url and body
if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH" || b.req.Method == "DELETE") && b.req.Body == nil {
// with files
if len(b.files) > 0 {
pr, pw := io.Pipe()
bodyWriter := multipart.NewWriter(pw)
go func() {
for formname, filename := range b.files {
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
if err != nil {
log.Println("Httplib:", err)
}
fh, err := os.Open(filename)
if err != nil {
log.Println("Httplib:", err)
}
// iocopy
_, err = io.Copy(fileWriter, fh)
fh.Close()
if err != nil {
log.Println("Httplib:", err)
}
}
for k, v := range b.params {
for _, vv := range v {
bodyWriter.WriteField(k, vv)
}
}
bodyWriter.Close()
pw.Close()
}()
b.Header("Content-Type", bodyWriter.FormDataContentType())
b.req.Body = ioutil.NopCloser(pr)
b.Header("Transfer-Encoding", "chunked")
return
}
// with params
if len(paramBody) > 0 {
b.Header("Content-Type", "application/x-www-form-urlencoded")
b.Body(paramBody)
}
}
}
func (b *BeegoHTTPRequest) getResponse() (*http.Response, error) {
if b.resp.StatusCode != 0 {
return b.resp, nil
}
resp, err := b.DoRequest()
if err != nil {
return nil, err
}
b.resp = resp
return resp, nil
}
// DoRequest executes client.Do
func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) {
return b.DoRequestWithCtx(context.Background())
}
func (b *BeegoHTTPRequest) DoRequestWithCtx(ctx context.Context) (resp *http.Response, err error) {
root := doRequestFilter
if len(b.setting.FilterChains) > 0 {
for i := len(b.setting.FilterChains) - 1; i >= 0; i-- {
root = b.setting.FilterChains[i](root)
}
}
return root(ctx, b)
}
func (b *BeegoHTTPRequest) doRequest(ctx context.Context) (resp *http.Response, err error) {
var paramBody string
if len(b.params) > 0 {
var buf bytes.Buffer
for k, v := range b.params {
for _, vv := range v {
buf.WriteString(url.QueryEscape(k))
buf.WriteByte('=')
buf.WriteString(url.QueryEscape(vv))
buf.WriteByte('&')
}
}
paramBody = buf.String()
paramBody = paramBody[0 : len(paramBody)-1]
}
b.buildURL(paramBody)
urlParsed, err := url.Parse(b.url)
if err != nil {
return nil, err
}
b.req.URL = urlParsed
trans := b.setting.Transport
if trans == nil {
// create default transport
trans = &http.Transport{
TLSClientConfig: b.setting.TLSClientConfig,
Proxy: b.setting.Proxy,
Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
MaxIdleConnsPerHost: 100,
}
} else {
// if b.transport is *http.Transport then set the settings.
if t, ok := trans.(*http.Transport); ok {
if t.TLSClientConfig == nil {
t.TLSClientConfig = b.setting.TLSClientConfig
}
if t.Proxy == nil {
t.Proxy = b.setting.Proxy
}
if t.Dial == nil {
t.Dial = TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout)
}
}
}
var jar http.CookieJar
if b.setting.EnableCookie {
if defaultCookieJar == nil {
createDefaultCookie()
}
jar = defaultCookieJar
}
client := &http.Client{
Transport: trans,
Jar: jar,
}
if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" {
b.req.Header.Set("User-Agent", b.setting.UserAgent)
}
if b.setting.CheckRedirect != nil {
client.CheckRedirect = b.setting.CheckRedirect
}
if b.setting.ShowDebug {
dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
if err != nil {
log.Println(err.Error())
}
b.dump = dump
}
// retries default value is 0, it will run once.
// retries equal to -1, it will run forever until success
// retries is setted, it will retries fixed times.
// Sleeps for a 400ms between calls to reduce spam
for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ {
resp, err = client.Do(b.req)
if err == nil {
break
}
time.Sleep(b.setting.RetryDelay)
}
return resp, err
}
// String returns the body string in response.
// Calls Response inner.
func (b *BeegoHTTPRequest) String() (string, error) {
data, err := b.Bytes()
if err != nil {
return "", err
}
return string(data), nil
}
// Bytes returns the body []byte in response.
// Calls Response inner.
func (b *BeegoHTTPRequest) Bytes() ([]byte, error) {
if b.body != nil {
return b.body, nil
}
resp, err := b.getResponse()
if err != nil {
return nil, err
}
if resp.Body == nil {
return nil, nil
}
defer resp.Body.Close()
if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" {
reader, err := gzip.NewReader(resp.Body)
if err != nil {
return nil, err
}
b.body, err = ioutil.ReadAll(reader)
return b.body, err
}
b.body, err = ioutil.ReadAll(resp.Body)
return b.body, err
}
// ToFile saves the body data in response to one file.
// Calls Response inner.
func (b *BeegoHTTPRequest) ToFile(filename string) error {
resp, err := b.getResponse()
if err != nil {
return err
}
if resp.Body == nil {
return nil
}
defer resp.Body.Close()
err = pathExistAndMkdir(filename)
if err != nil {
return err
}
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return err
}
// Check if the file directory exists. If it doesn't then it's created
func pathExistAndMkdir(filename string) (err error) {
filename = path.Dir(filename)
_, err = os.Stat(filename)
if err == nil {
return nil
}
if os.IsNotExist(err) {
err = os.MkdirAll(filename, os.ModePerm)
if err == nil {
return nil
}
}
return err
}
// ToJSON returns the map that marshals from the body bytes as json in response.
// Calls Response inner.
func (b *BeegoHTTPRequest) ToJSON(v interface{}) error {
data, err := b.Bytes()
if err != nil {
return err
}
return json.Unmarshal(data, v)
}
// ToXML returns the map that marshals from the body bytes as xml in response .
// Calls Response inner.
func (b *BeegoHTTPRequest) ToXML(v interface{}) error {
data, err := b.Bytes()
if err != nil {
return err
}
return xml.Unmarshal(data, v)
}
// ToYAML returns the map that marshals from the body bytes as yaml in response .
// Calls Response inner.
func (b *BeegoHTTPRequest) ToYAML(v interface{}) error {
data, err := b.Bytes()
if err != nil {
return err
}
return yaml.Unmarshal(data, v)
}
// Response executes request client gets response manually.
func (b *BeegoHTTPRequest) Response() (*http.Response, error) {
return b.getResponse()
}
// TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
return func(netw, addr string) (net.Conn, error) {
conn, err := net.DialTimeout(netw, addr, cTimeout)
if err != nil {
return nil, err
}
err = conn.SetDeadline(time.Now().Add(rwTimeout))
return conn, err
}
}
... ...
... ... @@ -23,6 +23,9 @@ github.com/astaxie/beego/plugins/cors
github.com/astaxie/beego/session
github.com/astaxie/beego/toolbox
github.com/astaxie/beego/utils
# github.com/beego/beego/v2 v2.0.1
## explicit
github.com/beego/beego/v2/client/httplib
# github.com/beorn7/perks v1.0.1
github.com/beorn7/perks/quantile
# github.com/bsm/sarama-cluster v2.1.15+incompatible
... ... @@ -283,7 +286,7 @@ golang.org/x/text/secure/bidirule
golang.org/x/text/transform
golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm
# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
# golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
golang.org/x/xerrors
golang.org/x/xerrors/internal
# google.golang.org/protobuf v1.25.0
... ...