|
@@ -4,7 +4,6 @@ import (
|
|
|
"fmt"
|
|
|
"log"
|
|
|
"strings"
|
|
|
- "sync"
|
|
|
"telemarketingEtl/config"
|
|
|
"telemarketingEtl/util"
|
|
|
"time"
|
|
@@ -48,17 +47,6 @@ B.3个自然日内有过“已接听”的通话记录且仍处于“商机线
|
|
|
|
|
|
*/
|
|
|
|
|
|
-var (
|
|
|
- oPool chan bool
|
|
|
- oWait = &sync.WaitGroup{}
|
|
|
-)
|
|
|
-
|
|
|
-func init() {
|
|
|
- ctx := gctx.New()
|
|
|
- poolSize := g.Cfg().MustGet(ctx, "poolSize").Int()
|
|
|
- oPool = make(chan bool, poolSize)
|
|
|
-}
|
|
|
-
|
|
|
func GetOpenSea() {
|
|
|
ctx := gctx.New()
|
|
|
//
|
|
@@ -131,40 +119,34 @@ func GetOpenSea() {
|
|
|
log.Println("一级公海开始.")
|
|
|
//一级公海
|
|
|
for thisData := map[string]interface{}{}; iter.Next(&thisData); {
|
|
|
- oPool <- true
|
|
|
- oWait.Add(1)
|
|
|
- go func(thisData map[string]interface{}) {
|
|
|
- defer func() {
|
|
|
- <-oPool
|
|
|
- oWait.Done()
|
|
|
- }()
|
|
|
- userid := gconv.String(thisData["userid"])
|
|
|
- if userid == "" {
|
|
|
- return
|
|
|
- }
|
|
|
- if !mongodb.IsObjectIdHex(userid) {
|
|
|
- userid = GetUserIdByPositionId(userid)
|
|
|
- }
|
|
|
- if userid == "" {
|
|
|
- return
|
|
|
- }
|
|
|
- //根据userid获取线索id
|
|
|
- if oneClassA[userid] {
|
|
|
- return
|
|
|
- }
|
|
|
- uuid := GetClueIdByUserId(userid)
|
|
|
- if uuid == "" {
|
|
|
- return
|
|
|
- }
|
|
|
- oneClassA[uuid] = true
|
|
|
- }(thisData)
|
|
|
+
|
|
|
+ userid := gconv.String(thisData["userid"])
|
|
|
+ if userid == "" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if !mongodb.IsObjectIdHex(userid) {
|
|
|
+ userid = GetUserIdByPositionId(userid)
|
|
|
+ }
|
|
|
+ if userid == "" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ //根据userid获取线索id
|
|
|
+ if oneClassA[userid] {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ uuid := GetClueIdByUserId(userid)
|
|
|
+ if uuid == "" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ oneClassA[uuid] = true
|
|
|
+
|
|
|
count++
|
|
|
if count%10 == 0 {
|
|
|
log.Printf("已完成%d条数据\n", count)
|
|
|
}
|
|
|
thisData = map[string]interface{}{}
|
|
|
}
|
|
|
- oWait.Wait()
|
|
|
+
|
|
|
log.Println("一级公海 pc 结束")
|
|
|
queryOneClassApp := map[string]interface{}{
|
|
|
"date": map[string]interface{}{
|
|
@@ -193,40 +175,33 @@ func GetOpenSea() {
|
|
|
count = 0
|
|
|
//一级公海
|
|
|
for thisData := map[string]interface{}{}; iter2.Next(&thisData); {
|
|
|
- oPool <- true
|
|
|
- oWait.Add(1)
|
|
|
- go func(thisData map[string]interface{}) {
|
|
|
- defer func() {
|
|
|
- <-oPool
|
|
|
- oWait.Done()
|
|
|
- }()
|
|
|
- userid := gconv.String(thisData["userid"])
|
|
|
- if userid == "" {
|
|
|
- return
|
|
|
- }
|
|
|
- if !mongodb.IsObjectIdHex(userid) {
|
|
|
- userid = GetUserIdByPositionId(userid)
|
|
|
- }
|
|
|
- if userid == "" {
|
|
|
- return
|
|
|
- }
|
|
|
- //根据userid获取线索id
|
|
|
- if oneClassA[userid] {
|
|
|
- return
|
|
|
- }
|
|
|
- uuid := GetClueIdByUserId(userid)
|
|
|
- if uuid == "" {
|
|
|
- return
|
|
|
- }
|
|
|
- oneClassA[uuid] = true
|
|
|
- }(thisData)
|
|
|
+
|
|
|
+ userid := gconv.String(thisData["userid"])
|
|
|
+ if userid == "" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if !mongodb.IsObjectIdHex(userid) {
|
|
|
+ userid = GetUserIdByPositionId(userid)
|
|
|
+ }
|
|
|
+ if userid == "" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ //根据userid获取线索id
|
|
|
+ if oneClassA[userid] {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ clubId := GetClueIdByUserId(userid)
|
|
|
+ if clubId == "" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ oneClassA[clubId] = true
|
|
|
+
|
|
|
count++
|
|
|
if count%10 == 0 {
|
|
|
log.Printf("已完成%d条数据\n", count)
|
|
|
}
|
|
|
thisData = map[string]interface{}{}
|
|
|
}
|
|
|
- oWait.Wait()
|
|
|
//更新公海
|
|
|
AddOpenSea(oneClassA, 1, "A")
|
|
|
log.Println("一级公海 更新结束。")
|
|
@@ -247,8 +222,8 @@ func GetOpenSea() {
|
|
|
if oneClassA[userid] {
|
|
|
continue
|
|
|
}
|
|
|
- uuid := GetClueIdByUserId(userid)
|
|
|
- oneClassB[uuid] = true
|
|
|
+ clubId := GetClueIdByUserId(userid)
|
|
|
+ oneClassB[clubId] = true
|
|
|
}
|
|
|
}
|
|
|
AddOpenSea(oneClassB, 1, "B")
|
|
@@ -261,7 +236,7 @@ func GetOpenSea() {
|
|
|
AddOpenSea(twoC, 2, "C")
|
|
|
AddOpenSea(twoD, 2, "D")
|
|
|
//三级公海
|
|
|
- ThreeOpenSea()
|
|
|
+ ThreeOpenSea(oneClassA, oneClassB, oneClassC, twoA, twoB, twoC, twoD)
|
|
|
//
|
|
|
log.Println("end")
|
|
|
}
|
|
@@ -307,21 +282,21 @@ func TwoOpenSea(oneClassA, oneClassB, oneClassC map[string]bool) (aMap, bMap, cM
|
|
|
if err != nil {
|
|
|
log.Println("row scan err:", err)
|
|
|
}
|
|
|
- uuid := GetClueIdByUserId(userid)
|
|
|
- if oneClassA[uuid] || oneClassB[uuid] || oneClassC[uuid] {
|
|
|
+ clubId := GetClueIdByUserId(userid)
|
|
|
+ if oneClassA[clubId] || oneClassB[clubId] || oneClassC[clubId] {
|
|
|
continue
|
|
|
}
|
|
|
if ct >= 15 {
|
|
|
- aMap[uuid] = true
|
|
|
+ aMap[clubId] = true
|
|
|
}
|
|
|
if ct < 15 && ct >= 10 {
|
|
|
- bMap[uuid] = true
|
|
|
+ bMap[clubId] = true
|
|
|
}
|
|
|
if ct < 10 && ct >= 5 {
|
|
|
- cMap[uuid] = true
|
|
|
+ cMap[clubId] = true
|
|
|
}
|
|
|
if ct < 5 && ct >= 1 {
|
|
|
- dMap[uuid] = true
|
|
|
+ dMap[clubId] = true
|
|
|
}
|
|
|
}
|
|
|
// Check for errors during iteration
|
|
@@ -334,14 +309,14 @@ func TwoOpenSea(oneClassA, oneClassB, oneClassC map[string]bool) (aMap, bMap, cM
|
|
|
|
|
|
// 三级公海:
|
|
|
// 最近30天内活跃天数=0天的客户。
|
|
|
-func ThreeOpenSea() {
|
|
|
+func ThreeOpenSea(oneA, oneB, oneC, twoA, twoB, twoC, twoD map[string]bool) {
|
|
|
ctx := gctx.New()
|
|
|
t := time.Now()
|
|
|
t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.Local)
|
|
|
day := g.Cfg().MustGet(ctx, "classThreeHighSeaslastDay").Int()
|
|
|
- t.AddDate(0, 0, day)
|
|
|
+ t = t.AddDate(0, 0, -day)
|
|
|
start := util.GetObjectId(t.Unix())
|
|
|
- createtime := time.Now().Format(date.Date_Full_Layout)
|
|
|
+ log.Println(t.Format(date.Date_Full_Layout), "~~")
|
|
|
|
|
|
config.JianyuSubjectdb.SelectByBath(500, func(l *[]map[string]interface{}) bool {
|
|
|
for _, v := range *l {
|
|
@@ -352,13 +327,16 @@ func ThreeOpenSea() {
|
|
|
"$gte": start,
|
|
|
},
|
|
|
}
|
|
|
- // uuid := gconv.String(v["uid"])
|
|
|
id := gconv.Int64(v["id"])
|
|
|
+ clubId := gconv.String(v["id"])
|
|
|
if r, ok := config.MgoLog.Find("subscribepay_logs", query, nil, `{"_id":1}`, false, 0, 1); ok && r != nil && len(*r) == 0 {
|
|
|
- config.JianyuSubjectdb.ExecBySql(`INSERT INTO dwd_f_crm_open_sea (comeintime,comeinsource,LEVEL,clue_level,clue_id)
|
|
|
- VALUES(?,?,?,?,?)
|
|
|
- ON DUPLICATE KEY UPDATE comeintime=?,comeinsource=?,LEVEL=?,clue_level=?`, createtime, 1, 3, "D", id, createtime, 1, 3, "D")
|
|
|
-
|
|
|
+ if oneA[clubId] || oneB[clubId] || oneC[clubId] || twoA[clubId] || twoB[clubId] || twoC[clubId] || twoD[clubId] {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ config.JianyuSubjectdb.Update("dwd_f_crm_open_sea", map[string]interface{}{"clue_id": id}, map[string]interface{}{
|
|
|
+ "LEVEL": 3,
|
|
|
+ "clue_level": "D",
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
return true
|
|
@@ -381,18 +359,20 @@ func GetClueIdByUserId(userid string) (uuid string) {
|
|
|
// level 公海级别
|
|
|
// clue_level线索级别
|
|
|
func AddOpenSea(m map[string]bool, level int, clue_level string) {
|
|
|
- createtime := time.Now().Format(date.Date_Full_Layout)
|
|
|
+ // createtime := time.Now().Format(date.Date_Full_Layout)
|
|
|
if len(m) > 0 {
|
|
|
for _, v := range m {
|
|
|
- config.JianyuSubjectdb.ExecBySql(`INSERT INTO dwd_f_crm_open_sea (comeintime,comeinsource,LEVEL,clue_level,clue_id)
|
|
|
- VALUES(?,?,?,?,?)
|
|
|
- ON DUPLICATE KEY UPDATE comeintime=?,comeinsource=?,LEVEL=?,clue_level=?`, createtime, 1, level, clue_level, v, createtime, 1, level, clue_level)
|
|
|
+ config.JianyuSubjectdb.Update("dwd_f_crm_open_sea", map[string]interface{}{"clue_id": v}, map[string]interface{}{
|
|
|
+ "clue_level": clue_level,
|
|
|
+ "LEVEL": level,
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 自动退出公海
|
|
|
// 线索状态为“空号停机”自动从线索池删除。
|
|
|
+// 空号停机 删除私海, 线索表分配状态改成 -1
|
|
|
func DeleteOpenSea() {
|
|
|
config.JianyuSubjectdb.SelectByBath(500, func(l *[]map[string]interface{}) bool {
|
|
|
ids := []interface{}{}
|
|
@@ -405,12 +385,12 @@ func DeleteOpenSea() {
|
|
|
whs = append(whs, "?")
|
|
|
}
|
|
|
wh := strings.Join(whs, ",")
|
|
|
- count := config.JianyuSubjectdb.UpdateOrDeleteBySql(`delete from dwd_f_crm_clue_info where id in (`+wh+`)`, ids...)
|
|
|
+ count := config.JianyuSubjectdb.UpdateOrDeleteBySql(`UPDATE dwd_f_crm_clue_info SET is_assign = -1 WHERE id in (`+wh+`)`, ids...)
|
|
|
if count > 0 {
|
|
|
return true
|
|
|
}
|
|
|
return true
|
|
|
- }, `select id from dwd_f_crm_clue_info where trailstatus = ?`, "00")
|
|
|
+ }, `select id from dwd_f_crm_clue_info where trailstatus = ?`, "02")
|
|
|
}
|
|
|
|
|
|
//自动退回公海
|
|
@@ -428,36 +408,62 @@ func ReturnOpenSea() {
|
|
|
t := time.Now()
|
|
|
t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.Local)
|
|
|
//高意向客户
|
|
|
- highIntentionCustomer := g.Cfg().MustGet(ctx, "highIntentionCustomer").Int()
|
|
|
+ highIntentionCustomer := g.Cfg().MustGet(ctx, "highIntentionCustomer").Int() //30
|
|
|
//意向客户
|
|
|
- intentionCustomer := g.Cfg().MustGet(ctx, "intentionCustomer").Int()
|
|
|
+ intentionCustomer := g.Cfg().MustGet(ctx, "intentionCustomer").Int() //30
|
|
|
//潜在客户
|
|
|
- latentCustomer := g.Cfg().MustGet(ctx, "latentCustomer").Int()
|
|
|
+ latentCustomer := g.Cfg().MustGet(ctx, "latentCustomer").Int() //60
|
|
|
//沉睡客户
|
|
|
- sleepCustomer := g.Cfg().MustGet(ctx, "sleepCustomer").Int()
|
|
|
+ sleepCustomer := g.Cfg().MustGet(ctx, "sleepCustomer").Int() //90
|
|
|
//商机线索
|
|
|
- businessLeads := g.Cfg().MustGet(ctx, "businessLeads").Int()
|
|
|
+ businessLeads := g.Cfg().MustGet(ctx, "businessLeads").Int() //2
|
|
|
//无意向客户
|
|
|
- noIdeaCustomer := g.Cfg().MustGet(ctx, "noIdeaCustomer").Int()
|
|
|
+ // noIdeaCustomer := g.Cfg().MustGet(ctx, "noIdeaCustomer").Int() //
|
|
|
+
|
|
|
+ codeMap := dwd_d_crm_trailstatus_code()
|
|
|
+
|
|
|
+ now := time.Now().Format(date.Date_Full_Layout)
|
|
|
+
|
|
|
//2.“高意向客户”超过30天未更新跟进记录自动退回公海;
|
|
|
for trailstatus, nexttime := range map[string]interface{}{
|
|
|
- "04": t.AddDate(0, 0, -highIntentionCustomer),
|
|
|
+ "06": t.AddDate(0, 0, -highIntentionCustomer),
|
|
|
"05": t.AddDate(0, 0, -intentionCustomer),
|
|
|
- "06": t.AddDate(0, 0, -latentCustomer),
|
|
|
- "07": t.AddDate(0, 0, -sleepCustomer),
|
|
|
+ "04": t.AddDate(0, 0, -latentCustomer),
|
|
|
+ "03": t.AddDate(0, 0, -sleepCustomer),
|
|
|
"01": t.AddDate(0, 0, -businessLeads),
|
|
|
- "08": t.AddDate(0, 0, -noIdeaCustomer),
|
|
|
+ "00": t,
|
|
|
} {
|
|
|
+ sql := `SELECT MAX(c.next_time) nexttime, b.id FROM dwd_f_crm_private_sea a
|
|
|
+ LEFT JOIN dwd_f_crm_clue_info b ON a.clue_id=b.id
|
|
|
+ LEFT JOIN dwd_f_crm_trail_content c ON b.id =c.clue_id
|
|
|
+ WHERE b.trailstatus =?`
|
|
|
+ argsSelect := []interface{}{trailstatus}
|
|
|
+ if trailstatus != "00" {
|
|
|
+ sql += " AND c.createtime <? "
|
|
|
+ argsSelect = append(argsSelect, nexttime)
|
|
|
+ }
|
|
|
+ sql += " GROUP BY b.id"
|
|
|
config.JianyuSubjectdb.SelectByBath(500, func(l *[]map[string]interface{}) bool {
|
|
|
//1新增 2私海手动退回 3私海高意向客户自动退回 4私海意向客户退回 5私海潜在客户退回 6私海沉睡客户退回 7私海商机线索退回 8私海无意向客户退回
|
|
|
comeinsource := GetComeSource()[trailstatus]
|
|
|
ids := []interface{}{}
|
|
|
args := []interface{}{}
|
|
|
+ changeArgs1 := []interface{}{}
|
|
|
+ changeArgs2 := []interface{}{}
|
|
|
+ changeArgs3 := []interface{}{}
|
|
|
+ changeArgs4 := []interface{}{}
|
|
|
for _, v := range *l {
|
|
|
id := v["id"]
|
|
|
+ position_id := v["position_id"]
|
|
|
+ seatNumber := gconv.String(v["seatNumber"])
|
|
|
ids = append(ids, id)
|
|
|
//
|
|
|
- args = append(args, id, time.Now().Format(date.Date_Full_Layout), comeinsource)
|
|
|
+ args = append(args, id, now, comeinsource)
|
|
|
+ changeArgs1 = append(changeArgs1, id, position_id, "trailstatus", "线索状态变更", codeMap[trailstatus], "流失", now, -1)
|
|
|
+ changeArgs2 = append(changeArgs2, id, position_id, "position_id", "所属人变更", GetPositionName(seatNumber), "/", now, -1)
|
|
|
+ changeArgs3 = append(changeArgs3, id, position_id, "退出任务车", "未更新跟进记录自动退回公海", now, -1)
|
|
|
+ changeArgs3 = append(changeArgs3, id, position_id, "退回公海", "未更新跟进记录自动退回公海", now, -1)
|
|
|
+
|
|
|
}
|
|
|
whs := []string{}
|
|
|
for i := 0; i < len(ids); i++ {
|
|
@@ -468,24 +474,34 @@ func ReturnOpenSea() {
|
|
|
count := config.JianyuSubjectdb.UpdateOrDeleteBySql(`delete from dwd_f_crm_private_sea where clue_id in (`+wh+`)`, ids...)
|
|
|
//进入公海
|
|
|
config.JianyuSubjectdb.InsertIgnoreBatch("dwd_f_crm_open_sea", []string{"clue_id", "comeintime", "comeinsource"}, args)
|
|
|
- //
|
|
|
+ //改线索表 职位id 坐席号 弄成空 分配状态改成未分配
|
|
|
+ config.JianyuSubjectdb.UpdateOrDeleteBySql(`UPDATE dwd_f_crm_clue_info SET seatNumber ='',position_id=0,is_assign=0 WHERE id in (`+wh+`)`, ids...)
|
|
|
+
|
|
|
+ // 添加线索修改记录
|
|
|
+
|
|
|
+ //线索状态变更记录
|
|
|
+ config.JianyuSubjectdb.InsertBatch("dwd_f_crm_clue_change_record", []string{"clue_id", "position_id", "change_field", "change_type", "old_value", "new_value", "createtime", "operator_id"}, changeArgs1)
|
|
|
+ config.JianyuSubjectdb.InsertBatch("dwd_f_crm_clue_change_record", []string{"clue_id", "position_id", "change_field", "change_type", "old_value", "new_value", "createtime", "operator_id"}, changeArgs2)
|
|
|
+ config.JianyuSubjectdb.InsertBatch("dwd_f_crm_clue_change_record", []string{"clue_id", "position_id", "change_type", "new_value", "createtime", "operator_id"}, changeArgs3)
|
|
|
+ config.JianyuSubjectdb.InsertBatch("dwd_f_crm_clue_change_record", []string{"clue_id", "position_id", "change_type", "new_value", "createtime", "operator_id"}, changeArgs4)
|
|
|
if count > 0 {
|
|
|
return true
|
|
|
}
|
|
|
return true
|
|
|
- }, `SELECT MAX(c.next_time) nexttime, b.id FROM dwd_f_crm_private_sea a
|
|
|
- LEFT JOIN dwd_f_crm_clue_info b ON a.clue_id=b.id
|
|
|
- LEFT JOIN dwd_f_crm_trail_content c ON b.id =c.clue_id
|
|
|
- WHERE b.trailstatus =? AND c.next_time >? GROUP BY b.id`, trailstatus, nexttime)
|
|
|
+ }, sql, argsSelect...)
|
|
|
}
|
|
|
log.Println("return sea end")
|
|
|
}
|
|
|
|
|
|
+//2私海手动退回 3私海高意向客户自动退回 4私海意向客户退回 5私海潜在客户退回 6私海沉睡客户退回 7私海商机线索退回 8私海无意向客户退回
|
|
|
func GetComeSource() map[string]int {
|
|
|
return map[string]int{
|
|
|
- "04": 2,
|
|
|
- "05": 4,
|
|
|
"06": 3,
|
|
|
+ "05": 4,
|
|
|
+ "04": 5,
|
|
|
+ "03": 6,
|
|
|
+ "01": 7,
|
|
|
+ "00": 8,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -499,3 +515,22 @@ func GetOneSeaC() map[string]bool {
|
|
|
}, `select clue_id from dwd_f_crm_open_sea where comeinsource in(2,3,4)`)
|
|
|
return m
|
|
|
}
|
|
|
+
|
|
|
+func dwd_d_crm_trailstatus_code() map[string]string {
|
|
|
+ m := map[string]string{}
|
|
|
+ data := config.JianyuSubjectdb.SelectBySql(`SELECT CODE,NAME FROM dwd_d_crm_trailstatus_code;`)
|
|
|
+ for _, v := range *data {
|
|
|
+ code := gconv.String(v["CODE"])
|
|
|
+ name := gconv.String(v["NAME"])
|
|
|
+ m[code] = name
|
|
|
+ }
|
|
|
+ return m
|
|
|
+}
|
|
|
+
|
|
|
+func GetPositionName(seatNumber string) string {
|
|
|
+ data := config.JianyuSubjectdb.SelectBySql(`select name from jy_salesperson_info where status = 0 and position != 0 and seatNumber = ? limit 1`, seatNumber)
|
|
|
+ if data != nil && len(*data) > 0 {
|
|
|
+ return gconv.String((*data)[0]["name"])
|
|
|
+ }
|
|
|
+ return ""
|
|
|
+}
|