package cust import ( "bytes" "context" "fmt" "math" "strconv" "strings" "dashoo.cn/opms_libary/myerrors" "dashoo.cn/opms_libary/plugin/dingtalk/message" "dashoo.cn/opms_libary/plugin/dingtalk/workflow" "github.com/360EntSecGroup-Skylar/excelize" "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/util/gconv" "github.com/mozillazg/go-pinyin" "dashoo.cn/micro/app/dao/cust" platdao "dashoo.cn/micro/app/dao/plat" sysDao "dashoo.cn/micro/app/dao/sys" model "dashoo.cn/micro/app/model/cust" workflowModel "dashoo.cn/micro/app/model/workflow" "dashoo.cn/micro/app/service" workflowService "dashoo.cn/micro/app/service/workflow" ) type CustomerService struct { *service.ContextService Dao *cust.CustCustomerDao BelongDao *cust.CustCustomerBelongDao DynamicsDao *cust.CustCustomerDynamicsDao ContactDao *cust.CustCustomerContactDao FollowDao *platdao.PlatFollowupDao UserDao *sysDao.SysUserDao 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) svc.UserDao = sysDao.NewSysUserDao(svc.Tenant) return svc, nil } // GetList 客户列表列表 func (s *CustomerService) GetList(req *model.CustCustomerSearchReq) (total int, customerList []*model.CustList, err error) { Model := s.Dao.M if req.TargetType == "" { if !req.IsPublic { Model = Model.Where(s.Dao.Columns.SalesId, s.CxtUser.Id).Where(s.Dao.Columns.IsPublic, noPublic) } else { Model = Model.Where(s.Dao.Columns.IsPublic, isPublic) } } //客户名称 if req.CustName != "" { Model = Model.Where(s.Dao.Columns.CustName+" like ?", "%"+req.CustName+"%") } //客户编码 if req.CustCode != "" { Model = Model.Where(s.Dao.Columns.CustCode+" like ?", "%"+req.CustCode+"%") } //客户行业 if req.CustIndustry != "" { Model = Model.Where(s.Dao.Columns.CustIndustry+" like ?", "%"+req.CustIndustry+"%") } //客户级别 if req.CustLevel != "" { Model = Model.Where(s.Dao.Columns.CustLevel, req.CustLevel) } // if req.FollowUpDate != "" { Model = Model.Where(s.Dao.Columns.FollowUpDate+" like ? ", req.FollowUpDate+"%") } 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 { 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.Columns.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.CustIndustryCode) if err != nil { return 0, err } cusTomer.CustCode = custCode cusTomer.CustStatus = "10" roles := s.GetCxtUserRoles() isSales := false for _, v := range roles { if v == "Sales" { // 销售角色 isSales = true break } } // 销售角色 if isSales { cusTomer.IsPublic = noPublic cusTomer.SalesId = s.GetCxtUserId() cusTomer.SalesName = s.CxtUser.NickName insertId, err = s.Dao.InsertAndGetId(cusTomer) if err != nil { g.Log().Error(err) return 0, err } s.CreateBelong(gconv.Int(insertId)) return } 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.Columns.CustName, req.CustName).WhereNot(s.Dao.Columns.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.Columns.CreatedTime, s.Dao.Columns.CreatedBy, s.Dao.Columns.CreatedName, s.Dao.Columns.Id, s.Dao.Columns.CustCode, s.Dao.Columns.SalesName, s.Dao.Columns.SalesId). WherePri(s.Dao.Columns.Id, req.Id).Update(CustomertData) if err != nil { g.Log().Error(err) return } return } // MoveToPubic 移入公海 func (s *CustomerService) MoveToPubic(Ids []int64) (err error) { count, err := s.Dao.WhereIn(s.Dao.Columns.Id, Ids).Count() if err != nil { g.Log().Error(err) return } if count == 0 { err = myerrors.TipsError("没有要移除的数据") return } userName := s.GetCxtUserName() currentTime := gtime.Now() // 当前时间 s.Dao.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { //更新客户信息 _, err = s.Dao.TX(tx).Data(g.Map{ "is_public": isPublic, "sales_id": 0, "dept_id": 0, "dept_name": "", "create_time": currentTime, "updated_by": s.GetCxtUserId(), "updated_name": userName, "updated_time": currentTime, }).WhereIn(s.ContactDao.Columns.Id, Ids).Update() if err != nil { g.Log().Error(err) return err } //更新销售归属表结束时间 _, err = s.BelongDao.TX(tx).Data( g.Map{ "updated_by": s.GetCxtUserId(), "updated_name": userName, "end_date": currentTime, }).WhereIn(s.BelongDao.Columns.CustId, Ids).Update() if err != nil { g.Log().Error(err) return err } return nil }) return } var AssignCustomerRequestProcessCode = "" func ptrString(s string) *string { return &s } // 领取客户申请 func (s *CustomerService) AssignCustomerRequest(ctx context.Context, req *model.AssignCustomerReq) error { 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)) } } workflowSrv, err := workflowService.NewFlowService(ctx) if err != nil { return err } for _, u := range data { bizCode := strconv.Itoa(u.Id) + ":" + strconv.Itoa(s.GetCxtUserId()) _, err = workflowSrv.StartProcessInstance(bizCode, "11", &workflow.StartProcessInstanceRequest{ ProcessCode: &AssignCustomerRequestProcessCode, FormComponentValues: []*workflow.StartProcessInstanceRequestFormComponentValues{ { Name: ptrString(""), Value: ptrString(""), }, }, }) if err != nil { return err } _, err = s.Dao.Where("id = ?", u.Id).Data(map[string]interface{}{ "is_public": noPublic, "cust_status": "20", }).Update() if err != nil { return err } } return nil } func (s *CustomerService) AssignCustomerRequestApproval(flow *workflowModel.PlatWorkflow, msg *message.MixMessage) error { 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) } 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) } 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" { 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 } err = s.ChangeCustBelong([]int64{int64(custId)}, int64(userId), user.NickName) if err != nil { return err } s.BatchCreatebelong([]*model.CustCustomer{cust}, &model.AssignCustomerReq{ Ids: []int64{int64(custId)}, SalesId: int64(user.Id), SalesName: user.NickName, Remark: "", Receive: Receive, }) return nil } // 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("无可分配客户") } for _, v := range data { if v.SalesId != 0 { return myerrors.TipsError(fmt.Sprintf("客户名称[%s]已被领取或分配", v.CustName)) } } err = s.ChangeCustBelong(req.Ids, req.SalesId, req.SalesName) if err != nil { return } if req.Receive != "" { req.Receive = Receive } else { req.Receive = AllocaTion } s.BatchCreatebelong(data, req) return } // 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.Columns.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.Columns.FollowUpDate, s.Dao.Columns.CreatedTime).FindOne(Id) if err != nil { g.Log().Error(err) return } if followTime == nil { err = myerrors.TipsError("获取客户信息不存在") return } now := gtime.Now() var hours float64 if followTime.FollowUpDate == nil { poor := now.Sub(gtime.New(followTime.CreatedTime)) hours = float64(poor.Hours() / 24) } else { poor := now.Sub(gtime.New(followTime.FollowUpDate)) hours = float64(poor.Hours() / 24) } if hours < 0 { followInfo.NotFollowDay = 0 } else { followInfo.NotFollowDay = int(math.Floor(hours)) } 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.Columns.CreatedTime, s.Dao.Columns.CreatedBy, s.Dao.Columns.CreatedName, s.Dao.Columns.Id, s.Dao.Columns.CustCode).WherePri(s.Dao.Columns.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.Columns.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.Columns.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(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 } 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), 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) } } } } f.SetActiveSheet(index) var buffer *bytes.Buffer buffer, _ = f.WriteToBuffer() con.Content = buffer.Bytes() return &con, err }