diff --git a/business/jxutils/cache/cache.go b/business/jxutils/cache/cache.go index 391173fdc..f0a4fcfd0 100644 --- a/business/jxutils/cache/cache.go +++ b/business/jxutils/cache/cache.go @@ -12,6 +12,7 @@ type ICacher interface { GetAs(key string, ptr interface{}) error Keys(prefix string) ([]string, error) + GetString(key string) string FlushDB() error Incr(key string) error LRange(key string) (retVal []string) diff --git a/business/jxutils/cache/redis/redis.go b/business/jxutils/cache/redis/redis.go index 38dfd5d59..7925b70ad 100644 --- a/business/jxutils/cache/redis/redis.go +++ b/business/jxutils/cache/redis/redis.go @@ -1,6 +1,7 @@ package redis import ( + "encoding/json" "fmt" "time" @@ -60,6 +61,18 @@ func (c *Cacher) Get(key string) interface{} { return nil } +func (c *Cacher) GetString(key string) string { + result, err := c.client.Get(key).Result() + if err == nil { + //return result + var retVal interface{} + if err = json.Unmarshal([]byte(result), &retVal); err == nil { + return retVal.(string) + } + } + return "" +} + func (c *Cacher) GetAs(key string, ptr interface{}) error { result, err := c.client.Get(key).Result() if err == nil { diff --git a/business/partner/im/im.go b/business/partner/im/im.go index 6f7d36a79..494329827 100644 --- a/business/partner/im/im.go +++ b/business/partner/im/im.go @@ -262,6 +262,9 @@ func ReadMsgFromVendor(vendorID int, elmAppID string, msg []byte) error { errList.AddErr(fmt.Errorf("客服自动回复出错:%v", err)) } + //5延迟40s 自动回复 + //timer := time.NewTimer(30 * time.Second) + if errList.GetErrListAsOne() != nil { return fmt.Errorf("ReadMsgFromVendor:%v", errList.GetErrListAsOne()) } @@ -519,10 +522,11 @@ type AutoReply struct { Timestamp int `json:"timestamp"` //消息接收时间 } -// CheckAndReply 判断并回复 +// CheckAndReply 判断并回复V1 func CheckAndReply(req *JXMsg, elmAppID string) (err error) { var ( - key string + key string + //keyDetail UserRelInfo flag = false vendorID int errList errlist.ErrList @@ -540,8 +544,14 @@ func CheckAndReply(req *JXMsg, elmAppID string) (err error) { } key = utils.Int2Str(mt.AppID) + ":" + utils.Int2Str(mt.OpenUserID) + ":autoReply" + keyDetailMt := UserRelInfo{ + AppID: utils.Int2Str(mt.AppID), + VendorStoreID: mt.AppPoiCode, + VendorID: VendorIDMTStr, + UserID: utils.Int2Str(mt.OpenUserID), + } //1 检测是否已自动回复 - flag = GetIfReply(key) + flag = GetIfReply(key, keyDetailMt) //判断flag状态 if !flag { apply = &AutoReply{ @@ -553,8 +563,8 @@ func CheckAndReply(req *JXMsg, elmAppID string) (err error) { temp := mt //获取自定义回复模板 - if template := GetCustomTemplate(utils.Int2Str(mt.AppID), mt.AppPoiCode); len(template) > 0 { - temp.MsgContent = template + if template := GetCustomTemplate(utils.Int2Str(mt.AppID), mt.AppPoiCode); len(template.Template) > 0 { + temp.MsgContent = template.Template } else { temp.MsgContent, err = GetDefaultTemplate(utils.Int2Str(mt.AppID), mt.AppPoiCode, VendorIDMT) if err != nil { @@ -575,22 +585,11 @@ func CheckAndReply(req *JXMsg, elmAppID string) (err error) { SendType: SendTypeJx, MsgContent: temp, } - //userList = &UserMessageList{ - // VendorID: VendorIDMT, - // UserID: utils.Int2Str(mt.OpenUserID), - // LatestMsg: mt.MsgContent, - // LatestTime: mt.Cts, - // OrderID: "", - //} - //1 存储详细聊天记录 + // 存储详细聊天记录 if err = SetMessageDetail(jxMsg, vendorID, elmAppID); err != nil { errList.AddErr(fmt.Errorf("自动回复:存储详细聊天记录错误:%v", err)) } - //2 存储展示列表时单条数据 - //if err = SetUserList(jxMsg, userList, vendorID, elmAppID); err != nil { - // errList.AddErr(fmt.Errorf("自动回复:存储STU聊天记录错误:%v", err)) - //} } //3 记录自动回复状态 if err = RecordAutoStatus(key, apply); err != nil { @@ -608,8 +607,14 @@ func CheckAndReply(req *JXMsg, elmAppID string) (err error) { return nil } + keyDetailElm := UserRelInfo{ + AppID: elmAppID, + VendorStoreID: elm.PlatformShopID, + VendorID: VendorIDELMStr, + UserID: elm.PayLoad.GroupID, + } //1 检测是否已自动回复 - flag = GetIfReply(key) + flag = GetIfReply(key, keyDetailElm) if !flag { apply = &AutoReply{ UserID: utils.Str2Int(elm.PayLoad.SenderID), @@ -631,8 +636,8 @@ func CheckAndReply(req *JXMsg, elmAppID string) (err error) { } temp := "" - if template := GetCustomTemplate(elmAppID, elm.PlatformShopID); len(template) > 0 { - temp = template + if template := GetCustomTemplate(elmAppID, elm.PlatformShopID); len(template.Template) > 0 { + temp = template.Template } else { temp, err = GetDefaultTemplate(elmAppID, elm.PlatformShopID, VendorIDELM) if err != nil { @@ -645,33 +650,23 @@ func CheckAndReply(req *JXMsg, elmAppID string) (err error) { //Text: temp, } tempContentStr, _ := json.Marshal(tempContent) + param.PayLoad.Content = string(tempContentStr) if err = partner.CurAPIManager.GetAPI(model.VendorIDEBAI, elmAppID).(*ebaiapi.API).BusinessSendMsg(param); err != nil { apply.IsApply = false globals.SugarLogger.Debugf("CheckAndReply mtSend err:%v", err) } else { - //t1 := json.Unmarshal(param.Payload.Content) jxMsg = &JXMsg{ SendType: SendTypeJx, MsgContent: param, } - //userList = &UserMessageList{ - // VendorID: VendorIDELM, - // UserID: elm.PayLoad.SenderID, - // LatestMsg: elm.PayLoad.Content, - // LatestTime: elm.PayLoad.CreateTime, - // OrderID: "", - //} - //1 存储详细聊天记录list + + //存储详细聊天记录list if err = SetMessageDetail(jxMsg, vendorID, elmAppID); err != nil { errList.AddErr(fmt.Errorf("自动回复:存储详细聊天记录错误:%v", err)) } - //2 存储展示列表时单条数据 - //if err = SetUserList(jxMsg, userList, vendorID, elmAppID); err != nil { - // errList.AddErr(fmt.Errorf("自动回复:存储STU聊天记录错误:%v", err)) - //} } //3 记录自动回复状态 if err = RecordAutoStatus(key, apply); err != nil { @@ -686,8 +681,170 @@ func CheckAndReply(req *JXMsg, elmAppID string) (err error) { return nil } +// CheckAndReplyV2 判断并回复V2 延迟35s +//func CheckAndReplyV2(req *JXMsg, elmAppID string) (err error) { +// var ( +// key string +// vendorID int +// errList errlist.ErrList +// jxMsg = &JXMsg{} +// ) +// +// if req.SendType == SendTypeMt { +// vendorID = VendorIDMT +// mt := req.MsgContent.(mtwmapi.PushContentReq) +// //获取聊天详情 +// imDetail, err := GetImChatDetail([]UserRelInfo{{ +// VendorStoreID: mt.AppPoiCode, +// VendorID: VendorIDMTStr, +// AppID: utils.Int2Str(mt.AppID), +// UserID: utils.Int2Str(mt.OpenUserID), +// }}) +// if err != nil { +// return err +// } +// if len(imDetail) > 0 { +// //获取此消息列表最后一条数据 +// key = GenMsgDetailID(req, VendorIDMT, elmAppID) +// temp := imDetail[key][len(imDetail)-1] +// tStr, _ := json.Marshal(temp) +// +// lastMsg := &JXMsg{} +// err = json.Unmarshal(tStr, lastMsg) +// if err != nil { +// return err +// } +// //京西发送或美团商家发送 跳过 +// if lastMsg.SendType == SendTypeJx { +// return nil +// } +// +// lastMt := lastMsg.MsgContent.(mtwmapi.PushContentReq) +// if lastMt.MsgSource == mtwmapi.MsgSourceStore { +// return nil +// } +// +// //生成回复消息体 +// sendMt := mt +// //获取自定义回复模板 +// if template := GetCustomTemplate(utils.Int2Str(mt.AppID), mt.AppPoiCode); len(template) > 0 { +// sendMt.MsgContent = template +// } else { +// sendMt.MsgContent, err = GetDefaultTemplate(utils.Int2Str(mt.AppID), mt.AppPoiCode, VendorIDMT) +// if err != nil { +// sendMt.MsgContent = AutoReplyByAppID[mt.AppID] +// } +// } +// sendMt.MsgType = mtwmapi.MsgTypeText +// sendMt.MsgSource = mtwmapi.MsgSourceStore +// sendMt.Cts = int(time.Now().Unix()) +// sendMt.MsgID = RandTimeNumber() +// +// //向美团发送消息 +// dataStr, _ := json.Marshal(sendMt) +// if _, err = partner.CurAPIManager.GetAPI(model.VendorIDMTWM, utils.Int2Str(mt.AppID)).(*mtwmapi.API).MsgSend(string(dataStr)); err != nil { +// globals.SugarLogger.Debugf("CheckAndReply mtSend err:%v", err) +// return err +// } else { +// jxMsg = &JXMsg{ +// SendType: SendTypeJx, +// MsgContent: temp, +// } +// if err = SetMessageDetail(jxMsg, vendorID, elmAppID); err != nil { +// errList.AddErr(fmt.Errorf("自动回复V2:存储详细聊天记录错误:%v", err)) +// } +// } +// } +// } +// +// if req.SendType == SendTypeElm { +// vendorID = VendorIDELM +// elm := req.MsgContent.(ebaiapi.ImMessageSend) +// imDetail, err := GetImChatDetail([]UserRelInfo{{ +// VendorStoreID: elm.PlatformShopID, +// VendorID: VendorIDELMStr, +// AppID: elmAppID, +// UserID: elm.PayLoad.SenderID, +// }}) +// if err != nil { +// return err +// } +// if len(imDetail) > 0 { +// //获取此消息列表最后一条数据 +// key = GenMsgDetailID(req, VendorIDELM, elmAppID) +// temp := imDetail[key][len(imDetail)-1] +// tStr, _ := json.Marshal(temp) +// +// lastMsg := &JXMsg{} +// err = json.Unmarshal(tStr, lastMsg) +// if err != nil { +// return err +// } +// //京西发送或美团商家发送 跳过 +// if lastMsg.SendType == SendTypeJx { +// return nil +// } +// +// //结构不一样后解 +// lastElm := lastMsg.MsgContent.(ebaiapi.ImMessageSend) +// if lastElm.PayLoad.SenderID == "" || elm.PayLoad.SenderID[:2] != ebaiapi.SenderTypeUser { +// return nil +// } +// +// param := &ebaiapi.BusinessSendMsgReq{ +// PlatformShopId: elm.PlatformShopID, +// BizType: ebaiapi.IMType, +// SubBizType: ebaiapi.IMTypeSendMsg, +// PayLoad: ebaiapi.BusinessMsgPayload{ +// GroupId: elm.PayLoad.GroupID, +// MsgId: utils.Int2Str(RandTimeNumber()), +// ReceiverIds: elm.PayLoad.ReceiverIDs, +// ContentType: utils.Int2Str(ebaiapi.ContentTypeNormal), +// }, +// } +// +// data := "" +// if template := GetCustomTemplate(elmAppID, elm.PlatformShopID); len(template) > 0 { +// data = template +// } else { +// data, err = GetDefaultTemplate(elmAppID, elm.PlatformShopID, VendorIDELM) +// if err != nil { +// data = LastTemplate +// } +// } +// dataStr, _ := json.Marshal(data) +// tempContent := ebaiapi.Content{ +// Text: string(dataStr), +// } +// tempContentStr, _ := json.Marshal(tempContent) +// param.PayLoad.Content = string(tempContentStr) +// +// if err = partner.CurAPIManager.GetAPI(model.VendorIDEBAI, elmAppID).(*ebaiapi.API).BusinessSendMsg(param); err != nil { +// globals.SugarLogger.Debugf("CheckAndReply mtSend err:%v", err) +// return err +// } else { +// jxMsg = &JXMsg{ +// SendType: SendTypeJx, +// MsgContent: param, +// } +// if err = SetMessageDetail(jxMsg, vendorID, elmAppID); err != nil { +// errList.AddErr(fmt.Errorf("自动回复V2:存储详细聊天记录错误:%v", err)) +// } +// } +// +// } +// +// } +// +// if errList.GetErrListAsOne() != nil { +// return fmt.Errorf("CheckAndReplyV2 err=%v", errList.GetErrListAsOne()) +// } +// +// return nil +//} + // GetIfReply 检查是否已回复 -func GetIfReply(key string) (flag bool) { +func GetIfReply(key string, keyDetail UserRelInfo) (flag bool) { flag = false if n, err := rdb.Exists(key); n > 0 && err == nil { str := rdb.LRange(key) @@ -698,6 +855,37 @@ func GetIfReply(key string) (flag bool) { } } } + + //检测是否商家回复 + var param []UserRelInfo + temp := append(param, keyDetail) + if detail, err := GetImChatDetail(temp); err == nil && detail != nil { + tKey := keyDetail.AppID + ":" + keyDetail.VendorStoreID + ":" + keyDetail.VendorID + ":" + keyDetail.UserID + if detail[tKey] != nil { + tDetail := detail[tKey][len(detail[tKey])-1] + //b, _ := json.Marshal(tDetail) + b := tDetail.(string) + lastMsg := &JXMsg{} + //err = json.Unmarshal(b, lastMsg) + err = json.Unmarshal([]byte(b), lastMsg) + if err != nil || lastMsg.SendType == SendTypeJx { + return false + } + switch keyDetail.VendorID { + case VendorIDMTStr: + mt := lastMsg.MsgContent.(map[string]interface{}) + if mt["msg_source"].(float64) != mtwmapi.MsgSourceUser { + return false + } + case VendorIDELMStr: + elm := lastMsg.MsgContent.(map[string]interface{}) + s := elm["payLoad"].(map[string]interface{})["senderId"].(string) + if s == "" || s[:2] != ebaiapi.SenderTypeUser { + return false + } + } + } + } return flag } @@ -705,7 +893,7 @@ func GetIfReply(key string) (flag bool) { func RecordAutoStatus(key string, apply *AutoReply) error { data, _ := json.Marshal(apply) err := rdb.RPush(key, string(data)) - ok, err := rdb.ExpireResult(key, ExpireTimeDay) + ok, err := rdb.ExpireResult(key, ExpireTimeAutoReply) if err != nil || !ok { globals.SugarLogger.Debugf("CheckAndReply apply err:%v", err) return err @@ -719,12 +907,24 @@ func GenCustomReplyID(appID, vendorStoreID string) string { } // GetCustomTemplate 获取自动回复模板 -func GetCustomTemplate(appID, vendorStoreID string) (storeTemplate string) { +func GetCustomTemplate(appID, vendorStoreID string) (storeTemplate StoreTemplate) { + var retVal string key := GenCustomReplyID(appID, vendorStoreID) - temp := rdb.Get(key) - if temp != nil { - storeTemplate = temp.(string) + + data := rdb.LRange(key) + if data != nil { + retVal = data[0] } + + //temp := rdb.Get(key) + //temp := regexp.MustCompile(`^"(.*)"$`).ReplaceAllString(retVal, `$1`) + //storeTemplate = strings.Trim(retVal, "/") + //fmt.Println(temp) + temp := StoreTemplate{} + _ = json.Unmarshal([]byte(retVal), &temp) + + storeTemplate = temp + return storeTemplate } @@ -736,9 +936,15 @@ func AddCustomReply(appID, vendorStoreID, replyTemplate string) (storeTemplate s key := GenCustomReplyID(appID, vendorStoreID) + template := StoreTemplate{ + Template: replyTemplate, + } + data, _ := json.Marshal(template) + err = rdb.Del(key) if err == nil { - err = rdb.Set(key, replyTemplate, 0) + err = rdb.RPush(key, data) + //err = rdb.Set(key, replyTemplate, 0) } return "", err } diff --git a/business/partner/im/im_model.go b/business/partner/im/im_model.go index fd6b3f01d..27b42af14 100644 --- a/business/partner/im/im_model.go +++ b/business/partner/im/im_model.go @@ -10,7 +10,9 @@ const ( BaseTemplate = "您好,特殊天气或节假日可能出现骑手变少,配送延迟等情况,烦请耐心等待,售后及其他问题请联系店长,电话:" LastTemplate = "您好,特殊天气或节假日可能出现骑手变少,配送延迟等情况,烦请耐心等待,售后及其他问题请联系店长" //兜底模板 - BasePhoneNum = "18048531223" + BasePhoneNum = "18048531223" + BaseUserListKey = "userList" + BaseMsgDetailKey = "messageDetail" ) var AutoReplyByAppID = map[int]string{ @@ -60,12 +62,19 @@ type UserRelInfo struct { UserID string `json:"userID"` //用户id/groupID } +type StoreTemplate struct { + Template string `json:"template"` +} + var ( rdb = api.Cacher VendorIDMT = 1 //im美团 VendorIDELM = 3 //im饿了么 VendorIDJX = 3 //im京西 + VendorIDMTStr = "1" //im美团 + VendorIDELMStr = "3" //im饿了么 + SendTypeJx = "jx" //京西客户端发送方标识 SendTypeMt = "mt" //美团用户发送方标识符 SendTypeElm = "elm" //饿了么用户发送方标识符 @@ -75,5 +84,7 @@ var ( ) const ( - ExpireTimeDay = 12 * time.Hour //redis过期时间 + ExpireTimeDay = 12 * time.Hour //redis过期时间 + ExpireTimeAutoReply = 20 * time.Second //redis过期时间 + ) diff --git a/globals/api/api.go b/globals/api/api.go index 2eb540b32..a7f336c07 100644 --- a/globals/api/api.go +++ b/globals/api/api.go @@ -296,8 +296,6 @@ func Init() { ShowAPI = showapi.New(beego.AppConfig.DefaultInt("showAppID", 0), beego.AppConfig.DefaultString("showAppSecret", "")) Cacher = redis.New(beego.AppConfig.DefaultString("redisHost", "localhost"), beego.AppConfig.DefaultInt("redisPort", 0), beego.AppConfig.DefaultString("redisPassword", "")) - //Todo 本地测试用 - //Cacher = redis.New(beego.AppConfig.DefaultString("redisHost", "127.0.0.1"), beego.AppConfig.DefaultInt("redisPort", 6379), beego.AppConfig.DefaultString("redisPassword", "123456")) AliUpcAPI = aliupcapi.New(beego.AppConfig.DefaultString("aliUpcAppCode", "")) diff --git a/globals/beegodb/beegodb.go b/globals/beegodb/beegodb.go index 7844d3a7a..6bde56b1a 100644 --- a/globals/beegodb/beegodb.go +++ b/globals/beegodb/beegodb.go @@ -21,11 +21,6 @@ func Init() { //orm.RegisterDataBase("api", "mysql", "root:WebServer@1@tcp(127.0.0.1:3306)/api?charset=utf8mb4&loc=Local&parseTime=true") //orm.RegisterDataBase("c4beta", "mysql", "ubuntu:WebServer@1@tcp(127.0.0.1:3306)/jxd_dev_0?charset=utf8mb4&loc=Local&parseTime=true") - //本地服务器测试用 -ysq - //orm.RegisterDataBase("default", "mysql", "root:123456@tcp(127.0.0.1:3306)/jxd_dev_0?charset=utf8mb4&loc=Local&parseTime=true") - //orm.RegisterDataBase("c4beta", "mysql", "root:123456@tcp(127.0.0.1:3306)/jxd_dev_0?charset=utf8mb4&loc=Local&parseTime=true") - //orm.RegisterDataBase("api", "mysql", "root:123456@tcp(127.0.0.1:3306)/api?charset=utf8mb4&loc=Local&parseTime=true") - // 开启sql打印 //orm.Debug = true