package cust import ( "bytes" "context" "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" model "dashoo.cn/micro/app/model/cust" workflowModel "dashoo.cn/micro/app/model/workflow" "dashoo.cn/micro/app/service" ) type CustomerService struct { *service.ContextService Dao *cust.CustCustomerDao BelongDao *cust.CustCustomerBelongDao DynamicsDao *cust.CustCustomerDynamicsDao ContactDao *cust.CustCustomerContactDao FollowDao *platdao.PlatFollowupDao BelongServer *CustomerbelongService ContanctServer *CustomercontactService } var isPublic, noPublic = "10", "20" // 公海,非公海 var AllocaTion, OperaTion, Receive, Merge = "10", "20", "30", "40" // 10分配20转移30领取40合并 type OpnType struct { OperaTion string } func NewCustomerService(ctx context.Context) (svc *CustomerService, err error) { svc = new(CustomerService) if svc.ContextService, err = svc.Init(ctx); err != nil { return nil, err } svc.Dao = cust.NewCustCustomerDao(svc.Tenant) svc.BelongDao = cust.NewCustCustomerBelongDao(svc.Tenant) svc.DynamicsDao = cust.NewCustCustomerDynamicsDao(svc.Tenant) svc.ContactDao = cust.NewCustCustomerContactDao(svc.Tenant) svc.FollowDao = platdao.NewPlatFollowupDao(svc.Tenant) svc.BelongServer, _ = NewCustomerBelongService(ctx) svc.ContanctServer, _ = NewCustomerContactService(ctx) return svc, nil } // GetList 客户列表列表 func (s *CustomerService) GetList(req *model.CustCustomerSearchReq) (total int, customerList []*model.CustList, err error) { Model := s.Dao.FieldsEx(s.Dao.C.DeletedTime) if req.IsPublic { Model = Model.Where(s.Dao.C.IsPublic, isPublic) } else { Model = Model.Where(s.Dao.C.IsPublic, noPublic) if req.TargetType == "self" { Model = Model.Where(s.Dao.C.SalesId, s.CxtUser.Id) } else if req.TargetType == "other" { Model = Model.Where(s.Dao.C.SalesId, s.DataScope["userIds"]).WhereNot(s.Dao.C.SalesId, s.CxtUser.Id) } else { Model = Model.DataScope(s.Ctx, "sales_id").Where(s.Dao.C.IsPublic, noPublic) } } //客户名称 if req.CustName != "" { Model = Model.Where(s.Dao.C.CustName+" like ?", "%"+req.CustName+"%") } //客户编码 if req.CustCode != "" { Model = Model.Where(s.Dao.C.CustCode+" like ?", "%"+req.CustCode+"%") } //客户行业 if req.CustIndustry != "" { Model = Model.Where(s.Dao.C.CustIndustry, req.CustIndustry) } //客户级别 if req.CustLevel != "" { Model = Model.Where(s.Dao.C.CustLevel, req.CustLevel) } // //if req.FollowUpDate != "" { // Model = Model.Where(s.Dao.C.FollowUpDate+" like ? ", req.FollowUpDate+"%") //} //省份 if req.CustProvince != "" { Model = Model.Where(s.Dao.C.CustProvince, req.CustProvince) } //市 if req.CustCity != "" { Model = Model.Where(s.Dao.C.CustCity, req.CustCity) } //所属销售 if req.SalesName != "" { Model = Model.Where(s.Dao.C.SalesName+" like ?", "%"+req.SalesName+"%") } // 获取日期区间范围内的记录 if req.BeginTime != "" && req.EndTime != "" { begin := strings.Split(req.BeginTime, " ")[0] + " 00:00:00" end := strings.Split(req.EndTime, " ")[0] + " 23:59:59" Model = Model.WhereBetween(s.Dao.C.FollowUpDate, begin, end) } if req.IsRemovePage { Model = Model } else { Model = Model.Page(req.GetPage()) } total, err = Model.Count() if err != nil { g.Log().Error(err) err = myerrors.DbError("获取总行数失败。") return } err = Model.Order("id desc").Scan(&customerList) if err != nil { g.Log().Error(err) return } return } func (s *CustomerService) customerCode(province, industry string) (string, error) { sequence, err := service.Sequence(s.Dao.DB, "customer_code") if err != nil { return "", err } return s.customerProvinceCode(province) + industry + sequence, nil } func (s *CustomerService) customerProvinceCode(province string) string { if province == "重庆市" { return "CQ" } province = strings.Trim(province, "市") province = strings.Trim(province, "省") province = strings.Trim(province, "特别行政区") province = strings.Trim(province, "自治区") province = strings.Trim(province, "壮族") province = strings.Trim(province, "回族") province = strings.Trim(province, "维吾尔") pinyinArg := pinyin.NewArgs() pinyinArg.Style = pinyin.FIRST_LETTER provincePinyin := pinyin.Pinyin(province, pinyinArg) provinceFirstletter := []string{} for _, i := range provincePinyin { provinceFirstletter = append(provinceFirstletter, i[0]) } provinceCode := strings.Join(provinceFirstletter, "") provinceCode = strings.ToUpper(provinceCode) fmt.Println(provincePinyin, provinceFirstletter, provinceCode) return provinceCode } // Create 创建客户 func (s *CustomerService) Create(req *model.CustomerAddSeq) (insertId int64, err error) { cusTomer := new(model.CustCustomer) count, err := s.Dao.Where(s.Dao.C.CustName, req.CustName).Count() if err != nil { g.Log().Error(err) return } if count > 0 { return 0, myerrors.TipsError("该客户信息已存在,不可重复添加") } if err = gconv.Struct(req, cusTomer); err != nil { return } service.SetCreatedInfo(cusTomer, s.GetCxtUserId(), s.CxtUser.NickName) custCode, err := s.customerCode(req.CustProvince, req.CustIndustry) if err != nil { return 0, err } cusTomer.CustCode = custCode cusTomer.CustStatus = "10" roles := s.GetCxtUserRoles() isSales := false for _, v := range roles { if v == "SalesEngineer" || v == "ProductLineManager" { // 销售角色 isSales = true break } } if _, ok := s.DataScope["is_big"]; ok { isSales = true } // 销售角色 if isSales { cusTomer.IsPublic = noPublic cusTomer.SalesId = s.GetCxtUserId() cusTomer.SalesName = s.CxtUser.NickName cusTomer.CustStatus = "30" insertId, err = s.Dao.InsertAndGetId(cusTomer) if err != nil { g.Log().Error(err) return 0, err } err = s.CreateBelong(gconv.Int(insertId)) return insertId, err } else { cusTomer.IsPublic = isPublic insertId, err = s.Dao.InsertAndGetId(cusTomer) if err != nil { g.Log().Error(err) return 0, err } } return insertId, err } // CreateBelong 创建客户归属信息 func (s *CustomerService) CreateBelong(custId int) (err error) { belong := new(model.CustomerBelongAddSeq) belong.CustId = custId belong.SaleName = s.CxtUser.NickName belong.OpnType = AllocaTion belong.OpnPeople = s.CxtUser.NickName err = s.BelongServer.Create(belong) if err != nil { g.Log().Error(err) return } return } // 删除客户 func (s *CustomerService) DeleteByIds(Ids []int64) (err error) { customerCount, err := s.Dao.Where(" id in (?)", Ids).Count() if err != nil { g.Log().Error(err) return } if customerCount == 0 { err = myerrors.TipsError("客户信息不存在") return } //删除客户表 s.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { //客户表 _, err = s.Dao.TX(tx).Where("id in (?)", Ids).Delete() if err != nil { g.Log().Error(err) return err } //客户联系人表 _, err = s.ContactDao.Where("cust_id in (?)", Ids).Delete() if err != nil { g.Log().Error(err) return err } //客户归属表 _, err = s.BelongDao.Where("cust_id in (?)", Ids).Delete() if err != nil { g.Log().Error(err) return err } return nil }) return } // UpdateById 修改客户 func (s *CustomerService) UpdateById(req *model.UpdateCustomer) (err error) { //判断数据是否存在 count, err := s.Dao.Where("id = ", req.Id).Count() if err != nil { g.Log().Error(err) return } if count == 0 { return } //新的客户名字是否存在 num, err := s.Dao.Where(s.Dao.C.CustName, req.CustName).WhereNot(s.Dao.C.Id, req.Id).Count() if err != nil { g.Log().Error(err) return err } if num > 0 { return myerrors.TipsError(fmt.Sprintf("客户名称[%s]已存在", req.CustName)) } CustomertData := new(model.CustomerAddSeq) if err = gconv.Struct(req, CustomertData); err != nil { return } service.SetUpdatedInfo(CustomertData, s.GetCxtUserId(), s.CxtUser.NickName) _, err = s.Dao.FieldsEx(s.Dao.C.CreatedTime, s.Dao.C.CreatedBy, s.Dao.C.CreatedName, s.Dao.C.Id, s.Dao.C.CustCode, s.Dao.C.SalesName, s.Dao.C.SalesId). WherePri(s.Dao.C.Id, req.Id).Update(CustomertData) if err != nil { g.Log().Error(err) return } return } var AssignCustomerRequestProcessCode = "PROC-FE42B2D1-6097-4DE8-8AC5-23541B7D5C8A" // BizCode: 11 var MoveToPubicRequestProcessCode = "PROC-C030BD3D-74A3-4FE5-9EBA-DF3BA9F8AEF9" // BizCode: 12 var TransCustomerRequestProcessCode = "PROC-03BE93D3-9A44-43A1-BA6D-D1BB75702542" // BizCode: 13 // AssignCustomerRequest 领取客户申请 func (s *CustomerService) AssignCustomerRequest(ctx context.Context, req *model.AssignCustomerReq) error { if req.ApplyRemark == "" { return myerrors.TipsError("请输入申请说明") } data, err := s.Dao.Where("id in (?)", req.Ids).LockShared().All() if err != nil { return err } if len(data) == 0 { return myerrors.TipsError("领取用户不能为空") } for _, v := range data { if v.CustStatus != "10" { return myerrors.TipsError(fmt.Sprintf("客户: %s 已被领取", v.CustName)) } if v.CustProvince == "" { return myerrors.TipsError(fmt.Sprintf("请先完善客户: %s 的所在省信息", v.CustName)) } if v.CustCity == "" { return myerrors.TipsError(fmt.Sprintf("请先完善客户: %s 的所在市信息", v.CustName)) } if v.CustIndustry == "" { return myerrors.TipsError(fmt.Sprintf("请先完善客户: %s 的客户类型信息", v.CustName)) } } city := []string{} cityData := s.DataScope["cust_city_id"] if cityData != nil { for _, i := range cityData.([]interface{}) { city = append(city, i.(string)) } } fmt.Println(city) cust := []*model.CustCustomer{} noNeedApprovalCust := []*model.CustCustomer{} noNeedApprovalCustId := []int64{} for _, v := range data { if service.StringSlicecontains(city, strconv.Itoa(v.CustCityId)) { noNeedApprovalCust = append(noNeedApprovalCust, v) noNeedApprovalCustId = append(noNeedApprovalCustId, int64(v.Id)) } else { cust = append(cust, v) } } if len(noNeedApprovalCustId) != 0 { err = s.ChangeCustBelong(noNeedApprovalCustId, req.SalesId, req.SalesName) if err != nil { return err } req.Receive = AllocaTion err = s.BatchCreatebelong(data, req) if err != nil { return err } err = s.CreateDynamics("领取客户", &model.AssignCustomerReq{ Ids: noNeedApprovalCustId, SalesId: req.SalesId, SalesName: req.SalesName, Remark: "", Receive: Receive, }, noNeedApprovalCustId...) if err != nil { return err } } userId := s.GetCxtUserId() userName := s.GetCxtUserName() s.CxtUser = &request.UserInfo{Id: userId, UserName: userName} err = s.Dao.DB.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { for _, u := range cust { _, err = s.Dao.TX(tx).Where("id = ?", u.Id).Data(map[string]interface{}{ "is_public": isPublic, "cust_status": "10", }).Update() if err != nil { return err } err = s.ChangeCustBelong([]int64{int64(u.Id)}, int64(userId), userName) if err != nil { return err } err = s.BatchCreatebelong([]*model.CustCustomer{u}, &model.AssignCustomerReq{ Ids: []int64{int64(u.Id)}, SalesId: int64(userId), SalesName: userName, Remark: "", Receive: Receive, }) if err != nil { return err } err = s.CreateDynamics("领取客户", map[string]interface{}{ "userId": userId, "custId": u.Id, }, int64(u.Id)) if err != nil { return err } } return nil }) if err != nil { return err } return nil } func (s *CustomerService) AssignCustomerRequestApproval(flow *workflowModel.PlatWorkflow, msg *message.MixMessage) error { remark := map[string]string{} err := json.Unmarshal([]byte(flow.Remark), &remark) if err != nil { return err } userName := remark["applyUserName"] bizCode := strings.Split(flow.BizCode, ":") if len(bizCode) != 2 { return fmt.Errorf("客户领取审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id) } custId, err := strconv.Atoi(bizCode[0]) if err != nil { return fmt.Errorf("客户领取审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id) } userId, err := strconv.Atoi(bizCode[1]) if err != nil { return fmt.Errorf("客户领取审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id) } cust, err := s.Dao.Where("id = ?", custId).One() if err != nil { return err } if cust == nil { return fmt.Errorf("客户不存在:%s Id: %d", flow.BizCode, flow.Id) } if msg.ProcessType != "finish" && msg.ProcessType != "terminate" { return fmt.Errorf("无法识别的 ProcessType :%s", msg.ProcessType) } if msg.Result != "agree" && msg.Result != "refuse" && msg.Result != "" { return fmt.Errorf("无法识别的 Result :%s", msg.Result) } if msg.ProcessType == "terminate" { _, err = s.Dao.Where("id = ?", custId).Data(map[string]interface{}{ "is_public": isPublic, "cust_status": "10", }).Update() return err } pass := msg.Result == "agree" if !pass { _, err = s.Dao.Where("id = ?", custId).Data(map[string]interface{}{ "is_public": isPublic, "cust_status": "10", }).Update() return err } s.CxtUser = &request.UserInfo{Id: userId, UserName: userName} err = s.ChangeCustBelong([]int64{int64(custId)}, int64(userId), remark["applyUserName"]) if err != nil { return err } err = s.BatchCreatebelong([]*model.CustCustomer{cust}, &model.AssignCustomerReq{ Ids: []int64{int64(custId)}, SalesId: int64(userId), SalesName: userName, Remark: "", Receive: Receive, }) if err != nil { return err } s.CxtUser = &request.UserInfo{Id: userId, UserName: userName} return s.CreateDynamics("领取客户", map[string]interface{}{ "userId": userId, "custId": cust.Id, }, int64(cust.Id)) } // 移回公海 func (s *CustomerService) MoveToPublicRequest(ctx context.Context, req *model.MoveToPubicRep) error { if req.Remark == "" { return myerrors.TipsError("请输入移回原因") } data, err := s.Dao.Where("id in (?)", req.Ids).All() if err != nil { return err } if len(data) == 0 { return myerrors.TipsError("移回用户不能为空") } for _, v := range data { if v.CustStatus == "10" { return myerrors.TipsError(fmt.Sprintf("客户: %s 已被移回公海", v.CustName)) } if v.CustStatus == "20" { return myerrors.TipsError(fmt.Sprintf("客户: %s 正在等待审批", v.CustName)) } if v.CustProvince == "" { return myerrors.TipsError(fmt.Sprintf("请先完善客户: %s 的所在省信息", v.CustName)) } if v.CustCity == "" { return myerrors.TipsError(fmt.Sprintf("请先完善客户: %s 的所在市信息", v.CustName)) } if v.CustIndustry == "" { return myerrors.TipsError(fmt.Sprintf("请先完善客户: %s 的客户类型信息", v.CustName)) } } userId := s.GetCxtUserId() userName := s.GetCxtUserName() s.CxtUser = &request.UserInfo{Id: userId, UserName: userName} err = s.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { for _, u := range data { //更新客户信息 _, err = s.Dao.Where("id = ?", u.Id).Data(map[string]interface{}{ "cust_status": "10", "is_public": isPublic, "sales_id": 0, "dept_id": 0, "dept_name": "", "updated_time": gtime.Now(), }).Update() if err != nil { return err } //更新销售归属表结束时间 _, err = s.BelongDao.TX(tx).Data(g.Map{ "end_date": gtime.Now(), }).Where("cust_id = ?", u.Id).Update() if err != nil { return err } err = s.CreateDynamics("移入公海", map[string]interface{}{ "userId": userId, "custId": u.Id, }, int64(u.Id)) if err != nil { return err } } return nil }) if err != nil { return err } return nil } // 移入公海回调 func (s *CustomerService) MoveToPublicApproval(flow *workflowModel.PlatWorkflow, msg *message.MixMessage) error { remark := map[string]string{} err := json.Unmarshal([]byte(flow.Remark), &remark) if err != nil { return err } userName := remark["applyUserName"] bizCode := strings.Split(flow.BizCode, ":") if len(bizCode) != 2 { return fmt.Errorf("客户移回审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id) } custId, err := strconv.Atoi(bizCode[0]) if err != nil { return fmt.Errorf("客户移回审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id) } userId, err := strconv.Atoi(bizCode[1]) if err != nil { return fmt.Errorf("客户移回审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id) } cust, err := s.Dao.Where("id = ?", custId).One() if err != nil { return err } if cust == nil { return fmt.Errorf("客户不存在:%s Id: %d", flow.BizCode, flow.Id) } if msg.ProcessType != "finish" && msg.ProcessType != "terminate" { return fmt.Errorf("无法识别的 ProcessType :%s", msg.ProcessType) } if msg.Result != "agree" && msg.Result != "refuse" && msg.Result != "" { return fmt.Errorf("无法识别的 Result :%s", msg.Result) } if msg.ProcessType == "terminate" { _, err = s.Dao.Where("id = ?", custId).Data(map[string]interface{}{ "cust_status": "30", }).Update() return err } pass := msg.Result == "agree" if !pass { _, err = s.Dao.Where("id = ?", custId).Data(map[string]interface{}{ "cust_status": "30", }).Update() return err } now := gtime.Now() err = s.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { //更新客户信息 _, err = s.Dao.TX(tx).Data(g.Map{ "cust_status": "10", "is_public": isPublic, "sales_id": 0, "dept_id": 0, "dept_name": "", "updated_time": now, }).Where("id = ?", cust.Id).Update() if err != nil { return err } //更新销售归属表结束时间 _, err = s.BelongDao.TX(tx).Data(g.Map{ "end_date": now, }).Where("cust_id = ?", cust.Id).Update() if err != nil { return err } return nil }) if err != nil { return err } s.CxtUser = &request.UserInfo{Id: userId, UserName: userName} return s.CreateDynamics("移入公海", map[string]interface{}{ "userId": userId, "custId": cust.Id, }, int64(cust.Id)) } // SysAdminTransCustomer 系统管理员转移客户(不走审批) func (s *CustomerService) SysAdminTransCustomer(ctx context.Context, req *model.AssignCustomerReq) error { // 校验用户是否有修改权限 arr := garray.NewStrArrayFrom(s.CxtUser.Roles, true) if !arr.Contains("SysAdmin") { return myerrors.TipsError("权限不足") } if req.Remark == "" { return myerrors.TipsError("请输入转移原因") } customerList, err := s.Dao.WherePri(req.Ids).Where(s.Dao.C.IsPublic, "20").All() if err != nil { return err } if len(customerList) == 0 { return myerrors.TipsError("转移客户不能为空") } var customerIds []int64 for _, item := range customerList { customerIds = append(customerIds, int64(item.Id)) } err = s.ChangeCustBelong(customerIds, req.SalesId, req.SalesName) if err != nil { return err } err = s.BatchCreatebelong(customerList, &model.AssignCustomerReq{ Ids: customerIds, SalesId: req.SalesId, SalesName: req.SalesName, Remark: req.Remark, Receive: OperaTion, }) if err != nil { return err } s.CreateDynamics("转移客户", map[string]interface{}{ "toUserId": req.SalesId, "toUserNickName": req.SalesName, "remark": "系统管理员转移客户", }, customerIds...) return nil } // 转移客户 func (s *CustomerService) TransCustomerRequest(ctx context.Context, req *model.AssignCustomerReq) error { if req.Remark == "" { return myerrors.TipsError("请输入转移原因") } data, err := s.Dao.Where("id in (?)", req.Ids).All() if err != nil { return err } if len(data) == 0 { return myerrors.TipsError("转移用户不能为空") } for _, v := range data { if v.CustStatus == "10" { return myerrors.TipsError(fmt.Sprintf("客户: %s 为公海客户", v.CustName)) } if v.CustStatus == "20" { return myerrors.TipsError(fmt.Sprintf("客户: %s 正在等待审批", v.CustName)) } if v.CustProvince == "" { return myerrors.TipsError(fmt.Sprintf("请先完善客户: %s 的所在省信息", v.CustName)) } if v.CustCity == "" { return myerrors.TipsError(fmt.Sprintf("请先完善客户: %s 的所在市信息", v.CustName)) } if v.CustIndustry == "" { return myerrors.TipsError(fmt.Sprintf("请先完善客户: %s 的客户类型信息", v.CustName)) } } userId := s.GetCxtUserId() userName := s.GetCxtUserName() toUserId := req.SalesId toUserName := req.SalesName s.CxtUser = &request.UserInfo{Id: userId, UserName: userName} err = s.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { for _, u := range data { _, err = s.Dao.Where("id = ?", u.Id).Data(map[string]interface{}{ "cust_status": "20", }).Update() if err != nil { return err } err = s.ChangeCustBelong([]int64{int64(u.Id)}, toUserId, toUserName) if err != nil { return err } err = s.BatchCreatebelong([]*model.CustCustomer{u}, &model.AssignCustomerReq{ Ids: []int64{int64(u.Id)}, SalesId: toUserId, SalesName: toUserName, Remark: "", Receive: OperaTion, }) if err != nil { return err } err = s.CreateDynamics("转移客户", map[string]interface{}{ "userId": userId, "custId": u.Id, "toUserId": toUserId, }, int64(u.Id)) if err != nil { return err } } return nil }) if err != nil { return err } return nil } // 转移客户回调 func (s *CustomerService) TransCustomerApproval(flow *workflowModel.PlatWorkflow, msg *message.MixMessage) error { remark := map[string]string{} err := json.Unmarshal([]byte(flow.Remark), &remark) if err != nil { return err } userName := remark["applyUserName"] toUserName := remark["toUserName"] bizCode := strings.Split(flow.BizCode, ":") if len(bizCode) != 3 { return fmt.Errorf("转移客户审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id) } custId, err := strconv.Atoi(bizCode[0]) if err != nil { return fmt.Errorf("转移客户审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id) } userId, err := strconv.Atoi(bizCode[1]) if err != nil { return fmt.Errorf("转移客户审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id) } toUserId, err := strconv.Atoi(bizCode[2]) if err != nil { return fmt.Errorf("转移客户审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id) } // user, err := s.UserDao.Where("id = ?", userId).One() // if err != nil { // return err // } // if user == nil { // return fmt.Errorf("用户不存在:%s Id: %d", flow.BizCode, flow.Id) // } // toUser, err := s.UserDao.Where("id = ?", toUserId).One() // if err != nil { // return err // } // if toUser == nil { // return fmt.Errorf("用户不存在:%s Id: %d", flow.BizCode, flow.Id) // } cust, err := s.Dao.Where("id = ?", custId).One() if err != nil { return err } if cust == nil { return fmt.Errorf("客户不存在:%s Id: %d", flow.BizCode, flow.Id) } if msg.ProcessType != "finish" && msg.ProcessType != "terminate" { return fmt.Errorf("无法识别的 ProcessType :%s", msg.ProcessType) } if msg.Result != "agree" && msg.Result != "refuse" && msg.Result != "" { return fmt.Errorf("无法识别的 Result :%s", msg.Result) } if msg.ProcessType == "terminate" { _, err = s.Dao.Where("id = ?", custId).Data(map[string]interface{}{ "cust_status": "30", }).Update() return err } pass := msg.Result == "agree" if !pass { _, err = s.Dao.Where("id = ?", custId).Data(map[string]interface{}{ "cust_status": "30", }).Update() return err } s.CxtUser = &request.UserInfo{Id: userId, UserName: userName} err = s.ChangeCustBelong([]int64{int64(custId)}, int64(toUserId), toUserName) if err != nil { return err } err = s.BatchCreatebelong([]*model.CustCustomer{cust}, &model.AssignCustomerReq{ Ids: []int64{int64(custId)}, SalesId: int64(toUserId), SalesName: toUserName, Remark: "", Receive: OperaTion, }) if err != nil { return err } s.CxtUser = &request.UserInfo{Id: userId, UserName: userName} return s.CreateDynamics("转移客户", map[string]interface{}{ "userId": userId, "custId": cust.Id, "toUserId": toUserId, }, int64(cust.Id)) } // AssignCustomer 分配客户 func (s *CustomerService) AssignCustomer(ctx context.Context, req *model.AssignCustomerReq) (err error) { if req.Receive != "" { return s.AssignCustomerRequest(ctx, req) } data, err := s.Dao.Where("id in (?)", req.Ids).LockShared().All() if err != nil { g.Log().Error(err) return } if len(data) == 0 { return myerrors.TipsError("无可分配客户") } customerNames := "" for _, v := range data { if v.SalesId != 0 { return myerrors.TipsError(fmt.Sprintf("客户名称[%s]已被领取或分配", v.CustName)) } if customerNames == "" { customerNames = v.CustName } else { customerNames += "、" + v.CustName } } err = s.ChangeCustBelong(req.Ids, req.SalesId, req.SalesName) if err != nil { return } req.Receive = AllocaTion err = s.BatchCreatebelong(data, req) if err != nil { return err } // 消息提醒 msg := g.MapStrStr{ "msgTitle": "公海客户分配提醒", "msgContent": fmt.Sprintf("客户%v已分配给您,请前往查看", customerNames), "msgType": "20", "recvUserIds": gconv.String(req.SalesId), "msgStatus": "10", "sendType": "30", } if err := service.CreateSystemMessage(msg); err != nil { g.Log().Error("公海客户分配提醒异常:", err) } return s.CreateDynamics("分配客户", req, req.Ids...) } // GetEntityById 客户详情 func (s *CustomerService) GetEntityById(Ids []int64) (entityInfo []*model.CustList, err error) { Model := s.Dao // err = Model.Where(" id in (?)", Ids).Scan(&entityInfo) if err != nil { g.Log().Error(err) return } return } // GetCustNameIsExist 判断客户名称是否存在 func (s *CustomerService) GetCustNameIsExist(req *model.IsExistsCustName) (exist bool, err error) { custDao := s.Dao.M if req.Id > 0 { custDao = custDao.Where("cust_name = ", req.CustName).WhereNot(" id ", req.Id) } else { custDao = custDao.Where("cust_name = ", req.CustName) } count, err := custDao.Count() if err != nil { g.Log().Error(err) return } exist = false if count > 0 { exist = true } return } // CustAbstract 客户摘要 func (s *CustomerService) CustAbstract(Id int64) (followInfo *model.Follow, err error) { count, err := s.FollowDao.Where(s.FollowDao.C.CustId, Id).Count() if err != nil { g.Log().Error(err) return } followInfo = new(model.Follow) followInfo.FollowCount = count followTime, err := s.Dao.Fields(s.Dao.C.FollowUpDate, s.Dao.C.CreatedTime).FindOne(Id) if err != nil { g.Log().Error(err) return } if followTime == nil { err = myerrors.TipsError("获取客户信息不存在") return } now := gtime.Now() var difference time.Duration if followTime.FollowUpDate.IsZero() { difference = now.Sub(gtime.New(followTime.CreatedTime)) } else { difference = now.Sub(gtime.New(followTime.FollowUpDate)) } days := difference.Hours() / 24 if days < 0 { followInfo.NotFollowDay = 0 } else { followInfo.NotFollowDay = int(math.Floor(days)) } return } // // TransCustomer 转移客户 // func (s *CustomerService) TransCustomer(req *model.AssignCustomerReq) (err error) { // data, err := s.Dao.Fields("sales_id,sales_name,id").Where("id in (?)", req.Ids).All() // if err != nil { // g.Log().Error(err) // return err // } // if len(data) == 0 { // return myerrors.TipsError("数据不存在") // } // err = s.ChangeCustBelong(req.Ids, req.SalesId, req.SalesName) // if err != nil { // g.Log().Error(err) // return // } // req.Receive = OperaTion // s.BatchCreatebelong(data, req) // return // } // ChangeCustBelong 变更客户所属关系 func (s *CustomerService) ChangeCustBelong(Ids []int64, salesId int64, salesName string) (err error) { _, err = s.Dao.Data(g.Map{ "cust_status": "30", "sales_id": salesId, "is_public": noPublic, "sales_name": salesName, "updated_by": s.GetCxtUserId(), "updated_name": s.GetCxtUserName(), "updated_time": gtime.Now(), }).Where("id in (?)", Ids).Update() return err } // CreateDynamics 创建客户动态信息 func (s *CustomerService) CreateDynamics(opnTpye string, content interface{}, ids ...int64) (err error) { datas := make([]*model.CustCustomerDynamics, 0) for _, id := range ids { dynameics := new(model.CustCustomerDynamics) dynameics.CustId = int(id) dynameics.OpnPeopleId = s.GetCxtUserId() dynameics.OpnPeople = s.GetCxtUserName() dynameics.OpnDate = gtime.Now() dynameics.OpnType = opnTpye v, _ := gjson.Encode(content) dynameics.OpnContent = gconv.String(v) service.SetCreatedInfo(dynameics, s.GetCxtUserId(), s.GetCxtUserName()) datas = append(datas, dynameics) } _, err = s.DynamicsDao.Insert(datas) if err != nil { g.Log().Error(err) return } return } // GetDynamicsList 客户动态 func (s *CustomerService) GetDynamicsList(req *model.CustomerDynameicsReq) (total int, result []interface{}, err error) { total, err = s.DynamicsDao.Where("cust_id = ", req.CustId).Count() if err != nil { g.Log().Error(err) return } dynamics := []*model.CustomerDynameicsRep{} err = s.DynamicsDao.Where("cust_id = ", req.CustId).Order("created_time desc").Scan(&dynamics) if err != nil { g.Log().Error(err) return } dynamicsList := make(map[string][]*model.CustomerDynameicsRep) for _, v := range dynamics { opnDate := gtime.New(v.OpnDate).Format("Y-m-d") dynamicsList[opnDate] = append(dynamicsList[opnDate], &model.CustomerDynameicsRep{ OpnPeople: v.OpnPeople, OpnDate: v.OpnDate, OpnType: v.OpnType, OpnContent: v.OpnContent, }) } result = append(result, dynamicsList) return } // MergeCustomer 合并客户 func (s *CustomerService) MergeCustomer(req *model.MergeCustomerRep) (err error) { //当前目标客户是否存在并取出合并前的销售 customer, err := s.Dao.Where("id = ", req.Id).One() if err != nil { g.Log().Error(err) return } if customer == nil { return myerrors.TipsError("该客户不存在") } _, err = s.ContactDao.Where(" cust_id in (?)", req.ChooseId).Delete() if err != nil { g.Log().Error(err) return } CustomertData := new(model.CustomerAddSeq) if err = gconv.Struct(req, CustomertData); err != nil { g.Log().Error(err) return } service.SetUpdatedInfo(CustomertData, s.GetCxtUserId(), s.GetCxtUserName()) _, err = s.Dao.FieldsEx(s.Dao.C.CreatedTime, s.Dao.C.CreatedBy, s.Dao.C.CreatedName, s.Dao.C.Id, s.Dao.C.CustCode).WherePri(s.Dao.C.Id, req.Id).Update(CustomertData) if err != nil { g.Log().Error(err) return } //删除被合并的客户信息 _, err = s.Dao.Where(" id in (?)", req.ChooseId).Delete() if err != nil { g.Log().Error(err) return } //删除 所选客户销售联系人 _, err = s.BelongDao.Where(" cust_id in (?)", req.ChooseId).Delete() if err != nil { g.Log().Error(err) return } //插入一条合并成功的归属记录 //更新销售归属表销售结束时间 _, err = s.BelongDao.Data( g.Map{ "updated_by": s.GetCxtUserId(), "updated_name": s.GetCxtUserName(), "end_date": gtime.Now(), }).WhereIn(s.BelongDao.C.CustId, req.Id).Update() if err != nil { g.Log().Error(err) return } req.CustomerBelongAddSeq.CustId = int(req.Id) req.CustomerBelongAddSeq.OpnType = Merge req.CustomerBelongAddSeq.OpnPeople = s.GetCxtUserName() req.CustomerBelongAddSeq.OrigSaleName = customer.SalesName req.CustomerBelongAddSeq.SaleName = req.SalesName s.BelongServer.Create(req.CustomerBelongAddSeq) return } // CreateContact 联系人(合并)预留 func (s *CustomerService) CreateContact(Ids []int64, req *model.CustCustomerContactSeq) (err error) { _, err = s.ContactDao.Where(" cust_id in (?)", Ids).Delete() if err != nil { g.Log().Error(err) return } s.ContanctServer.Create(req) return } // BatchCreatebelong 批量插入客户归属记录表 func (s *CustomerService) BatchCreatebelong(rep []*model.CustCustomer, req *model.AssignCustomerReq, n ...interface{}) (err error) { //更新销售归属表销售结束时间 _, err = s.BelongDao.Data( g.Map{ "updated_by": s.GetCxtUserId(), "updated_name": s.GetCxtUserName(), "end_date": gtime.Now(), }).WhereIn(s.BelongDao.C.CustId, req.Ids).Update() if err != nil { return err } var belongData []*model.CustCustomerBelong userName := s.GetCxtUserName() for _, v := range rep { orig_sale_name := v.SalesName belong := new(model.CustCustomerBelong) belong.CustId = v.Id belong.SaleName = req.SalesName belong.OrigSaleName = orig_sale_name belong.OpnType = req.Receive belong.OpnPeople = userName belong.CreatedName = userName belong.StartDate = gtime.Now() //新增开始时间 belong.OpnDatetime = gtime.Now() belong.Remark = req.Remark belong.CreatedBy = s.GetCxtUserId() belongData = append(belongData, belong) } _, err = s.BelongDao.Insert(belongData) return err } // 导出数据 func (s *CustomerService) Export(ctx context.Context, req *model.CustCustomerExport) (content *model.CustExport, err error) { var con model.CustExport req.IsRemovePage = true // 去掉分页标识 total, data, err := s.GetList(&req.CustCustomerSearchReq) if err != nil { return } cusType, err := service.GetDictDataByType(ctx, "cust_idy") if err != nil { return nil, err } f := excelize.NewFile() index := f.NewSheet("Sheet1") for index, item := range req.Columns { sheetPosition := service.Div(index+1) + "1" f.SetCellValue("Sheet1", sheetPosition, item) } if total > 0 { for ck, item := range data { for index, v := range req.Columns { if v == "客户编码" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.CustCode) } if v == "客户名称" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.CustName) } if v == "助记名" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.AbbrName) } if v == "助记名" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.AbbrName) } if v == "所在地区" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.CustLocation) } if v == "客户类型" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), cusType[item.CustIndustry]) } if v == "客户级别" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.CustLevel) } if v == "客户状态" { var CustStatus string CustStatus = "正常" if item.CustStatus != "10" { CustStatus = "异常" } f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), CustStatus) } if v == "创建人" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.CreatedName) } if v == "最后跟进时间" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.FollowUpDate) } if v == "创建时间" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.CreatedTime) } if v == "所在省" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.CustProvince) } if v == "所在市" { f.SetCellValue("Sheet1", service.Div(index+1)+strconv.Itoa(ck+2), item.CustCity) } } } } f.SetActiveSheet(index) var buffer *bytes.Buffer buffer, _ = f.WriteToBuffer() con.Content = buffer.Bytes() 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 }