|
|
@@ -3,24 +3,31 @@ package cust
|
|
|
import (
|
|
|
"bytes"
|
|
|
"context"
|
|
|
- "encoding/json"
|
|
|
- "fmt"
|
|
|
- "github.com/gogf/gf/container/garray"
|
|
|
- "math"
|
|
|
- "strconv"
|
|
|
- "strings"
|
|
|
- "time"
|
|
|
-
|
|
|
+ "dashoo.cn/micro/app/model/base"
|
|
|
+ "dashoo.cn/micro/app/model/contract"
|
|
|
+ server "dashoo.cn/micro/app/service/base"
|
|
|
+ "dashoo.cn/micro/app/service/partner"
|
|
|
"dashoo.cn/opms_libary/myerrors"
|
|
|
"dashoo.cn/opms_libary/plugin/dingtalk/message"
|
|
|
"dashoo.cn/opms_libary/request"
|
|
|
+ "encoding/base64"
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
"github.com/360EntSecGroup-Skylar/excelize"
|
|
|
+ "github.com/gogf/gf/container/garray"
|
|
|
"github.com/gogf/gf/database/gdb"
|
|
|
"github.com/gogf/gf/encoding/gjson"
|
|
|
"github.com/gogf/gf/frame/g"
|
|
|
"github.com/gogf/gf/os/gtime"
|
|
|
+ "github.com/gogf/gf/text/gstr"
|
|
|
"github.com/gogf/gf/util/gconv"
|
|
|
+ "github.com/gogf/gf/util/gvalid"
|
|
|
"github.com/mozillazg/go-pinyin"
|
|
|
+ excelizev2 "github.com/xuri/excelize/v2"
|
|
|
+ "math"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
|
|
|
"dashoo.cn/micro/app/dao/cust"
|
|
|
platdao "dashoo.cn/micro/app/dao/plat"
|
|
|
@@ -1277,3 +1284,335 @@ func (s *CustomerService) Export(ctx context.Context, req *model.CustCustomerExp
|
|
|
|
|
|
return &con, err
|
|
|
}
|
|
|
+func reverseMap(originalMap map[string]string) map[string]string {
|
|
|
+ reversedMap := make(map[string]string)
|
|
|
+ for key, value := range originalMap {
|
|
|
+ reversedMap[value] = key
|
|
|
+ }
|
|
|
+ return reversedMap
|
|
|
+}
|
|
|
+
|
|
|
+var contactExcelHeader = []partner.ExcelHeader{
|
|
|
+ {Name: "*客户名称", Width: 30},
|
|
|
+ {Name: "助计名", Width: 30},
|
|
|
+ {Name: "*客户类型", Width: 30},
|
|
|
+ {Name: "*客户来源", Width: 30},
|
|
|
+ {Name: "所在省", Width: 30},
|
|
|
+ {Name: "所在市", Width: 30},
|
|
|
+ {Name: "所在区县", Width: 30},
|
|
|
+ {Name: "详细地址", Width: 30},
|
|
|
+ {Name: "招标关键字(以逗号分隔)", Width: 40},
|
|
|
+ {Name: "备注", Width: 30},
|
|
|
+ {Name: "*联系人姓名", Width: 30},
|
|
|
+ {Name: "*联系人性别", Width: 30},
|
|
|
+ {Name: "*联系人电话", Width: 30},
|
|
|
+ {Name: "联系人微信", Width: 30},
|
|
|
+ {Name: "联系人邮箱", Width: 30},
|
|
|
+ {Name: "联系人部门", Width: 30},
|
|
|
+ {Name: "*联系人职位", Width: 30},
|
|
|
+ {Name: "联系人办公地点", Width: 30},
|
|
|
+ {Name: "*关键决策人(是/否)", Width: 30},
|
|
|
+ {Name: "联系人备注", Width: 30},
|
|
|
+}
|
|
|
+
|
|
|
+var contactFailedExcelHeader = []partner.ExcelHeader{
|
|
|
+ {Name: "*客户名称", Width: 30},
|
|
|
+ {Name: "助计名", Width: 30},
|
|
|
+ {Name: "*客户类型", Width: 30},
|
|
|
+ {Name: "*客户来源", Width: 30},
|
|
|
+ {Name: "所在省", Width: 30},
|
|
|
+ {Name: "所在市", Width: 30},
|
|
|
+ {Name: "所在区县", Width: 30},
|
|
|
+ {Name: "详细地址", Width: 30},
|
|
|
+ {Name: "招标关键字(以逗号分隔)", Width: 40},
|
|
|
+ {Name: "备注", Width: 30},
|
|
|
+ {Name: "*联系人姓名", Width: 30},
|
|
|
+ {Name: "*联系人性别", Width: 30},
|
|
|
+ {Name: "*联系人电话", Width: 30},
|
|
|
+ {Name: "联系人微信", Width: 30},
|
|
|
+ {Name: "联系人邮箱", Width: 30},
|
|
|
+ {Name: "联系人部门", Width: 30},
|
|
|
+ {Name: "*联系人职位", Width: 30},
|
|
|
+ {Name: "联系人办公地点", Width: 30},
|
|
|
+ {Name: "*关键决策人(是/否)", Width: 30},
|
|
|
+ {Name: "联系人备注", Width: 30},
|
|
|
+ {Name: "失败原因", Width: 30},
|
|
|
+}
|
|
|
+
|
|
|
+func ExcelTemplate() (*excelizev2.File, error) {
|
|
|
+ tempData := [][]string{
|
|
|
+ {
|
|
|
+ "张三", "张三(备注)", "医院", "经销商", "北京市", "北京市", "朝阳区", "ffff", "20387,生物样本库", "备注",
|
|
|
+ "李四", "男", "18000000000", "weixin", "abc@163.com", "部门", "职务", "办公地点", "否", "联系人备注",
|
|
|
+ },
|
|
|
+ }
|
|
|
+ excel, err := partner.NewExcel(contactExcelHeader, tempData)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return excel, nil
|
|
|
+}
|
|
|
+
|
|
|
+// Create 导入客户
|
|
|
+func (s *CustomerService) Import(ctx context.Context, req *contract.ExcelImportReq) (err error, faildCount int, faildData string) {
|
|
|
+ validErr := gvalid.CheckStruct(ctx, req, nil)
|
|
|
+ if validErr != nil {
|
|
|
+ return myerrors.TipsError(validErr.Current().Error()), 0, ""
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下载文件
|
|
|
+ buf, err := partner.DownFile(req.ExcelUrl)
|
|
|
+ if err != nil {
|
|
|
+ return myerrors.TipsError(fmt.Sprintf("下载 excel 异常 %s", err.Error())), 0, ""
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析excel
|
|
|
+ excelData, err := s.parseExcel(buf)
|
|
|
+ if err != nil {
|
|
|
+ return myerrors.TipsError(fmt.Sprintf("解析 excel 异常 %s", err.Error())), 0, ""
|
|
|
+ }
|
|
|
+ //客户类型
|
|
|
+ idyMap, err := service.GetDictDataByType(ctx, "cust_idy")
|
|
|
+ if err != nil {
|
|
|
+ return err, 0, ""
|
|
|
+ }
|
|
|
+ idyMap = reverseMap(idyMap)
|
|
|
+ //客户来源
|
|
|
+ sourceMap, err := service.GetDictDataByType(ctx, "cust_source")
|
|
|
+ if err != nil {
|
|
|
+ return err, 0, ""
|
|
|
+ }
|
|
|
+ sourceMap = reverseMap(sourceMap)
|
|
|
+
|
|
|
+ //关键字
|
|
|
+ keywordsMap, err := service.GetDictDataByType(ctx, "customer_bidding_keywords")
|
|
|
+ if err != nil {
|
|
|
+ return err, 0, ""
|
|
|
+ }
|
|
|
+ keywordsMap = reverseMap(keywordsMap)
|
|
|
+ svc, err := server.NewDistrictService(ctx)
|
|
|
+ treeList, err := svc.GetProvincesList(0)
|
|
|
+ contactService, err := NewCustomerContactService(ctx)
|
|
|
+ if err != nil {
|
|
|
+ return err, 0, ""
|
|
|
+ }
|
|
|
+ CustomerService, err := NewCustomerService(ctx)
|
|
|
+ if err != nil {
|
|
|
+ return err, 0, ""
|
|
|
+ }
|
|
|
+ yesOrNoMap := map[string]string{
|
|
|
+ "是": "10",
|
|
|
+ "否": "20",
|
|
|
+ }
|
|
|
+ genderMap := map[string]string{
|
|
|
+ "男": "10",
|
|
|
+ "女": "20",
|
|
|
+ }
|
|
|
+ failedList := make([]*model.CustomerAddImport, 0)
|
|
|
+
|
|
|
+ e := s.Dao.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
|
|
|
+ for _, data := range excelData {
|
|
|
+ var province *base.ProvincesTree
|
|
|
+ var city *base.ProvincesTree
|
|
|
+ var region *base.ProvincesTree
|
|
|
+
|
|
|
+ keywords := []string{}
|
|
|
+ data.Keyword = gstr.Trim(data.Keyword)
|
|
|
+ if data.Keyword != "" && data.Keyword != "无" && data.Keyword != "-" {
|
|
|
+ keywords = strings.Split(data.Keyword, ",")
|
|
|
+
|
|
|
+ for _, keyword := range keywords {
|
|
|
+ if _, ok := keywordsMap[keyword]; !ok {
|
|
|
+ data.FailedReason = fmt.Sprintf("关键字 %s 不存在", keyword)
|
|
|
+
|
|
|
+ //return myerrors.TipsError(fmt.Sprintf("关键字 %s 不存在", keyword))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if data.FailedReason != "" {
|
|
|
+ failedList = append(failedList, data)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if data.CustProvince == "" {
|
|
|
+ data.FailedReason = "客户省份不能为空"
|
|
|
+ failedList = append(failedList, data)
|
|
|
+ continue
|
|
|
+ //return myerrors.TipsError("客户省份不能为空")
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tree := range treeList {
|
|
|
+ if tree.DistName == data.CustProvince {
|
|
|
+ province = tree
|
|
|
+ if data.CustCity != "" && province.Children != nil {
|
|
|
+ for _, cityData := range province.Children {
|
|
|
+ if cityData.DistName == data.CustCity {
|
|
|
+ city = cityData
|
|
|
+ if data.CustRegion != "" && city.Children != nil {
|
|
|
+ for _, RegionData := range cityData.Children {
|
|
|
+ if RegionData.DistName == data.CustRegion {
|
|
|
+ region = RegionData
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (province == nil) || (city == nil && data.CustCity != "") || (region == nil && data.CustRegion != "") {
|
|
|
+ data.FailedReason = "省市区填写错误,请检查后重试"
|
|
|
+ failedList = append(failedList, data)
|
|
|
+ continue
|
|
|
+ //return myerrors.TipsError("客户省份不能为空")
|
|
|
+ //return myerrors.TipsError("省市区填写错误,请检查后重试")
|
|
|
+ }
|
|
|
+ if idyMap[data.CustIndustry] == "" {
|
|
|
+ data.FailedReason = "客户类型填写错误,请检查后重试"
|
|
|
+ failedList = append(failedList, data)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if sourceMap[data.CustSource] == "" {
|
|
|
+ data.FailedReason = "客户来源填写错误,请检查后重试"
|
|
|
+ failedList = append(failedList, data)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ insertCustomer := &model.CustomerAddSeq{
|
|
|
+ CustName: data.CustName,
|
|
|
+ AbbrName: data.AbbrName,
|
|
|
+ CustAddress: data.CustAddress,
|
|
|
+ CustProvinceId: province.Id,
|
|
|
+ CustProvince: data.CustProvince,
|
|
|
+ CustCityId: city.Id,
|
|
|
+ CustCity: data.CustCity,
|
|
|
+ CustRegionId: region.Id,
|
|
|
+ CustRegion: data.CustRegion,
|
|
|
+ CustIndustry: idyMap[data.CustIndustry],
|
|
|
+ CustSource: sourceMap[data.CustSource],
|
|
|
+ CustDistCode: province.Id,
|
|
|
+ Remark: data.Remark,
|
|
|
+ Keyword: keywords,
|
|
|
+ }
|
|
|
+
|
|
|
+ id, err := s.Create(insertCustomer)
|
|
|
+ if err != nil {
|
|
|
+ if err.Error() == "该客户信息已存在,不可重复添加" {
|
|
|
+ g.Log(fmt.Sprintf("[%s]%s", data.CustName, "该客户信息已存在,不可重复添加"))
|
|
|
+ g.Log().Error(err)
|
|
|
+ data.FailedReason = fmt.Sprintf("[%s]%s", data.CustName, "该客户信息已存在,不可重复添加")
|
|
|
+ failedList = append(failedList, data)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ g.Log().Error(err)
|
|
|
+
|
|
|
+ //return myerrors.TipsError(fmt.Sprintf("[%s]%s", data.CustName, "数据有误,请联系管理员"))
|
|
|
+ }
|
|
|
+ s.CreateDynamics("创建客户", req, id)
|
|
|
+ data.CuctName = gstr.Trim(data.CuctName)
|
|
|
+ if data.CuctName != "无" && data.CuctName != "" {
|
|
|
+ seq := model.CustCustomerContactSeq{
|
|
|
+ CustId: int(id),
|
|
|
+ CuctName: data.CuctName,
|
|
|
+ CuctGender: genderMap[data.CuctGender],
|
|
|
+ Telephone: data.Telephone,
|
|
|
+ Wechat: data.Wechat,
|
|
|
+ Email: data.Email,
|
|
|
+ Dept: data.Dept,
|
|
|
+ Postion: data.Postion,
|
|
|
+ OfficeLocation: data.OfficeLocation,
|
|
|
+ IsDecision: yesOrNoMap[data.IsDecision],
|
|
|
+ Remark: data.ContactRemark,
|
|
|
+ }
|
|
|
+ err = contactService.Create(&seq)
|
|
|
+ if err != nil {
|
|
|
+ data.FailedReason = "添加联系人 验证失败"
|
|
|
+ failedList = append(failedList, data)
|
|
|
+ continue
|
|
|
+ //return err
|
|
|
+ }
|
|
|
+
|
|
|
+ CustomerService.CreateDynamics("创建联系人", req, gconv.Int64(seq.CustId))
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+
|
|
|
+ if e != nil {
|
|
|
+ return e, 0, ""
|
|
|
+ }
|
|
|
+ if len(failedList) > 0 {
|
|
|
+ f, err := ExportFailedData(failedList)
|
|
|
+ if err != nil {
|
|
|
+ return err, 0, ""
|
|
|
+ }
|
|
|
+ buf, err := f.WriteToBuffer()
|
|
|
+ if err != nil {
|
|
|
+ return err, 0, ""
|
|
|
+ }
|
|
|
+ faildData = base64.StdEncoding.EncodeToString(buf.Bytes())
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil, len(failedList), faildData
|
|
|
+}
|
|
|
+
|
|
|
+// excel解构为数据
|
|
|
+func (s CustomerService) parseExcel(b []byte) ([]*model.CustomerAddImport, error) {
|
|
|
+ f, err := excelize.OpenReader(bytes.NewBuffer(b))
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ sheet := "Sheet1"
|
|
|
+ rows := f.GetRows(sheet)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ // 客户名称 助计名 所在地区 详细地址 所在省 所在市 所在区县 客户行业 客户级别 客户来源 备注 销售名称 开票抬头 关联客户 姓名 性别 电话 微信 邮箱 部门 职位 办公地点 是否关键决策人 招标关键字
|
|
|
+ var saleTargets []*model.CustomerAddImport
|
|
|
+ for _, row := range rows[1:] {
|
|
|
+ temp := &model.CustomerAddImport{
|
|
|
+ CustName: row[0],
|
|
|
+ AbbrName: row[1],
|
|
|
+ CustIndustry: row[2],
|
|
|
+ CustSource: row[3],
|
|
|
+ CustProvince: row[4],
|
|
|
+ CustCity: row[5],
|
|
|
+ CustRegion: row[6],
|
|
|
+ CustAddress: row[7],
|
|
|
+ Keyword: row[8],
|
|
|
+ Remark: row[9],
|
|
|
+ CuctName: row[10],
|
|
|
+ CuctGender: row[11],
|
|
|
+ Telephone: row[12],
|
|
|
+ Wechat: row[13],
|
|
|
+ Email: row[14],
|
|
|
+ Dept: row[15],
|
|
|
+ Postion: row[16],
|
|
|
+ OfficeLocation: row[17],
|
|
|
+ IsDecision: row[18],
|
|
|
+ ContactRemark: row[19],
|
|
|
+ }
|
|
|
+
|
|
|
+ saleTargets = append(saleTargets, temp)
|
|
|
+ }
|
|
|
+
|
|
|
+ return saleTargets, nil
|
|
|
+}
|
|
|
+func ExportFailedData(failedList []*model.CustomerAddImport) (*excelizev2.File, error) {
|
|
|
+ tempData := [][]string{}
|
|
|
+ for _, data := range failedList {
|
|
|
+ tempData = append(tempData, []string{
|
|
|
+ data.CustName, data.AbbrName, data.CustIndustry, data.CustSource, data.CustProvince, data.CustCity, data.CustRegion, data.CustAddress, data.Keyword, data.Remark,
|
|
|
+ data.CuctName, data.CuctGender, data.Telephone, data.Wechat, data.Email, data.Dept, data.Postion, data.OfficeLocation, data.IsDecision, data.ContactRemark, data.FailedReason,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ excel, err := partner.NewExcel(contactFailedExcelHeader, tempData)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return excel, nil
|
|
|
+}
|