package service import ( "app.yhyue.com/moapp/jybase/common" "app.yhyue.com/moapp/jybase/date" "app.yhyue.com/moapp/jybase/mail" "app.yhyue.com/moapp/jybase/redis" . "bp.jydev.jianyu360.cn/BaseService/biService/entity" "bp.jydev.jianyu360.cn/BaseService/biService/rpc/biservice" "database/sql" "fmt" "github.com/gogf/gf/v2/util/gconv" "github.com/zeromicro/go-zero/core/logx" "log" "strings" "time" ) // AddAcceptance 处理受理单新增逻辑 // 参数: // // in - 受理单请求数据 // productMap - 产品映射关系 // entId - 企业ID // // 返回值: // // 成功返回受理单编号,失败返回空字符串 func AddAcceptance(in *biservice.AcceptanceReq, productMap []ProductMap, entId int64) string { // 清理参数数据中的换行符 in.ParamData = strings.ReplaceAll(in.ParamData, "\n", "") nowTime := time.Now().Format(date.Date_Full_Layout) // 线索标识初始化 is_clue := 1 deptName := "" // 如果有部门ID,查询部门名称 if in.DeptId != "" { deptData := JyMysql.FindOne("entniche_department", map[string]interface{}{ "id": in.DeptId, }, "name", "") if deptData != nil && len(*deptData) > 0 { deptName = gconv.String((*deptData)["name"]) } } // 生成受理单编号:SLD+年月日+4位序号 acceptance_no := fmt.Sprintf("SLD%s%s", time.Now().Format(date.Date_yyyyMMdd), FindNumber("sld")) // 准备受理单主表数据 acceptanceMap := map[string]interface{}{ "acceptance_no": acceptance_no, // 受理单编号 "propose_type": in.ProposeType, // 提出类型 "propose_time": in.ProposeTime, // 提出时间 "channel": in.Channel, // 渠道 "acceptance_type": in.AcceptanceType, // 受理类型 "status": in.Status, // 状态 "initiator_name": in.EntUserName, // 发起人姓名 "initiator_position_id": in.PositionId, // 发起人职位ID "department_no": in.DeptId, // 部门编号 "remark": in.Remark, // 备注 "is_delete": 1, // 删除标识 "creator_name": in.EntUserName, // 创建人姓名 "creator_position_id": in.PositionId, // 创建人职位ID "creator_time": nowTime, // 创建时间 "department_name": deptName, // 部门名称 } // 开启事务处理 ok := WorkOrder.ExecTx("受理单处理", func(tx *sql.Tx) bool { // 处理受理单子表数据 childMap := gconv.Map(in.ParamData) if childMap != nil && len(childMap) > 0 { phone := "" // 客户联系方式 company := "" // 公司名称 innerArr := []interface{}{} // 子表数据数组 innerStr := []string{"acceptance_no", "field_name", "field_value", "creator_name", "creator_position_id", "creator_time"} // 遍历子表字段 for k, v := range childMap { innerArr = append(innerArr, acceptance_no) // 受理单编号 innerArr = append(innerArr, k) // 字段名 innerArr = append(innerArr, v) // 字段值 innerArr = append(innerArr, in.EntUserName) // 创建人 innerArr = append(innerArr, in.PositionId) // 职位ID innerArr = append(innerArr, nowTime) // 创建时间 // 特殊字段处理 switch k { case "公司名称": company = gconv.String(v) case "联系方式num": phone = gconv.String(v) } } // 批量插入子表数据 if len(innerArr) > 0 { ok2, ok3 := WorkOrder.InsertBatchByTx(tx, "order_acceptance_children", innerStr, innerArr) if ok2 <= 0 && ok3 <= 0 { return false } } // 处理产品信息 selectedProductMap := map[string]SelectProductMap{} // 选中的产品映射 productArr := []string{} // 产品数组 // 如果有咨询产品字段 if _, isOk := childMap["咨询产品"]; isOk { // 分割产品字符串 for _, v := range strings.Split(gconv.String(childMap["咨询产品"]), ",") { // 匹配产品信息 for _, product := range productMap { if strings.Contains(product.Product, v) { selectedProduct := selectedProductMap[product.ProductCode] selectedProduct.PersonArr = product.PersonArr selectedProduct.Product = append(selectedProduct.Product, v) selectedProduct.IsAddMarketing = product.IsAddMarketing productArr = append(productArr, v) selectedProductMap[product.ProductCode] = selectedProduct continue } } } // 检查是否所有产品都匹配成功 if len(strings.Split(gconv.String(childMap["咨询产品"]), ",")) != len(productArr) { is_clue = 2 // 设置为线索 } } // 为每个产品创建工单 for k, v := range selectedProductMap { ok, positionId, product, dkPerson := AddOrderWork(k, acceptance_no, nowTime, phone, company, tx, in, v, entId) if !ok { return false } else { // 如果需要加入营销 if v.IsAddMarketing { go MarketingSave(in, positionId, product, dkPerson) } } } } // 设置线索标识并插入受理单主表 acceptanceMap["is_clue"] = is_clue ok1 := WorkOrder.InsertByTx(tx, "order_acceptance", acceptanceMap) if ok1 <= 0 { logx.Info("受理单创建失败") return false } return true }) if ok { return acceptance_no } return "" } // AddOrderWork 创建工单 // 参数: // // orderType - 工单类型 // acceptance_no - 受理单编号 // nowTime - 当前时间 // phone - 联系电话 // company - 公司名称 // tx - 事务对象 // in - 受理单请求数据 // selectPersonMap - 选择的人员映射 // entId - 企业ID // // 返回值: // // bool - 是否成功 // int64 - 职位ID // string - 产品信息 // string - 处理人员姓名 func AddOrderWork(orderType, acceptance_no, nowTime, phone, company string, tx *sql.Tx, in *biservice.AcceptanceReq, selectPersonMap SelectProductMap, entId int64) (bool, int64, string, string) { dkPerson := "" // 大客人员姓名 dkdeptName := "" // 部门名称 dkdeptId := int64(0) // 部门ID dkPositionId := int64(0) // 职位ID // 查找候选人 personMap := FindCandidate(selectPersonMap.PersonArr, entId, orderType) dkPositionId = personMap.PositionId dkPerson = personMap.Name dkdeptId = personMap.DeptId dkdeptName = personMap.DeptName // 如果有有效的职位ID if dkPositionId != 0 { // 生成工单编号:GD+年月日+4位序号 work_order_no := fmt.Sprintf("GD%s%s", time.Now().Format(date.Date_yyyyMMdd), FindNumber("gd")) // 准备工单数据 orderWorkMap := map[string]interface{}{ "work_order_no": work_order_no, // 工单编号 "acceptance_no": acceptance_no, // 受理单编号 "type": strings.Join(selectPersonMap.Product, ","), // 工单类型 "status": 1, // 状态 "initiator_name": in.EntUserName, // 发起人姓名 "initiator_position_id": in.PositionId, // 发起人职位ID "current_name": dkPerson, // 当前处理人 "current_position_id": dkPositionId, // 当前处理人职位ID "is_delete": 1, // 删除标识 "creator_name": in.EntUserName, // 创建人 "creator_position_id": in.PositionId, // 创建人职位ID "creator_time": nowTime, // 创建时间 "two_type": orderType, // 二级类型 "department_no": dkdeptId, // 部门编号 "department_name": dkdeptName, // 部门名称 "update_time": nil, // 更新时间 } logx.Info(orderWorkMap, "11111", selectPersonMap) // 插入工单数据 ok3 := WorkOrder.InsertByTx(tx, "order_work", orderWorkMap) if ok3 <= 0 { log.Println("工单保存失败") return false, 0, "", "" } // 准备审批记录数据 approvalRecordMap := map[string]interface{}{ "work_order_no": work_order_no, // 工单编号 "status": 1, // 状态 "new_status": nil, // 新状态 "handle_name": dkPerson, // 处理人姓名 "handle_position_id": dkPositionId, // 处理人职位ID "handle_dept_id": dkdeptId, // 处理部门ID "handle_dept_name": dkdeptName, // 处理部门名称 "creator_name": in.EntUserName, // 创建人 "creator_position_id": in.PositionId, // 创建人职位ID "is_delete": 1, // 删除标识 "creator_time": nowTime, // 创建时间 } // 插入审批记录 ok4 := WorkOrder.InsertByTx(tx, "approval_record", approvalRecordMap) if ok4 <= 0 { log.Println("工单记录保存失败") return false, 0, "", "" } log.Println(GmailAuth, personMap, strings.Join(selectPersonMap.Product, ","), dkPerson, in.EntUserName, nowTime, work_order_no, phone, company) // 发送工作邮件 WorkMail(GmailAuth, personMap, strings.Join(selectPersonMap.Product, ","), dkPerson, in.EntUserName, nowTime, work_order_no, phone, company) } return true, dkPositionId, strings.Join(selectPersonMap.Product, ","), dkPerson } // PersonJson 人员信息结构体 type PersonJson struct { Name string // 姓名 Phone string // 电话 Mail string // 邮箱 DeptId int64 // 部门ID DeptName string // 部门名称 PositionId int64 // 职位ID IsResign bool // 是否离职 EntUserId int64 // 企业用户ID DeptPersonMail string // 部门负责人邮箱 DeptPersonName string // 部门负责人姓名 SuperiorDepthPersonMail string // 上级部门负责人邮箱 SuperiorDepthPersonName string // 上级部门负责人姓名 } // FindCandidate 查找候选人 // 参数: // // personArr - 人员数组 // entId - 企业ID // orderType - 工单类型 // // 返回值: // // PersonJson - 找到的候选人信息 func FindCandidate(personArr []Person, entId int64, orderType string) PersonJson { personEntity := PersonJson{} personMap := make(map[string]map[string]interface{}) // 人员信息映射 phoneArr := make([]string, len(personArr)) // 电话号码数组 persons := make([]PersonJson, len(personArr)) // 人员信息数组 // 初始化人员信息和电话数组 for k, v := range personArr { phone := gconv.String(v.Phone) phoneArr[k] = fmt.Sprintf(`"%s"`, phone) personMap[phone] = map[string]interface{}{ "name": gconv.String(v.Name), } persons[k] = PersonJson{ Name: v.Name, Phone: v.Phone, IsResign: v.IsResign, } } // 查询人员邮箱和部门信息 entUserArr := JyMysql.SelectBySql(fmt.Sprintf(` SELECT a.name, a.mail, b.dept_id AS deptId, a.phone, c.name AS deptName,a.id as entUserId FROM entniche_user a INNER JOIN entniche_department_user b ON a.ent_id = %d AND a.phone IN %s AND a.id = b.user_id INNER JOIN entniche_department c ON b.dept_id = c.id `, entId, fmt.Sprintf("(%s)", strings.Join(phoneArr, ",")))) // 填充人员邮箱和部门信息 if entUserArr != nil { for _, v := range *entUserArr { phone := gconv.String(v["phone"]) personMap[phone]["mail"] = gconv.String(v["mail"]) personMap[phone]["deptId"] = gconv.String(v["deptId"]) personMap[phone]["deptName"] = gconv.String(v["deptName"]) personMap[phone]["entUserId"] = gconv.String(v["entUserId"]) } } // 查询职位信息 positionArrMap := JyMysql.SelectBySql(fmt.Sprintf(` SELECT a.phone, b.id FROM base_service.base_user a INNER JOIN base_service.base_position b ON b.ent_id = %d AND a.phone IN %s AND b.user_id = a.id AND b.type = 1 `, entId, fmt.Sprintf("(%s)", strings.Join(phoneArr, ",")))) // 填充职位信息 if positionArrMap != nil { for _, v := range *positionArrMap { phone := gconv.String(v["phone"]) personMap[phone]["positionId"] = gconv.Int64(v["id"]) } } // 更新人员数组中的完整信息 for k, v := range persons { phone := v.Phone if info, exists := personMap[phone]; exists { persons[k].Mail = gconv.String(info["mail"]) persons[k].DeptId = gconv.Int64(info["deptId"]) persons[k].DeptName = gconv.String(info["deptName"]) persons[k].PositionId = gconv.Int64(info["positionId"]) persons[k].EntUserId = gconv.Int64(info["entUserId"]) } } // 查询最近创建的工单 orderWorkMap := WorkOrder.SelectBySql(fmt.Sprintf(` SELECT * FROM order_work WHERE two_type = "%s" ORDER BY creator_time DESC LIMIT 1 `, orderType)) var k int // 如果没有工单记录,从第一个人员开始 if orderWorkMap == nil || len(*orderWorkMap) == 0 { k = findNextPersonIndex(persons, 0) } else { // 否则查找上次处理人,并找下一个 currentName := gconv.String((*orderWorkMap)[0]["current_name"]) k = findPersonIndexByName(persons, currentName) k = findNextPersonIndex(persons, k) } personEntity = persons[k] // 查询部门管理员信息 fetchDeptAdmin(&personEntity) // 查询上级部门管理员信息 fetchSuperiorAdmin(&personEntity) return personEntity } // findNextPersonIndex 查找下一个人员索引 // 参数: // // personArr - 人员数组 // startIndex - 起始索引 // // 返回值: // // 下一个未离职人员的索引 func findNextPersonIndex(personArr []PersonJson, startIndex int) int { n := len(personArr) // 如果当前索引是最后一个,则从0开始 if startIndex >= n-1 { startIndex = -1 // 设置为-1以便在下次循环中变为0 } // 循环查找下一个未离职人员 for count := 0; count < n; count++ { startIndex++ if startIndex >= n { startIndex = 0 } if !personArr[startIndex].IsResign { return startIndex } } return 0 } // findPersonIndexByName 根据姓名查找人员索引 // 参数: // // personArr - 人员数组 // name - 要查找的姓名 // // 返回值: // // 找到的索引,没找到返回0 func findPersonIndexByName(personArr []PersonJson, name string) int { for i, v := range personArr { if v.Name == name { return i } } return 0 } // fetchDeptAdmin 获取部门管理员信息 // 参数: // // person - 人员信息(会被修改) func fetchDeptAdmin(person *PersonJson) { deptMap := JyMysql.SelectBySql(`SELECT c.name AS name, c.mail AS mail FROM entniche_department_user a INNER JOIN entniche_user_role b ON a.dept_id = ? AND a.user_id = b.user_id AND b.role_id != "" INNER JOIN entniche_user c ON a.user_id = c.id`, person.DeptId) if deptMap != nil && len(*deptMap) > 0 { person.DeptPersonName = gconv.String((*deptMap)[0]["name"]) person.DeptPersonMail = gconv.String((*deptMap)[0]["mail"]) } } // fetchSuperiorAdmin 获取上级部门管理员信息 // 参数: // // person - 人员信息(会被修改) func fetchSuperiorAdmin(person *PersonJson) { superiorMap := JyMysql.SelectBySql(`SELECT c.* FROM entniche_department d INNER JOIN entniche_department_user a ON d.id = ? AND d.pid = a.dept_id INNER JOIN entniche_user_role b ON a.user_id = b.user_id AND b.role_id != "" INNER JOIN entniche_user c ON a.user_id = c.id`, person.DeptId) if superiorMap != nil && len(*superiorMap) > 0 { person.SuperiorDepthPersonName = gconv.String((*superiorMap)[0]["name"]) person.SuperiorDepthPersonMail = gconv.String((*superiorMap)[0]["mail"]) } } // FindNumber 获取编号 // 参数: // // moudle - 模块标识 // // 返回值: // // 4位序号字符串 func FindNumber(moudle string) string { today := time.Now().Format("2006-01-02") yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02") key := fmt.Sprintf("%s_%s", today, moudle) yesterdayKey := fmt.Sprintf("%s_%s", yesterday, moudle) // 删除昨天的缓存 if ok, _ := redis.Exists("newother", yesterdayKey); ok { redis.Del("newother", yesterdayKey) } // 自增获取序号 count := redis.Incr("newother", key) log.Println("编号获取", moudle, fmt.Sprintf("%04d", count)) return fmt.Sprintf("%04d", count) } // WorkMail 发送工作邮件 // 参数: // // gmailAuth - Gmail认证信息 // personMap - 人员信息 // productStr - 产品字符串 // personName1 - 处理人姓名 // personName2 - 发起人姓名 // createTimeStr - 创建时间字符串 // acceptance_no - 受理单编号 // phone - 联系电话 // company - 公司名称 func WorkMail(gmailAuth []*mail.GmailAuth, personMap PersonJson, productStr string, personName1, personName2, createTimeStr, acceptance_no, phone, company string) { orderType := fmt.Sprintf(`客户咨询线索(%s)`, productStr) title := fmt.Sprintf("%s通知", orderType) // 避免重复发送给同一个人 if personName1 == personMap.DeptPersonName { personMap.DeptPersonMail = "" } if personName1 == personMap.SuperiorDepthPersonName { personMap.SuperiorDepthPersonMail = "" } // 构建邮件内容 content := fmt.Sprintf(`%s,您好,“%s”于%s新增了1条"%s”(工单编号:%s)%s%s,请及时前往【剑鱼PC工作台-受理-工单管理-我负责的】进行工单处理。`, personName1, personName2, createTimeStr, orderType, acceptance_no, gconv.String(common.If(phone == "", "", fmt.Sprintf(`,客户联系方式为:%s`, phone))), gconv.String(common.If(company == "", "", fmt.Sprintf(`,公司名称:%s`, company)))) toMail := personMap.Mail mailArr := []string{} // 添加抄送人 if gconv.String(common.If(personMap.DeptPersonMail == "", "", personMap.DeptPersonMail)) != "" { mailArr = append(mailArr, gconv.String(common.If(personMap.DeptPersonMail == "", "", personMap.DeptPersonMail))) } if gconv.String(common.If(personMap.SuperiorDepthPersonMail == "", "", personMap.SuperiorDepthPersonMail)) != "" { mailArr = append(mailArr, gconv.String(common.If(personMap.SuperiorDepthPersonMail == "", "", personMap.SuperiorDepthPersonMail))) } // 设置抄送 toCc := strings.Join(mailArr, ",") if len(mailArr) > 0 { toMail = fmt.Sprintf("%s|%s", toMail, toCc) } log.Println(toMail, title, content) if toMail == "" { return } // 尝试使用多个Gmail账号发送 for k, v := range gmailAuth { fool := mail.GSendMail_q("剑鱼标讯", toMail, "", "", title, content, "", "", v) if fool { logx.Info(toMail, fmt.Sprintf("使用%s发送邮件成功", v.User)) break } if k < len(gmailAuth)-1 { logx.Info(toMail, fmt.Sprintf("使用%s发送邮件失败!3s后使用其他邮箱尝试", v.User)) } else { logx.Info(toMail, fmt.Sprintf("使用%s发送邮件失败!", v.User)) } time.Sleep(time.Second * 3) } } // MarketingSave 营销线索保存 // 参数: // // in - 受理单请求数据 // positionId - 职位ID // product - 产品信息 // dkPerson - 大客人员姓名 func MarketingSave(in *biservice.AcceptanceReq, positionId int64, product, dkPerson string) { // 查询用户系统映射 userMapping := JyMysql.FindOne("data_service.user_system", map[string]interface{}{"position_id": positionId, "status": 1}, "", "") if userMapping == nil || len(*userMapping) == 0 { log.Println("大客用户信息获取失败") return } log.Println(in) entId := gconv.Int64((*userMapping)["ent_id"]) entUserId := gconv.Int64((*userMapping)["ent_user_id"]) // 开启事务处理营销线索 CrmService.ExecTx("线索进营销保存", func(tx *sql.Tx) bool { childMap := gconv.Map(in.ParamData) company := "" // 公司名称 phone := "" // 联系电话 demand := "" // 客户需求 customerName := "" // 客户姓名 channel := "" //渠道查询 channelMap := WorkOrder.FindOne("dictionaries", map[string]interface{}{ "id": in.Channel, }, "", "") if channelMap == nil || len(*channelMap) == 0 { log.Println(in.Channel, "渠道查询不到") } else { channel = gconv.String((*channelMap)["name"]) } // 提取关键字段 for k, v := range childMap { switch k { case "公司名称": company = gconv.String(v) case "联系方式num": phone = gconv.String(v) case "客户需求": demand = gconv.String(v) case "客户姓名": customerName = gconv.String(v) } } // 构建备注JSON remarkFields := []string{ fmt.Sprintf("%s :%s", "提出时间", time.Now().Format(time.DateTime)), fmt.Sprintf("%s :%s", "销售线索来源", "主动咨询客服留资客户"), fmt.Sprintf("%s :%s", "姓名", common.If(customerName == "", phone, customerName)), fmt.Sprintf("%s :%s", "联系方式", phone), fmt.Sprintf("%s :%s", "公司名称", company), fmt.Sprintf("%s :%s", "咨询产品", product), fmt.Sprintf("%s :%s", "客户需求", demand), fmt.Sprintf("%s :%s", "备注", in.Remark), } filterStr := strings.Join(remarkFields, "\n") // 保存销售线索 clueId := CrmService.InsertByTx(tx, "sale_clue", map[string]interface{}{ "position_id": positionId, // 职位ID "ent_id": entId, // 企业ID "ent_user_id": entUserId, // 企业用户ID "employ_info_id": 0, // 雇佣信息ID "name": common.If(company == "", phone, company), // 名称 "source_BAK": "", // 来源备份 "summary": common.If(company == "", phone, company), // 摘要 "is_close": 0, // 是否关闭 "close_reason_classify": nil, // 关闭原因分类 "close_reason_desc": nil, // 关闭原因描述 "create_person": dkPerson, // 创建人 "create_time": time.Now().Format(date.Date_Full_Layout), // 创建时间 "channel": channel, // 渠道 "source": "主动咨询客服留资客户", // 来源 }) // 保存任务 taskId := CrmService.InsertByTx(tx, "task", map[string]interface{}{ "ent_id": entId, // 企业ID "name": common.If(customerName == "", phone, customerName), // 任务名称 "source": 1, // 来源类型 "source_id": clueId, // 来源ID "create_way": 2, // 创建方式 "status": 2, // 状态 "create_time": time.Now().Format(date.Date_Full_Layout), // 创建时间 "join_task_vehicle": 0, // 是否加入任务车辆 "remark": filterStr, // 备注 }) if taskId <= 0 { return false } // 保存任务划转记录 CrmService.InsertByTx(tx, "task_transfer", map[string]interface{}{ "task_id": taskId, // 任务ID "transfer_id": positionId, // 划转ID "responsible_id": positionId, // 负责人ID "is_transfer": 0, // 是否划转 "create_time": time.Now().Format(date.Date_Full_Layout), // 创建时间 }) // 保存任务团队 CrmService.InsertByTx(tx, "task_team", map[string]interface{}{ "task_id": taskId, // 任务ID "position_id": positionId, // 职位ID "ent_user_id": entUserId, // 企业用户ID "name": dkPerson, // 姓名 "role": 1, // 角色 "create_time": time.Now().Format(date.Date_Full_Layout), // 创建时间 }) return true }) return }