autoTask.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. package main
  2. import (
  3. "database/sql"
  4. "github.com/gogf/gf/v2/util/gconv"
  5. "log"
  6. "time"
  7. "app.yhyue.com/moapp/jybase/common"
  8. "app.yhyue.com/moapp/jybase/date"
  9. )
  10. func autoTask() {
  11. log.Println("超时未跟进定时任务开始")
  12. t := time.Now()
  13. nowTime := time.Now().Format(date.Date_Full_Layout)
  14. statusMap := map[string]int{
  15. "07": 2, //待签署客户
  16. "06": 2, //高意向客户
  17. "05": 3, //意向客户
  18. "04": 7, //潜在客户
  19. }
  20. //判断节假日
  21. for status, statusInt := range statusMap {
  22. count, counts := 0, 0
  23. for {
  24. count++
  25. currentTime := t.AddDate(0, 0, -count)
  26. if currentTime.Weekday() == time.Sunday || currentTime.Weekday() == time.Saturday {
  27. isok := false
  28. for k, v := range DateMap {
  29. if currentTime.Format(date.Date_Short_Layout) == k && v == 2 {
  30. isok = true
  31. }
  32. }
  33. if isok {
  34. counts++
  35. }
  36. } else {
  37. isok := true
  38. for k, v := range DateMap {
  39. if currentTime.Format(date.Date_Short_Layout) == k && v == 1 {
  40. isok = false
  41. }
  42. }
  43. if isok {
  44. counts++
  45. }
  46. }
  47. if counts >= statusInt {
  48. break
  49. }
  50. }
  51. statusMap[status] = count
  52. }
  53. log.Println(statusMap)
  54. for trailstatus := range map[string]string{
  55. "07": "", //待签署客户
  56. "06": "", //高意向客户
  57. "05": "", //意向客户
  58. "04": "", //潜在客户
  59. } {
  60. sql := `SELECT id,position_id,seatNumber,out_task_status FROM dwd_f_crm_clue_info
  61. WHERE trailstatus = ?`
  62. argsSelect := []interface{}{trailstatus}
  63. intime := ""
  64. sql += " AND comeintime <?"
  65. // nt := nexttime.(time.Time)
  66. nt := t.AddDate(0, 0, -statusMap[trailstatus])
  67. intime = nt.Format(date.Date_Full_Layout)
  68. argsSelect = append(argsSelect, intime)
  69. //
  70. TiDb.SelectByBath(100, func(l *[]map[string]interface{}) bool {
  71. for _, v := range *l {
  72. clueId := common.Int64All(v["id"])
  73. position_id := common.Int64All(v["position_id"])
  74. out_task_status := common.IntAll(v["out_task_status"])
  75. args2 := []interface{}{clueId}
  76. //获取跟进内容
  77. // sql1 := `select COUNT(1) FROM dwd_f_crm_trail_content WHERE clue_id =?;`
  78. sql2 := `SELECT COUNT(1) FROM dwd_f_crm_trail_content WHERE clue_id = ? and position_id = ?`
  79. if intime != "" {
  80. sql2 += ` and createtime > ?`
  81. args2 = append(args2, position_id)
  82. args2 = append(args2, intime)
  83. }
  84. //保留未跟进线索
  85. // if c1, c2 := TiDb.CountBySql(sql1, clueId), TiDb.CountBySql(sql2, args2...); (c1 != 0 && c2 > 0) || out_task_status == 1 {
  86. // log.Println("不满足线索过滤", clueId)
  87. // continue
  88. // }
  89. log.Println("intime ", clueId, intime, sql2)
  90. if c2 := TiDb.CountBySql(sql2, args2...); c2 > 0 || out_task_status == 1 {
  91. log.Println("不满足线索过滤", clueId)
  92. continue
  93. }
  94. if TiDb.Update("dwd_f_crm_clue_info", map[string]interface{}{"id": clueId}, map[string]interface{}{
  95. "is_task": 1,
  96. "task_time": nowTime,
  97. "tasktime": time.Now().Format(date.Date_Short_Layout) + " 10:00:00",
  98. "taskstatus": 0,
  99. "tasksource": "超时未跟进自动加车",
  100. }) {
  101. TiDb.Insert("dwd_f_crm_clue_change_record", map[string]interface{}{
  102. "clue_id": clueId,
  103. "position_id": position_id,
  104. "change_type": "加入任务车",
  105. "new_value": "超时未跟进自动加车",
  106. "createtime": nowTime,
  107. "BCPCID": common.GetRandom(32),
  108. "operator_id": -1,
  109. })
  110. }
  111. }
  112. return true
  113. }, sql, argsSelect...)
  114. }
  115. log.Println("超时未跟进定时任务结束")
  116. }
  117. func autoTasks() {
  118. log.Println("按照跟进时间提前一天进入任务车定时任务开始")
  119. nowTime2 := time.Now().Format(date.Date_Full_Layout)
  120. nextTime := time.Now().AddDate(0, 0, 1).Format(date.Date_Full_Layout)
  121. TiDb.SelectByBath(100, func(l *[]map[string]interface{}) bool {
  122. for _, v := range *l {
  123. clueId := common.Int64All(v["id"])
  124. position_id := common.Int64All(v["position_id"])
  125. out_task_status := common.IntAll(v["out_task_status"])
  126. if position_id > 0 {
  127. if out_task_status != 1 && position_id > 0 {
  128. if TiDb.Update("dwd_f_crm_clue_info", map[string]interface{}{"id": clueId}, map[string]interface{}{
  129. "is_task": 1,
  130. "task_time": nowTime2,
  131. "tasktime": nowTime2,
  132. "taskstatus": 0,
  133. "tasksource": "即将到达下次跟进时间",
  134. }) {
  135. TiDb.Insert("dwd_f_crm_clue_change_record", map[string]interface{}{
  136. "clue_id": clueId,
  137. "position_id": position_id,
  138. "change_type": "加入任务车",
  139. "new_value": "即将到达下次跟进时间",
  140. "createtime": nowTime2,
  141. "BCPCID": common.GetRandom(32),
  142. "operator_id": -1,
  143. })
  144. }
  145. }
  146. }
  147. }
  148. return true
  149. }, `SELECT a.id,a.position_id,a.seatNumber,a.out_task_status FROM dwd_f_crm_clue_info a
  150. LEFT JOIN dwd_f_crm_trail_content c ON c.clue_id=a.id
  151. WHERE c.next_time >= "`+nowTime2+`" and c.next_time <= "`+nextTime+`"`)
  152. log.Println("按照跟进时间提前一天进入任务车定时任务结束")
  153. }
  154. // 所有人达上限退公海处理
  155. func UpperLimitAutoExitSea(upperLimit int64) {
  156. countData := TiDb.SelectBySql(`SELECT
  157. COUNT(b.ID) AS count ,a.position_id
  158. FROM
  159. dwd_f_crm_clue_info b
  160. right JOIN
  161. ( select position_id from dwd_f_crm_personnel_management where assign_type = 1
  162. AND resign = 0 ) a on a.position_id=b.position_id and b.is_transfer != 1
  163. GROUP BY
  164. a.position_id HAVING count<?`, upperLimit)
  165. if countData == nil || len(*countData) > 0 {
  166. return
  167. }
  168. log.Println("所有人达上限退公海处理任务开始")
  169. data := TiDb.SelectBySql(`select * from dwd_f_crm_clue_info where (trailstatus = "04" or trailstatus = "03" or trailstatus = "01" ) and (is_assign = 1)`)
  170. if data != nil && len(*data) > 0 {
  171. fool := false
  172. for _, v := range *data {
  173. trailstatus := gconv.String(v["trailstatus"])
  174. //坐席信息查询
  175. clueId := common.Int64All(v["id"])
  176. createtime := gconv.String(v["comeintime"])
  177. positionId := common.Int64All(v["position_id"])
  178. seatNumber := common.ObjToString(v["seatNumber"])
  179. if trailstatus == "01" {
  180. //该线索最近一次分配给该电销人员后存在通话记录
  181. uid := gconv.String(v["uid"])
  182. if createtime == "" {
  183. continue
  184. }
  185. if !PhoneState(uid, seatNumber, createtime) {
  186. continue
  187. }
  188. }
  189. //退私海处理
  190. nowTime := time.Now().Format(date.Date_Full_Layout)
  191. positionMap := map[int64]interface{}{}
  192. if TiDb.ExecTx("退出线索", func(tx *sql.Tx) bool {
  193. positionMap[positionId] = true
  194. recordId := TiDb.UpdateOrDeleteBySqlByTx(tx, `UPDATE dwd_f_crm_clue_info SET is_assign=0,position_id=null,seatNumber=null,updatetime = ?,comeinsource_open=null,level_open=null,next_trail_time=null,is_task=null,tasktime=null,taskstatus=null,comeinsource_private=null,tasksource=null WHERE id = ?`, nowTime, clueId) //,start_trail_time=null,content=null
  195. recordId2 := TiDb.InsertByTx(tx, "dwd_f_crm_clue_change_record", map[string]interface{}{
  196. "clue_id": clueId,
  197. "position_id": positionId,
  198. "change_type": "退回公海",
  199. "new_value": "所有人员私海已达上限",
  200. "createtime": nowTime,
  201. "BCPCID": common.GetRandom(32),
  202. "operator_id": -1,
  203. })
  204. recordId1 := TiDb.InsertByTx(tx, "dwd_f_crm_clue_change_record", map[string]interface{}{
  205. "clue_id": clueId,
  206. "position_id": positionId,
  207. "change_field": "position_id",
  208. "change_type": "所属人变更",
  209. "old_value": GetPositionName(seatNumber),
  210. "new_value": "/",
  211. "createtime": nowTime,
  212. "BCPCID": common.GetRandom(32),
  213. "operator_id": -1,
  214. })
  215. return recordId > -1 && recordId1 > -1 && recordId2 > -1
  216. }) {
  217. //发送消息,找wjh
  218. fool = true
  219. log.Println("达上限自动退出线索成功")
  220. } else {
  221. log.Println("达上限自动退出线索失败")
  222. }
  223. }
  224. //释放邮件
  225. //发送信息
  226. if fool {
  227. AutoReleaseNots()
  228. } else {
  229. CantBeAssignedNots()
  230. }
  231. }
  232. }
  233. func autoExitSea() {
  234. log.Println("自动退海任务开始")
  235. data := TiDb.SelectBySql(`select * from dwd_f_crm_clue_info where trailstatus = "02" and (is_assign = 1 or is_assign = 0)`)
  236. if data != nil && len(*data) > 0 {
  237. for _, v := range *data {
  238. clueId := common.Int64All(v["id"])
  239. positionId := common.Int64All(v["position_id"])
  240. seatNumber := common.ObjToString(v["seatNumber"])
  241. nowTime := time.Now().Format(date.Date_Full_Layout)
  242. // is_assign := common.IntAll(v["is_assign"])
  243. if TiDb.ExecTx("退出线索", func(tx *sql.Tx) bool {
  244. recordId := TiDb.UpdateOrDeleteBySqlByTx(tx, `UPDATE dwd_f_crm_clue_info SET is_assign=-1,position_id=null,seatNumber=null,updatetime = ?,comeinsource_open=null,level_open=null,next_trail_time=null,is_task=null,tasktime=null,taskstatus=null,comeinsource_private=null,tasksource=null WHERE id = ?`, nowTime, clueId) //,start_trail_time=null,content=null
  245. // ok1 := true
  246. // if is_assign == 1 {
  247. // ok1 = TiDb.DeleteByTx(tx, "dwd_f_crm_private_sea", map[string]interface{}{"clue_id": clueId})
  248. // } else if is_assign == 0 {
  249. // ok1 = TiDb.DeleteByTx(tx, "dwd_f_crm_open_sea", map[string]interface{}{"clue_id": clueId})
  250. // }
  251. recordId1 := TiDb.InsertByTx(tx, "dwd_f_crm_clue_change_record", map[string]interface{}{
  252. "clue_id": clueId,
  253. "position_id": positionId,
  254. "change_field": "position_id",
  255. "change_type": "所属人变更",
  256. "old_value": GetPositionName(seatNumber),
  257. "new_value": "/",
  258. "createtime": nowTime,
  259. "BCPCID": common.GetRandom(32),
  260. "operator_id": -1,
  261. })
  262. recordId2 := TiDb.InsertByTx(tx, "dwd_f_crm_clue_change_record", map[string]interface{}{
  263. "clue_id": clueId,
  264. "position_id": positionId,
  265. "change_field": "trailstatus",
  266. "change_type": "基本信息变更",
  267. "old_value": "空号停机",
  268. "new_value": "流失",
  269. "createtime": nowTime,
  270. "BCPCID": common.GetRandom(32),
  271. "operator_id": -1,
  272. })
  273. recordId3 := TiDb.InsertByTx(tx, "dwd_f_crm_clue_change_record", map[string]interface{}{
  274. "clue_id": clueId,
  275. "position_id": positionId,
  276. "change_type": "退出任务车",
  277. "new_value": "空号停机自动从线索池删除",
  278. "createtime": nowTime,
  279. "BCPCID": common.GetRandom(32),
  280. "operator_id": -1,
  281. })
  282. recordId4 := TiDb.InsertByTx(tx, "dwd_f_crm_clue_change_record", map[string]interface{}{
  283. "clue_id": clueId,
  284. "position_id": positionId,
  285. "change_type": "退出公海",
  286. "new_value": "空号停机自动从线索池删除",
  287. "createtime": nowTime,
  288. "BCPCID": common.GetRandom(32),
  289. "operator_id": -1,
  290. })
  291. return recordId > -1 && recordId1 > -1 && recordId2 > -1 && recordId3 > -1 && recordId4 > -1
  292. }) {
  293. log.Println("自动退出线索成功")
  294. } else {
  295. log.Println("自动退出线索失败")
  296. }
  297. }
  298. }
  299. datas := TiDb.Find("dwd_f_crm_clue_info", map[string]interface{}{"trailstatus": "00", "is_assign": 1}, "", "", -1, -1)
  300. if datas != nil && len(*datas) > 0 {
  301. for _, v := range *datas {
  302. clueId := common.Int64All(v["id"])
  303. positionId := common.Int64All(v["position_id"])
  304. seatNumber := common.ObjToString(v["seatNumber"])
  305. nowTime := time.Now().Format(date.Date_Full_Layout)
  306. // is_assign := common.IntAll(v["is_assign"])
  307. if TiDb.ExecTx("自动退海", func(tx *sql.Tx) bool {
  308. recordId := TiDb.UpdateOrDeleteBySqlByTx(tx, `UPDATE dwd_f_crm_clue_info SET is_assign=0,position_id=null,seatNumber=null,updatetime = ?,comeintime_open = ?,comeinsource_open=8,level_open=4,next_trail_time=null,is_task=null,tasktime=null,taskstatus=null,comeinsource_private=null,tasksource=null WHERE id = ?`, nowTime, nowTime, clueId) //,content=null,start_trail_time=null
  309. recordId1 := TiDb.InsertByTx(tx, "dwd_f_crm_clue_change_record", map[string]interface{}{
  310. "clue_id": clueId,
  311. "position_id": positionId,
  312. "change_field": "position_id",
  313. "change_type": "所属人变更",
  314. "old_value": GetPositionName(seatNumber),
  315. "new_value": "/",
  316. "createtime": nowTime,
  317. "BCPCID": common.GetRandom(32),
  318. "operator_id": -1,
  319. })
  320. recordId2 := TiDb.InsertByTx(tx, "dwd_f_crm_clue_change_record", map[string]interface{}{
  321. "clue_id": clueId,
  322. "position_id": positionId,
  323. "change_field": "trailstatus",
  324. "change_type": "基本信息变更",
  325. "old_value": "无意向客户",
  326. "new_value": "流失",
  327. "createtime": nowTime,
  328. "BCPCID": common.GetRandom(32),
  329. "operator_id": -1,
  330. })
  331. recordId3 := TiDb.InsertByTx(tx, "dwd_f_crm_clue_change_record", map[string]interface{}{
  332. "clue_id": clueId,
  333. "position_id": positionId,
  334. "change_type": "退出任务车",
  335. "new_value": "无意向客户自动退回公海",
  336. "createtime": nowTime,
  337. "BCPCID": common.GetRandom(32),
  338. "operator_id": -1,
  339. })
  340. recordId4 := TiDb.InsertByTx(tx, "dwd_f_crm_clue_change_record", map[string]interface{}{
  341. "clue_id": clueId,
  342. "position_id": positionId,
  343. "change_type": "退回公海",
  344. "new_value": "无意向客户自动退回公海",
  345. "createtime": nowTime,
  346. "BCPCID": common.GetRandom(32),
  347. "operator_id": -1,
  348. })
  349. return recordId > -1 && recordId1 > -1 && recordId2 > -1 && recordId3 > -1 && recordId4 > -1
  350. }) {
  351. log.Println("自动退出私海成功")
  352. } else {
  353. log.Println("自动退出私海失败")
  354. }
  355. }
  356. }
  357. log.Println("自动退海任务结束")
  358. }
  359. func GetPositionName(seatNumber string) string {
  360. data := TiDb.SelectBySql(`select name from dwd_f_crm_personnel_management where resign = 0 and seat_number = ? limit 1`, seatNumber)
  361. if data != nil && len(*data) > 0 {
  362. return common.ObjToString((*data)[0]["name"])
  363. }
  364. return ""
  365. }
  366. func Thaw() {
  367. //3个工作日查询
  368. k := db.ThawDay
  369. nowTime := time.Now()
  370. for {
  371. if k == 0 {
  372. break
  373. }
  374. nowTime = nowTime.AddDate(0, 0, -1)
  375. if !IsBreak(nowTime) {
  376. continue
  377. }
  378. k--
  379. }
  380. nameMap := map[int64]string{}
  381. nameList := TiDb.SelectBySql("select name,position_id from dwd_f_crm_personnel_management")
  382. if nameList != nil && len(*nameList) > 0 {
  383. for _, v := range *nameList {
  384. nameMap[gconv.Int64(v["position_id"])] = gconv.String(v["name"])
  385. }
  386. }
  387. //查询
  388. layout := "2006-01-02 15:04:05"
  389. //三天以后退公海处理
  390. TiDb.SelectByBath(100, func(l *[]map[string]interface{}) bool {
  391. for _, v := range *l {
  392. clueId := gconv.Int64(v["id"])
  393. positionId := gconv.Int64(v["position_id"])
  394. freezeInt64 := int64(0)
  395. freezeStr := gconv.String(v["FREEZE_TIME"])
  396. if freezeStr != "" {
  397. t, _ := time.ParseInLocation(layout, freezeStr, time.Local)
  398. freezeInt64 = t.Unix()
  399. }
  400. if freezeInt64 < nowTime.Unix() {
  401. //超三天处理
  402. //分配状态改改
  403. if TiDb.Update("dwd_f_crm_clue_info", map[string]interface{}{"id": clueId}, map[string]interface{}{
  404. "is_assign": 0,
  405. "position_id": 0,
  406. }) {
  407. TiDb.Insert("dwd_f_crm_clue_change_record", map[string]interface{}{
  408. "clue_id": clueId,
  409. "position_id": 0,
  410. "change_field": "position_id",
  411. "change_type": "所属人变更",
  412. "old_value": nameMap[positionId],
  413. "new_value": "/",
  414. "createtime": time.Now().Format(date.Date_Full_Layout),
  415. "BCPCID": common.GetRandom(32),
  416. "operator_id": -1,
  417. })
  418. }
  419. TiDb.Insert("dwd_f_crm_clue_change_record", map[string]interface{}{
  420. "clue_id": clueId,
  421. "position_id": 0,
  422. "change_field": "position_id",
  423. "change_type": "线索解冻",
  424. "new_value": "自动退回公海",
  425. "createtime": time.Now().Format(date.Date_Full_Layout),
  426. "BCPCID": common.GetRandom(32),
  427. "operator_id": -1,
  428. })
  429. } else {
  430. if !FindUpperLimit(gconv.String(positionId), "", false) {
  431. if TiDb.Update("dwd_f_crm_clue_info", map[string]interface{}{"id": clueId}, map[string]interface{}{
  432. "is_assign": 1,
  433. }) {
  434. TiDb.Insert("dwd_f_crm_clue_change_record", map[string]interface{}{
  435. "clue_id": clueId,
  436. "position_id": positionId,
  437. "change_field": "position_id",
  438. "change_type": "所属人变更",
  439. "old_value": "/",
  440. "new_value": nameMap[positionId],
  441. "createtime": time.Now().Format(date.Date_Full_Layout),
  442. "BCPCID": common.GetRandom(32),
  443. "operator_id": -1,
  444. })
  445. }
  446. TiDb.Insert("dwd_f_crm_clue_change_record", map[string]interface{}{
  447. "clue_id": clueId,
  448. "position_id": positionId,
  449. "change_field": "position_id",
  450. "change_type": "线索解冻",
  451. "new_value": "销售人员私海线索已释放",
  452. "createtime": time.Now().Format(date.Date_Full_Layout),
  453. "BCPCID": common.GetRandom(32),
  454. "operator_id": -1,
  455. })
  456. }
  457. }
  458. }
  459. return true
  460. }, `select id,position_id,FREEZE_TIME from dwd_f_crm_clue_info where is_assign = -3 `)
  461. }
  462. func IsBreak(currentTime time.Time) bool {
  463. if currentTime.Weekday() == time.Sunday {
  464. isok := false
  465. for k, v := range DateMap {
  466. if currentTime.Format(date.Date_Short_Layout) == k && v == 2 {
  467. isok = true
  468. }
  469. }
  470. return isok
  471. } else {
  472. isok := true
  473. for k, v := range DateMap {
  474. if currentTime.Format(date.Date_Short_Layout) == k && v == 1 {
  475. isok = false
  476. }
  477. }
  478. return isok
  479. }
  480. }
  481. func PhoneState(uid, seatNumber, createtime string) bool {
  482. phoneData := TiDb.SelectBySql("select DISTINCT phone from dwd_f_userbase_contacts where baseinfo_id= ?", uid)
  483. if phoneData == nil || len(*phoneData) == 0 {
  484. //数据不对跳过去
  485. return false
  486. }
  487. for _, v1 := range *phoneData {
  488. phone := gconv.String(v1["phone"])
  489. callData := TiDb.SelectBySql("select DISTINCT Exten from Call_Accounting.voice_record where CalledNo=? and createtime>=? ORDER BY createTime", phone, createtime)
  490. if callData == nil || len(*callData) == 0 {
  491. //手机号没有通话记录
  492. continue
  493. }
  494. for _, v2 := range *callData {
  495. exten := gconv.String(v2["Exten"])
  496. if seatNumber == exten {
  497. return true
  498. }
  499. }
  500. }
  501. return false
  502. }