email.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. package service
  2. import (
  3. "bytes"
  4. "context"
  5. "fmt"
  6. "html/template"
  7. "time"
  8. "go-common/app/job/main/mcn/conf"
  9. "go-common/app/job/main/mcn/model"
  10. accgrpc "go-common/app/service/main/account/api"
  11. "go-common/library/log"
  12. "go-common/library/xstr"
  13. "github.com/pkg/errors"
  14. )
  15. // var .
  16. var (
  17. // ErrNoAdminName no admin name
  18. ErrNoAdminName = errors.New("no admin name")
  19. tmplSignDueTitle *template.Template
  20. tmplSignDueContent *template.Template
  21. tmplPayDueTitle *template.Template
  22. tmplPayDueContent *template.Template
  23. )
  24. // use for template function call
  25. var funcHelper = template.FuncMap{
  26. "Now": time.Now,
  27. }
  28. func (s *Service) initEmailTemplate() (err error) {
  29. if conf.Conf.MailTemplateConf.SignTmplTitle == "" ||
  30. conf.Conf.MailTemplateConf.SignTmplContent == "" ||
  31. conf.Conf.MailTemplateConf.PayTmplTitle == "" ||
  32. conf.Conf.MailTemplateConf.PayTmplContent == "" {
  33. err = fmt.Errorf(`mail template conf is invalid, check mail-template.toml file, make sure all the following has value:
  34. TaskTmplContent
  35. TaskTmplTitle
  36. PayTmplContent
  37. PayTmplTitle`)
  38. return
  39. }
  40. tmplSignDueTitle, err = template.New("signTitle").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.SignTmplTitle)
  41. if err != nil {
  42. log.Error("parse template fail, err=%v", err)
  43. return
  44. }
  45. tmplSignDueContent, err = template.New("signContent").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.SignTmplContent)
  46. if err != nil {
  47. log.Error("parse template fail, err=%v", err)
  48. return
  49. }
  50. tmplPayDueTitle, err = template.New("payTitle").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.PayTmplTitle)
  51. if err != nil {
  52. log.Error("parse template fail, err=%v", err)
  53. return
  54. }
  55. tmplPayDueContent, err = template.New("payContent").Funcs(funcHelper).Parse(conf.Conf.MailTemplateConf.PayTmplContent)
  56. if err != nil {
  57. log.Error("parse template fail, err=%v", err)
  58. return
  59. }
  60. return
  61. }
  62. // CheckDateDueCron .
  63. func (s *Service) CheckDateDueCron() {
  64. log.Info("start run CheckDateDueJob, date=%s", time.Now().Format(model.TimeFormatSec))
  65. s.checkSignUpDue()
  66. log.Info("finish run CheckDateDueJob, date=%s", time.Now().Format(model.TimeFormatSec))
  67. }
  68. type stateFunc func(context.Context, []int64) (int64, error)
  69. type emailData struct {
  70. IDs []int64
  71. AdminName []string
  72. Data interface{}
  73. UpStateFunc stateFunc
  74. Title, Content *template.Template
  75. }
  76. func (e *emailData) addEmailDatas(es *[]*emailData) {
  77. *es = append(*es, e)
  78. }
  79. // buildEmail .
  80. func buildEmail(ids []int64, data interface{}, title, content *template.Template, upStateFunc stateFunc, adminName ...string) *emailData {
  81. return &emailData{
  82. IDs: ids,
  83. Data: data,
  84. Title: title,
  85. Content: content,
  86. UpStateFunc: upStateFunc,
  87. AdminName: adminName,
  88. }
  89. }
  90. type dueData struct {
  91. Signs []*model.MCNSignInfo
  92. Pays []*model.SignPayInfo
  93. Sids, Pids []int64
  94. }
  95. func (d *dueData) addSign(sign *model.MCNSignInfo) {
  96. d.Signs = append(d.Signs, sign)
  97. d.Sids = append(d.Sids, sign.SignID)
  98. }
  99. func (d *dueData) addPay(pay *model.SignPayInfo) {
  100. d.Pays = append(d.Pays, pay)
  101. d.Pids = append(d.Pids, pay.SignPayID)
  102. }
  103. func (d *dueData) addName(infoMap map[int64]*accgrpc.Info) {
  104. for _, v := range d.Signs {
  105. v.McnName = getName(infoMap, v.McnMid)
  106. }
  107. for _, v := range d.Pays {
  108. v.McnName = getName(infoMap, v.McnMid)
  109. }
  110. }
  111. // func getOrCreate(dataMap map[string]*dueData, key string) *dueData {
  112. // var data, ok = dataMap[key]
  113. // if !ok {
  114. // data = &dueData{}
  115. // dataMap[key] = data
  116. // }
  117. // return data
  118. // }
  119. func getName(infoMap map[int64]*accgrpc.Info, mid int64) string {
  120. if info, ok := infoMap[mid]; ok {
  121. return info.Name
  122. }
  123. return ""
  124. }
  125. func (s *Service) checkSignUpDue() {
  126. var (
  127. mids []int64
  128. emailDatas []*emailData
  129. data = &dueData{}
  130. c = context.Background()
  131. infoMap map[int64]*accgrpc.Info
  132. )
  133. // 30天内到期 sign
  134. listDue, err := s.dao.McnSignDues(c)
  135. if err != nil {
  136. log.Error("s.dao.McnSignDues error(%+v)", err)
  137. return
  138. }
  139. for _, v := range listDue {
  140. mids = append(mids, v.McnMid)
  141. data.addSign(v)
  142. }
  143. // 7天内到期的pay
  144. listPayDue, err := s.dao.McnSignPayDues(c)
  145. if err != nil {
  146. log.Error("s.dao.McnSignPayDues error(%+v)", err)
  147. return
  148. }
  149. for _, v := range listPayDue {
  150. mids = append(mids, v.McnMid)
  151. data.addPay(v)
  152. }
  153. mids = uniqNoEmpty(mids)
  154. infosReply, err := s.accGRPC.Infos3(c, &accgrpc.MidsReq{Mids: mids})
  155. if err != nil {
  156. log.Error("s.accGRPC.Infos3(%s) error(%+v)", xstr.JoinInts(mids), err)
  157. err = nil
  158. } else {
  159. infoMap = infosReply.Infos
  160. }
  161. emailDatas = make([]*emailData, 0)
  162. data.addName(infoMap)
  163. buildEmail(data.Sids, data.Signs, tmplSignDueTitle, tmplSignDueContent, s.dao.UpMcnSignEmailState, conf.Conf.MailConf.DueAuthorityGroups...).addEmailDatas(&emailDatas)
  164. buildEmail(data.Pids, data.Pays, tmplPayDueTitle, tmplPayDueContent, s.dao.UpMcnSignPayEmailState, conf.Conf.MailConf.DueAuthorityGroups...).addEmailDatas(&emailDatas)
  165. for _, e := range emailDatas {
  166. s.doSendEmailFunc(c, e)
  167. }
  168. }
  169. func (s *Service) doSendEmailFunc(c context.Context, e *emailData) {
  170. s.worker.Do(c, func(c context.Context) {
  171. if len(e.IDs) == 0 {
  172. log.Warn("not need to update")
  173. return
  174. }
  175. var err error
  176. if err = s.sendMailWithTemplate(e.Data, e.Title, e.Content, e.AdminName...); err != nil {
  177. log.Error("s.sendMailWithTemplate(%+v,%+v,%+v,%+v) error(%+v)", e.Data, e.Title, e.Content, e.AdminName, err)
  178. return
  179. }
  180. if _, err = e.UpStateFunc(c, e.IDs); err != nil {
  181. log.Error("upfunc(%+v,%s) error(%+v)", e.UpStateFunc, xstr.JoinInts(e.IDs), err)
  182. return
  183. }
  184. log.Info("func(%s) update succ", xstr.JoinInts(e.IDs))
  185. })
  186. }
  187. // data, data to generate email content
  188. // contentTmpl, template to generate email content
  189. // adminname, slice for all admin name
  190. //
  191. func (s *Service) sendMailWithTemplate(data interface{}, subjectTmpl, contentTmpl *template.Template, adminName ...string) (err error) {
  192. if contentTmpl == nil {
  193. err = fmt.Errorf("template for email is nil, data=%+v", data)
  194. log.Error("%s", err)
  195. return
  196. }
  197. var contentBuf = bytes.NewBuffer(nil)
  198. err = contentTmpl.Execute(contentBuf, data)
  199. if err != nil {
  200. log.Error("template fail to execute, err=%v", err)
  201. return
  202. }
  203. var subjectBuf = bytes.NewBuffer(nil)
  204. err = subjectTmpl.Execute(subjectBuf, data)
  205. if err != nil {
  206. log.Error("template fail to execute, err=%v", err)
  207. return
  208. }
  209. var addrs []string
  210. for _, v := range adminName {
  211. if v == "" {
  212. log.Warn("admin name is empty")
  213. continue
  214. }
  215. addrs = append(addrs, v)
  216. }
  217. if len(addrs) == 0 {
  218. log.Error("admin name is empty, cannot send email, data=%+v", data)
  219. err = ErrNoAdminName
  220. return
  221. }
  222. if err = s.dao.SendMail(contentBuf.String(), subjectBuf.String(), addrs); err != nil {
  223. log.Error("s.dao.SendMail(%s,%s,%+v) error(%+v)", contentBuf.String(), subjectBuf.String(), addrs, err)
  224. return
  225. }
  226. log.Info("email send succ, sub=%s, admin=%s", subjectBuf.String(), adminName)
  227. return
  228. }
  229. func chain(ids ...[]int64) []int64 {
  230. res := make([]int64, 0, len(ids))
  231. for _, l := range ids {
  232. res = append(res, l...)
  233. }
  234. return res
  235. }
  236. // func uniq(ids ...[]int64) []int64 {
  237. // hm := make(map[int64]struct{})
  238. // for _, i := range chain(ids...) {
  239. // hm[i] = struct{}{}
  240. // }
  241. // res := make([]int64, 0, len(ids))
  242. // for i := range hm {
  243. // res = append(res, i)
  244. // }
  245. // return res
  246. // }
  247. func uniqNoEmpty(ids ...[]int64) []int64 {
  248. hm := make(map[int64]struct{})
  249. for _, i := range chain(ids...) {
  250. hm[i] = struct{}{}
  251. }
  252. res := make([]int64, 0, len(ids))
  253. for i := range hm {
  254. if i > 0 {
  255. res = append(res, i)
  256. }
  257. }
  258. return res
  259. }