作者 陈志颖

feat:添加excel校验

... ... @@ -18,6 +18,8 @@ var (
BUSINESS_ADMIN_HOST = "http://suplus-business-admin-test.fjmaimaimai.com" //企业平台的地址
)
var EXCEL_COLUMN = 12
func init() {
if os.Getenv("LOG_LEVEL") != "" {
LOG_LEVEL = os.Getenv("LOG_LEVEL")
... ...
... ... @@ -35,7 +35,7 @@ type AdminUserFindOneQuery struct {
type AdminUserRepository interface {
Save(AdminUser) (*AdminUser, error)
FindOne(qureyOptions AdminUserFindOneQuery) (*AdminUser, error)
FindOne(queryOptions AdminUserFindOneQuery) (*AdminUser, error)
Find(queryOptions AdminUserFindQuery) ([]AdminUser, error)
CountAll(queryOption AdminUserFindQuery) (int, error)
}
... ...
... ... @@ -31,3 +31,19 @@ func GenerateRangeNum(min, max int) int {
randNum := rand.Intn(max-min) + min
return randNum
}
/**
* @Author SteveChan
* @Description // 判断数组是否包含
* @Date 14:30 2021/1/6
* @Param
* @return
**/
func IsContain(items []string, item string) bool {
for _, eachItem := range items {
if eachItem == item {
return true
}
}
return false
}
... ...
package controllers
import (
"encoding/json"
"errors"
"fmt"
"path"
"regexp"
"strconv"
"strings"
"time"
"unicode/utf8"
"github.com/360EntSecGroup-Skylar/excelize/v2"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/constant"
"github.com/astaxie/beego/logs"
orderCmd "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/command"
orderQuery "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/query"
orderService "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/service"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/utils"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/lib"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/lib/exceltool"
)
... ... @@ -603,3 +609,256 @@ func (c *OrderInfoController) ListOrderForExcel() {
c.ResponseExcelByFile(c.Ctx, excelMaker)
return
}
/**
* @Author SteveChan
* @Description //TODO 导入excel订单
* @Date 10:52 2021/1/6
* @Param
* @return
**/
func (c *OrderInfoController) ImportOrderFromExcel() {
where := c.GetString("where")
file, h, _ := c.GetFile("file")
jsonMap := make(map[string]interface{})
err := json.Unmarshal([]byte(where), &jsonMap)
if err != nil {
logs.Error(err)
c.ResponseError(errors.New("json数据解析失败"))
}
// 返回字段定义
ret := map[string]interface{}{}
// 返回信息表头定义
// 0: 订单号, 1: 发货单号, 3: 客户名称, 3: 订单区域, 4: 编号, 5: 合伙人, 6: 类型, 7: 业务抽成比例, 8: 产品名称, 9: 数量, 10: 单价, 11: 合伙人分红比例
var tableHeader = []string{"错误详情", "行号", "订单号", "发货单号", "客户名称", "订单区域", "编号", "合伙人", "类型", "业务抽成比例", "产品名称", "数量", "单价", "合伙人分红比例"}
// 文件后缀名校验
ext := path.Ext(h.Filename)
AllowExtMap := map[string]bool{
".xlsx": true,
}
if _, ok := AllowExtMap[ext]; !ok {
c.ResponseError(errors.New("文件后缀名不符合上传要求,请上传正确的文件"))
return
}
// 打开文件
xlsx, err := excelize.OpenReader(file)
if err != nil {
c.ResponseError(errors.New("文件打开失败"))
return
}
// 文件行数校验
rows, _ := xlsx.GetRows("工作表1")
if len(rows) > 303 {
c.ResponseError(errors.New("导入文件的行数超过300行,请调整行数后重新导入"))
return
}
// 空文件校验
if len(rows) < 3 {
c.ResponseError(errors.New("导入的excel文件为空文件,请上传正确的文件"))
}
// 必填项校验
nullLine := make([]interface{}, 0)
nullFlag := false
for i, row := range rows {
if i > 2 {
if len(row) == constant.EXCEL_COLUMN { // 中间空字符校验
var myRow = row
for j, cell := range row {
if j != 8 { // 业务员抽成比例不校验
if cell == "" || cell == " " { // 空字符串填充
myRow[j] = "null"
nullFlag = true
}
}
}
if nullFlag {
myRow = append(myRow, "必填项不能为空") // 错误信息
s := strconv.Itoa(i + 1)
myRow = append(myRow, s) // 行号
myRow = append(myRow, row...) // 错误行数据
nullLine = append(nullLine, myRow)
nullFlag = false
}
} else if len(row) < constant.EXCEL_COLUMN && len(row) > 0 { // 尾部空字符校验
var myRow []string
for i := 0; i < constant.EXCEL_COLUMN-len(row); i++ {
myRow = append(myRow, "null")
}
myRow = append(myRow, "必填项不能为空") // 错误信息
s := strconv.Itoa(i + 1)
myRow = append(myRow, s) // 行号
myRow = append(myRow, row...) // 错误行数据
nullLine = append(nullLine, myRow)
}
}
}
if len(nullLine) > 0 {
ret = map[string]interface{}{
"successCount": 0,
"fail": map[string]interface{}{
"tableHeader": tableHeader,
"tableData": nullLine,
},
}
c.ResponseData(ret)
return
}
// 单元格长度、内容校验
errorLine := make([]interface{}, 0)
var partnerType = []string{"事业合伙", "业务合伙", "研发合伙", "业务-产品应用合伙"}
for i, row := range rows {
if i > 2 && len(row) == constant.EXCEL_COLUMN { // 数据行
var myRow []string
for j, cell := range row {
switch j {
case 0, 1, 2, 3, 4, 5, 8: // 订单号、发货单号、客户名称、订单区域、编号、合伙人、产品名称、
{
if len(cell) > 50 {
var tmpRow []string
tmpRow = append(tmpRow, tableHeader[j+2]+"长度超过50,请重新输入") // 错误信息
s := strconv.Itoa(i + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, row...) // 错误行数据
myRow = tmpRow
}
}
case 6: // 合伙人类型校验(事业合伙、业务合伙、研发合伙、业务-产品应用合伙)
{
if !utils.IsContain(partnerType, cell) {
var tmpRow []string
tmpRow = append(tmpRow, "合伙人类型须为以下类型:事业合伙、业务合伙、研发合伙、业务-产品应用合伙") // 错误信息
s := strconv.Itoa(i + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, row...) // 错误行数据
myRow = tmpRow
}
}
case 7: // 业务员抽成比例,非必填,精确到小数点后两位
{
if len(cell) > 0 {
_, err := strconv.ParseFloat(cell, 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
}
//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)
if err != nil {
var tmpRow []string
tmpRow = append(tmpRow, "数量须为整数,请重新填写") // 错误信息
s := strconv.Itoa(i + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, row...) // 错误行数据
myRow = tmpRow
}
if len(cell) > 16 {
var tmpRow []string
tmpRow = append(tmpRow, "数量长度超过最大限制十六位整数,请重新填写") // 错误信息
s := strconv.Itoa(i + 1)
tmpRow = append(tmpRow, s) // 行号
tmpRow = append(tmpRow, row...) // 错误行数据
myRow = tmpRow
}
}
case 10: // 单价,精确到小数点后两位,小数点左侧最多可输入16位数字
{
_, err := strconv.ParseFloat(cell, 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
}
//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
//}
}
case 11: // 合伙人分红比例,精确到小数点后两位
{
_, err := strconv.ParseFloat(cell, 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
}
//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
//}
}
}
}
errorLine = append(errorLine, myRow)
}
}
if len(errorLine) > 0 {
ret = map[string]interface{}{
"successCount": 0,
"fail": map[string]interface{}{
"tableHeader": tableHeader,
"tableData": errorLine,
},
}
c.ResponseData(ret)
return
}
// 归类订单
for i, row := range rows {
if i > 2 && len(row) == 13 {
}
}
// 新增失败记录
failureDataList := make([]interface{}, 0)
// 新增成功记录计数
//var successDataCount int64
c.ResponseData(failureDataList)
return
}
... ...
... ... @@ -36,19 +36,18 @@ func init() {
),
beego.NSNamespace("/order",
beego.NSRouter("/actual/list", &controllers.OrderInfoController{}, "POST:PageListOrderReal"),
beego.NSRouter("/actual/list/excel", &controllers.OrderInfoController{}, "POST:ListOrderForExcel"),
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/close", &controllers.OrderInfoController{}, "POST:OrderDisable"),
),
beego.NSNamespace("/common",
beego.NSRouter("/partner", &controllers.CommonController{}, "POST:GetPartnerList"),
beego.NSRouter("/partnerType", &controllers.CommonController{}, "POST:GetPartnerCategory"),
beego.NSRouter("/orderType", &controllers.CommonController{}, "POST:GetOrderType"),
),
beego.NSNamespace("/enterprises",
beego.NSRouter("/setPhone", &controllers.CompanyController{}, "POST:SetPhone"),
),
... ...
package utils
import (
"github.com/astaxie/beego/context"
"github.com/linmadan/egglib-go/core/application"
)
type JsonResponse map[string]interface{}
func ResponseData(ctx *context.Context, data interface{}) JsonResponse {
jsonResponse := JsonResponse{}
jsonResponse["code"] = 0
jsonResponse["msg"] = "ok"
jsonResponse["data"] = data
ctx.Input.SetData("outputData", jsonResponse)
return jsonResponse
}
func ResponseError(ctx *context.Context, err error) JsonResponse {
serviceError := err.(*application.ServiceError)
jsonResponse := JsonResponse{}
jsonResponse["code"] = serviceError.Code
jsonResponse["msg"] = serviceError.Error()
ctx.Input.SetData("outputData", jsonResponse)
return jsonResponse
}
... ...
... ... @@ -105,6 +105,7 @@ github.com/klauspost/compress/zlib
github.com/linmadan/egglib-go/core/application
github.com/linmadan/egglib-go/core/domain
github.com/linmadan/egglib-go/utils/snowflake
github.com/linmadan/egglib-go/web/beego/utils
# github.com/mattn/go-colorable v0.1.6
## explicit
# github.com/matttproud/golang_protobuf_extensions v1.0.1
... ...