作者 陈志颖

feat:添加excel校验

@@ -18,6 +18,8 @@ var ( @@ -18,6 +18,8 @@ var (
18 BUSINESS_ADMIN_HOST = "http://suplus-business-admin-test.fjmaimaimai.com" //企业平台的地址 18 BUSINESS_ADMIN_HOST = "http://suplus-business-admin-test.fjmaimaimai.com" //企业平台的地址
19 ) 19 )
20 20
  21 +var EXCEL_COLUMN = 12
  22 +
21 func init() { 23 func init() {
22 if os.Getenv("LOG_LEVEL") != "" { 24 if os.Getenv("LOG_LEVEL") != "" {
23 LOG_LEVEL = os.Getenv("LOG_LEVEL") 25 LOG_LEVEL = os.Getenv("LOG_LEVEL")
@@ -35,7 +35,7 @@ type AdminUserFindOneQuery struct { @@ -35,7 +35,7 @@ type AdminUserFindOneQuery struct {
35 35
36 type AdminUserRepository interface { 36 type AdminUserRepository interface {
37 Save(AdminUser) (*AdminUser, error) 37 Save(AdminUser) (*AdminUser, error)
38 - FindOne(qureyOptions AdminUserFindOneQuery) (*AdminUser, error) 38 + FindOne(queryOptions AdminUserFindOneQuery) (*AdminUser, error)
39 Find(queryOptions AdminUserFindQuery) ([]AdminUser, error) 39 Find(queryOptions AdminUserFindQuery) ([]AdminUser, error)
40 CountAll(queryOption AdminUserFindQuery) (int, error) 40 CountAll(queryOption AdminUserFindQuery) (int, error)
41 } 41 }
@@ -31,3 +31,19 @@ func GenerateRangeNum(min, max int) int { @@ -31,3 +31,19 @@ func GenerateRangeNum(min, max int) int {
31 randNum := rand.Intn(max-min) + min 31 randNum := rand.Intn(max-min) + min
32 return randNum 32 return randNum
33 } 33 }
  34 +
  35 +/**
  36 + * @Author SteveChan
  37 + * @Description // 判断数组是否包含
  38 + * @Date 14:30 2021/1/6
  39 + * @Param
  40 + * @return
  41 + **/
  42 +func IsContain(items []string, item string) bool {
  43 + for _, eachItem := range items {
  44 + if eachItem == item {
  45 + return true
  46 + }
  47 + }
  48 + return false
  49 +}
1 package controllers 1 package controllers
2 2
3 import ( 3 import (
  4 + "encoding/json"
4 "errors" 5 "errors"
5 "fmt" 6 "fmt"
  7 + "path"
6 "regexp" 8 "regexp"
7 "strconv" 9 "strconv"
8 "strings" 10 "strings"
9 "time" 11 "time"
10 "unicode/utf8" 12 "unicode/utf8"
11 13
  14 + "github.com/360EntSecGroup-Skylar/excelize/v2"
  15 + "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/constant"
  16 +
12 "github.com/astaxie/beego/logs" 17 "github.com/astaxie/beego/logs"
13 orderCmd "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/command" 18 orderCmd "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/command"
14 orderQuery "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/query" 19 orderQuery "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/query"
15 orderService "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/service" 20 orderService "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/service"
16 "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain" 21 "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
  22 + "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/utils"
17 "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/lib" 23 "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/lib"
18 "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/lib/exceltool" 24 "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/lib/exceltool"
19 ) 25 )
@@ -603,3 +609,256 @@ func (c *OrderInfoController) ListOrderForExcel() { @@ -603,3 +609,256 @@ func (c *OrderInfoController) ListOrderForExcel() {
603 c.ResponseExcelByFile(c.Ctx, excelMaker) 609 c.ResponseExcelByFile(c.Ctx, excelMaker)
604 return 610 return
605 } 611 }
  612 +
  613 +/**
  614 + * @Author SteveChan
  615 + * @Description //TODO 导入excel订单
  616 + * @Date 10:52 2021/1/6
  617 + * @Param
  618 + * @return
  619 + **/
  620 +func (c *OrderInfoController) ImportOrderFromExcel() {
  621 + where := c.GetString("where")
  622 + file, h, _ := c.GetFile("file")
  623 +
  624 + jsonMap := make(map[string]interface{})
  625 +
  626 + err := json.Unmarshal([]byte(where), &jsonMap)
  627 + if err != nil {
  628 + logs.Error(err)
  629 + c.ResponseError(errors.New("json数据解析失败"))
  630 + }
  631 +
  632 + // 返回字段定义
  633 + ret := map[string]interface{}{}
  634 +
  635 + // 返回信息表头定义
  636 + // 0: 订单号, 1: 发货单号, 3: 客户名称, 3: 订单区域, 4: 编号, 5: 合伙人, 6: 类型, 7: 业务抽成比例, 8: 产品名称, 9: 数量, 10: 单价, 11: 合伙人分红比例
  637 + var tableHeader = []string{"错误详情", "行号", "订单号", "发货单号", "客户名称", "订单区域", "编号", "合伙人", "类型", "业务抽成比例", "产品名称", "数量", "单价", "合伙人分红比例"}
  638 +
  639 + // 文件后缀名校验
  640 + ext := path.Ext(h.Filename)
  641 + AllowExtMap := map[string]bool{
  642 + ".xlsx": true,
  643 + }
  644 + if _, ok := AllowExtMap[ext]; !ok {
  645 + c.ResponseError(errors.New("文件后缀名不符合上传要求,请上传正确的文件"))
  646 + return
  647 + }
  648 +
  649 + // 打开文件
  650 + xlsx, err := excelize.OpenReader(file)
  651 + if err != nil {
  652 + c.ResponseError(errors.New("文件打开失败"))
  653 + return
  654 + }
  655 +
  656 + // 文件行数校验
  657 + rows, _ := xlsx.GetRows("工作表1")
  658 + if len(rows) > 303 {
  659 + c.ResponseError(errors.New("导入文件的行数超过300行,请调整行数后重新导入"))
  660 + return
  661 + }
  662 +
  663 + // 空文件校验
  664 + if len(rows) < 3 {
  665 + c.ResponseError(errors.New("导入的excel文件为空文件,请上传正确的文件"))
  666 + }
  667 +
  668 + // 必填项校验
  669 + nullLine := make([]interface{}, 0)
  670 + nullFlag := false
  671 + for i, row := range rows {
  672 + if i > 2 {
  673 + if len(row) == constant.EXCEL_COLUMN { // 中间空字符校验
  674 + var myRow = row
  675 + for j, cell := range row {
  676 + if j != 8 { // 业务员抽成比例不校验
  677 + if cell == "" || cell == " " { // 空字符串填充
  678 + myRow[j] = "null"
  679 + nullFlag = true
  680 + }
  681 + }
  682 + }
  683 + if nullFlag {
  684 + myRow = append(myRow, "必填项不能为空") // 错误信息
  685 + s := strconv.Itoa(i + 1)
  686 + myRow = append(myRow, s) // 行号
  687 + myRow = append(myRow, row...) // 错误行数据
  688 + nullLine = append(nullLine, myRow)
  689 + nullFlag = false
  690 + }
  691 + } else if len(row) < constant.EXCEL_COLUMN && len(row) > 0 { // 尾部空字符校验
  692 + var myRow []string
  693 + for i := 0; i < constant.EXCEL_COLUMN-len(row); i++ {
  694 + myRow = append(myRow, "null")
  695 + }
  696 + myRow = append(myRow, "必填项不能为空") // 错误信息
  697 + s := strconv.Itoa(i + 1)
  698 + myRow = append(myRow, s) // 行号
  699 + myRow = append(myRow, row...) // 错误行数据
  700 + nullLine = append(nullLine, myRow)
  701 + }
  702 + }
  703 + }
  704 +
  705 + if len(nullLine) > 0 {
  706 + ret = map[string]interface{}{
  707 + "successCount": 0,
  708 + "fail": map[string]interface{}{
  709 + "tableHeader": tableHeader,
  710 + "tableData": nullLine,
  711 + },
  712 + }
  713 + c.ResponseData(ret)
  714 + return
  715 + }
  716 +
  717 + // 单元格长度、内容校验
  718 + errorLine := make([]interface{}, 0)
  719 + var partnerType = []string{"事业合伙", "业务合伙", "研发合伙", "业务-产品应用合伙"}
  720 + for i, row := range rows {
  721 + if i > 2 && len(row) == constant.EXCEL_COLUMN { // 数据行
  722 + var myRow []string
  723 + for j, cell := range row {
  724 + switch j {
  725 + case 0, 1, 2, 3, 4, 5, 8: // 订单号、发货单号、客户名称、订单区域、编号、合伙人、产品名称、
  726 + {
  727 + if len(cell) > 50 {
  728 + var tmpRow []string
  729 + tmpRow = append(tmpRow, tableHeader[j+2]+"长度超过50,请重新输入") // 错误信息
  730 + s := strconv.Itoa(i + 1)
  731 + tmpRow = append(tmpRow, s) // 行号
  732 + tmpRow = append(tmpRow, row...) // 错误行数据
  733 + myRow = tmpRow
  734 + }
  735 + }
  736 + case 6: // 合伙人类型校验(事业合伙、业务合伙、研发合伙、业务-产品应用合伙)
  737 + {
  738 + if !utils.IsContain(partnerType, cell) {
  739 + var tmpRow []string
  740 + tmpRow = append(tmpRow, "合伙人类型须为以下类型:事业合伙、业务合伙、研发合伙、业务-产品应用合伙") // 错误信息
  741 + s := strconv.Itoa(i + 1)
  742 + tmpRow = append(tmpRow, s) // 行号
  743 + tmpRow = append(tmpRow, row...) // 错误行数据
  744 + myRow = tmpRow
  745 + }
  746 + }
  747 + case 7: // 业务员抽成比例,非必填,精确到小数点后两位
  748 + {
  749 + if len(cell) > 0 {
  750 + _, err := strconv.ParseFloat(cell, 64)
  751 + if err != nil {
  752 + var tmpRow []string
  753 + tmpRow = append(tmpRow, "业务员抽成比例格式错误,请输入正确的业务员抽成比例比例,保留两位小数") // 错误信息
  754 + s := strconv.Itoa(i + 1)
  755 + tmpRow = append(tmpRow, s) // 行号
  756 + tmpRow = append(tmpRow, row...) // 错误行数据
  757 + myRow = tmpRow
  758 + }
  759 + //if isOk, _ := regexp.MatchString("^(.[0-9]{0,1,2})?$", cell); !isOk { // 最多两位小数
  760 + // var tmpRow []string
  761 + // tmpRow = append(tmpRow, "业务员员抽成比例格式错误,请输入正确的单价,保留两位小数") // 错误信息
  762 + // s := strconv.Itoa(i + 1)
  763 + // tmpRow = append(tmpRow, s) // 行号
  764 + // tmpRow = append(tmpRow, row...) // 错误行数据
  765 + // myRow = tmpRow
  766 + //}
  767 + }
  768 + }
  769 + case 9: // 数量不超过16位正整数
  770 + {
  771 + _, err := strconv.ParseInt(cell, 10, 64)
  772 + if err != nil {
  773 + var tmpRow []string
  774 + tmpRow = append(tmpRow, "数量须为整数,请重新填写") // 错误信息
  775 + s := strconv.Itoa(i + 1)
  776 + tmpRow = append(tmpRow, s) // 行号
  777 + tmpRow = append(tmpRow, row...) // 错误行数据
  778 + myRow = tmpRow
  779 + }
  780 +
  781 + if len(cell) > 16 {
  782 + var tmpRow []string
  783 + tmpRow = append(tmpRow, "数量长度超过最大限制十六位整数,请重新填写") // 错误信息
  784 + s := strconv.Itoa(i + 1)
  785 + tmpRow = append(tmpRow, s) // 行号
  786 + tmpRow = append(tmpRow, row...) // 错误行数据
  787 + myRow = tmpRow
  788 + }
  789 + }
  790 + case 10: // 单价,精确到小数点后两位,小数点左侧最多可输入16位数字
  791 + {
  792 + _, err := strconv.ParseFloat(cell, 64)
  793 + if err != nil {
  794 + var tmpRow []string
  795 + tmpRow = append(tmpRow, "单价格式错误,请输入正确的单价,保留两位小数点,小数点前面不能超过十六位数字") // 错误信息
  796 + s := strconv.Itoa(i + 1)
  797 + tmpRow = append(tmpRow, s) // 行号
  798 + tmpRow = append(tmpRow, row...) // 错误行数据
  799 + myRow = tmpRow
  800 + }
  801 + //if isOk, _ := regexp.MatchString("^d{1,16}(.[0-9]{0,1,2})?$", cell); !isOk { // 非负的16位整数最多两位小数
  802 + // var tmpRow []string
  803 + // tmpRow = append(tmpRow, "单价格式错误,请输入正确的单价,保留两位小数点,小数点前面不能超过十六位数字") // 错误信息
  804 + // s := strconv.Itoa(i + 1)
  805 + // tmpRow = append(tmpRow, s) // 行号
  806 + // tmpRow = append(tmpRow, row...) // 错误行数据
  807 + // myRow = tmpRow
  808 + //}
  809 + }
  810 + case 11: // 合伙人分红比例,精确到小数点后两位
  811 + {
  812 + _, err := strconv.ParseFloat(cell, 64)
  813 + if err != nil {
  814 + var tmpRow []string
  815 + tmpRow = append(tmpRow, "合伙人分红比例格式错误,请输入正确的合伙人分红比例,保留两位小数") // 错误信息
  816 + s := strconv.Itoa(i + 1)
  817 + tmpRow = append(tmpRow, s) // 行号
  818 + tmpRow = append(tmpRow, row...) // 错误行数据
  819 + myRow = tmpRow
  820 + }
  821 + //if isOk, _ := regexp.MatchString("^(.[0-9]{1,2})?$", cell); !isOk { // 最多两位小数
  822 + // var tmpRow []string
  823 + // tmpRow = append(tmpRow, "合伙人分红比例错误,请输入正确的合伙人分红比例,保留两位小数") // 错误信息
  824 + // s := strconv.Itoa(i + 1)
  825 + // tmpRow = append(tmpRow, s) // 行号
  826 + // tmpRow = append(tmpRow, row...) // 错误行数据
  827 + // myRow = tmpRow
  828 + //}
  829 + }
  830 + }
  831 + }
  832 + errorLine = append(errorLine, myRow)
  833 + }
  834 + }
  835 +
  836 + if len(errorLine) > 0 {
  837 + ret = map[string]interface{}{
  838 + "successCount": 0,
  839 + "fail": map[string]interface{}{
  840 + "tableHeader": tableHeader,
  841 + "tableData": errorLine,
  842 + },
  843 + }
  844 + c.ResponseData(ret)
  845 + return
  846 + }
  847 +
  848 + // 归类订单
  849 + for i, row := range rows {
  850 + if i > 2 && len(row) == 13 {
  851 +
  852 + }
  853 + }
  854 +
  855 + // 新增失败记录
  856 + failureDataList := make([]interface{}, 0)
  857 +
  858 + // 新增成功记录计数
  859 + //var successDataCount int64
  860 +
  861 + c.ResponseData(failureDataList)
  862 +
  863 + return
  864 +}
@@ -36,19 +36,18 @@ func init() { @@ -36,19 +36,18 @@ func init() {
36 ), 36 ),
37 beego.NSNamespace("/order", 37 beego.NSNamespace("/order",
38 beego.NSRouter("/actual/list", &controllers.OrderInfoController{}, "POST:PageListOrderReal"), 38 beego.NSRouter("/actual/list", &controllers.OrderInfoController{}, "POST:PageListOrderReal"),
39 - beego.NSRouter("/actual/list/excel", &controllers.OrderInfoController{}, "POST:ListOrderForExcel"), 39 + beego.NSRouter("/actual/list/excel", &controllers.OrderInfoController{}, "POST:ListOrderForExcel"), // 导出excel
  40 + beego.NSRouter("/actual/import/excel", &controllers.OrderInfoController{}, "POST:ImportOrderFromExcel"), // 导入订单数据
40 beego.NSRouter("/actual/detail", &controllers.OrderInfoController{}, "POST:GetOrderReal"), 41 beego.NSRouter("/actual/detail", &controllers.OrderInfoController{}, "POST:GetOrderReal"),
41 beego.NSRouter("/actual/del", &controllers.OrderInfoController{}, "POST:RemoveOrderReal"), 42 beego.NSRouter("/actual/del", &controllers.OrderInfoController{}, "POST:RemoveOrderReal"),
42 beego.NSRouter("/actual/update", &controllers.OrderInfoController{}, "POST:UpdateOrderReal"), 43 beego.NSRouter("/actual/update", &controllers.OrderInfoController{}, "POST:UpdateOrderReal"),
43 beego.NSRouter("/actual/close", &controllers.OrderInfoController{}, "POST:OrderDisable"), 44 beego.NSRouter("/actual/close", &controllers.OrderInfoController{}, "POST:OrderDisable"),
44 ), 45 ),
45 -  
46 beego.NSNamespace("/common", 46 beego.NSNamespace("/common",
47 beego.NSRouter("/partner", &controllers.CommonController{}, "POST:GetPartnerList"), 47 beego.NSRouter("/partner", &controllers.CommonController{}, "POST:GetPartnerList"),
48 beego.NSRouter("/partnerType", &controllers.CommonController{}, "POST:GetPartnerCategory"), 48 beego.NSRouter("/partnerType", &controllers.CommonController{}, "POST:GetPartnerCategory"),
49 beego.NSRouter("/orderType", &controllers.CommonController{}, "POST:GetOrderType"), 49 beego.NSRouter("/orderType", &controllers.CommonController{}, "POST:GetOrderType"),
50 ), 50 ),
51 -  
52 beego.NSNamespace("/enterprises", 51 beego.NSNamespace("/enterprises",
53 beego.NSRouter("/setPhone", &controllers.CompanyController{}, "POST:SetPhone"), 52 beego.NSRouter("/setPhone", &controllers.CompanyController{}, "POST:SetPhone"),
54 ), 53 ),
  1 +package utils
  2 +
  3 +import (
  4 + "github.com/astaxie/beego/context"
  5 + "github.com/linmadan/egglib-go/core/application"
  6 +)
  7 +
  8 +type JsonResponse map[string]interface{}
  9 +
  10 +func ResponseData(ctx *context.Context, data interface{}) JsonResponse {
  11 + jsonResponse := JsonResponse{}
  12 + jsonResponse["code"] = 0
  13 + jsonResponse["msg"] = "ok"
  14 + jsonResponse["data"] = data
  15 + ctx.Input.SetData("outputData", jsonResponse)
  16 + return jsonResponse
  17 +}
  18 +
  19 +func ResponseError(ctx *context.Context, err error) JsonResponse {
  20 + serviceError := err.(*application.ServiceError)
  21 + jsonResponse := JsonResponse{}
  22 + jsonResponse["code"] = serviceError.Code
  23 + jsonResponse["msg"] = serviceError.Error()
  24 + ctx.Input.SetData("outputData", jsonResponse)
  25 + return jsonResponse
  26 +}
@@ -105,6 +105,7 @@ github.com/klauspost/compress/zlib @@ -105,6 +105,7 @@ github.com/klauspost/compress/zlib
105 github.com/linmadan/egglib-go/core/application 105 github.com/linmadan/egglib-go/core/application
106 github.com/linmadan/egglib-go/core/domain 106 github.com/linmadan/egglib-go/core/domain
107 github.com/linmadan/egglib-go/utils/snowflake 107 github.com/linmadan/egglib-go/utils/snowflake
  108 +github.com/linmadan/egglib-go/web/beego/utils
108 # github.com/mattn/go-colorable v0.1.6 109 # github.com/mattn/go-colorable v0.1.6
109 ## explicit 110 ## explicit
110 # github.com/matttproud/golang_protobuf_extensions v1.0.1 111 # github.com/matttproud/golang_protobuf_extensions v1.0.1