liuxiaolong
2020-09-08 f58d6c7e121dc6a16f6b1e12e5beba1e6ce1b992
service/msgPush.go
@@ -2,10 +2,12 @@
import (
   "car-service/extend/util"
   "car-service/models"
   "encoding/json"
   "errors"
   "fmt"
   "github.com/astaxie/beego"
   "strings"
   "sync"
   "time"
)
@@ -45,6 +47,114 @@
   }()
}
//批量解绑别名
func UnbindAlias(cid string) (bool, error) {
   var uc models.UserClient
   ucList := uc.GetByCid(cid)
   if ucList ==nil {
      return true,errors.New("ucList is nil")
   }
   var aliasArr []string
   for _,als := range ucList {
      aliasArr = append(aliasArr, als.PhoneNum)
   }
   appId := beego.AppConfig.String("pushAppId")
   baseUrl := beego.AppConfig.String("pushBaseUrl") + appId
   retryTimes := 0
ReTry:
   token := getCacheToken()
   if token == "" {
      return false, errors.New("token is nil")
   }
   url := baseUrl + "/user/alias"
   header := map[string]string {
      "token": token,
   }
   caArr := make([]map[string]string, 0)
   for _,as := range aliasArr {
      caArr = append(caArr, map[string]string{
         "cid": cid,
         "alias": as,
      })
   }
   reqBody := map[string]interface{}{
      "data_list": caArr,
   }
   b,err := util.DoDeleteRequest(url, util.CONTENT_TYPE_UTF8_JSON, reqBody, header)
   if err !=nil {
      fmt.Println("DoDelete err:", err)
      return false, err
   }
   var result PushResult
   err = json.Unmarshal(b, &result)
   if err != nil {
      fmt.Println("unmarshal err:", err)
      return false, err
   }
   if result.Code == 0 { //解绑成功
      return true, nil
   } else if result.Code == 10001 { //token过期
      if retryTimes <=3 {
         newToken, err := RefreshToken()
         if err == nil {
            updateToken(newToken)
            retryTimes++
            goto ReTry
         }
      }
   }
   return false, errors.New(result.Msg)
}
//一个别名最多允许绑定10个cid
func BindAlias(cid, alias string) (bool, error) {
   appId := beego.AppConfig.String("pushAppId")
   baseUrl := beego.AppConfig.String("pushBaseUrl") + appId
   retryTimes := 0
ReTry:
   token := getCacheToken()
   if token == "" {
      return false, errors.New("token is nil")
   }
   url := baseUrl+"/user/alias"
   caArr := make([]map[string]string, 0)
   caArr = append(caArr, map[string]string{
      "cid": cid,
      "alias": alias,
   })
   reqBody := map[string]interface{} {
      "data_list":caArr,
   }
   header := map[string]string {
      "token": token,
   }
   b, err := util.DoPostRequest(url, util.CONTENT_TYPE_UTF8_JSON, reqBody, nil, header)
   if err !=nil {
      fmt.Println("DoPost err:", err)
      return false, err
   }
   var result PushResult
   err = json.Unmarshal(b, &result)
   if err != nil {
      fmt.Println("unmarshal err:", err)
      return false, err
   }
   if result.Code == 0 { //绑定成功
      return true, nil
   } else if result.Code == 10001 { //token过期
      if retryTimes <=3 {
         newToken, err := RefreshToken()
         if err == nil {
            updateToken(newToken)
            retryTimes++
            goto ReTry
         }
      }
   }
   return false, errors.New(result.Msg)
}
type PushResult struct {
   Code       int          `json:"code"`
   Msg       string          `json:"msg"`
@@ -56,7 +166,467 @@
   Token       string          `json:"token"`
}
func Push(title string, msg string) (bool,error) {
type CreatePushMsgResult struct {
   TaskId       string          `json:"taskid"`
}
func createPushMsg(title string, msg string) (bool,string, error) {
   appId := beego.AppConfig.String("pushAppId")
   baseUrl := beego.AppConfig.String("pushBaseUrl") + appId
   retryTimes := 0
ReTry:
   token := getCacheToken()
   if token == "" {
      return false, "", errors.New("token is nil")
   }
   url := baseUrl+"/push/list/message"
   intent := "intent:#Intent;action=android.intent.action.oppopush;launchFlags=0x14000000;component=uni.UNIEDF0B5C/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title="+title+";S.content="+msg+";S.payload=test;end"
   androidPush := map[string]map[string]map[string]string {
      "ups": {
         "notification": {
            "title": title,
            "body": msg,
            "big_text": msg,
            "click_type": "intent",
            "intent": intent,
         },
      },
   }
   iosPush := map[string]interface{}{
      "type":"notify",
      "payload": "育英中学停车",
      "aps":map[string]interface{}{
         "alert":map[string]string{
            "title": title,
            "body": msg,
         },
         "content-available":0,
      },
   }
   reqBody := map[string]interface{} {
      "request_id": time.Now().Format("20060102150405") + util.GenValidateCode(6),
      "settings":map[string]int {
         "ttl": 8 * 3600 * 1000,
      },
      "push_message": map[string]map[string]string {
         "notification": {
            "title": title,
            "body": msg,
            "click_type": "intent",
            "intent": intent,
         },
      },
      "push_channel": map[string]interface{} {
         "android": androidPush,
         "ios":iosPush,
      },
   }
   header := map[string]string {
      "token": token,
   }
   b, err := util.DoPostRequest(url, util.CONTENT_TYPE_UTF8_JSON, reqBody, nil, header)
   if err !=nil {
      fmt.Println("DoPost err:", err)
      return false, "",err
   }
   var result PushResult
   err = json.Unmarshal(b, &result)
   if err != nil {
      fmt.Println("unmarshal err:", err)
      return false, "",err
   }
   if result.Code == 0 {
      rb,uErr := json.Marshal(result.Data)
      if uErr == nil {
         var tResult CreatePushMsgResult
         if uErr =json.Unmarshal(rb, &tResult); uErr ==nil {
            return true, tResult.TaskId, nil
         } else {
            return false, "", uErr
         }
      } else {
         return false, "", uErr
      }
   } else if result.Code == 10001 { //token过期
      if retryTimes <=3 {
         newToken, err := RefreshToken()
         if err == nil {
            updateToken(newToken)
            retryTimes++
            goto ReTry
         }
      }
   }
   return false, "", errors.New(result.Msg)
}
//对已注册的用户进行消息推送。调用此接口前需调用创建消息接口设置消息内容
func PushByAlias(title string, msg string, isTest bool) (bool, error, []string) {
   var aliasArr []string
   pushUserM := make(map[string]string)
   var userE models.User
   allUsers, _ := userE.GetAllUsers()
   if allUsers != nil {
      for _,u := range allUsers {
         pushUserM[u.Id] = u.PhoneNum
      }
   }
   if len(pushUserM) == 0 {
      return false,errors.New("len(pushUserM) == 0"),aliasArr
   }
   carPersonM := make(map[string]string) //以车牌号为key,value是hik的personId
   csv := NewCarService()
   carPersons := csv.GetVehicleListByPerson("")
   if carPersons != nil {
      for _, cp := range carPersons {
         ncPlateNo := preDealPlateNo(cp.PlateNo) //去掉汉字,D和0替换成*
         if ncPlateNo != "" {
            carPersonM[ncPlateNo] = cp.PersonId
         }
      }
   }
   delPersonIdM := make(map[string]string)
   spaceNos := csv.FindSpaceNo("")
   for _,sn := range spaceNos {
      if sn.State == 1 && sn.PlateNo != "" { //已经把车停到停车场的车主,不再推送消息
         realPlateNo := preDealPlateNo(sn.PlateNo)
         if realPlateNo != "" {
            if pId,ok := carPersonM[realPlateNo];ok {
               delPersonIdM[pId] = pId
               delete(carPersonM, realPlateNo)
            }
         }
      }
   }
   var uc models.UserClient
   for _,personId := range carPersonM {
      if phoneNum,ok := pushUserM[personId]; ok { //此人已注册到系统,并且车不在停车库内
         if _,in := delPersonIdM[personId];!in {
            if uc.Exist(phoneNum) {
               aliasArr = append(aliasArr, phoneNum)
            }
         }
      }
   }
   lenAS := len(aliasArr)
   if  lenAS == 0 {
      return false, errors.New("没有推送目标,aliasArr is empty"),aliasArr
   }
   if isTest {
      //只给内部手机号推
      testPhones := beego.AppConfig.String("testPushPhones")
      if testPhones == "" {
         return false, errors.New("test push aliasArr is empty"),aliasArr
      }
      aliasArr = strings.Split(testPhones, ",")
   }
   cResult, taskId, ce := createPushMsg(title, msg)
   fmt.Println("createPushMsg taskId:", taskId, "cResult:",cResult, "err:", ce)
   if !cResult {
      return false, errors.New("创建推送前置消息失败"),aliasArr
   }
   //alias 单次推送长度上限是200
   pushTime := 1
   if lenAS > 200 {
      pushTime = lenAS / 200
      if lenAS % 200 >0 {
         pushTime++
      }
   }
   isSuccess := false
   for i:=0;i<pushTime;i++ {
      var curAliasArr []string
      start := i*200
      end := (i+1)*200 -1
      if i<pushTime-1 {
         curAliasArr = aliasArr[start:end]
      } else {
         //取剩余所有
         curAliasArr = aliasArr[start:]
      }
      if len(curAliasArr) >0 {
         flag, e := doPush(taskId, curAliasArr)
         if e != nil {
            fmt.Println("doPush err:", e)
         }
         if flag {
            isSuccess = true
         }
      }
   }
   if isSuccess {
      return true, nil,aliasArr
   }
   return false, errors.New("推送失败"),aliasArr
}
//预处理车牌号,去除首个汉字,以及忽略D和0
func preDealPlateNo(pn string) string {
   if pn != "" {
      r := []rune(pn)
      ncStr := string(r[1:])
      newPlateNo := strings.ReplaceAll(ncStr, "D", "*")
      newPlateNo = strings.ReplaceAll(newPlateNo, "0", "*")
      return newPlateNo
   }
   return ""
}
func doPush(taskId string, aliasArr []string) (bool,error) {
   appId := beego.AppConfig.String("pushAppId")
   baseUrl := beego.AppConfig.String("pushBaseUrl") + appId
   retryTimes := 0
ReTry:
   token := getCacheToken()
   if token == "" {
      return false, errors.New("token is nil")
   }
   url := baseUrl+"/push/list/alias"
   reqBody := map[string]interface{} {
      "audience":map[string]interface{}{
         "alias": aliasArr,
      },
      "taskid": taskId,
      "is_async": false,
   }
   header := map[string]string {
      "token": token,
   }
   b, err := util.DoPostRequest(url, util.CONTENT_TYPE_UTF8_JSON, reqBody, nil, header)
   if err !=nil {
      fmt.Println("DoPost err:", err)
      return false, err
   }
   var result PushResult
   err = json.Unmarshal(b, &result)
   if err != nil {
      fmt.Println("unmarshal err:", err)
      return false, err
   }
   if result.Code == 0 {
      return true, nil
   } else if result.Code == 10001 { //token过期
      if retryTimes <=3 {
         newToken, err := RefreshToken()
         if err == nil {
            updateToken(newToken)
            retryTimes++
            goto ReTry
         }
      }
   } else {
      fmt.Println("推送结果:", result)
   }
   return false, errors.New("推送失败")
}
func NightPush(title string, msg string) (bool, error, []string, []string) {
   var aliasArr []string
   var carOwners []string
   pushUserM := make(map[string]string)
   var userE models.User
   allUsers, _ := userE.GetAllUsers()
   if allUsers != nil {
      for _,u := range allUsers {
         pushUserM[u.Id] = u.PhoneNum
      }
   }
   if len(pushUserM) == 0 {
      return false, errors.New("len(pushUserM) == 0"), aliasArr, carOwners
   }
   carPersonM := make(map[string]string)
   csv := NewCarService()
   carPersons := csv.GetVehicleListByPerson("")
   if carPersons != nil {
      for _, cp := range carPersons {
         cnPlateNo := preDealPlateNo(cp.PlateNo)
         if cnPlateNo != "" {
            carPersonM[cnPlateNo] = cp.PersonId
         }
      }
   }
   //夜间某些领导的车可以停在车库内
   var vipPlateNoArr []string
   vipArr := strings.Split(beego.AppConfig.String("nightVipPlateNos"), ",")
   if vipArr != nil && len(vipArr) >0 {
      for _,po := range vipArr {
         if po != "" {
            cnPo := preDealPlateNo(po)
            if cnPo != "" {
               vipPlateNoArr = append(vipPlateNoArr, cnPo)
            }
         }
      }
   }
   hikPersonMap := csv.GetHikPersonMap()
   spaceNos := csv.FindSpaceNo("")
   var uc models.UserClient
   for _,sn := range spaceNos {
      if sn.State == 1 { //车尚在停车场的车牌
         inCnPlateNo := preDealPlateNo(sn.PlateNo)
         if inCnPlateNo != "" {
            if !isVipCar(inCnPlateNo, vipPlateNoArr) {
               if personId,ok := carPersonM[inCnPlateNo];ok {
                  if phoneNum,ok := pushUserM[personId]; ok {
                     if uc.Exist(phoneNum) {
                        aliasArr = append(aliasArr, phoneNum)
                     }
                  }
                  if v,ex := hikPersonMap[personId]; ex {
                     carOwners = append(carOwners, sn.PlateNo+"("+v.PersonName+")")
                  }
               } else { //找不到车主,即视为临时车,将车牌作为车主姓名推送
                  if sn.PlateNo == "无车牌" {
                     carOwners = append(carOwners, sn.PlateNo)
                  } else {
                     carOwners = append(carOwners, sn.PlateNo+"()")
                  }
               }
            }
         }
      }
   }
   if len(aliasArr) == 0 {
      fmt.Println("没有推送目标,aliasArr is empty")
      return false, errors.New("没有推送目标,aliasArr is empty"), aliasArr, carOwners
   }
   cResult, taskId, ce := createPushMsg(title, msg)
   if !cResult {
      fmt.Println("createPushMsg taskId:", taskId, "err:", ce)
      return false, errors.New("创建推送前置消息失败"), aliasArr, carOwners
   }
   b,e := doPush(taskId, aliasArr)
   return b,e, aliasArr, carOwners
}
//此方法做测试使用
//获取停留在车库内车主的姓名,找不到姓名返回这牌
func GetLeftCarOwners() []string {
   var carOwners []string
   pushUserM := make(map[string]string)
   var userE models.User
   allUsers, _ := userE.GetAllUsers()
   if allUsers != nil {
      for _,u := range allUsers {
         pushUserM[u.Id] = u.PhoneNum
      }
   }
   carPersonM := make(map[string]string)
   csv := NewCarService()
   carPersons := csv.GetVehicleListByPerson("")
   if carPersons != nil {
      for _, cp := range carPersons {
         cnPlateNo := preDealPlateNo(cp.PlateNo)
         if cnPlateNo != "" {
            carPersonM[cnPlateNo] = cp.PersonId
         }
      }
   }
   //夜间某些领导的车可以停在车库内
   var vipPlateNoArr []string
   vipArr := strings.Split(beego.AppConfig.String("nightVipPlateNos"), ",")
   if vipArr != nil && len(vipArr) >0 {
      for _,po := range vipArr {
         if po != "" {
            cnPo := preDealPlateNo(po)
            if cnPo != "" {
               vipPlateNoArr = append(vipPlateNoArr, cnPo)
            }
         }
      }
   }
   hikPersonMap := csv.GetHikPersonMap()
   spaceNos := csv.FindSpaceNo("")
   for _,sn := range spaceNos {
      if sn.State == 1 { //车尚在停车场的车牌
         inCnPlateNo := preDealPlateNo(sn.PlateNo)
         if inCnPlateNo != "" {
            if !isVipCar(inCnPlateNo, vipPlateNoArr) {
               if personId,ok := carPersonM[inCnPlateNo];ok {
                  if v,ex := hikPersonMap[personId]; ex {
                     carOwners = append(carOwners, sn.PlateNo+"("+v.PersonName+")")
                  }
               } else { //找不到车主,即视为临时车,将车牌作为车主姓名推送
                  if sn.PlateNo == "无车牌" {
                     carOwners = append(carOwners, sn.PlateNo)
                  } else {
                     carOwners = append(carOwners, sn.PlateNo+"()")
                  }
               }
            }
         }
      }
   }
   return carOwners
}
//vip车主,不接收请离开的通知,也不通知管理员这个车还在车库内
func isVipCar(targetPlateNo string, vipPlateNoArr []string) bool {
   b := false
   if vipPlateNoArr != nil {
      for _,v := range vipPlateNoArr {
         if targetPlateNo == v {
            b = true
            break
         }
      }
   }
   return b
}
//如果夜间有给车库内的车主推送“请尽快驶离”的消息,则告知管理员 有哪些车尚停在车库内
func Push2Manager(title string, msg string, isTest bool) (bool, error, []string) {
   if len(msg) > 256 {
      fmt.Println("夜间停留车太多,message:", msg)
      r := []rune(msg)
      msg = string(r[:125])+"..."
   }
   managers := ""
   if isTest {
      managers = beego.AppConfig.String("testPushPhones")
   } else {
      managers = beego.AppConfig.String("nightManagerPhones")
   }
   if managers == "" {
      return false, errors.New("夜间推送管理员手机号未配置"), []string{}
   }
   managerArr := strings.Split(managers, ",")
   if len(managerArr) == 0 {
      fmt.Println("没有推送目标,managerArr is empty")
      return false, errors.New("管理员手机号未配置"), managerArr
   }
   cResult, taskId, ce := createPushMsg(title, msg)
   if !cResult {
      fmt.Println("createPushMsg taskId:", taskId, "err:", ce)
      return false, errors.New("创建推送前置消息失败"), managerArr
   }
   b,e := doPush(taskId, managerArr)
   return b,e, managerArr
}
/*
func PushAll(title string, msg string) (bool,error) {
   appId := beego.AppConfig.String("pushAppId")
   baseUrl := beego.AppConfig.String("pushBaseUrl") + appId
   retryTimes := 0
@@ -66,17 +636,48 @@
      return false, errors.New("token is nil")
   }
   url := baseUrl+"/push/all"
   intent := "intent:#Intent;action=android.intent.action.oppopush;launchFlags=0x14000000;component=uni.UNIEDF0B5C/io.dcloud.PandoraEntry;S.UP-OL-SU=true;S.title="+title+";S.content="+msg+";S.payload=test;end"
   androidPush := map[string]map[string]map[string]string {
      "ups": {
         "notification": {
            "title": title,
            "body": msg,
            "click_type": "intent",
            "intent": intent,
         },
      },
   }
   iosPush := map[string]interface{}{
      "type":"notify",
      "payload":"育英中学停车",
      "aps":map[string]interface{}{
         "alert":map[string]string{
            "title": title,
            "body": msg,
         },
         "content-available":0,
         //"sound":"com.gexin.ios.silence",
         //"category":"ACTIONABLE",
      },
      "auto_badge":"+1",
   }
   reqBody := map[string]interface{} {
      "request_id": time.Now().Format("20060102150405") + util.GenValidateCode(6),
      "settings":map[string]int {
         "ttl": 8 * 3600 * 1000,
      },
      "audience": "all",
      "push_message": map[string]string{
         "title": title,
         "body": msg,
         "click_type": "url",
         "url": "http://baidu.com",
      "push_message": map[string]map[string]string {
         "notification": {
            "title": title,
            "body": msg,
            "click_type": "intent",
            "intent": intent,
         },
      },
      "push_channel": map[string]interface{} {
         "android": androidPush,
         "ios":     iosPush,
      },
   }
   header := map[string]string {
@@ -104,21 +705,18 @@
            goto ReTry
         }
      }
   } else {
      fmt.Println("推送结果:", result)
   }
   return false, errors.New("推送失败")
}
}*/
func RefreshToken() (*TokenResult,error) {
   //appId := beego.AppConfig.String("pushAppId")
   //appKey := beego.AppConfig.String("pushAppKey")
   //masterSecret := beego.AppConfig.String("pushMasterSecret")
   //baseUrl := beego.AppConfig.String("pushBaseUrl") + appId
   appId := "WU8KzZBaTwADIjTwaMSzW5"
   appKey := "9JLs4B1ZVkAVh93JXX3MP4"
   masterSecret := "IbSAe83ZoZAczhKSA3Bwj9"
   baseUrl := "https://restapi.getui.com/v2/"+appId
   appId := beego.AppConfig.String("pushAppId")
   appKey := beego.AppConfig.String("pushAppKey")
   masterSecret := beego.AppConfig.String("pushMasterSecret")
   baseUrl := beego.AppConfig.String("pushBaseUrl") + appId
   url := baseUrl +"/auth"
   timestamp := fmt.Sprintf("%v", time.Now().UnixNano() / 1e6)
   sign := util.Sha256(appKey + timestamp + masterSecret)