Merge branch 'dev' of http://gitlab.fjmaimaimai.com/mmm-go/partnermg into dev
正在显示
16 个修改的文件
包含
1259 行增加
和
114 行删除
@@ -7,6 +7,7 @@ require ( | @@ -7,6 +7,7 @@ require ( | ||
7 | github.com/Shopify/sarama v1.23.1 | 7 | github.com/Shopify/sarama v1.23.1 |
8 | github.com/ajg/form v1.5.1 // indirect | 8 | github.com/ajg/form v1.5.1 // indirect |
9 | github.com/astaxie/beego v1.12.2 | 9 | github.com/astaxie/beego v1.12.2 |
10 | + github.com/beego/beego/v2 v2.0.1 | ||
10 | github.com/bsm/sarama-cluster v2.1.15+incompatible | 11 | github.com/bsm/sarama-cluster v2.1.15+incompatible |
11 | github.com/dgrijalva/jwt-go v3.2.0+incompatible | 12 | github.com/dgrijalva/jwt-go v3.2.0+incompatible |
12 | github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect | 13 | github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect |
@@ -7,14 +7,25 @@ type ListOrderBaseQuery struct { | @@ -7,14 +7,25 @@ type ListOrderBaseQuery struct { | ||
7 | // 查询限制 | 7 | // 查询限制 |
8 | Limit int `json:"limit"` | 8 | Limit int `json:"limit"` |
9 | //发货单号 | 9 | //发货单号 |
10 | - PartnerOrCode string `json:"partnerOrCode"` | ||
11 | - CompanyId int64 `json:"companyId"` | 10 | + //PartnerOrCode string `json:"partnerOrCode"` |
11 | + //合伙人姓名 | ||
12 | + PartnerName string `json:"partnerName"` | ||
13 | + //订单号 | ||
14 | + OrderCode string `json:"orderCode"` | ||
15 | + //发货单号 | ||
16 | + DeliveryCode string `json:"deliveryCode"` | ||
17 | + //公司id | ||
18 | + CompanyId int64 `json:"companyId"` | ||
12 | //订单类型 | 19 | //订单类型 |
13 | OrderType int `json:"orderType"` | 20 | OrderType int `json:"orderType"` |
14 | //合伙人分类 | 21 | //合伙人分类 |
15 | - PartnerCategory int `json:"partnerCategory"` | 22 | + PartnerCategory int `json:"partnerCategory"` |
23 | + //更新时间开始 | ||
16 | UpdateTimeBegin string `json:"updateTimeBegin"` | 24 | UpdateTimeBegin string `json:"updateTimeBegin"` |
17 | - UpdateTimeEnd string `json:"updateTimeEnd"` | 25 | + //更新时间截止 |
26 | + UpdateTimeEnd string `json:"updateTimeEnd"` | ||
27 | + //创建时间开始 | ||
18 | CreateTimeBegin string `json:"createTimeBegin"` | 28 | CreateTimeBegin string `json:"createTimeBegin"` |
19 | - CreateTimeEnd string `json:"createTimeEnd"` | 29 | + //创建时间截止 |
30 | + CreateTimeEnd string `json:"createTimeEnd"` | ||
20 | } | 31 | } |
@@ -26,7 +26,13 @@ func NewOrderInfoService(option map[string]interface{}) *OrderInfoService { | @@ -26,7 +26,13 @@ func NewOrderInfoService(option map[string]interface{}) *OrderInfoService { | ||
26 | return newAdminUserService | 26 | return newAdminUserService |
27 | } | 27 | } |
28 | 28 | ||
29 | -// PageListOrderBase 获取订单列表 | 29 | +/** |
30 | + * @Author SteveChan | ||
31 | + * @Description // 获取订单列表 | ||
32 | + * @Date 22:05 2021/1/10 | ||
33 | + * @Param | ||
34 | + * @return | ||
35 | + **/ | ||
30 | func (service OrderInfoService) PageListOrderBase(listOrderQuery query.ListOrderBaseQuery) ([]map[string]interface{}, int, error) { | 36 | func (service OrderInfoService) PageListOrderBase(listOrderQuery query.ListOrderBaseQuery) ([]map[string]interface{}, int, error) { |
31 | var err error | 37 | var err error |
32 | transactionContext, err := factory.CreateTransactionContext(nil) | 38 | transactionContext, err := factory.CreateTransactionContext(nil) |
@@ -53,7 +59,9 @@ func (service OrderInfoService) PageListOrderBase(listOrderQuery query.ListOrder | @@ -53,7 +59,9 @@ func (service OrderInfoService) PageListOrderBase(listOrderQuery query.ListOrder | ||
53 | orders, cnt, err = orderDao.OrderListByCondition( | 59 | orders, cnt, err = orderDao.OrderListByCondition( |
54 | listOrderQuery.CompanyId, | 60 | listOrderQuery.CompanyId, |
55 | listOrderQuery.OrderType, | 61 | listOrderQuery.OrderType, |
56 | - listOrderQuery.PartnerOrCode, | 62 | + listOrderQuery.PartnerName, // 合伙人姓名 |
63 | + listOrderQuery.OrderCode, // 订单号 | ||
64 | + listOrderQuery.DeliveryCode, // 发货单号 | ||
57 | [2]string{listOrderQuery.UpdateTimeBegin, listOrderQuery.UpdateTimeEnd}, | 65 | [2]string{listOrderQuery.UpdateTimeBegin, listOrderQuery.UpdateTimeEnd}, |
58 | [2]string{listOrderQuery.CreateTimeBegin, listOrderQuery.CreateTimeEnd}, | 66 | [2]string{listOrderQuery.CreateTimeBegin, listOrderQuery.CreateTimeEnd}, |
59 | listOrderQuery.PartnerCategory, | 67 | listOrderQuery.PartnerCategory, |
@@ -865,6 +873,13 @@ func (service OrderInfoService) ListOrderBonusForExcel(listOrderQuery query.List | @@ -865,6 +873,13 @@ func (service OrderInfoService) ListOrderBonusForExcel(listOrderQuery query.List | ||
865 | return resultMaps, column, nil | 873 | return resultMaps, column, nil |
866 | } | 874 | } |
867 | 875 | ||
876 | +/** | ||
877 | + * @Author SteveChan | ||
878 | + * @Description // 导出订单数据 | ||
879 | + * @Date 22:05 2021/1/10 | ||
880 | + * @Param | ||
881 | + * @return | ||
882 | + **/ | ||
868 | func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrderBaseQuery) ([]map[string]string, [][2]string, error) { | 883 | func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrderBaseQuery) ([]map[string]string, [][2]string, error) { |
869 | transactionContext, err := factory.CreateTransactionContext(nil) | 884 | transactionContext, err := factory.CreateTransactionContext(nil) |
870 | if err != nil { | 885 | if err != nil { |
@@ -876,6 +891,7 @@ func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrder | @@ -876,6 +891,7 @@ func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrder | ||
876 | defer func() { | 891 | defer func() { |
877 | transactionContext.RollbackTransaction() | 892 | transactionContext.RollbackTransaction() |
878 | }() | 893 | }() |
894 | + | ||
879 | var ( | 895 | var ( |
880 | orderBaseDao *dao.OrderBaseDao | 896 | orderBaseDao *dao.OrderBaseDao |
881 | ) | 897 | ) |
@@ -887,7 +903,9 @@ func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrder | @@ -887,7 +903,9 @@ func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrder | ||
887 | } | 903 | } |
888 | ordersData, err := orderBaseDao.OrderListForExcel( | 904 | ordersData, err := orderBaseDao.OrderListForExcel( |
889 | listOrderQuery.CompanyId, | 905 | listOrderQuery.CompanyId, |
890 | - listOrderQuery.PartnerOrCode, | 906 | + listOrderQuery.PartnerName, // 合伙人姓名 |
907 | + listOrderQuery.OrderCode, // 订单号 | ||
908 | + listOrderQuery.DeliveryCode, // 发货单号 | ||
891 | [2]string{listOrderQuery.UpdateTimeBegin, listOrderQuery.UpdateTimeEnd}, | 909 | [2]string{listOrderQuery.UpdateTimeBegin, listOrderQuery.UpdateTimeEnd}, |
892 | [2]string{listOrderQuery.CreateTimeBegin, listOrderQuery.CreateTimeEnd}, | 910 | [2]string{listOrderQuery.CreateTimeBegin, listOrderQuery.CreateTimeEnd}, |
893 | listOrderQuery.PartnerCategory, | 911 | listOrderQuery.PartnerCategory, |
@@ -944,28 +962,29 @@ func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrder | @@ -944,28 +962,29 @@ func (service OrderInfoService) ListOrderForExcel(listOrderQuery query.ListOrder | ||
944 | 962 | ||
945 | /** | 963 | /** |
946 | * @Author SteveChan | 964 | * @Author SteveChan |
947 | - * @Description // 批量导入创建订单 | 965 | + * @Description //TODO 批量导入创建订单 |
948 | * @Date 11:00 2021/1/7 | 966 | * @Date 11:00 2021/1/7 |
949 | * @Param | 967 | * @Param |
950 | * @return | 968 | * @return |
951 | **/ | 969 | **/ |
952 | -func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*command.CreateOrderCommand) ([]interface{}, error) { | 970 | +func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*command.CreateOrderCommand) ([]*domain.ImportInfo, error) { |
953 | // 事务初始化 | 971 | // 事务初始化 |
954 | var ( | 972 | var ( |
955 | transactionContext, _ = factory.CreateTransactionContext(nil) | 973 | transactionContext, _ = factory.CreateTransactionContext(nil) |
956 | err error | 974 | err error |
957 | - failureDataList []interface{} // 错误数据返回 | 975 | + errorDataList []*domain.ImportInfo // 错误数据返回 |
958 | ) | 976 | ) |
959 | 977 | ||
960 | // 循环校验命令 | 978 | // 循环校验命令 |
961 | for _, cmd := range createOrderCommands { | 979 | for _, cmd := range createOrderCommands { |
962 | if err = cmd.Valid(); err != nil { | 980 | if err = cmd.Valid(); err != nil { |
963 | // 返回信息 0: 订单号, 1: 发货单号, 2: 客户名称, 3: 订单区域, 4: 编号, 5: 合伙人, 6: 类型, 7: 业务抽成比例, 8: 产品名称, 9: 数量, 10: 单价, 11: 合伙人分红比例 | 981 | // 返回信息 0: 订单号, 1: 发货单号, 2: 客户名称, 3: 订单区域, 4: 编号, 5: 合伙人, 6: 类型, 7: 业务抽成比例, 8: 产品名称, 9: 数量, 10: 单价, 11: 合伙人分红比例 |
964 | - row := []interface{}{ | ||
965 | - lib.ThrowError(lib.BUSINESS_ERROR, err.Error()), // 错误信息 | ||
966 | - cmd.LineNumbers, // 错误影响的行 | 982 | + row := &domain.ImportInfo{ |
983 | + Error: lib.ThrowError(lib.BUSINESS_ERROR, err.Error()), // 错误信息 | ||
984 | + LineNumbers: cmd.LineNumbers, // 错误影响的行 | ||
985 | + GoodLine: map[int]interface{}{}, | ||
967 | } | 986 | } |
968 | - failureDataList = append(failureDataList, row) | 987 | + errorDataList = append(errorDataList, row) |
969 | continue | 988 | continue |
970 | } | 989 | } |
971 | } | 990 | } |
@@ -987,6 +1006,7 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co | @@ -987,6 +1006,7 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co | ||
987 | categoryRepository domain.PartnerCategoryRepository | 1006 | categoryRepository domain.PartnerCategoryRepository |
988 | orderBaseDao *dao.OrderBaseDao | 1007 | orderBaseDao *dao.OrderBaseDao |
989 | ) | 1008 | ) |
1009 | + | ||
990 | // 合伙人信息仓储初始化 | 1010 | // 合伙人信息仓储初始化 |
991 | if PartnerInfoRepository, err = factory.CreatePartnerInfoRepository(map[string]interface{}{ | 1011 | if PartnerInfoRepository, err = factory.CreatePartnerInfoRepository(map[string]interface{}{ |
992 | "transactionContext": transactionContext, | 1012 | "transactionContext": transactionContext, |
@@ -1028,26 +1048,55 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co | @@ -1028,26 +1048,55 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co | ||
1028 | var partnerData *domain.PartnerInfo | 1048 | var partnerData *domain.PartnerInfo |
1029 | partnerData, err = PartnerInfoRepository.FindOne(domain.PartnerFindOneQuery{UserId: cmd.PartnerId}) | 1049 | partnerData, err = PartnerInfoRepository.FindOne(domain.PartnerFindOneQuery{UserId: cmd.PartnerId}) |
1030 | if err != nil { | 1050 | if err != nil { |
1031 | - return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("检索合伙人数据失败")) | 1051 | + row := &domain.ImportInfo{ |
1052 | + Error: lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("检索合伙人数据失败")), | ||
1053 | + LineNumbers: cmd.LineNumbers, // 错误影响的行 | ||
1054 | + GoodLine: map[int]interface{}{}, | ||
1055 | + } | ||
1056 | + errorDataList = append(errorDataList, row) | ||
1057 | + continue | ||
1032 | } | 1058 | } |
1033 | 1059 | ||
1034 | // 批量校验订单 | 1060 | // 批量校验订单 |
1035 | if ok, err := orderBaseDao.CheckOrderExist(cmd.CompanyId, cmd.OrderCode, cmd.DeliveryCode, | 1061 | if ok, err := orderBaseDao.CheckOrderExist(cmd.CompanyId, cmd.OrderCode, cmd.DeliveryCode, |
1036 | cmd.PartnerCategory, cmd.PartnerId, 0); err != nil { | 1062 | cmd.PartnerCategory, cmd.PartnerId, 0); err != nil { |
1037 | - return nil, lib.ThrowError(lib.TRANSACTION_ERROR, err.Error()) | 1063 | + row := &domain.ImportInfo{ |
1064 | + Error: lib.ThrowError(lib.TRANSACTION_ERROR, err.Error()), | ||
1065 | + LineNumbers: cmd.LineNumbers, // 错误影响的行 | ||
1066 | + GoodLine: map[int]interface{}{}, | ||
1067 | + } | ||
1068 | + errorDataList = append(errorDataList, row) | ||
1069 | + continue | ||
1038 | } else if ok { | 1070 | } else if ok { |
1039 | - return nil, lib.ThrowError(lib.BUSINESS_ERROR, "订单已存在") | 1071 | + row := &domain.ImportInfo{ |
1072 | + Error: lib.ThrowError(lib.BUSINESS_ERROR, "订单已存在"), | ||
1073 | + LineNumbers: cmd.LineNumbers, // 错误影响的行 | ||
1074 | + GoodLine: map[int]interface{}{}, | ||
1075 | + } | ||
1076 | + errorDataList = append(errorDataList, row) | ||
1077 | + continue | ||
1040 | } | 1078 | } |
1041 | 1079 | ||
1042 | // 批量校验产品 | 1080 | // 批量校验产品 |
1043 | var goodMap = map[string]int{} | 1081 | var goodMap = map[string]int{} |
1082 | + goodErrMap := make(map[int]interface{}, 0) | ||
1044 | for i := range cmd.Goods { | 1083 | for i := range cmd.Goods { |
1045 | goodName := cmd.Goods[i].GoodName | 1084 | goodName := cmd.Goods[i].GoodName |
1046 | if _, ok := goodMap[goodName]; ok { | 1085 | if _, ok := goodMap[goodName]; ok { |
1047 | - return nil, lib.ThrowError(lib.BUSINESS_ERROR, "订单中货品重复已存在") | 1086 | + goodErrMap[cmd.Goods[i].LineNumber] = lib.ThrowError(lib.BUSINESS_ERROR, "订单中货品重复已存在") |
1087 | + continue | ||
1048 | } | 1088 | } |
1049 | goodMap[goodName] = 1 | 1089 | goodMap[goodName] = 1 |
1050 | } | 1090 | } |
1091 | + if len(goodErrMap) > 0 { | ||
1092 | + row := &domain.ImportInfo{ | ||
1093 | + Error: lib.ThrowError(lib.BUSINESS_ERROR, "订单中货品重复已存在"), | ||
1094 | + LineNumbers: cmd.LineNumbers, // 错误影响的行 | ||
1095 | + GoodLine: goodErrMap, // 错误产品行号记录 | ||
1096 | + } | ||
1097 | + errorDataList = append(errorDataList, row) | ||
1098 | + continue | ||
1099 | + } | ||
1051 | 1100 | ||
1052 | newOrder := &domain.OrderBase{ | 1101 | newOrder := &domain.OrderBase{ |
1053 | OrderType: cmd.OrderType, OrderCode: cmd.OrderCode, | 1102 | OrderType: cmd.OrderType, OrderCode: cmd.OrderCode, |
@@ -1083,12 +1132,19 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co | @@ -1083,12 +1132,19 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co | ||
1083 | } | 1132 | } |
1084 | } | 1133 | } |
1085 | if !cmdPartnerCategoryOk { | 1134 | if !cmdPartnerCategoryOk { |
1086 | - return nil, lib.ThrowError(lib.BUSINESS_ERROR, "合伙人类型选择错误") | 1135 | + row := &domain.ImportInfo{ |
1136 | + Error: lib.ThrowError(lib.BUSINESS_ERROR, "合伙人类型选择错误"), | ||
1137 | + LineNumbers: cmd.LineNumbers, // 错误影响的行 | ||
1138 | + GoodLine: map[int]interface{}{}, | ||
1139 | + } | ||
1140 | + errorDataList = append(errorDataList, row) | ||
1141 | + continue | ||
1087 | } | 1142 | } |
1088 | 1143 | ||
1089 | // 订单产品分红核算 | 1144 | // 订单产品分红核算 |
1090 | var orderGoods []domain.OrderGood | 1145 | var orderGoods []domain.OrderGood |
1091 | - for _, good := range cmd.Goods { | 1146 | + orderGoodErrMap := make(map[int]interface{}, 0) |
1147 | + for i, good := range cmd.Goods { | ||
1092 | m := domain.NewOrderGood() | 1148 | m := domain.NewOrderGood() |
1093 | m.OrderId = 0 | 1149 | m.OrderId = 0 |
1094 | m.GoodName = good.GoodName | 1150 | m.GoodName = good.GoodName |
@@ -1097,28 +1153,54 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co | @@ -1097,28 +1153,54 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co | ||
1097 | m.PartnerBonusPercent = good.PartnerBonusPercent | 1153 | m.PartnerBonusPercent = good.PartnerBonusPercent |
1098 | m.Remark = good.Remark | 1154 | m.Remark = good.Remark |
1099 | m.CompanyId = cmd.CompanyId | 1155 | m.CompanyId = cmd.CompanyId |
1156 | + | ||
1100 | err = m.Compute() | 1157 | err = m.Compute() |
1101 | if err != nil { | 1158 | if err != nil { |
1102 | - return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中商品的数值失败:%s", err)) | 1159 | + orderGoodErrMap[cmd.Goods[i].LineNumber] = lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中商品的数值失败:%s", err)) |
1160 | + continue | ||
1103 | } | 1161 | } |
1162 | + | ||
1104 | err = m.CurrentBonusStatus.WartPayPartnerBonus(&m) | 1163 | err = m.CurrentBonusStatus.WartPayPartnerBonus(&m) |
1105 | if err != nil { | 1164 | if err != nil { |
1106 | - return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中商品的分红数值失败:%s", err)) | 1165 | + orderGoodErrMap[cmd.Goods[i].LineNumber] = lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中商品的分红数值失败:%s", err)) |
1166 | + continue | ||
1107 | } | 1167 | } |
1168 | + | ||
1108 | orderGoods = append(orderGoods, m) | 1169 | orderGoods = append(orderGoods, m) |
1109 | } | 1170 | } |
1171 | + if len(orderGoodErrMap) > 0 { | ||
1172 | + row := &domain.ImportInfo{ | ||
1173 | + Error: lib.ThrowError(lib.BUSINESS_ERROR, "核算订单中商品错误"), | ||
1174 | + LineNumbers: cmd.LineNumbers, // 错误影响的行 | ||
1175 | + GoodLine: orderGoodErrMap, // 错误产品行号记录 | ||
1176 | + } | ||
1177 | + errorDataList = append(errorDataList, row) | ||
1178 | + continue | ||
1179 | + } | ||
1110 | 1180 | ||
1111 | newOrder.Goods = orderGoods | 1181 | newOrder.Goods = orderGoods |
1112 | 1182 | ||
1113 | err = newOrder.Compute() | 1183 | err = newOrder.Compute() |
1114 | if err != nil { | 1184 | if err != nil { |
1115 | - return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中合计的数值失败:%s", err)) | 1185 | + row := &domain.ImportInfo{ |
1186 | + Error: lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中合计的数值失败:%s", err)), | ||
1187 | + LineNumbers: cmd.LineNumbers, // 错误影响的行 | ||
1188 | + GoodLine: map[int]interface{}{}, | ||
1189 | + } | ||
1190 | + errorDataList = append(errorDataList, row) | ||
1191 | + continue | ||
1116 | } | 1192 | } |
1117 | 1193 | ||
1118 | // 保存订单数据 | 1194 | // 保存订单数据 |
1119 | err = orderBaseRepository.Save(newOrder) | 1195 | err = orderBaseRepository.Save(newOrder) |
1120 | if err != nil { | 1196 | if err != nil { |
1121 | - return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("保存订单数据失败:%s", err)) | 1197 | + row := &domain.ImportInfo{ |
1198 | + Error: lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("保存订单数据失败:%s", err)), | ||
1199 | + LineNumbers: cmd.LineNumbers, // 错误影响的行 | ||
1200 | + GoodLine: map[int]interface{}{}, | ||
1201 | + } | ||
1202 | + errorDataList = append(errorDataList, row) | ||
1203 | + continue | ||
1122 | } | 1204 | } |
1123 | 1205 | ||
1124 | for i := range newOrder.Goods { | 1206 | for i := range newOrder.Goods { |
@@ -1128,22 +1210,27 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co | @@ -1128,22 +1210,27 @@ func (service OrderInfoService) CreateNewOrderByImport(createOrderCommands []*co | ||
1128 | // 保存订单产品 | 1210 | // 保存订单产品 |
1129 | err = orderGoodRepository.Save(orderGoods) | 1211 | err = orderGoodRepository.Save(orderGoods) |
1130 | if err != nil { | 1212 | if err != nil { |
1131 | - return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("保存订单中的商品数据失败:%s", err)) | 1213 | + row := &domain.ImportInfo{ |
1214 | + Error: lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("保存订单中的商品数据失败:%s", err)), | ||
1215 | + LineNumbers: cmd.LineNumbers, // 错误影响的行 | ||
1216 | + GoodLine: map[int]interface{}{}, | ||
1217 | + } | ||
1218 | + errorDataList = append(errorDataList, row) | ||
1219 | + continue | ||
1132 | } | 1220 | } |
1133 | - | ||
1134 | newOrder.Goods = orderGoods | 1221 | newOrder.Goods = orderGoods |
1135 | } | 1222 | } |
1136 | 1223 | ||
1137 | - if len(failureDataList) == 0 { | 1224 | + if len(errorDataList) == 0 { |
1138 | // 完成事务 | 1225 | // 完成事务 |
1139 | err = transactionContext.CommitTransaction() | 1226 | err = transactionContext.CommitTransaction() |
1140 | if err != nil { | 1227 | if err != nil { |
1141 | return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error()) | 1228 | return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error()) |
1142 | } | 1229 | } |
1143 | - return failureDataList, nil | 1230 | + return errorDataList, nil |
1144 | } | 1231 | } |
1145 | 1232 | ||
1146 | - return failureDataList, nil | 1233 | + return errorDataList, nil |
1147 | } | 1234 | } |
1148 | 1235 | ||
1149 | /** | 1236 | /** |
@@ -6,6 +6,7 @@ const SERVICE_NAME = "partnermg" | @@ -6,6 +6,7 @@ const SERVICE_NAME = "partnermg" | ||
6 | 6 | ||
7 | var LOG_LEVEL = "debug" | 7 | var LOG_LEVEL = "debug" |
8 | var LOG_File = "./logs/partnermg.log" | 8 | var LOG_File = "./logs/partnermg.log" |
9 | +var IMPORT_EXCEL = "./download/订单数据模板.xlsx" | ||
9 | var Log_PREFIX = "[partnermg_dev]" | 10 | var Log_PREFIX = "[partnermg_dev]" |
10 | var ( | 11 | var ( |
11 | UCENTER_HOST = "https://suplus-ucenter-test.fjmaimaimai.com" //统一用户中心地址 | 12 | UCENTER_HOST = "https://suplus-ucenter-test.fjmaimaimai.com" //统一用户中心地址 |
@@ -14,7 +14,7 @@ var KafkaCfg KafkaConfig | @@ -14,7 +14,7 @@ var KafkaCfg KafkaConfig | ||
14 | 14 | ||
15 | func init() { | 15 | func init() { |
16 | KafkaCfg = KafkaConfig{ | 16 | KafkaCfg = KafkaConfig{ |
17 | - Servers: []string{"106.52.15.41:9092"}, | 17 | + Servers: []string{"127.0.0.1:9092"}, |
18 | ConsumerId: "partnermg_dev", | 18 | ConsumerId: "partnermg_dev", |
19 | } | 19 | } |
20 | if os.Getenv("KAFKA_HOST") != "" { | 20 | if os.Getenv("KAFKA_HOST") != "" { |
@@ -314,6 +314,17 @@ type OrderBaseFindQuery struct { | @@ -314,6 +314,17 @@ type OrderBaseFindQuery struct { | ||
314 | CompanyId int64 | 314 | CompanyId int64 |
315 | } | 315 | } |
316 | 316 | ||
317 | +// 导入错误信息 | ||
318 | +type ImportInfo struct { | ||
319 | + Error error | ||
320 | + LineNumbers []int | ||
321 | + GoodLine map[int]interface{} | ||
322 | +} | ||
323 | + | ||
324 | +// 导入产品错误信息 | ||
325 | +type GoodErrInfo struct { | ||
326 | +} | ||
327 | + | ||
317 | type OrderBaseRepository interface { | 328 | type OrderBaseRepository interface { |
318 | Save(order *OrderBase) error | 329 | Save(order *OrderBase) error |
319 | FindOne(qureyOptions OrderBaseFindOneQuery) (*OrderBase, error) | 330 | FindOne(qureyOptions OrderBaseFindOneQuery) (*OrderBase, error) |
@@ -187,7 +187,7 @@ func (dao OrderBaseDao) OrderBonusListForExcel(companyId int64, orderType int, p | @@ -187,7 +187,7 @@ func (dao OrderBaseDao) OrderBonusListForExcel(companyId int64, orderType int, p | ||
187 | //@param partnerCategory 合伙人类型id | 187 | //@param partnerCategory 合伙人类型id |
188 | //@param updateTime 订单更新时间范围"[开始时间,结束时间]",时间格式"2006-01-02 15:04:05+07" | 188 | //@param updateTime 订单更新时间范围"[开始时间,结束时间]",时间格式"2006-01-02 15:04:05+07" |
189 | //@param createTime 订单的创建时间范围"[开始时间,结束时间]" 时间格式"2006-01-02 15:04:05+07" | 189 | //@param createTime 订单的创建时间范围"[开始时间,结束时间]" 时间格式"2006-01-02 15:04:05+07" |
190 | -func (dao OrderBaseDao) OrderListByCondition(companyId int64, orderType int, partnerOrCode string, | 190 | +func (dao OrderBaseDao) OrderListByCondition(companyId int64, orderType int, partnerName string, orderCode string, deliveryCode string, |
191 | updateTime [2]string, createTime [2]string, partnerCategory int, limit, offset int) ([]models.OrderBase, int, error) { | 191 | updateTime [2]string, createTime [2]string, partnerCategory int, limit, offset int) ([]models.OrderBase, int, error) { |
192 | tx := dao.transactionContext.GetDB() | 192 | tx := dao.transactionContext.GetDB() |
193 | var orders []models.OrderBase | 193 | var orders []models.OrderBase |
@@ -212,16 +212,25 @@ func (dao OrderBaseDao) OrderListByCondition(companyId int64, orderType int, par | @@ -212,16 +212,25 @@ func (dao OrderBaseDao) OrderListByCondition(companyId int64, orderType int, par | ||
212 | if len(createTime[1]) > 0 { | 212 | if len(createTime[1]) > 0 { |
213 | query = query.Where(`order_base.create_time<=?`, createTime[1]) | 213 | query = query.Where(`order_base.create_time<=?`, createTime[1]) |
214 | } | 214 | } |
215 | - | ||
216 | - if len(partnerOrCode) > 0 { | 215 | + if len(partnerName) > 0 { |
217 | query = query.Join("LEFT JOIN partner_info as p ON order_base.partner_id=p.id"). | 216 | query = query.Join("LEFT JOIN partner_info as p ON order_base.partner_id=p.id"). |
218 | - WhereGroup(func(q *orm.Query) (*orm.Query, error) { | ||
219 | - q = q.WhereOr("order_base.order_code like ? ", "%"+partnerOrCode+"%"). | ||
220 | - WhereOr("order_base.delivery_code like ? ", "%"+partnerOrCode+"%"). | ||
221 | - WhereOr("p.partner_name like ? ", "%"+partnerOrCode+"%") | ||
222 | - return q, nil | ||
223 | - }) | 217 | + Where("p.partner_name like ? ", "%"+partnerName+"%") |
218 | + } | ||
219 | + if len(orderCode) > 0 { | ||
220 | + query = query.Where("order_base.order_code like ? ", "%"+orderCode+"%") | ||
224 | } | 221 | } |
222 | + if len(deliveryCode) > 0 { | ||
223 | + query = query.Where("order_base.delivery_code like ? ", "%"+deliveryCode+"%") | ||
224 | + } | ||
225 | + //if len(partnerOrCode) > 0 { | ||
226 | + // query = query.Join("LEFT JOIN partner_info as p ON order_base.partner_id=p.id"). | ||
227 | + // WhereGroup(func(q *orm.Query) (*orm.Query, error) { | ||
228 | + // q = q.WhereOr("order_base.order_code like ? ", "%"+partnerOrCode+"%"). | ||
229 | + // WhereOr("order_base.delivery_code like ? ", "%"+partnerOrCode+"%"). | ||
230 | + // WhereOr("p.partner_name like ? ", "%"+partnerOrCode+"%") | ||
231 | + // return q, nil | ||
232 | + // }) | ||
233 | + //} | ||
225 | query = query.Order("order_base.create_time DESC"). | 234 | query = query.Order("order_base.create_time DESC"). |
226 | Offset(offset). | 235 | Offset(offset). |
227 | Limit(limit) | 236 | Limit(limit) |
@@ -254,7 +263,7 @@ type CustomOrderListForExcel struct { | @@ -254,7 +263,7 @@ type CustomOrderListForExcel struct { | ||
254 | //@param partnerCategory 合伙人类型id | 263 | //@param partnerCategory 合伙人类型id |
255 | //@param updateTime 订单更新时间范围"[开始时间,结束时间]",时间格式"2006-01-02 15:04:05+07" | 264 | //@param updateTime 订单更新时间范围"[开始时间,结束时间]",时间格式"2006-01-02 15:04:05+07" |
256 | //@param createTime 订单的创建时间范围"[开始时间,结束时间]" 时间格式"2006-01-02 15:04:05+07" | 265 | //@param createTime 订单的创建时间范围"[开始时间,结束时间]" 时间格式"2006-01-02 15:04:05+07" |
257 | -func (dao OrderBaseDao) OrderListForExcel(companyId int64, partnerOrCode string, | 266 | +func (dao OrderBaseDao) OrderListForExcel(companyId int64, partnerName string, orderCode string, deliveryCode string, |
258 | updateTime [2]string, createTime [2]string, partnerCategory int) ( | 267 | updateTime [2]string, createTime [2]string, partnerCategory int) ( |
259 | result []CustomOrderListForExcel, err error) { | 268 | result []CustomOrderListForExcel, err error) { |
260 | sqlstr := ` | 269 | sqlstr := ` |
@@ -270,12 +279,26 @@ func (dao OrderBaseDao) OrderListForExcel(companyId int64, partnerOrCode string, | @@ -270,12 +279,26 @@ func (dao OrderBaseDao) OrderListForExcel(companyId int64, partnerOrCode string, | ||
270 | WHERE 1=1 AND t1.order_type = 1 AND t1.company_id=? | 279 | WHERE 1=1 AND t1.order_type = 1 AND t1.company_id=? |
271 | ` | 280 | ` |
272 | params := []interface{}{companyId} | 281 | params := []interface{}{companyId} |
273 | - if len(partnerOrCode) > 0 { | ||
274 | - like := "%" + partnerOrCode + "%" | ||
275 | - params = append(params, like, like, like) | ||
276 | - sqlstr += " AND (t1.order_code like ? OR t1.delivery_code like ? OR t2.partner_name like ? ) " | 282 | + //if len(partnerOrCode) > 0 { |
283 | + // like := "%" + partnerOrCode + "%" | ||
284 | + // params = append(params, like, like, like) | ||
285 | + // sqlstr += " AND (t1.order_code like ? OR t1.delivery_code like ? OR t2.partner_name like ? ) " | ||
286 | + //} | ||
287 | + if len(partnerName) > 0 { | ||
288 | + like := "%" + partnerName + "%" | ||
289 | + params = append(params, like) | ||
290 | + sqlstr += ` AND t2.partner_name like ? ` | ||
291 | + } | ||
292 | + if len(orderCode) > 0 { | ||
293 | + like := "%" + orderCode + "%" | ||
294 | + params = append(params, like) | ||
295 | + sqlstr += ` AND t1.order_code like ? ` | ||
296 | + } | ||
297 | + if len(deliveryCode) > 0 { | ||
298 | + like := "%" + deliveryCode + "%" | ||
299 | + params = append(params, like) | ||
300 | + sqlstr += ` AND t1.delivery_code like ? ` | ||
277 | } | 301 | } |
278 | - | ||
279 | if partnerCategory > 0 { | 302 | if partnerCategory > 0 { |
280 | params = append(params, partnerCategory) | 303 | params = append(params, partnerCategory) |
281 | sqlstr += ` AND t1.partner_category@>'{"id":?}' ` | 304 | sqlstr += ` AND t1.partner_category@>'{"id":?}' ` |
@@ -3,9 +3,10 @@ package controllers | @@ -3,9 +3,10 @@ package controllers | ||
3 | import ( | 3 | import ( |
4 | "crypto/md5" | 4 | "crypto/md5" |
5 | "encoding/hex" | 5 | "encoding/hex" |
6 | - "encoding/json" | ||
7 | "errors" | 6 | "errors" |
8 | "fmt" | 7 | "fmt" |
8 | + "github.com/beego/beego/v2/client/httplib" | ||
9 | + "os" | ||
9 | "path" | 10 | "path" |
10 | "regexp" | 11 | "regexp" |
11 | "strconv" | 12 | "strconv" |
@@ -70,6 +71,7 @@ func (postData *postPurposeOrderDetail) Valid() error { | @@ -70,6 +71,7 @@ func (postData *postPurposeOrderDetail) Valid() error { | ||
70 | } | 71 | } |
71 | if postData.PartnerId == 0 { | 72 | if postData.PartnerId == 0 { |
72 | return lib.ThrowError(lib.ARG_ERROR, "合伙人信息必填") | 73 | return lib.ThrowError(lib.ARG_ERROR, "合伙人信息必填") |
74 | + | ||
73 | } | 75 | } |
74 | if len(postData.OrderDist) == 0 { | 76 | if len(postData.OrderDist) == 0 { |
75 | return lib.ThrowError(lib.ARG_ERROR, "订单区域必填") | 77 | return lib.ThrowError(lib.ARG_ERROR, "订单区域必填") |
@@ -149,10 +151,19 @@ func (postData *postOrderPurposeDelivery) Valid() error { | @@ -149,10 +151,19 @@ func (postData *postOrderPurposeDelivery) Valid() error { | ||
149 | return nil | 151 | return nil |
150 | } | 152 | } |
151 | 153 | ||
152 | -//PageListOrderReal 获取实发订单列表 | 154 | +/** |
155 | + * @Author SteveChan | ||
156 | + * @Description // 获取实发订单列表,修改搜索条件 | ||
157 | + * @Date 20:23 2021/1/10 | ||
158 | + * @Param | ||
159 | + * @return | ||
160 | + **/ | ||
153 | func (c *OrderInfoController) PageListOrderReal() { | 161 | func (c *OrderInfoController) PageListOrderReal() { |
154 | type Parameter struct { | 162 | type Parameter struct { |
155 | - SearchText string `json:"searchText"` | 163 | + //SearchText string `json:"searchText"` |
164 | + PartnerName string `json:"partnerName"` // 合伙人姓名 | ||
165 | + OrderCode string `json:"orderCode"` // 订单号 | ||
166 | + DeliveryCode string `json:"deliveryCode"` // 发货单号 | ||
156 | PartnerCategory int `json:"PartnerCategory"` | 167 | PartnerCategory int `json:"PartnerCategory"` |
157 | PageSize int `json:"pageSize"` | 168 | PageSize int `json:"pageSize"` |
158 | PageNumber int `json:"pageNumber"` | 169 | PageNumber int `json:"pageNumber"` |
@@ -230,7 +241,10 @@ func (c *OrderInfoController) PageListOrderReal() { | @@ -230,7 +241,10 @@ func (c *OrderInfoController) PageListOrderReal() { | ||
230 | companyId := c.GetUserCompany() | 241 | companyId := c.GetUserCompany() |
231 | orderSrv := orderService.NewOrderInfoService(nil) | 242 | orderSrv := orderService.NewOrderInfoService(nil) |
232 | orderinfos, cnt, err := orderSrv.PageListOrderBase(orderQuery.ListOrderBaseQuery{ | 243 | orderinfos, cnt, err := orderSrv.PageListOrderBase(orderQuery.ListOrderBaseQuery{ |
233 | - PartnerOrCode: param.SearchText, | 244 | + //PartnerOrCode: param.SearchText, |
245 | + PartnerName: param.PartnerName, | ||
246 | + OrderCode: param.OrderCode, | ||
247 | + DeliveryCode: param.DeliveryCode, | ||
234 | OrderType: domain.OrderReal, | 248 | OrderType: domain.OrderReal, |
235 | Limit: param.PageSize, | 249 | Limit: param.PageSize, |
236 | Offset: (param.PageNumber - 1) * param.PageSize, | 250 | Offset: (param.PageNumber - 1) * param.PageSize, |
@@ -514,7 +528,10 @@ func (c *OrderInfoController) RemoveOrderReal() { | @@ -514,7 +528,10 @@ func (c *OrderInfoController) RemoveOrderReal() { | ||
514 | //ListOrderForExcel excel 导出实际订单的列表 | 528 | //ListOrderForExcel excel 导出实际订单的列表 |
515 | func (c *OrderInfoController) ListOrderForExcel() { | 529 | func (c *OrderInfoController) ListOrderForExcel() { |
516 | type Parameter struct { | 530 | type Parameter struct { |
517 | - SearchText string `json:"searchText"` | 531 | + //SearchText string `json:"searchText"` |
532 | + PartnerName string `json:"partnerName"` // 合伙人姓名 | ||
533 | + OrderCode string `json:"orderCode"` // 订单号 | ||
534 | + DeliveryCode string `json:"deliveryCode"` // 发货单号 | ||
518 | PartnerCategory int `json:"PartnerCategory"` | 535 | PartnerCategory int `json:"PartnerCategory"` |
519 | UpdateTime []string `json:"updateTime"` | 536 | UpdateTime []string `json:"updateTime"` |
520 | CreateTime []string `json:"createTime"` | 537 | CreateTime []string `json:"createTime"` |
@@ -584,7 +601,10 @@ func (c *OrderInfoController) ListOrderForExcel() { | @@ -584,7 +601,10 @@ func (c *OrderInfoController) ListOrderForExcel() { | ||
584 | companyId := c.GetUserCompany() | 601 | companyId := c.GetUserCompany() |
585 | orderSrv := orderService.NewOrderInfoService(nil) | 602 | orderSrv := orderService.NewOrderInfoService(nil) |
586 | orderinfos, columns, err := orderSrv.ListOrderForExcel(orderQuery.ListOrderBaseQuery{ | 603 | orderinfos, columns, err := orderSrv.ListOrderForExcel(orderQuery.ListOrderBaseQuery{ |
587 | - PartnerOrCode: param.SearchText, | 604 | + //PartnerOrCode: param.SearchText, |
605 | + PartnerName: param.PartnerName, | ||
606 | + OrderCode: param.OrderCode, | ||
607 | + DeliveryCode: param.DeliveryCode, | ||
588 | OrderType: domain.OrderReal, | 608 | OrderType: domain.OrderReal, |
589 | CompanyId: companyId, | 609 | CompanyId: companyId, |
590 | PartnerCategory: param.PartnerCategory, | 610 | PartnerCategory: param.PartnerCategory, |
@@ -614,6 +634,62 @@ func (c *OrderInfoController) ListOrderForExcel() { | @@ -614,6 +634,62 @@ func (c *OrderInfoController) ListOrderForExcel() { | ||
614 | 634 | ||
615 | /** | 635 | /** |
616 | * @Author SteveChan | 636 | * @Author SteveChan |
637 | + * @Description // 下载导入模板 | ||
638 | + * @Date 16:48 2021/1/8 | ||
639 | + * @Param | ||
640 | + * @return | ||
641 | + **/ | ||
642 | +func (c *OrderInfoController) DownloadTemplate() { | ||
643 | + type Parameter struct { | ||
644 | + TYPE string `json:"type"` | ||
645 | + } | ||
646 | + | ||
647 | + var ( | ||
648 | + param Parameter | ||
649 | + err error | ||
650 | + ) | ||
651 | + | ||
652 | + if err = c.BindJsonData(¶m); err != nil { | ||
653 | + logs.Error(err) | ||
654 | + c.ResponseError(errors.New("json数据解析失败")) | ||
655 | + return | ||
656 | + } | ||
657 | + | ||
658 | + // 校验类型编码 | ||
659 | + if param.TYPE != "PARTNER_ORDER_FILE" { | ||
660 | + c.ResponseError(errors.New("类型编码错误")) | ||
661 | + } | ||
662 | + | ||
663 | + // 创建下载文件夹 | ||
664 | + mkErr := os.Mkdir("download", os.ModePerm) | ||
665 | + if mkErr != nil { | ||
666 | + fmt.Println(mkErr) | ||
667 | + } | ||
668 | + | ||
669 | + // 获取导入模板 | ||
670 | + req := httplib.Get("http://suplus-file-dev.fjmaimaimai.com/upload/file/2021010803305336443.xlsx") | ||
671 | + err = req.ToFile(constant.IMPORT_EXCEL) | ||
672 | + if err != nil { | ||
673 | + logs.Error("could not save to file: ", err) | ||
674 | + } | ||
675 | + | ||
676 | + // 返回字段定义 | ||
677 | + ret := map[string]interface{}{} | ||
678 | + | ||
679 | + resp, err := req.Response() | ||
680 | + if err != nil { | ||
681 | + logs.Error("could not get response: ", err) | ||
682 | + } else { | ||
683 | + logs.Info(resp) | ||
684 | + ret = map[string]interface{}{ | ||
685 | + "url": "http://" + c.Ctx.Request.Host + "/download/订单数据模板.xlsx", | ||
686 | + } | ||
687 | + c.ResponseData(ret) | ||
688 | + } | ||
689 | +} | ||
690 | + | ||
691 | +/** | ||
692 | + * @Author SteveChan | ||
617 | * @Description //TODO 导入excel订单 | 693 | * @Description //TODO 导入excel订单 |
618 | * @Date 10:52 2021/1/6 | 694 | * @Date 10:52 2021/1/6 |
619 | * @Param | 695 | * @Param |
@@ -621,17 +697,20 @@ func (c *OrderInfoController) ListOrderForExcel() { | @@ -621,17 +697,20 @@ func (c *OrderInfoController) ListOrderForExcel() { | ||
621 | **/ | 697 | **/ |
622 | func (c *OrderInfoController) ImportOrderFromExcel() { | 698 | func (c *OrderInfoController) ImportOrderFromExcel() { |
623 | // 获取参数 | 699 | // 获取参数 |
624 | - where := c.GetString("where") | 700 | + typeCode := c.GetString("type") |
625 | file, h, _ := c.GetFile("file") | 701 | file, h, _ := c.GetFile("file") |
626 | - | ||
627 | companyId := c.GetUserCompany() | 702 | companyId := c.GetUserCompany() |
628 | 703 | ||
629 | // Json数据解析 | 704 | // Json数据解析 |
630 | - jsonMap := make(map[string]interface{}) | ||
631 | - err := json.Unmarshal([]byte(where), &jsonMap) | ||
632 | - if err != nil { | ||
633 | - logs.Error(err) | ||
634 | - c.ResponseError(errors.New("json数据解析失败")) | 705 | + //jsonMap := make(map[string]interface{}) |
706 | + //err := json.Unmarshal([]byte(where), &jsonMap) | ||
707 | + //if err != nil { | ||
708 | + // logs.Error(err) | ||
709 | + // c.ResponseError(errors.New("json数据解析失败")) | ||
710 | + //} | ||
711 | + | ||
712 | + if typeCode != "PARTNER_ORDER_IMPORT" { | ||
713 | + c.ResponseError(errors.New("类型编码错误")) | ||
635 | } | 714 | } |
636 | 715 | ||
637 | // 返回字段定义 | 716 | // 返回字段定义 |
@@ -664,6 +743,9 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | @@ -664,6 +743,9 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | ||
664 | return | 743 | return |
665 | } | 744 | } |
666 | 745 | ||
746 | + // 数据行计数 | ||
747 | + rowCnt := 0 | ||
748 | + | ||
667 | // 空文件校验 | 749 | // 空文件校验 |
668 | if len(rows) < 3 { | 750 | if len(rows) < 3 { |
669 | c.ResponseError(errors.New("导入的excel文件为空文件,请上传正确的文件")) | 751 | c.ResponseError(errors.New("导入的excel文件为空文件,请上传正确的文件")) |
@@ -674,11 +756,12 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | @@ -674,11 +756,12 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | ||
674 | nullFlag := false | 756 | nullFlag := false |
675 | for i, row := range rows { | 757 | for i, row := range rows { |
676 | if i > 2 && row != nil { | 758 | if i > 2 && row != nil { |
759 | + rowCnt++ | ||
677 | if len(row) == constant.EXCEL_COLUMN { // 中间空字符校验 | 760 | if len(row) == constant.EXCEL_COLUMN { // 中间空字符校验 |
678 | var tmpRow = row | 761 | var tmpRow = row |
679 | var myRow []string | 762 | var myRow []string |
680 | for j, cell := range row { | 763 | for j, cell := range row { |
681 | - if j != 8 { // 业务员抽成比例不校验 | 764 | + if j != 8 { // 业务员抽成比例非必填 |
682 | if cell == "" || cell == " " { // 空字符串填充 | 765 | if cell == "" || cell == " " { // 空字符串填充 |
683 | tmpRow[j] = "null" | 766 | tmpRow[j] = "null" |
684 | nullFlag = true | 767 | nullFlag = true |
@@ -728,11 +811,13 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | @@ -728,11 +811,13 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | ||
728 | var myRow []string | 811 | var myRow []string |
729 | for j, cell := range row { | 812 | for j, cell := range row { |
730 | switch j { | 813 | switch j { |
731 | - case 0, 1, 2, 3, 4, 5, 8: // 订单号、发货单号、客户名称、订单区域、编号、合伙人、产品名称、 | 814 | + case 0, 1, 2, 3, 4, 5, 8: // 订单号、发货单号、客户名称、订单区域、编号、合伙人、产品名称长度校验 |
732 | { | 815 | { |
733 | - if len(cell) > 50 { | 816 | + cellStr := strings.TrimSpace(cell) |
817 | + lenCellStr := utf8.RuneCountInString(cellStr) | ||
818 | + if lenCellStr > 50 { | ||
734 | var tmpRow []string | 819 | var tmpRow []string |
735 | - tmpRow = append(tmpRow, tableHeader[j+2]+"长度超过50,请重新输入") // 错误信息 | 820 | + tmpRow = append(tmpRow, tableHeader[j+2]+"长度超过50位,请重新输入") // 错误信息 |
736 | s := strconv.Itoa(i + 1) | 821 | s := strconv.Itoa(i + 1) |
737 | tmpRow = append(tmpRow, s) // 行号 | 822 | tmpRow = append(tmpRow, s) // 行号 |
738 | tmpRow = append(tmpRow, row...) // 错误行数据 | 823 | tmpRow = append(tmpRow, row...) // 错误行数据 |
@@ -752,50 +837,76 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | @@ -752,50 +837,76 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | ||
752 | } | 837 | } |
753 | case 7: // 业务员抽成比例,非必填,精确到小数点后两位 | 838 | case 7: // 业务员抽成比例,非必填,精确到小数点后两位 |
754 | { | 839 | { |
840 | + var ( | ||
841 | + typeErrFlag bool | ||
842 | + lenErrFlag bool | ||
843 | + ratioErrFlag bool | ||
844 | + ) | ||
755 | if len(cell) > 0 { | 845 | if len(cell) > 0 { |
756 | - _, err := strconv.ParseFloat(cell, 64) | 846 | + // 参数类型转换 |
847 | + shareRatio, err := strconv.ParseFloat(cell, 64) | ||
757 | if err != nil { | 848 | if err != nil { |
849 | + typeErrFlag = true | ||
850 | + } | ||
851 | + | ||
852 | + // 比例不能超过100% | ||
853 | + if shareRatio > 100 { | ||
854 | + ratioErrFlag = true | ||
855 | + } | ||
856 | + | ||
857 | + // 长度校验 | ||
858 | + regexpStr := `^(100|[1-9]\d|\d)(.\d{1,2})?$` | ||
859 | + ok := regexp.MustCompile(regexpStr).MatchString(cell) | ||
860 | + if !ok { | ||
861 | + lenErrFlag = true | ||
862 | + } | ||
863 | + | ||
864 | + if typeErrFlag || lenErrFlag || ratioErrFlag { | ||
758 | var tmpRow []string | 865 | var tmpRow []string |
759 | tmpRow = append(tmpRow, "业务员抽成比例格式错误,请输入正确的业务员抽成比例比例,保留两位小数") // 错误信息 | 866 | tmpRow = append(tmpRow, "业务员抽成比例格式错误,请输入正确的业务员抽成比例比例,保留两位小数") // 错误信息 |
760 | s := strconv.Itoa(i + 1) | 867 | s := strconv.Itoa(i + 1) |
761 | tmpRow = append(tmpRow, s) // 行号 | 868 | tmpRow = append(tmpRow, s) // 行号 |
762 | tmpRow = append(tmpRow, row...) // 错误行数据 | 869 | tmpRow = append(tmpRow, row...) // 错误行数据 |
763 | myRow = tmpRow | 870 | myRow = tmpRow |
871 | + typeErrFlag = false | ||
872 | + lenErrFlag = false | ||
873 | + ratioErrFlag = false | ||
764 | } | 874 | } |
765 | - //if isOk, _ := regexp.MatchString("^(.[0-9]{0,1,2})?$", cell); !isOk { // 最多两位小数 | ||
766 | - // var tmpRow []string | ||
767 | - // tmpRow = append(tmpRow, "业务员员抽成比例格式错误,请输入正确的单价,保留两位小数") // 错误信息 | ||
768 | - // s := strconv.Itoa(i + 1) | ||
769 | - // tmpRow = append(tmpRow, s) // 行号 | ||
770 | - // tmpRow = append(tmpRow, row...) // 错误行数据 | ||
771 | - // myRow = tmpRow | ||
772 | - //} | ||
773 | } | 875 | } |
774 | } | 876 | } |
775 | case 9: // 数量不超过16位正整数 | 877 | case 9: // 数量不超过16位正整数 |
776 | { | 878 | { |
777 | - _, err := strconv.ParseInt(cell, 10, 64) | 879 | + var ( |
880 | + typeErrFlag bool | ||
881 | + lenErrFlag bool | ||
882 | + ) | ||
883 | + | ||
884 | + //参数类型转换 | ||
885 | + orderNum, err := strconv.ParseInt(cell, 10, 64) | ||
778 | if err != nil { | 886 | if err != nil { |
779 | - var tmpRow []string | ||
780 | - tmpRow = append(tmpRow, "数量须为整数,请重新填写") // 错误信息 | ||
781 | - s := strconv.Itoa(i + 1) | ||
782 | - tmpRow = append(tmpRow, s) // 行号 | ||
783 | - tmpRow = append(tmpRow, row...) // 错误行数据 | ||
784 | - myRow = tmpRow | 887 | + typeErrFlag = true |
888 | + } | ||
889 | + | ||
890 | + // 长度校验 | ||
891 | + if orderNum > 1e16 { | ||
892 | + lenErrFlag = true | ||
785 | } | 893 | } |
786 | 894 | ||
787 | - if len(cell) > 16 { | 895 | + if typeErrFlag || lenErrFlag { |
788 | var tmpRow []string | 896 | var tmpRow []string |
789 | tmpRow = append(tmpRow, "数量长度超过最大限制十六位整数,请重新填写") // 错误信息 | 897 | tmpRow = append(tmpRow, "数量长度超过最大限制十六位整数,请重新填写") // 错误信息 |
790 | s := strconv.Itoa(i + 1) | 898 | s := strconv.Itoa(i + 1) |
791 | tmpRow = append(tmpRow, s) // 行号 | 899 | tmpRow = append(tmpRow, s) // 行号 |
792 | tmpRow = append(tmpRow, row...) // 错误行数据 | 900 | tmpRow = append(tmpRow, row...) // 错误行数据 |
793 | myRow = tmpRow | 901 | myRow = tmpRow |
902 | + typeErrFlag = false | ||
903 | + lenErrFlag = false | ||
794 | } | 904 | } |
795 | } | 905 | } |
796 | case 10: // 单价,精确到小数点后两位,小数点左侧最多可输入16位数字 | 906 | case 10: // 单价,精确到小数点后两位,小数点左侧最多可输入16位数字 |
797 | { | 907 | { |
798 | - _, err := strconv.ParseFloat(cell, 64) | 908 | + // 参数类型转换 |
909 | + univalent, err := strconv.ParseFloat(cell, 64) | ||
799 | if err != nil { | 910 | if err != nil { |
800 | var tmpRow []string | 911 | var tmpRow []string |
801 | tmpRow = append(tmpRow, "单价格式错误,请输入正确的单价,保留两位小数点,小数点前面不能超过十六位数字") // 错误信息 | 912 | tmpRow = append(tmpRow, "单价格式错误,请输入正确的单价,保留两位小数点,小数点前面不能超过十六位数字") // 错误信息 |
@@ -804,34 +915,53 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | @@ -804,34 +915,53 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | ||
804 | tmpRow = append(tmpRow, row...) // 错误行数据 | 915 | tmpRow = append(tmpRow, row...) // 错误行数据 |
805 | myRow = tmpRow | 916 | myRow = tmpRow |
806 | } | 917 | } |
807 | - //if isOk, _ := regexp.MatchString("^d{1,16}(.[0-9]{0,1,2})?$", cell); !isOk { // 非负的16位整数最多两位小数 | ||
808 | - // var tmpRow []string | ||
809 | - // tmpRow = append(tmpRow, "单价格式错误,请输入正确的单价,保留两位小数点,小数点前面不能超过十六位数字") // 错误信息 | ||
810 | - // s := strconv.Itoa(i + 1) | ||
811 | - // tmpRow = append(tmpRow, s) // 行号 | ||
812 | - // tmpRow = append(tmpRow, row...) // 错误行数据 | ||
813 | - // myRow = tmpRow | ||
814 | - //} | 918 | + // 长度校验 |
919 | + if univalent >= 1e16 { | ||
920 | + var tmpRow []string | ||
921 | + tmpRow = append(tmpRow, "单价格式错误,请输入正确的单价,保留两位小数点,小数点前面不能超过十六位数字") // 错误信息 | ||
922 | + s := strconv.Itoa(i + 1) | ||
923 | + tmpRow = append(tmpRow, s) // 行号 | ||
924 | + tmpRow = append(tmpRow, row...) // 错误行数据 | ||
925 | + myRow = tmpRow | ||
926 | + } | ||
815 | } | 927 | } |
816 | case 11: // 合伙人分红比例,精确到小数点后两位 | 928 | case 11: // 合伙人分红比例,精确到小数点后两位 |
817 | { | 929 | { |
818 | - _, err := strconv.ParseFloat(cell, 64) | 930 | + var ( |
931 | + typeErrFlag bool | ||
932 | + lenErrFlag bool | ||
933 | + ratioErrFlag bool | ||
934 | + ) | ||
935 | + | ||
936 | + //参数类型转换 | ||
937 | + partnerRatio, err := strconv.ParseFloat(cell, 64) | ||
819 | if err != nil { | 938 | if err != nil { |
939 | + typeErrFlag = true | ||
940 | + } | ||
941 | + | ||
942 | + // 合伙人分红比例超额 | ||
943 | + if partnerRatio > 100 { | ||
944 | + ratioErrFlag = true | ||
945 | + } | ||
946 | + | ||
947 | + // 长度判断 | ||
948 | + regexpStr := `^(100|[1-9]\d|\d)(.\d{1,2})?$` | ||
949 | + ok := regexp.MustCompile(regexpStr).MatchString(cell) | ||
950 | + if !ok { | ||
951 | + lenErrFlag = true | ||
952 | + } | ||
953 | + | ||
954 | + if typeErrFlag || lenErrFlag || ratioErrFlag { | ||
820 | var tmpRow []string | 955 | var tmpRow []string |
821 | tmpRow = append(tmpRow, "合伙人分红比例格式错误,请输入正确的合伙人分红比例,保留两位小数") // 错误信息 | 956 | tmpRow = append(tmpRow, "合伙人分红比例格式错误,请输入正确的合伙人分红比例,保留两位小数") // 错误信息 |
822 | s := strconv.Itoa(i + 1) | 957 | s := strconv.Itoa(i + 1) |
823 | tmpRow = append(tmpRow, s) // 行号 | 958 | tmpRow = append(tmpRow, s) // 行号 |
824 | tmpRow = append(tmpRow, row...) // 错误行数据 | 959 | tmpRow = append(tmpRow, row...) // 错误行数据 |
825 | myRow = tmpRow | 960 | myRow = tmpRow |
961 | + typeErrFlag = false | ||
962 | + lenErrFlag = false | ||
963 | + ratioErrFlag = false | ||
826 | } | 964 | } |
827 | - //if isOk, _ := regexp.MatchString("^(.[0-9]{1,2})?$", cell); !isOk { // 最多两位小数 | ||
828 | - // var tmpRow []string | ||
829 | - // tmpRow = append(tmpRow, "合伙人分红比例错误,请输入正确的合伙人分红比例,保留两位小数") // 错误信息 | ||
830 | - // s := strconv.Itoa(i + 1) | ||
831 | - // tmpRow = append(tmpRow, s) // 行号 | ||
832 | - // tmpRow = append(tmpRow, row...) // 错误行数据 | ||
833 | - // myRow = tmpRow | ||
834 | - //} | ||
835 | } | 965 | } |
836 | } | 966 | } |
837 | } | 967 | } |
@@ -888,11 +1018,12 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | @@ -888,11 +1018,12 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | ||
888 | PlanGoodNumber: int(amount), | 1018 | PlanGoodNumber: int(amount), |
889 | Price: price, | 1019 | Price: price, |
890 | PartnerBonusPercent: percent, | 1020 | PartnerBonusPercent: percent, |
1021 | + LineNumber: i, | ||
891 | }, | 1022 | }, |
892 | }, | 1023 | }, |
893 | CompanyId: companyId, | 1024 | CompanyId: companyId, |
894 | PartnerCategory: 1, | 1025 | PartnerCategory: 1, |
895 | - LineNumbers: []int{i}, | 1026 | + LineNumbers: []int{i}, // 记录行号 |
896 | } | 1027 | } |
897 | 1028 | ||
898 | // 获取partnerId | 1029 | // 获取partnerId |
@@ -940,10 +1071,36 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | @@ -940,10 +1071,36 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | ||
940 | } | 1071 | } |
941 | } | 1072 | } |
942 | 1073 | ||
1074 | + // 产品数量校验 | ||
1075 | + productNumberError := make([]interface{}, 0) | ||
1076 | + | ||
943 | // 批量创建订单命令集 | 1077 | // 批量创建订单命令集 |
944 | var createOrderCommands []*orderCmd.CreateOrderCommand | 1078 | var createOrderCommands []*orderCmd.CreateOrderCommand |
945 | for _, orderCommand := range orderCommands { | 1079 | for _, orderCommand := range orderCommands { |
946 | - createOrderCommands = append(createOrderCommands, orderCommand) | 1080 | + if len(orderCommand.Goods) > 50 { // 产品数量校验 |
1081 | + for _, line := range orderCommand.LineNumbers { | ||
1082 | + var tmpRow []string | ||
1083 | + tmpRow = append(tmpRow, "单笔订单产品超过50种") // 错误信息 | ||
1084 | + s := strconv.Itoa(line + 1) | ||
1085 | + tmpRow = append(tmpRow, s) // 行号 | ||
1086 | + tmpRow = append(tmpRow, rows[line]...) // 错误行数据 | ||
1087 | + productNumberError = append(productNumberError, tmpRow) | ||
1088 | + } | ||
1089 | + } else { | ||
1090 | + createOrderCommands = append(createOrderCommands, orderCommand) | ||
1091 | + } | ||
1092 | + } | ||
1093 | + | ||
1094 | + if len(productNumberError) > 0 { | ||
1095 | + ret = map[string]interface{}{ | ||
1096 | + "successCount": 0, | ||
1097 | + "fail": map[string]interface{}{ | ||
1098 | + "tableHeader": tableHeader, | ||
1099 | + "tableData": productNumberError, | ||
1100 | + }, | ||
1101 | + } | ||
1102 | + c.ResponseData(ret) | ||
1103 | + return | ||
947 | } | 1104 | } |
948 | 1105 | ||
949 | // 新增失败记录 | 1106 | // 新增失败记录 |
@@ -952,17 +1109,37 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | @@ -952,17 +1109,37 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | ||
952 | // 新增成功记录计数 | 1109 | // 新增成功记录计数 |
953 | var successDataCount int64 | 1110 | var successDataCount int64 |
954 | 1111 | ||
955 | - // 新增错误信息 | ||
956 | - var createError error | ||
957 | - | ||
958 | // 批量新增订单 | 1112 | // 批量新增订单 |
959 | - failureDataList, createError = orderSrv.CreateNewOrderByImport(createOrderCommands) | 1113 | + errorDataList, createError := orderSrv.CreateNewOrderByImport(createOrderCommands) |
960 | if createError != nil { | 1114 | if createError != nil { |
961 | c.ResponseError(createError) | 1115 | c.ResponseError(createError) |
962 | return | 1116 | return |
963 | } else { | 1117 | } else { |
964 | - if len(failureDataList) > 0 { // 导入失败返回 | 1118 | + if len(errorDataList) > 0 { // 导入失败返回 |
965 | successDataCount = 0 | 1119 | successDataCount = 0 |
1120 | + // 错误记录处理 | ||
1121 | + for _, errorData := range errorDataList { | ||
1122 | + if len(errorData.GoodLine) == 0 { // 订单错误 | ||
1123 | + for _, line := range errorData.LineNumbers { | ||
1124 | + var tmpRow []string | ||
1125 | + tmpRow = append(tmpRow, errorData.Error.Error()) // 错误信息 | ||
1126 | + s := strconv.Itoa(line + 1) | ||
1127 | + tmpRow = append(tmpRow, s) // 行号 | ||
1128 | + tmpRow = append(tmpRow, rows[line]...) // 错误行数据 | ||
1129 | + failureDataList = append(failureDataList, tmpRow) | ||
1130 | + } | ||
1131 | + } else if len(errorData.GoodLine) > 0 { // 订单产品错误 | ||
1132 | + for line := range errorData.GoodLine { | ||
1133 | + var tmpRow []string | ||
1134 | + tmpRow = append(tmpRow, errorData.Error.Error()) // 错误信息 | ||
1135 | + s := strconv.Itoa(line + 1) | ||
1136 | + tmpRow = append(tmpRow, s) // 行号 | ||
1137 | + tmpRow = append(tmpRow, rows[line]...) // 错误行数据 | ||
1138 | + failureDataList = append(failureDataList, tmpRow) | ||
1139 | + } | ||
1140 | + } | ||
1141 | + } | ||
1142 | + | ||
966 | ret = map[string]interface{}{ | 1143 | ret = map[string]interface{}{ |
967 | "successCount": successDataCount, | 1144 | "successCount": successDataCount, |
968 | "fail": map[string]interface{}{ | 1145 | "fail": map[string]interface{}{ |
@@ -971,8 +1148,8 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | @@ -971,8 +1148,8 @@ func (c *OrderInfoController) ImportOrderFromExcel() { | ||
971 | }, | 1148 | }, |
972 | } | 1149 | } |
973 | } else { // 导入成功返回 | 1150 | } else { // 导入成功返回 |
974 | - successDataCount = int64(len(rows) - 3 - len(failureDataList)) | ||
975 | - if successDataCount == int64(len(rows)-3) { | 1151 | + successDataCount = int64(rowCnt - len(failureDataList)) |
1152 | + if successDataCount == int64(rowCnt) { | ||
976 | ret = map[string]interface{}{ | 1153 | ret = map[string]interface{}{ |
977 | "successCount": successDataCount, | 1154 | "successCount": successDataCount, |
978 | "fail": nil, | 1155 | "fail": nil, |
@@ -6,6 +6,10 @@ import ( | @@ -6,6 +6,10 @@ import ( | ||
6 | ) | 6 | ) |
7 | 7 | ||
8 | func init() { | 8 | func init() { |
9 | + // 导入相关 | ||
10 | + beego.Router("/fileImportTemplate", &controllers.OrderInfoController{}, "POST:DownloadTemplate") // 下载导入模板 | ||
11 | + beego.Router("/fileImport", &controllers.OrderInfoController{}, "POST:ImportOrderFromExcel") // 导入订单数据 | ||
12 | + | ||
9 | adminRouter := beego.NewNamespace("/v1", | 13 | adminRouter := beego.NewNamespace("/v1", |
10 | beego.NSNamespace("/auth", | 14 | beego.NSNamespace("/auth", |
11 | beego.NSRouter("/login", &controllers.AdminLoginController{}, "POST:Login"), | 15 | beego.NSRouter("/login", &controllers.AdminLoginController{}, "POST:Login"), |
@@ -35,12 +39,11 @@ func init() { | @@ -35,12 +39,11 @@ func init() { | ||
35 | beego.NSRouter("/list/excel", &controllers.OrderDividendController{}, "POST:ListOrderBonusForExcel"), | 39 | beego.NSRouter("/list/excel", &controllers.OrderDividendController{}, "POST:ListOrderBonusForExcel"), |
36 | ), | 40 | ), |
37 | beego.NSNamespace("/order", | 41 | beego.NSNamespace("/order", |
38 | - beego.NSRouter("/actual/list", &controllers.OrderInfoController{}, "POST:PageListOrderReal"), // 返归订单列表 | ||
39 | - beego.NSRouter("/actual/list/excel", &controllers.OrderInfoController{}, "POST:ListOrderForExcel"), // 导出excel | ||
40 | - beego.NSRouter("/actual/import/excel", &controllers.OrderInfoController{}, "POST:ImportOrderFromExcel"), // 导入订单数据 | ||
41 | - beego.NSRouter("/actual/detail", &controllers.OrderInfoController{}, "POST:GetOrderReal"), // 查看实际订单详情 | ||
42 | - beego.NSRouter("/actual/del", &controllers.OrderInfoController{}, "POST:RemoveOrderReal"), // 删除实际订单 | ||
43 | - beego.NSRouter("/actual/update", &controllers.OrderInfoController{}, "POST:UpdateOrderReal"), // 新增实际订单 | 42 | + beego.NSRouter("/actual/list", &controllers.OrderInfoController{}, "POST:PageListOrderReal"), // 返归订单列表 |
43 | + beego.NSRouter("/actual/list/excel", &controllers.OrderInfoController{}, "POST:ListOrderForExcel"), // 导出订单记录 | ||
44 | + beego.NSRouter("/actual/detail", &controllers.OrderInfoController{}, "POST:GetOrderReal"), // 查看实际订单详情 | ||
45 | + beego.NSRouter("/actual/del", &controllers.OrderInfoController{}, "POST:RemoveOrderReal"), // 删除实际订单 | ||
46 | + beego.NSRouter("/actual/update", &controllers.OrderInfoController{}, "POST:UpdateOrderReal"), // 新增实际订单 | ||
44 | beego.NSRouter("/actual/close", &controllers.OrderInfoController{}, "POST:OrderDisable"), | 47 | beego.NSRouter("/actual/close", &controllers.OrderInfoController{}, "POST:OrderDisable"), |
45 | ), | 48 | ), |
46 | beego.NSNamespace("/common", | 49 | beego.NSNamespace("/common", |
@@ -20,4 +20,7 @@ func init() { | @@ -20,4 +20,7 @@ func init() { | ||
20 | http.ServeFile(ctx.ResponseWriter, ctx.Request, constant.LOG_File) | 20 | http.ServeFile(ctx.ResponseWriter, ctx.Request, constant.LOG_File) |
21 | return | 21 | return |
22 | }) | 22 | }) |
23 | + | ||
24 | + // 静态文件路径映射 | ||
25 | + beego.SetStaticPath("/download", "download") | ||
23 | } | 26 | } |
vendor/github.com/beego/beego/v2/LICENSE
0 → 100644
1 | +Copyright 2014 astaxie | ||
2 | + | ||
3 | +Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +you may not use this file except in compliance with the License. | ||
5 | +You may obtain a copy of the License at | ||
6 | + | ||
7 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | + | ||
9 | +Unless required by applicable law or agreed to in writing, software | ||
10 | +distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +See the License for the specific language governing permissions and | ||
13 | +limitations under the License. |
1 | +# httplib | ||
2 | + | ||
3 | +httplib is an libs help you to curl remote url. | ||
4 | + | ||
5 | +# How to use? | ||
6 | + | ||
7 | +## GET | ||
8 | + | ||
9 | +you can use Get to crawl data. | ||
10 | + | ||
11 | + import "github.com/beego/beego/v2/httplib" | ||
12 | + | ||
13 | + str, err := httplib.Get("http://beego.me/").String() | ||
14 | + if err != nil { | ||
15 | + // error | ||
16 | + } | ||
17 | + fmt.Println(str) | ||
18 | + | ||
19 | +## POST | ||
20 | + | ||
21 | +POST data to remote url | ||
22 | + | ||
23 | + req := httplib.Post("http://beego.me/") | ||
24 | + req.Param("username","astaxie") | ||
25 | + req.Param("password","123456") | ||
26 | + str, err := req.String() | ||
27 | + if err != nil { | ||
28 | + // error | ||
29 | + } | ||
30 | + fmt.Println(str) | ||
31 | + | ||
32 | +## Set timeout | ||
33 | + | ||
34 | +The default timeout is `60` seconds, function prototype: | ||
35 | + | ||
36 | + SetTimeout(connectTimeout, readWriteTimeout time.Duration) | ||
37 | + | ||
38 | +Example: | ||
39 | + | ||
40 | + // GET | ||
41 | + httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second) | ||
42 | + | ||
43 | + // POST | ||
44 | + httplib.Post("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second) | ||
45 | + | ||
46 | +## Debug | ||
47 | + | ||
48 | +If you want to debug the request info, set the debug on | ||
49 | + | ||
50 | + httplib.Get("http://beego.me/").Debug(true) | ||
51 | + | ||
52 | +## Set HTTP Basic Auth | ||
53 | + | ||
54 | + str, err := Get("http://beego.me/").SetBasicAuth("user", "passwd").String() | ||
55 | + if err != nil { | ||
56 | + // error | ||
57 | + } | ||
58 | + fmt.Println(str) | ||
59 | + | ||
60 | +## Set HTTPS | ||
61 | + | ||
62 | +If request url is https, You can set the client support TSL: | ||
63 | + | ||
64 | + httplib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) | ||
65 | + | ||
66 | +More info about the `tls.Config` please visit http://golang.org/pkg/crypto/tls/#Config | ||
67 | + | ||
68 | +## Set HTTP Version | ||
69 | + | ||
70 | +some servers need to specify the protocol version of HTTP | ||
71 | + | ||
72 | + httplib.Get("http://beego.me/").SetProtocolVersion("HTTP/1.1") | ||
73 | + | ||
74 | +## Set Cookie | ||
75 | + | ||
76 | +some http request need setcookie. So set it like this: | ||
77 | + | ||
78 | + cookie := &http.Cookie{} | ||
79 | + cookie.Name = "username" | ||
80 | + cookie.Value = "astaxie" | ||
81 | + httplib.Get("http://beego.me/").SetCookie(cookie) | ||
82 | + | ||
83 | +## Upload file | ||
84 | + | ||
85 | +httplib support mutil file upload, use `req.PostFile()` | ||
86 | + | ||
87 | + req := httplib.Post("http://beego.me/") | ||
88 | + req.Param("username","astaxie") | ||
89 | + req.PostFile("uploadfile1", "httplib.pdf") | ||
90 | + str, err := req.String() | ||
91 | + if err != nil { | ||
92 | + // error | ||
93 | + } | ||
94 | + fmt.Println(str) | ||
95 | + | ||
96 | +See godoc for further documentation and examples. | ||
97 | + | ||
98 | +* [godoc.org/github.com/beego/beego/v2/httplib](https://godoc.org/github.com/beego/beego/v2/httplib) |
1 | +// Copyright 2020 beego | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +package httplib | ||
16 | + | ||
17 | +import ( | ||
18 | + "context" | ||
19 | + "net/http" | ||
20 | +) | ||
21 | + | ||
22 | +type FilterChain func(next Filter) Filter | ||
23 | + | ||
24 | +type Filter func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error) |
1 | +// Copyright 2014 beego Author. All Rights Reserved. | ||
2 | +// | ||
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | +// you may not use this file except in compliance with the License. | ||
5 | +// You may obtain a copy of the License at | ||
6 | +// | ||
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | +// | ||
9 | +// Unless required by applicable law or agreed to in writing, software | ||
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | +// See the License for the specific language governing permissions and | ||
13 | +// limitations under the License. | ||
14 | + | ||
15 | +// Package httplib is used as http.Client | ||
16 | +// Usage: | ||
17 | +// | ||
18 | +// import "github.com/beego/beego/v2/httplib" | ||
19 | +// | ||
20 | +// b := httplib.Post("http://beego.me/") | ||
21 | +// b.Param("username","astaxie") | ||
22 | +// b.Param("password","123456") | ||
23 | +// b.PostFile("uploadfile1", "httplib.pdf") | ||
24 | +// b.PostFile("uploadfile2", "httplib.txt") | ||
25 | +// str, err := b.String() | ||
26 | +// if err != nil { | ||
27 | +// t.Fatal(err) | ||
28 | +// } | ||
29 | +// fmt.Println(str) | ||
30 | +// | ||
31 | +// more docs http://beego.me/docs/module/httplib.md | ||
32 | +package httplib | ||
33 | + | ||
34 | +import ( | ||
35 | + "bytes" | ||
36 | + "compress/gzip" | ||
37 | + "context" | ||
38 | + "crypto/tls" | ||
39 | + "encoding/json" | ||
40 | + "encoding/xml" | ||
41 | + "io" | ||
42 | + "io/ioutil" | ||
43 | + "log" | ||
44 | + "mime/multipart" | ||
45 | + "net" | ||
46 | + "net/http" | ||
47 | + "net/http/cookiejar" | ||
48 | + "net/http/httputil" | ||
49 | + "net/url" | ||
50 | + "os" | ||
51 | + "path" | ||
52 | + "strings" | ||
53 | + "sync" | ||
54 | + "time" | ||
55 | + | ||
56 | + "gopkg.in/yaml.v2" | ||
57 | +) | ||
58 | + | ||
59 | +var defaultSetting = BeegoHTTPSettings{ | ||
60 | + UserAgent: "beegoServer", | ||
61 | + ConnectTimeout: 60 * time.Second, | ||
62 | + ReadWriteTimeout: 60 * time.Second, | ||
63 | + Gzip: true, | ||
64 | + DumpBody: true, | ||
65 | +} | ||
66 | + | ||
67 | +var defaultCookieJar http.CookieJar | ||
68 | +var settingMutex sync.Mutex | ||
69 | + | ||
70 | +// it will be the last filter and execute request.Do | ||
71 | +var doRequestFilter = func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error) { | ||
72 | + return req.doRequest(ctx) | ||
73 | +} | ||
74 | + | ||
75 | +// createDefaultCookie creates a global cookiejar to store cookies. | ||
76 | +func createDefaultCookie() { | ||
77 | + settingMutex.Lock() | ||
78 | + defer settingMutex.Unlock() | ||
79 | + defaultCookieJar, _ = cookiejar.New(nil) | ||
80 | +} | ||
81 | + | ||
82 | +// SetDefaultSetting overwrites default settings | ||
83 | +func SetDefaultSetting(setting BeegoHTTPSettings) { | ||
84 | + settingMutex.Lock() | ||
85 | + defer settingMutex.Unlock() | ||
86 | + defaultSetting = setting | ||
87 | +} | ||
88 | + | ||
89 | +// NewBeegoRequest returns *BeegoHttpRequest with specific method | ||
90 | +func NewBeegoRequest(rawurl, method string) *BeegoHTTPRequest { | ||
91 | + var resp http.Response | ||
92 | + u, err := url.Parse(rawurl) | ||
93 | + if err != nil { | ||
94 | + log.Println("Httplib:", err) | ||
95 | + } | ||
96 | + req := http.Request{ | ||
97 | + URL: u, | ||
98 | + Method: method, | ||
99 | + Header: make(http.Header), | ||
100 | + Proto: "HTTP/1.1", | ||
101 | + ProtoMajor: 1, | ||
102 | + ProtoMinor: 1, | ||
103 | + } | ||
104 | + return &BeegoHTTPRequest{ | ||
105 | + url: rawurl, | ||
106 | + req: &req, | ||
107 | + params: map[string][]string{}, | ||
108 | + files: map[string]string{}, | ||
109 | + setting: defaultSetting, | ||
110 | + resp: &resp, | ||
111 | + } | ||
112 | +} | ||
113 | + | ||
114 | +// Get returns *BeegoHttpRequest with GET method. | ||
115 | +func Get(url string) *BeegoHTTPRequest { | ||
116 | + return NewBeegoRequest(url, "GET") | ||
117 | +} | ||
118 | + | ||
119 | +// Post returns *BeegoHttpRequest with POST method. | ||
120 | +func Post(url string) *BeegoHTTPRequest { | ||
121 | + return NewBeegoRequest(url, "POST") | ||
122 | +} | ||
123 | + | ||
124 | +// Put returns *BeegoHttpRequest with PUT method. | ||
125 | +func Put(url string) *BeegoHTTPRequest { | ||
126 | + return NewBeegoRequest(url, "PUT") | ||
127 | +} | ||
128 | + | ||
129 | +// Delete returns *BeegoHttpRequest DELETE method. | ||
130 | +func Delete(url string) *BeegoHTTPRequest { | ||
131 | + return NewBeegoRequest(url, "DELETE") | ||
132 | +} | ||
133 | + | ||
134 | +// Head returns *BeegoHttpRequest with HEAD method. | ||
135 | +func Head(url string) *BeegoHTTPRequest { | ||
136 | + return NewBeegoRequest(url, "HEAD") | ||
137 | +} | ||
138 | + | ||
139 | +// BeegoHTTPSettings is the http.Client setting | ||
140 | +type BeegoHTTPSettings struct { | ||
141 | + ShowDebug bool | ||
142 | + UserAgent string | ||
143 | + ConnectTimeout time.Duration | ||
144 | + ReadWriteTimeout time.Duration | ||
145 | + TLSClientConfig *tls.Config | ||
146 | + Proxy func(*http.Request) (*url.URL, error) | ||
147 | + Transport http.RoundTripper | ||
148 | + CheckRedirect func(req *http.Request, via []*http.Request) error | ||
149 | + EnableCookie bool | ||
150 | + Gzip bool | ||
151 | + DumpBody bool | ||
152 | + Retries int // if set to -1 means will retry forever | ||
153 | + RetryDelay time.Duration | ||
154 | + FilterChains []FilterChain | ||
155 | +} | ||
156 | + | ||
157 | +// BeegoHTTPRequest provides more useful methods than http.Request for requesting a url. | ||
158 | +type BeegoHTTPRequest struct { | ||
159 | + url string | ||
160 | + req *http.Request | ||
161 | + params map[string][]string | ||
162 | + files map[string]string | ||
163 | + setting BeegoHTTPSettings | ||
164 | + resp *http.Response | ||
165 | + body []byte | ||
166 | + dump []byte | ||
167 | +} | ||
168 | + | ||
169 | +// GetRequest returns the request object | ||
170 | +func (b *BeegoHTTPRequest) GetRequest() *http.Request { | ||
171 | + return b.req | ||
172 | +} | ||
173 | + | ||
174 | +// Setting changes request settings | ||
175 | +func (b *BeegoHTTPRequest) Setting(setting BeegoHTTPSettings) *BeegoHTTPRequest { | ||
176 | + b.setting = setting | ||
177 | + return b | ||
178 | +} | ||
179 | + | ||
180 | +// SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password. | ||
181 | +func (b *BeegoHTTPRequest) SetBasicAuth(username, password string) *BeegoHTTPRequest { | ||
182 | + b.req.SetBasicAuth(username, password) | ||
183 | + return b | ||
184 | +} | ||
185 | + | ||
186 | +// SetEnableCookie sets enable/disable cookiejar | ||
187 | +func (b *BeegoHTTPRequest) SetEnableCookie(enable bool) *BeegoHTTPRequest { | ||
188 | + b.setting.EnableCookie = enable | ||
189 | + return b | ||
190 | +} | ||
191 | + | ||
192 | +// SetUserAgent sets User-Agent header field | ||
193 | +func (b *BeegoHTTPRequest) SetUserAgent(useragent string) *BeegoHTTPRequest { | ||
194 | + b.setting.UserAgent = useragent | ||
195 | + return b | ||
196 | +} | ||
197 | + | ||
198 | +// Debug sets show debug or not when executing request. | ||
199 | +func (b *BeegoHTTPRequest) Debug(isdebug bool) *BeegoHTTPRequest { | ||
200 | + b.setting.ShowDebug = isdebug | ||
201 | + return b | ||
202 | +} | ||
203 | + | ||
204 | +// Retries sets Retries times. | ||
205 | +// default is 0 (never retry) | ||
206 | +// -1 retry indefinitely (forever) | ||
207 | +// Other numbers specify the exact retry amount | ||
208 | +func (b *BeegoHTTPRequest) Retries(times int) *BeegoHTTPRequest { | ||
209 | + b.setting.Retries = times | ||
210 | + return b | ||
211 | +} | ||
212 | + | ||
213 | +// RetryDelay sets the time to sleep between reconnection attempts | ||
214 | +func (b *BeegoHTTPRequest) RetryDelay(delay time.Duration) *BeegoHTTPRequest { | ||
215 | + b.setting.RetryDelay = delay | ||
216 | + return b | ||
217 | +} | ||
218 | + | ||
219 | +// DumpBody sets the DumbBody field | ||
220 | +func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest { | ||
221 | + b.setting.DumpBody = isdump | ||
222 | + return b | ||
223 | +} | ||
224 | + | ||
225 | +// DumpRequest returns the DumpRequest | ||
226 | +func (b *BeegoHTTPRequest) DumpRequest() []byte { | ||
227 | + return b.dump | ||
228 | +} | ||
229 | + | ||
230 | +// SetTimeout sets connect time out and read-write time out for BeegoRequest. | ||
231 | +func (b *BeegoHTTPRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHTTPRequest { | ||
232 | + b.setting.ConnectTimeout = connectTimeout | ||
233 | + b.setting.ReadWriteTimeout = readWriteTimeout | ||
234 | + return b | ||
235 | +} | ||
236 | + | ||
237 | +// SetTLSClientConfig sets TLS connection configuration if visiting HTTPS url. | ||
238 | +func (b *BeegoHTTPRequest) SetTLSClientConfig(config *tls.Config) *BeegoHTTPRequest { | ||
239 | + b.setting.TLSClientConfig = config | ||
240 | + return b | ||
241 | +} | ||
242 | + | ||
243 | +// Header adds header item string in request. | ||
244 | +func (b *BeegoHTTPRequest) Header(key, value string) *BeegoHTTPRequest { | ||
245 | + b.req.Header.Set(key, value) | ||
246 | + return b | ||
247 | +} | ||
248 | + | ||
249 | +// SetHost set the request host | ||
250 | +func (b *BeegoHTTPRequest) SetHost(host string) *BeegoHTTPRequest { | ||
251 | + b.req.Host = host | ||
252 | + return b | ||
253 | +} | ||
254 | + | ||
255 | +// SetProtocolVersion sets the protocol version for incoming requests. | ||
256 | +// Client requests always use HTTP/1.1. | ||
257 | +func (b *BeegoHTTPRequest) SetProtocolVersion(vers string) *BeegoHTTPRequest { | ||
258 | + if len(vers) == 0 { | ||
259 | + vers = "HTTP/1.1" | ||
260 | + } | ||
261 | + | ||
262 | + major, minor, ok := http.ParseHTTPVersion(vers) | ||
263 | + if ok { | ||
264 | + b.req.Proto = vers | ||
265 | + b.req.ProtoMajor = major | ||
266 | + b.req.ProtoMinor = minor | ||
267 | + } | ||
268 | + | ||
269 | + return b | ||
270 | +} | ||
271 | + | ||
272 | +// SetCookie adds a cookie to the request. | ||
273 | +func (b *BeegoHTTPRequest) SetCookie(cookie *http.Cookie) *BeegoHTTPRequest { | ||
274 | + b.req.Header.Add("Cookie", cookie.String()) | ||
275 | + return b | ||
276 | +} | ||
277 | + | ||
278 | +// SetTransport sets the transport field | ||
279 | +func (b *BeegoHTTPRequest) SetTransport(transport http.RoundTripper) *BeegoHTTPRequest { | ||
280 | + b.setting.Transport = transport | ||
281 | + return b | ||
282 | +} | ||
283 | + | ||
284 | +// SetProxy sets the HTTP proxy | ||
285 | +// example: | ||
286 | +// | ||
287 | +// func(req *http.Request) (*url.URL, error) { | ||
288 | +// u, _ := url.ParseRequestURI("http://127.0.0.1:8118") | ||
289 | +// return u, nil | ||
290 | +// } | ||
291 | +func (b *BeegoHTTPRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *BeegoHTTPRequest { | ||
292 | + b.setting.Proxy = proxy | ||
293 | + return b | ||
294 | +} | ||
295 | + | ||
296 | +// SetCheckRedirect specifies the policy for handling redirects. | ||
297 | +// | ||
298 | +// If CheckRedirect is nil, the Client uses its default policy, | ||
299 | +// which is to stop after 10 consecutive requests. | ||
300 | +func (b *BeegoHTTPRequest) SetCheckRedirect(redirect func(req *http.Request, via []*http.Request) error) *BeegoHTTPRequest { | ||
301 | + b.setting.CheckRedirect = redirect | ||
302 | + return b | ||
303 | +} | ||
304 | + | ||
305 | +// SetFilters will use the filter as the invocation filters | ||
306 | +func (b *BeegoHTTPRequest) SetFilters(fcs ...FilterChain) *BeegoHTTPRequest { | ||
307 | + b.setting.FilterChains = fcs | ||
308 | + return b | ||
309 | +} | ||
310 | + | ||
311 | +// AddFilters adds filter | ||
312 | +func (b *BeegoHTTPRequest) AddFilters(fcs ...FilterChain) *BeegoHTTPRequest { | ||
313 | + b.setting.FilterChains = append(b.setting.FilterChains, fcs...) | ||
314 | + return b | ||
315 | +} | ||
316 | + | ||
317 | +// Param adds query param in to request. | ||
318 | +// params build query string as ?key1=value1&key2=value2... | ||
319 | +func (b *BeegoHTTPRequest) Param(key, value string) *BeegoHTTPRequest { | ||
320 | + if param, ok := b.params[key]; ok { | ||
321 | + b.params[key] = append(param, value) | ||
322 | + } else { | ||
323 | + b.params[key] = []string{value} | ||
324 | + } | ||
325 | + return b | ||
326 | +} | ||
327 | + | ||
328 | +// PostFile adds a post file to the request | ||
329 | +func (b *BeegoHTTPRequest) PostFile(formname, filename string) *BeegoHTTPRequest { | ||
330 | + b.files[formname] = filename | ||
331 | + return b | ||
332 | +} | ||
333 | + | ||
334 | +// Body adds request raw body. | ||
335 | +// Supports string and []byte. | ||
336 | +func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest { | ||
337 | + switch t := data.(type) { | ||
338 | + case string: | ||
339 | + bf := bytes.NewBufferString(t) | ||
340 | + b.req.Body = ioutil.NopCloser(bf) | ||
341 | + b.req.ContentLength = int64(len(t)) | ||
342 | + case []byte: | ||
343 | + bf := bytes.NewBuffer(t) | ||
344 | + b.req.Body = ioutil.NopCloser(bf) | ||
345 | + b.req.ContentLength = int64(len(t)) | ||
346 | + } | ||
347 | + return b | ||
348 | +} | ||
349 | + | ||
350 | +// XMLBody adds the request raw body encoded in XML. | ||
351 | +func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) { | ||
352 | + if b.req.Body == nil && obj != nil { | ||
353 | + byts, err := xml.Marshal(obj) | ||
354 | + if err != nil { | ||
355 | + return b, err | ||
356 | + } | ||
357 | + b.req.Body = ioutil.NopCloser(bytes.NewReader(byts)) | ||
358 | + b.req.ContentLength = int64(len(byts)) | ||
359 | + b.req.Header.Set("Content-Type", "application/xml") | ||
360 | + } | ||
361 | + return b, nil | ||
362 | +} | ||
363 | + | ||
364 | +// YAMLBody adds the request raw body encoded in YAML. | ||
365 | +func (b *BeegoHTTPRequest) YAMLBody(obj interface{}) (*BeegoHTTPRequest, error) { | ||
366 | + if b.req.Body == nil && obj != nil { | ||
367 | + byts, err := yaml.Marshal(obj) | ||
368 | + if err != nil { | ||
369 | + return b, err | ||
370 | + } | ||
371 | + b.req.Body = ioutil.NopCloser(bytes.NewReader(byts)) | ||
372 | + b.req.ContentLength = int64(len(byts)) | ||
373 | + b.req.Header.Set("Content-Type", "application/x+yaml") | ||
374 | + } | ||
375 | + return b, nil | ||
376 | +} | ||
377 | + | ||
378 | +// JSONBody adds the request raw body encoded in JSON. | ||
379 | +func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) { | ||
380 | + if b.req.Body == nil && obj != nil { | ||
381 | + byts, err := json.Marshal(obj) | ||
382 | + if err != nil { | ||
383 | + return b, err | ||
384 | + } | ||
385 | + b.req.Body = ioutil.NopCloser(bytes.NewReader(byts)) | ||
386 | + b.req.ContentLength = int64(len(byts)) | ||
387 | + b.req.Header.Set("Content-Type", "application/json") | ||
388 | + } | ||
389 | + return b, nil | ||
390 | +} | ||
391 | + | ||
392 | +func (b *BeegoHTTPRequest) buildURL(paramBody string) { | ||
393 | + // build GET url with query string | ||
394 | + if b.req.Method == "GET" && len(paramBody) > 0 { | ||
395 | + if strings.Contains(b.url, "?") { | ||
396 | + b.url += "&" + paramBody | ||
397 | + } else { | ||
398 | + b.url = b.url + "?" + paramBody | ||
399 | + } | ||
400 | + return | ||
401 | + } | ||
402 | + | ||
403 | + // build POST/PUT/PATCH url and body | ||
404 | + if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH" || b.req.Method == "DELETE") && b.req.Body == nil { | ||
405 | + // with files | ||
406 | + if len(b.files) > 0 { | ||
407 | + pr, pw := io.Pipe() | ||
408 | + bodyWriter := multipart.NewWriter(pw) | ||
409 | + go func() { | ||
410 | + for formname, filename := range b.files { | ||
411 | + fileWriter, err := bodyWriter.CreateFormFile(formname, filename) | ||
412 | + if err != nil { | ||
413 | + log.Println("Httplib:", err) | ||
414 | + } | ||
415 | + fh, err := os.Open(filename) | ||
416 | + if err != nil { | ||
417 | + log.Println("Httplib:", err) | ||
418 | + } | ||
419 | + // iocopy | ||
420 | + _, err = io.Copy(fileWriter, fh) | ||
421 | + fh.Close() | ||
422 | + if err != nil { | ||
423 | + log.Println("Httplib:", err) | ||
424 | + } | ||
425 | + } | ||
426 | + for k, v := range b.params { | ||
427 | + for _, vv := range v { | ||
428 | + bodyWriter.WriteField(k, vv) | ||
429 | + } | ||
430 | + } | ||
431 | + bodyWriter.Close() | ||
432 | + pw.Close() | ||
433 | + }() | ||
434 | + b.Header("Content-Type", bodyWriter.FormDataContentType()) | ||
435 | + b.req.Body = ioutil.NopCloser(pr) | ||
436 | + b.Header("Transfer-Encoding", "chunked") | ||
437 | + return | ||
438 | + } | ||
439 | + | ||
440 | + // with params | ||
441 | + if len(paramBody) > 0 { | ||
442 | + b.Header("Content-Type", "application/x-www-form-urlencoded") | ||
443 | + b.Body(paramBody) | ||
444 | + } | ||
445 | + } | ||
446 | +} | ||
447 | + | ||
448 | +func (b *BeegoHTTPRequest) getResponse() (*http.Response, error) { | ||
449 | + if b.resp.StatusCode != 0 { | ||
450 | + return b.resp, nil | ||
451 | + } | ||
452 | + resp, err := b.DoRequest() | ||
453 | + if err != nil { | ||
454 | + return nil, err | ||
455 | + } | ||
456 | + b.resp = resp | ||
457 | + return resp, nil | ||
458 | +} | ||
459 | + | ||
460 | +// DoRequest executes client.Do | ||
461 | +func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) { | ||
462 | + return b.DoRequestWithCtx(context.Background()) | ||
463 | +} | ||
464 | + | ||
465 | +func (b *BeegoHTTPRequest) DoRequestWithCtx(ctx context.Context) (resp *http.Response, err error) { | ||
466 | + | ||
467 | + root := doRequestFilter | ||
468 | + if len(b.setting.FilterChains) > 0 { | ||
469 | + for i := len(b.setting.FilterChains) - 1; i >= 0; i-- { | ||
470 | + root = b.setting.FilterChains[i](root) | ||
471 | + } | ||
472 | + } | ||
473 | + return root(ctx, b) | ||
474 | +} | ||
475 | + | ||
476 | +func (b *BeegoHTTPRequest) doRequest(ctx context.Context) (resp *http.Response, err error) { | ||
477 | + var paramBody string | ||
478 | + if len(b.params) > 0 { | ||
479 | + var buf bytes.Buffer | ||
480 | + for k, v := range b.params { | ||
481 | + for _, vv := range v { | ||
482 | + buf.WriteString(url.QueryEscape(k)) | ||
483 | + buf.WriteByte('=') | ||
484 | + buf.WriteString(url.QueryEscape(vv)) | ||
485 | + buf.WriteByte('&') | ||
486 | + } | ||
487 | + } | ||
488 | + paramBody = buf.String() | ||
489 | + paramBody = paramBody[0 : len(paramBody)-1] | ||
490 | + } | ||
491 | + | ||
492 | + b.buildURL(paramBody) | ||
493 | + urlParsed, err := url.Parse(b.url) | ||
494 | + if err != nil { | ||
495 | + return nil, err | ||
496 | + } | ||
497 | + | ||
498 | + b.req.URL = urlParsed | ||
499 | + | ||
500 | + trans := b.setting.Transport | ||
501 | + | ||
502 | + if trans == nil { | ||
503 | + // create default transport | ||
504 | + trans = &http.Transport{ | ||
505 | + TLSClientConfig: b.setting.TLSClientConfig, | ||
506 | + Proxy: b.setting.Proxy, | ||
507 | + Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout), | ||
508 | + MaxIdleConnsPerHost: 100, | ||
509 | + } | ||
510 | + } else { | ||
511 | + // if b.transport is *http.Transport then set the settings. | ||
512 | + if t, ok := trans.(*http.Transport); ok { | ||
513 | + if t.TLSClientConfig == nil { | ||
514 | + t.TLSClientConfig = b.setting.TLSClientConfig | ||
515 | + } | ||
516 | + if t.Proxy == nil { | ||
517 | + t.Proxy = b.setting.Proxy | ||
518 | + } | ||
519 | + if t.Dial == nil { | ||
520 | + t.Dial = TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout) | ||
521 | + } | ||
522 | + } | ||
523 | + } | ||
524 | + | ||
525 | + var jar http.CookieJar | ||
526 | + if b.setting.EnableCookie { | ||
527 | + if defaultCookieJar == nil { | ||
528 | + createDefaultCookie() | ||
529 | + } | ||
530 | + jar = defaultCookieJar | ||
531 | + } | ||
532 | + | ||
533 | + client := &http.Client{ | ||
534 | + Transport: trans, | ||
535 | + Jar: jar, | ||
536 | + } | ||
537 | + | ||
538 | + if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" { | ||
539 | + b.req.Header.Set("User-Agent", b.setting.UserAgent) | ||
540 | + } | ||
541 | + | ||
542 | + if b.setting.CheckRedirect != nil { | ||
543 | + client.CheckRedirect = b.setting.CheckRedirect | ||
544 | + } | ||
545 | + | ||
546 | + if b.setting.ShowDebug { | ||
547 | + dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody) | ||
548 | + if err != nil { | ||
549 | + log.Println(err.Error()) | ||
550 | + } | ||
551 | + b.dump = dump | ||
552 | + } | ||
553 | + // retries default value is 0, it will run once. | ||
554 | + // retries equal to -1, it will run forever until success | ||
555 | + // retries is setted, it will retries fixed times. | ||
556 | + // Sleeps for a 400ms between calls to reduce spam | ||
557 | + for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ { | ||
558 | + resp, err = client.Do(b.req) | ||
559 | + if err == nil { | ||
560 | + break | ||
561 | + } | ||
562 | + time.Sleep(b.setting.RetryDelay) | ||
563 | + } | ||
564 | + return resp, err | ||
565 | +} | ||
566 | + | ||
567 | +// String returns the body string in response. | ||
568 | +// Calls Response inner. | ||
569 | +func (b *BeegoHTTPRequest) String() (string, error) { | ||
570 | + data, err := b.Bytes() | ||
571 | + if err != nil { | ||
572 | + return "", err | ||
573 | + } | ||
574 | + | ||
575 | + return string(data), nil | ||
576 | +} | ||
577 | + | ||
578 | +// Bytes returns the body []byte in response. | ||
579 | +// Calls Response inner. | ||
580 | +func (b *BeegoHTTPRequest) Bytes() ([]byte, error) { | ||
581 | + if b.body != nil { | ||
582 | + return b.body, nil | ||
583 | + } | ||
584 | + resp, err := b.getResponse() | ||
585 | + if err != nil { | ||
586 | + return nil, err | ||
587 | + } | ||
588 | + if resp.Body == nil { | ||
589 | + return nil, nil | ||
590 | + } | ||
591 | + defer resp.Body.Close() | ||
592 | + if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" { | ||
593 | + reader, err := gzip.NewReader(resp.Body) | ||
594 | + if err != nil { | ||
595 | + return nil, err | ||
596 | + } | ||
597 | + b.body, err = ioutil.ReadAll(reader) | ||
598 | + return b.body, err | ||
599 | + } | ||
600 | + b.body, err = ioutil.ReadAll(resp.Body) | ||
601 | + return b.body, err | ||
602 | +} | ||
603 | + | ||
604 | +// ToFile saves the body data in response to one file. | ||
605 | +// Calls Response inner. | ||
606 | +func (b *BeegoHTTPRequest) ToFile(filename string) error { | ||
607 | + resp, err := b.getResponse() | ||
608 | + if err != nil { | ||
609 | + return err | ||
610 | + } | ||
611 | + if resp.Body == nil { | ||
612 | + return nil | ||
613 | + } | ||
614 | + defer resp.Body.Close() | ||
615 | + err = pathExistAndMkdir(filename) | ||
616 | + if err != nil { | ||
617 | + return err | ||
618 | + } | ||
619 | + f, err := os.Create(filename) | ||
620 | + if err != nil { | ||
621 | + return err | ||
622 | + } | ||
623 | + defer f.Close() | ||
624 | + _, err = io.Copy(f, resp.Body) | ||
625 | + return err | ||
626 | +} | ||
627 | + | ||
628 | +// Check if the file directory exists. If it doesn't then it's created | ||
629 | +func pathExistAndMkdir(filename string) (err error) { | ||
630 | + filename = path.Dir(filename) | ||
631 | + _, err = os.Stat(filename) | ||
632 | + if err == nil { | ||
633 | + return nil | ||
634 | + } | ||
635 | + if os.IsNotExist(err) { | ||
636 | + err = os.MkdirAll(filename, os.ModePerm) | ||
637 | + if err == nil { | ||
638 | + return nil | ||
639 | + } | ||
640 | + } | ||
641 | + return err | ||
642 | +} | ||
643 | + | ||
644 | +// ToJSON returns the map that marshals from the body bytes as json in response. | ||
645 | +// Calls Response inner. | ||
646 | +func (b *BeegoHTTPRequest) ToJSON(v interface{}) error { | ||
647 | + data, err := b.Bytes() | ||
648 | + if err != nil { | ||
649 | + return err | ||
650 | + } | ||
651 | + return json.Unmarshal(data, v) | ||
652 | +} | ||
653 | + | ||
654 | +// ToXML returns the map that marshals from the body bytes as xml in response . | ||
655 | +// Calls Response inner. | ||
656 | +func (b *BeegoHTTPRequest) ToXML(v interface{}) error { | ||
657 | + data, err := b.Bytes() | ||
658 | + if err != nil { | ||
659 | + return err | ||
660 | + } | ||
661 | + return xml.Unmarshal(data, v) | ||
662 | +} | ||
663 | + | ||
664 | +// ToYAML returns the map that marshals from the body bytes as yaml in response . | ||
665 | +// Calls Response inner. | ||
666 | +func (b *BeegoHTTPRequest) ToYAML(v interface{}) error { | ||
667 | + data, err := b.Bytes() | ||
668 | + if err != nil { | ||
669 | + return err | ||
670 | + } | ||
671 | + return yaml.Unmarshal(data, v) | ||
672 | +} | ||
673 | + | ||
674 | +// Response executes request client gets response manually. | ||
675 | +func (b *BeegoHTTPRequest) Response() (*http.Response, error) { | ||
676 | + return b.getResponse() | ||
677 | +} | ||
678 | + | ||
679 | +// TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field. | ||
680 | +func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) { | ||
681 | + return func(netw, addr string) (net.Conn, error) { | ||
682 | + conn, err := net.DialTimeout(netw, addr, cTimeout) | ||
683 | + if err != nil { | ||
684 | + return nil, err | ||
685 | + } | ||
686 | + err = conn.SetDeadline(time.Now().Add(rwTimeout)) | ||
687 | + return conn, err | ||
688 | + } | ||
689 | +} |
@@ -23,6 +23,9 @@ github.com/astaxie/beego/plugins/cors | @@ -23,6 +23,9 @@ github.com/astaxie/beego/plugins/cors | ||
23 | github.com/astaxie/beego/session | 23 | github.com/astaxie/beego/session |
24 | github.com/astaxie/beego/toolbox | 24 | github.com/astaxie/beego/toolbox |
25 | github.com/astaxie/beego/utils | 25 | github.com/astaxie/beego/utils |
26 | +# github.com/beego/beego/v2 v2.0.1 | ||
27 | +## explicit | ||
28 | +github.com/beego/beego/v2/client/httplib | ||
26 | # github.com/beorn7/perks v1.0.1 | 29 | # github.com/beorn7/perks v1.0.1 |
27 | github.com/beorn7/perks/quantile | 30 | github.com/beorn7/perks/quantile |
28 | # github.com/bsm/sarama-cluster v2.1.15+incompatible | 31 | # github.com/bsm/sarama-cluster v2.1.15+incompatible |
@@ -283,7 +286,7 @@ golang.org/x/text/secure/bidirule | @@ -283,7 +286,7 @@ golang.org/x/text/secure/bidirule | ||
283 | golang.org/x/text/transform | 286 | golang.org/x/text/transform |
284 | golang.org/x/text/unicode/bidi | 287 | golang.org/x/text/unicode/bidi |
285 | golang.org/x/text/unicode/norm | 288 | golang.org/x/text/unicode/norm |
286 | -# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 | 289 | +# golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 |
287 | golang.org/x/xerrors | 290 | golang.org/x/xerrors |
288 | golang.org/x/xerrors/internal | 291 | golang.org/x/xerrors/internal |
289 | # google.golang.org/protobuf v1.25.0 | 292 | # google.golang.org/protobuf v1.25.0 |
-
请 注册 或 登录 后发表评论