send_msg.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. package crontask
  2. import (
  3. "encoding/json"
  4. "net/url"
  5. "path"
  6. "time"
  7. "wechat-api/ent"
  8. "wechat-api/ent/batchmsg"
  9. "wechat-api/ent/contact"
  10. "wechat-api/ent/custom_types"
  11. "wechat-api/ent/labelrelationship"
  12. "wechat-api/ent/msg"
  13. "wechat-api/ent/wx"
  14. "wechat-api/hook"
  15. "wechat-api/internal/utils/dberrorhandler"
  16. )
  17. func (l *CronTask) sendMsg() {
  18. // 获取 BatchMsg 表中 start_time 小于当前时间并且 status 为 0 或 1 的数据
  19. batchList, err := l.svcCtx.DB.BatchMsg.Query().Where(
  20. batchmsg.StartTimeLT(time.Now()),
  21. batchmsg.StatusIn(0, 1),
  22. batchmsg.CtypeIn(1, 3),
  23. ).All(l.ctx)
  24. l.Logger.Infof("send_msg.go BatchList %v\n", batchList)
  25. if err != nil {
  26. l.Logger.Errorf("batchList err: %v\n", err)
  27. return
  28. }
  29. startTime := time.Now()
  30. for _, batch := range batchList {
  31. // 记录当前批次开始处理
  32. l.Logger.Infof("batch start: %s\n", batch.BatchNo)
  33. // 如果 批次 status 为 0,则先产生待发送消息
  34. if batch.Status == 0 {
  35. userList := make([]*ent.Contact, 0)
  36. groupList := make([]*ent.Contact, 0)
  37. tagMap := make(map[string][]uint64)
  38. err = json.Unmarshal([]byte(batch.Tagids), &tagMap)
  39. if err != nil {
  40. continue
  41. }
  42. l.Logger.Infof("send_msg.go batch.Tagids = %v\n", tagMap)
  43. var allContact, allGroup, ok bool
  44. var contactTags, groupTags []uint64
  45. if contactTags, ok = tagMap["contact_tag"]; ok {
  46. allContact = hasAll(contactTags, 0)
  47. l.Logger.Infof("contactTags=%v \n", contactTags)
  48. }
  49. if groupTags, ok = tagMap["group_tag"]; ok {
  50. allGroup = hasAll(groupTags, 0)
  51. l.Logger.Infof("groupTags=%v \n", groupTags)
  52. }
  53. var err error
  54. if allContact && allGroup {
  55. // 获取 contact 表中 wx_wxid 等于 req.Fromwxid 的 type 为1或2的数据
  56. userList, err = l.svcCtx.DB.Contact.Query().Where(
  57. contact.WxWxid(batch.Fromwxid),
  58. contact.TypeIn(1, 2),
  59. contact.CtypeIn(1, 3),
  60. ).All(l.ctx)
  61. if err != nil {
  62. l.Logger.Errorf("userlist err: %v \n", err)
  63. continue
  64. }
  65. } else {
  66. if allContact { // 所有联系人
  67. // 获取 contact 表中 wx_wxid 等于 req.Fromwxid 的 type 为1的数据
  68. userList, err = l.svcCtx.DB.Contact.Query().Where(
  69. contact.WxWxid(batch.Fromwxid),
  70. contact.TypeEQ(1),
  71. contact.CtypeIn(1, 3),
  72. ).All(l.ctx)
  73. if err != nil {
  74. l.Logger.Errorf("userList err: %v \n", err)
  75. continue
  76. }
  77. } else { //获取指定标签的联系人
  78. userList, err = l.getContactList(contactTags, batch.Fromwxid, 1)
  79. if err != nil {
  80. l.Logger.Errorf("userList err: %v \n", err)
  81. continue
  82. }
  83. }
  84. if allGroup { //所有群
  85. // 获取 contact 表中 wx_wxid 等于 req.Fromwxid 的 type 为2的数据
  86. groupList, err = l.svcCtx.DB.Contact.Query().Where(
  87. contact.WxWxid(batch.Fromwxid),
  88. contact.TypeEQ(2),
  89. contact.CtypeIn(1, 3),
  90. ).All(l.ctx)
  91. if err != nil {
  92. l.Logger.Errorf("groupList err: %v \n", err)
  93. continue
  94. }
  95. } else { //获取指定标签的群
  96. groupList, err = l.getContactList(groupTags, batch.Fromwxid, 2)
  97. if err != nil {
  98. l.Logger.Errorf("groupList err: %v \n", err)
  99. continue
  100. }
  101. }
  102. if len(groupList) > 0 {
  103. userList = append(userList, groupList...)
  104. }
  105. }
  106. // 这里是待插入到 msg 表的数据
  107. msgs := make([]*ent.MsgCreate, 0)
  108. // 这里是把 batch.Msg 转换为 json 数组
  109. msgArray := make([]custom_types.Action, 0)
  110. err = json.Unmarshal([]byte(batch.Msg), &msgArray)
  111. l.Logger.Infof("msgArray length= %v, err:%v \n", len(msgArray), err)
  112. if err != nil {
  113. // json 解析失败
  114. msgArray = make([]custom_types.Action, 0)
  115. }
  116. for _, user := range userList {
  117. // 这里改动主要是 batch_msg 目前支持批量添加图文,导致 batch_msg 的 msg 字段为 json
  118. // msg 里包括文字和图片,msgtype=1 为文字, msgtype=2 为图片
  119. // 每一条文字或者图片 都是一条单独的消息
  120. if len(msgArray) > 0 {
  121. // 这里是新格式(msg内容为json),需要遍历数组
  122. for _, msgItem := range msgArray {
  123. msgRow := l.svcCtx.DB.Msg.Create().
  124. SetNotNilFromwxid(&batch.Fromwxid).
  125. SetNotNilToid(&user.Wxid).
  126. SetMsgtype(int32(msgItem.Type)).
  127. SetNotNilMsg(&msgItem.Content).
  128. SetStatus(0).
  129. SetNotNilBatchNo(&batch.BatchNo)
  130. msgs = append(msgs, msgRow)
  131. }
  132. }
  133. }
  134. if len(msgs) > 0 {
  135. // 加事务,批量操作一条 batch_msg 和 一堆 msg 信息
  136. tx, err := l.svcCtx.DB.Tx(l.ctx)
  137. if err != nil {
  138. l.Logger.Errorf("start db transaction err: %v \n", err)
  139. continue
  140. }
  141. _, err = tx.BatchMsg.UpdateOneID(batch.ID).Where(batchmsg.StatusNEQ(1)).SetStatus(1).Save(l.ctx)
  142. if err != nil {
  143. _ = tx.Rollback()
  144. l.Logger.Errorf("batchmsg update err: %v \n", err)
  145. continue
  146. }
  147. _, err = tx.Msg.CreateBulk(msgs...).Save(l.ctx)
  148. if err != nil {
  149. _ = tx.Rollback()
  150. l.Logger.Errorf("msg CreateBulk err: %v \n", err)
  151. continue
  152. }
  153. _ = tx.Commit()
  154. } else {
  155. // 如果没有消息,直接更新批次状态为已发送
  156. _, err = l.svcCtx.DB.BatchMsg.UpdateOneID(batch.ID).
  157. SetStatus(2).
  158. SetTotal(0).
  159. SetSuccess(0).
  160. SetFail(0).
  161. Save(l.ctx)
  162. if err != nil {
  163. l.Logger.Errorf("batchmsg update err: %v \n", err)
  164. }
  165. continue
  166. }
  167. }
  168. // 获取当前批次的所有待发送消息
  169. msglist, err := l.svcCtx.DB.Msg.Query().Where(
  170. msg.BatchNoEQ(batch.BatchNo),
  171. msg.StatusEQ(0),
  172. ).All(l.ctx)
  173. if err != nil {
  174. l.Logger.Errorf("msglist err: %v \n", err)
  175. continue
  176. }
  177. wxInfo, err := l.svcCtx.DB.Wx.Query().Where(wx.Wxid(batch.Fromwxid)).Only(l.ctx)
  178. if err != nil {
  179. l.Logger.Errorf("wxInfo err: %v \n", err)
  180. continue
  181. }
  182. privateIP := ""
  183. adminPort := ""
  184. port := ""
  185. ctype := batch.Ctype
  186. if wxInfo.ServerID != 0 {
  187. serverInfo, err := l.svcCtx.DB.Server.Get(l.ctx, wxInfo.ServerID)
  188. if err != nil {
  189. l.Logger.Errorf("serverInfo err: %v \n", err)
  190. continue
  191. }
  192. privateIP = serverInfo.PrivateIP
  193. adminPort = serverInfo.AdminPort
  194. port = wxInfo.Port
  195. }
  196. var hookClient *hook.Hook
  197. if ctype == 3 {
  198. hookClient = hook.NewWecomHook("", adminPort, port)
  199. } else {
  200. hookClient = hook.NewHook(privateIP, adminPort, port)
  201. }
  202. //循环发送消息
  203. for _, msg := range msglist {
  204. // 这里之前只有文字消息(既 msgtype=1) 目前增加了图片 所以增加了msgtype=2
  205. // 所以增加了一个判断,判断发送的内容类型,如果是文字就调用SendTextMsg,如果是图片就调用SendPicMsg
  206. if msg.Msgtype == 1 {
  207. err = hookClient.SendTextMsg(msg.Toid, msg.Msg, wxInfo.Wxid)
  208. } else if msg.Msgtype == 2 {
  209. diyfilename := getFileName(msg.Msg)
  210. err = hookClient.SendPicMsg(msg.Toid, msg.Msg, diyfilename, wxInfo.Wxid)
  211. }
  212. // 每次发完暂停1秒
  213. time.Sleep(time.Second)
  214. if err != nil {
  215. l.Logger.Errorf("send msg err: %v \n", err)
  216. _, err = l.svcCtx.DB.Msg.UpdateOneID(msg.ID).SetStatus(2).Save(l.ctx)
  217. if err != nil {
  218. l.Logger.Errorf("msg update err: %v \n", err)
  219. continue
  220. }
  221. continue
  222. }
  223. _, err = l.svcCtx.DB.Msg.UpdateOneID(msg.ID).SetStatus(1).Save(l.ctx)
  224. if err != nil {
  225. l.Logger.Errorf("msg update err: %v \n", err)
  226. continue
  227. }
  228. }
  229. // 获取当前批次的所有发送的消息总数
  230. total, _ := l.svcCtx.DB.Msg.Query().Where(msg.BatchNoEQ(batch.BatchNo)).Count(l.ctx)
  231. // 获取当前批次的所有发送成功的消息总数
  232. success, _ := l.svcCtx.DB.Msg.Query().Where(msg.BatchNoEQ(batch.BatchNo), msg.StatusEQ(1)).Count(l.ctx)
  233. // 获取当前批次的所有发送失败的消息总数
  234. fail, _ := l.svcCtx.DB.Msg.Query().Where(msg.BatchNoEQ(batch.BatchNo), msg.StatusEQ(2)).Count(l.ctx)
  235. // 更新批次状态为已发送,同时更新发送总数、发送成功数量、失败数量、结束时间
  236. _, err = l.svcCtx.DB.BatchMsg.UpdateOneID(batch.ID).
  237. SetStatus(2).
  238. SetTotal(int32(total)).
  239. SetSuccess(int32(success)).
  240. SetFail(int32(fail)).
  241. SetStopTime(time.Now()).
  242. Save(l.ctx)
  243. if err != nil {
  244. l.Logger.Errorf("batchmsg update err: %v \n", err)
  245. continue
  246. }
  247. l.Logger.Infof("batch stop:%s \n", batch.BatchNo)
  248. }
  249. finishTime := time.Now()
  250. l.Logger.Infof("This process cost %v \n", finishTime.Sub(startTime).String())
  251. return
  252. }
  253. // 根据URL获取图片名
  254. func getFileName(photoUrl string) string {
  255. u, err := url.Parse(photoUrl)
  256. if err != nil {
  257. return ""
  258. }
  259. return path.Base(u.Path)
  260. }
  261. func hasAll(array []uint64, target uint64) bool {
  262. for _, val := range array {
  263. if val == target {
  264. return true
  265. }
  266. }
  267. return false
  268. }
  269. func (l *CronTask) getContactList(labels []uint64, fromWxId string, stype int) ([]*ent.Contact, error) {
  270. // 获取 label_relationship 表中,label_id 等于 labids 的 contact_id
  271. labelrelationships, err := l.svcCtx.DB.LabelRelationship.Query().Where(
  272. labelrelationship.LabelIDIn(labels...),
  273. ).All(l.ctx)
  274. if err != nil {
  275. return nil, dberrorhandler.DefaultEntError(l.Logger, err, nil)
  276. }
  277. contact_ids := make([]uint64, 0, len(labelrelationships))
  278. for _, labelrelationship := range labelrelationships {
  279. contact_ids = append(contact_ids, labelrelationship.ContactID)
  280. }
  281. userList := make([]*ent.Contact, 0)
  282. if len(contact_ids) > 0 {
  283. // 获取 contact 表中 wx_wxid 等于 req.Fromwxid 并且 id 等于 contact_ids 并且 type 为1或2 的数据
  284. userList, err = l.svcCtx.DB.Contact.Query().Where(
  285. contact.WxWxid(fromWxId),
  286. contact.IDIn(contact_ids...),
  287. contact.TypeEQ(stype),
  288. contact.CtypeIn(1, 3),
  289. ).All(l.ctx)
  290. if err != nil {
  291. return nil, dberrorhandler.DefaultEntError(l.Logger, err, nil)
  292. }
  293. }
  294. return userList, nil
  295. }