send_msg.go 9.4 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. if err != nil {
  25. l.Logger.Errorf("batchList err: %v", err)
  26. return
  27. }
  28. startTime := time.Now()
  29. for _, batch := range batchList {
  30. // 记录当前批次开始处理
  31. l.Logger.Info("batch start: ", batch.BatchNo)
  32. // 如果 批次 status 为 0,则先产生待发送消息
  33. if batch.Status == 0 {
  34. userList := make([]*ent.Contact, 0)
  35. groupList := make([]*ent.Contact, 0)
  36. tagMap := make(map[string][]uint64)
  37. err = json.Unmarshal([]byte(batch.Tagids), &tagMap)
  38. if err != nil {
  39. continue
  40. }
  41. l.Logger.Infof("send_msg.go batch.Tagids = %v\n", tagMap)
  42. var allContact, allGroup, ok bool
  43. var contactTags, groupTags []uint64
  44. if contactTags, ok = tagMap["contact_tag"]; ok {
  45. allContact = hasAll(contactTags, 0)
  46. l.Logger.Infof("contactTags=%v", contactTags)
  47. }
  48. if groupTags, ok = tagMap["group_tag"]; ok {
  49. allGroup = hasAll(groupTags, 0)
  50. l.Logger.Infof("groupTags=%v", groupTags)
  51. }
  52. var err error
  53. if allContact && allGroup {
  54. // 获取 contact 表中 wx_wxid 等于 req.Fromwxid 的 type 为1或2的数据
  55. userList, err = l.svcCtx.DB.Contact.Query().Where(
  56. contact.WxWxid(batch.Fromwxid),
  57. contact.TypeIn(1, 2),
  58. contact.CtypeIn(1, 3),
  59. ).All(l.ctx)
  60. if err != nil {
  61. l.Logger.Errorf("userlist err: %v", err)
  62. continue
  63. }
  64. } else {
  65. if allContact { // 所有联系人
  66. // 获取 contact 表中 wx_wxid 等于 req.Fromwxid 的 type 为1的数据
  67. userList, err = l.svcCtx.DB.Contact.Query().Where(
  68. contact.WxWxid(batch.Fromwxid),
  69. contact.TypeEQ(1),
  70. contact.CtypeIn(1, 3),
  71. ).All(l.ctx)
  72. if err != nil {
  73. l.Logger.Errorf("userList err: %v", err)
  74. continue
  75. }
  76. } else { //获取指定标签的联系人
  77. userList, err = l.getContactList(contactTags, batch.Fromwxid, 1)
  78. if err != nil {
  79. l.Logger.Errorf("userList err: %v", err)
  80. continue
  81. }
  82. }
  83. if allGroup { //所有群
  84. // 获取 contact 表中 wx_wxid 等于 req.Fromwxid 的 type 为2的数据
  85. groupList, err = l.svcCtx.DB.Contact.Query().Where(
  86. contact.WxWxid(batch.Fromwxid),
  87. contact.TypeEQ(2),
  88. contact.CtypeIn(1, 3),
  89. ).All(l.ctx)
  90. if err != nil {
  91. l.Logger.Errorf("groupList err: %v", err)
  92. continue
  93. }
  94. } else { //获取指定标签的群
  95. groupList, err = l.getContactList(groupTags, batch.Fromwxid, 2)
  96. if err != nil {
  97. l.Logger.Errorf("groupList err: %v", err)
  98. continue
  99. }
  100. }
  101. if len(groupList) > 0 {
  102. userList = append(userList, groupList...)
  103. }
  104. }
  105. // 这里是待插入到 msg 表的数据
  106. msgs := make([]*ent.MsgCreate, 0)
  107. // 这里是把 batch.Msg 转换为 json 数组
  108. msgArray := make([]custom_types.Action, 0)
  109. err = json.Unmarshal([]byte(batch.Msg), &msgArray)
  110. l.Logger.Infof("msgArray length= %v, err:%v", len(msgArray), err)
  111. if err != nil {
  112. // json 解析失败
  113. msgArray = make([]custom_types.Action, 0)
  114. }
  115. for _, user := range userList {
  116. // 这里改动主要是 batch_msg 目前支持批量添加图文,导致 batch_msg 的 msg 字段为 json
  117. // msg 里包括文字和图片,msgtype=1 为文字, msgtype=2 为图片
  118. // 每一条文字或者图片 都是一条单独的消息
  119. if len(msgArray) > 0 {
  120. // 这里是新格式(msg内容为json),需要遍历数组
  121. for _, msgItem := range msgArray {
  122. msgRow := l.svcCtx.DB.Msg.Create().
  123. SetNotNilFromwxid(&batch.Fromwxid).
  124. SetNotNilToid(&user.Wxid).
  125. SetMsgtype(int32(msgItem.Type)).
  126. SetNotNilMsg(&msgItem.Content).
  127. SetStatus(0).
  128. SetNotNilBatchNo(&batch.BatchNo)
  129. msgs = append(msgs, msgRow)
  130. }
  131. }
  132. }
  133. if len(msgs) > 0 {
  134. // 加事务,批量操作一条 batch_msg 和 一堆 msg 信息
  135. tx, err := l.svcCtx.DB.Tx(l.ctx)
  136. if err != nil {
  137. l.Logger.Errorf("start db transaction err: %v", err)
  138. continue
  139. }
  140. _, err = tx.BatchMsg.UpdateOneID(batch.ID).Where(batchmsg.StatusNEQ(1)).SetStatus(1).Save(l.ctx)
  141. if err != nil {
  142. _ = tx.Rollback()
  143. l.Logger.Errorf("batchmsg update err: %v", err)
  144. continue
  145. }
  146. _, err = tx.Msg.CreateBulk(msgs...).Save(l.ctx)
  147. if err != nil {
  148. _ = tx.Rollback()
  149. l.Logger.Errorf("msg CreateBulk err: %v", err)
  150. continue
  151. }
  152. _ = tx.Commit()
  153. } else {
  154. // 如果没有消息,直接更新批次状态为已发送
  155. _, err = l.svcCtx.DB.BatchMsg.UpdateOneID(batch.ID).
  156. SetStatus(2).
  157. SetTotal(0).
  158. SetSuccess(0).
  159. SetFail(0).
  160. Save(l.ctx)
  161. if err != nil {
  162. l.Logger.Errorf("batchmsg update err: %v", err)
  163. }
  164. continue
  165. }
  166. }
  167. // 获取当前批次的所有待发送消息
  168. msglist, err := l.svcCtx.DB.Msg.Query().Where(
  169. msg.BatchNoEQ(batch.BatchNo),
  170. msg.StatusEQ(0),
  171. ).All(l.ctx)
  172. if err != nil {
  173. l.Logger.Errorf("msglist err: %v", err)
  174. continue
  175. }
  176. wxInfo, err := l.svcCtx.DB.Wx.Query().Where(wx.Wxid(batch.Fromwxid)).Only(l.ctx)
  177. if err != nil {
  178. l.Logger.Errorf("wxInfo err: %v", err)
  179. continue
  180. }
  181. privateIP := ""
  182. adminPort := ""
  183. port := ""
  184. var ctype uint64
  185. if wxInfo.ServerID != 0 {
  186. serverInfo, err := l.svcCtx.DB.Server.Get(l.ctx, wxInfo.ServerID)
  187. if err != nil {
  188. l.Logger.Errorf("serverInfo err: %v", err)
  189. continue
  190. }
  191. privateIP = serverInfo.PrivateIP
  192. adminPort = serverInfo.AdminPort
  193. port = wxInfo.Port
  194. ctype = wxInfo.Ctype
  195. }
  196. var hookClient *hook.Hook
  197. if ctype == uint64(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", 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", 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", 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", err)
  245. continue
  246. }
  247. l.Logger.Info("batch stop: ", batch.BatchNo)
  248. }
  249. finishTime := time.Now()
  250. l.Logger.Infof("This process cost %v", 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. }