admin_email.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "entrance-grpc/iam"
  6. "fmt"
  7. "gadmin/config"
  8. "gadmin/internal/admin/consts"
  9. "gadmin/internal/admin/forms"
  10. "gadmin/internal/admin/gm_rpc/rpc_share"
  11. "gadmin/internal/gorm/model"
  12. "gadmin/internal/gorm/query"
  13. "gadmin/package/gmdata"
  14. "gadmin/utility"
  15. "gadmin/utility/character"
  16. "gadmin/utility/player"
  17. "gadmin/utility/serializer"
  18. "gadmin/utility/token"
  19. "github.com/gin-gonic/gin"
  20. "github.com/sirupsen/logrus"
  21. "github.com/spf13/cast"
  22. "github.com/xuri/excelize/v2"
  23. model2 "leafstalk/covenant/model"
  24. msg2 "leafstalk/covenant/msg"
  25. "strconv"
  26. "strings"
  27. "time"
  28. )
  29. // AdminEmail 邮件
  30. var AdminEmail = new(sAdminEmail)
  31. type sAdminEmail struct{}
  32. type Ret struct {
  33. model.AdminEmail
  34. Sender string `json:"sender"`
  35. }
  36. type Ext struct {
  37. Id string `json:"id"`
  38. Count string `json:"count"`
  39. }
  40. func (s *sAdminEmail) List(ctx *gin.Context, req forms.AdminEmailListReq) serializer.Response {
  41. var (
  42. q = query.Use(config.DB).AdminEmail
  43. m = q.WithContext(ctx)
  44. //u = query.Use(config.DB).AdminUser
  45. offset int64 = 0
  46. models forms.UserAccountListRes
  47. lists []*Ret
  48. )
  49. req.Page, req.PerPage, offset = forms.CalculatePage(req.Page, req.PerPage)
  50. models.Page = req.Page
  51. models.PerPage = req.PerPage
  52. if req.Sender != "" {
  53. first, err := config.GetIamClient().GetAdminUserByNickName(ctx, &iam.GetAdminUserByNickNameReq{
  54. NickName: req.Sender,
  55. })
  56. if err == nil && first.Code == 0 {
  57. m = m.Where(q.OperatorID.Eq(first.Data.ID))
  58. }
  59. }
  60. if req.OperatorId > 0 {
  61. m = m.Where(q.OperatorID.Eq(req.OperatorId))
  62. }
  63. if len(req.CreatedAt) == 2 {
  64. m = m.Where(q.CreatedAt.Between(req.CreatedAt[0], req.CreatedAt[1]))
  65. }
  66. m = m.Order(q.ID.Desc())
  67. count, err := m.Count()
  68. if err != nil {
  69. return serializer.Err(consts.CodeParamErr, "查询出错 count", err)
  70. }
  71. if count == 0 {
  72. return serializer.Suc(models)
  73. }
  74. if req.IsExport == 1 {
  75. if err = m.Scan(&lists); err != nil {
  76. return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
  77. }
  78. err = s.Export(ctx, lists)
  79. if err != nil {
  80. return serializer.Err(consts.CodeParamErr, "导出错误", err)
  81. }
  82. return serializer.Response{}
  83. }
  84. if err = m.Limit(int(req.PerPage)).Offset(int(offset)).Scan(&lists); err != nil {
  85. return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
  86. }
  87. uIdMap := make(map[int64]struct{})
  88. for i, v := range lists {
  89. if v.OperatorID > 0 {
  90. uIdMap[v.OperatorID] = struct{}{}
  91. }
  92. lists[i].PlayerIds = strings.Replace(v.PlayerIds, "[", "", -1)
  93. lists[i].PlayerIds = strings.Replace(v.PlayerIds, "]", "", -1)
  94. lists[i].PlayerIds = strings.Replace(v.PlayerIds, "\"", "", -1)
  95. }
  96. uIds := make([]int64, 0, len(lists))
  97. for uid := range uIdMap {
  98. uIds = append(uIds, uid)
  99. }
  100. res, err := config.GetIamClient().BatchGetAdminUser(context.Background(), &iam.BatchGetAdminUserReq{UIds: uIds})
  101. if err != nil || res.Code != 0 {
  102. return serializer.Err(consts.CodeParamErr, "查询出错 lists", err)
  103. }
  104. uMap := make(map[int64]*iam.AdminUserInfo)
  105. for _, usr := range res.Data {
  106. uMap[usr.ID] = usr
  107. }
  108. for _, it := range lists {
  109. if _, ok := uMap[it.OperatorID]; !ok {
  110. continue
  111. }
  112. it.Sender = uMap[it.OperatorID].NickName
  113. }
  114. models.List = lists
  115. models.PageCount = (count + req.PerPage - 1) / req.PerPage
  116. return serializer.Suc(models)
  117. }
  118. func (s *sAdminEmail) Verify(ctx *gin.Context, req forms.AdminEmailVerifyReq) serializer.Response {
  119. var q = query.Use(config.DB).AdminEmail
  120. first, err := q.WithContext(ctx).Where(q.ID.Eq(req.Id)).First()
  121. if err != nil {
  122. return serializer.ParamErr(err.Error(), err)
  123. }
  124. if first == nil {
  125. return serializer.ParamErr("邮件不存在", nil)
  126. }
  127. if first.Status != consts.EmailStatusVerify {
  128. return serializer.ParamErr("当前邮件状态非审核状态,操作失败!", nil)
  129. }
  130. // 拒绝发送
  131. if req.VerifyStatus == 2 {
  132. _, err2 := q.WithContext(ctx).Where(q.ID.Eq(first.ID)).Updates(model.AdminEmail{
  133. Status: consts.EmailStatusCanceled,
  134. })
  135. if err2 != nil {
  136. return serializer.ParamErr(err.Error(), err)
  137. }
  138. return serializer.Suc(nil)
  139. }
  140. // 同意发送,进入发送环节
  141. var status int32
  142. // 立即发送,进入发送中状态
  143. if first.SendType == 1 {
  144. status = consts.EmailStatusIn
  145. }
  146. // 延迟发送
  147. if first.SendType == 2 {
  148. status = consts.EmailStatusWait
  149. }
  150. // 先更新数据
  151. if _, err = q.WithContext(ctx).Where(q.ID.Eq(first.ID)).Updates(model.AdminEmail{Status: status}); err != nil {
  152. return serializer.ParamErr(err.Error(), err)
  153. }
  154. // 立即发送
  155. if status == consts.EmailStatusIn {
  156. q := query.Use(config.DB).AdminEmail
  157. if err = s.SendEmailToWorld(first); err != nil {
  158. logrus.Warnf("邮件发送失败-3:%+v", err)
  159. _, err2 := q.WithContext(ctx).Where(q.ID.Eq(first.ID)).Updates(model.AdminEmail{Status: consts.EmailStatusErr})
  160. if err2 != nil {
  161. logrus.Warnf("邮件更新失败:%+v", err2)
  162. }
  163. return serializer.ParamErr(err.Error(), err)
  164. }
  165. // 发送成功
  166. _, err2 := q.WithContext(ctx).Where(q.ID.Eq(first.ID)).Updates(model.AdminEmail{
  167. Status: consts.EmailStatusSent,
  168. SendAt: time.Now().Unix(),
  169. })
  170. if err2 != nil {
  171. logrus.Warnf("邮件更新失败-2:%+v", err2)
  172. }
  173. }
  174. return serializer.Suc(nil)
  175. }
  176. func (s *sAdminEmail) Add(ctx *gin.Context, req forms.AdminEmailAddReq) serializer.Response {
  177. ext, err := json.Marshal(req.Ext)
  178. if err != nil {
  179. return serializer.ParamErr(err.Error(), err)
  180. }
  181. serverIds, err := json.Marshal(req.ServerIds)
  182. if err != nil {
  183. return serializer.ParamErr(err.Error(), err)
  184. }
  185. playerIds, err := json.Marshal(req.PlayerIds)
  186. if err != nil {
  187. return serializer.ParamErr(err.Error(), err)
  188. }
  189. var status int32
  190. // 立即发送,进入发送中状态
  191. if req.SendType == 1 {
  192. status = consts.EmailStatusIn
  193. }
  194. // 延迟发送
  195. if req.SendType == 2 {
  196. status = consts.EmailStatusWait
  197. }
  198. // 非超管发送全服或指定服邮件直接进入待审核状态
  199. if ctx.GetInt64("admin_role_id") != 1 {
  200. status = consts.EmailStatusVerify
  201. }
  202. logrus.Warnf("AdminEmailAdd req:%+v", utility.DumpToJSON(req))
  203. data := &model.AdminEmail{
  204. ID: 0,
  205. Title: req.Title,
  206. Content: req.Content,
  207. Ext: string(ext),
  208. SendType: req.SendType,
  209. SendAt: req.SendAt,
  210. Expired: req.Expired,
  211. ReceiptType: req.ReceiptType,
  212. ServerIds: string(serverIds),
  213. PlayerIds: string(playerIds),
  214. OperatorID: token.GetUID(ctx),
  215. Status: status,
  216. UpdatedAt: time.Now().Unix(),
  217. CreatedAt: time.Now().Unix(),
  218. Remark: req.Remark,
  219. }
  220. logrus.Warnf("AdminEmailAdd data:%+v", utility.DumpToJSON(data))
  221. if err = query.Use(config.DB).AdminEmail.WithContext(ctx).Create(data); err != nil {
  222. return serializer.ParamErr(err.Error(), err)
  223. }
  224. logrus.Infof("新增邮件内容:%+v", utility.DumpToJSON(data))
  225. // 立即发送
  226. if status == consts.EmailStatusIn {
  227. q := query.Use(config.DB).AdminEmail
  228. if err = s.SendEmailToWorld(data); err != nil {
  229. logrus.Warnf("邮件发送失败:%+v", err)
  230. _, err2 := q.WithContext(ctx).Where(q.ID.Eq(data.ID)).Updates(model.AdminEmail{Status: consts.EmailStatusErr})
  231. if err2 != nil {
  232. logrus.Warnf("邮件更新失败:%+v", err2)
  233. }
  234. return serializer.ParamErr(err.Error(), err)
  235. }
  236. // 发送成功
  237. _, err2 := q.WithContext(ctx).Where(q.ID.Eq(data.ID)).Updates(model.AdminEmail{
  238. Status: consts.EmailStatusSent,
  239. SendAt: time.Now().Unix(),
  240. })
  241. if err2 != nil {
  242. logrus.Warnf("邮件更新失败-2:%+v", err2)
  243. }
  244. }
  245. return serializer.Suc(nil)
  246. }
  247. // DecodeExtData 解析奖励道具
  248. func (s *sAdminEmail) DecodeExtData(es string) (data []*model2.DropedItem, err error) {
  249. type Extra struct {
  250. Id string `json:"id"`
  251. Num string `json:"num"`
  252. Type int64 `json:"type"`
  253. }
  254. var (
  255. extra []*Extra
  256. )
  257. if err = json.Unmarshal([]byte(es), &extra); err != nil {
  258. return []*model2.DropedItem{}, err
  259. }
  260. for _, it := range extra {
  261. data = append(data, &model2.DropedItem{
  262. Id: cast.ToInt64(it.Id),
  263. Num: cast.ToInt64(it.Num),
  264. Type: it.Type,
  265. })
  266. }
  267. return
  268. }
  269. // SendEmailToWorld 发送邮件到游戏服务器
  270. func (s *sAdminEmail) SendEmailToWorld(params *model.AdminEmail) (err error) {
  271. letter := &model2.MailGlobal{
  272. MailType: 1,
  273. MaxPlayerId: 0,
  274. Title: params.Title,
  275. Content: params.Content,
  276. ExtraInfo: nil,
  277. CreateTime: time.Now(),
  278. ExpireTime: time.Now().AddDate(0, 0, int(params.Expired)),
  279. }
  280. letter.Extra, err = s.DecodeExtData(params.Ext)
  281. if err != nil {
  282. return err
  283. }
  284. switch params.ReceiptType {
  285. case 1: // 全服
  286. msg := msg2.GMLetter{
  287. MsgId: character.GenerateMsgId(),
  288. Letter: letter,
  289. Players: []int64{},
  290. OperatorId: params.OperatorID,
  291. }
  292. rpc_share.MsgMap[msg.MsgId] = fmt.Sprintf("%s,GM邮件(编号 %d) 投递成功,全服投递",
  293. utility.FormatSecond(time.Now()),
  294. params.ID)
  295. for _, serverId := range ServerOption.GetServerIds() {
  296. DB, err := player.GetDBByServerID(serverId)
  297. if err != nil {
  298. logrus.Warnf("sendLetterToWorld GetDBByServerID err:%+v", err)
  299. continue
  300. }
  301. var resp *msg2.ResponseGMLetter
  302. res, err := config.GmNats.GmRequest(DB, "GMLetter", msg)
  303. if err != nil {
  304. logrus.Warnf("sendLetterToWorld GmRequest err:%+v", err)
  305. continue
  306. }
  307. if err = json.Unmarshal(res, &resp); err != nil {
  308. logrus.Warnf("sendLetterToWorld nats Unmarshal err:%+v", err)
  309. continue
  310. }
  311. rpc_share.LogChan <- rpc_share.LogMsg{
  312. MsgID: msg.MsgId,
  313. Data: resp,
  314. }
  315. }
  316. case 2: // 指定服
  317. var serverIds []int32
  318. if err = json.Unmarshal([]byte(params.ServerIds), &serverIds); err != nil {
  319. return err
  320. }
  321. for _, serverId := range serverIds {
  322. sId := cast.ToInt(serverId)
  323. if sId > 0 {
  324. DB, err := player.GetDBByServerID(sId)
  325. if err != nil {
  326. logrus.Warnf("sendLetterToWorld2 GetDBByServerID2 err:%+v", err)
  327. continue
  328. }
  329. msg := msg2.GMLetter{
  330. MsgId: character.GenerateMsgId(),
  331. Letter: letter,
  332. Players: []int64{},
  333. OperatorId: params.OperatorID,
  334. }
  335. rpc_share.MsgMap[msg.MsgId] = fmt.Sprintf("%s,GM邮件(编号 %d) 投递成功,投递服务器ID:%v",
  336. utility.FormatSecond(time.Now()), params.ID, sId)
  337. var resp *msg2.ResponseGMLetter
  338. res, err := config.GmNats.GmRequest(DB, "GMLetter", msg)
  339. if err != nil {
  340. logrus.Warnf("sendLetterToWorld2 GmRequest err:%+v", err)
  341. continue
  342. }
  343. if err = json.Unmarshal(res, &resp); err != nil {
  344. logrus.Warnf("sendLetterToWorld2 nats Unmarshal err:%+v", err)
  345. continue
  346. }
  347. rpc_share.LogChan <- rpc_share.LogMsg{
  348. MsgID: msg.MsgId,
  349. Data: resp,
  350. }
  351. }
  352. }
  353. case 3: // 指定玩家
  354. var playerIds []string
  355. if err = json.Unmarshal([]byte(params.PlayerIds), &playerIds); err != nil {
  356. return err
  357. }
  358. lineMap := make(map[int][]int64)
  359. errPlayerIds := make([]int64, 0)
  360. for _, v := range playerIds {
  361. playerID := cast.ToInt64(v)
  362. if playerID <= 0 {
  363. errPlayerIds = append(errPlayerIds, playerID)
  364. continue
  365. }
  366. db, err := player.GetDBByUserId(playerID)
  367. if err != nil {
  368. errPlayerIds = append(errPlayerIds, playerID)
  369. logrus.Warnf("sendLetterToWorld3 GetDBByUserId err:%+v", err)
  370. continue
  371. }
  372. if _, ok := lineMap[db]; ok {
  373. lineMap[db] = append(lineMap[db], playerID)
  374. } else {
  375. lineMap[db] = []int64{playerID}
  376. }
  377. }
  378. if len(errPlayerIds) > 0 {
  379. logrus.Warnf("sendLetterToWorld3 errPlayerIds:%v", errPlayerIds)
  380. }
  381. for line, ids := range lineMap {
  382. msg := msg2.GMLetter{
  383. MsgId: character.GenerateMsgId(),
  384. Letter: letter,
  385. Players: ids,
  386. OperatorId: params.OperatorID,
  387. }
  388. rpc_share.MsgMap[msg.MsgId] = fmt.Sprintf("%s,GM邮件(编号 %d) 投递成功,投递玩家ID:%v",
  389. utility.FormatSecond(time.Now()), params.ID, ids)
  390. var resp *msg2.ResponseGMLetter
  391. res, err := config.GmNats.GmRequest(line, "GMLetter", msg)
  392. if err != nil {
  393. logrus.Warnf("sendLetterToWorld3 GmRequest err:%+v", err)
  394. continue
  395. }
  396. if err = json.Unmarshal(res, &resp); err != nil {
  397. logrus.Warnf("sendLetterToWorld3 nats Unmarshal err:%+v", err)
  398. continue
  399. }
  400. rpc_share.LogChan <- rpc_share.LogMsg{
  401. MsgID: msg.MsgId,
  402. Data: resp,
  403. }
  404. }
  405. }
  406. return
  407. }
  408. func (s *sAdminEmail) Export(ctx *gin.Context, list []*Ret) error {
  409. f := excelize.NewFile()
  410. f.SetColWidth("Sheet1", "A", "A", 15)
  411. f.SetColWidth("Sheet1", "B", "B", 15)
  412. f.SetColWidth("Sheet1", "C", "C", 15)
  413. f.SetColWidth("Sheet1", "D", "D", 20)
  414. f.SetColWidth("Sheet1", "E", "E", 25)
  415. f.SetColWidth("Sheet1", "F", "F", 30)
  416. f.SetColWidth("Sheet1", "G", "G", 30)
  417. f.SetColWidth("Sheet1", "H", "H", 15)
  418. f.SetColWidth("Sheet1", "I", "I", 15)
  419. f.SetColWidth("Sheet1", "J", "J", 15)
  420. f.SetColWidth("Sheet1", "K", "K", 15)
  421. // 创建一个工作表
  422. f.SetCellValue("Sheet1", "A1", "序号")
  423. f.SetCellValue("Sheet1", "B1", "标题")
  424. f.SetCellValue("Sheet1", "C1", "收件人")
  425. f.SetCellValue("Sheet1", "D1", "有效期")
  426. f.SetCellValue("Sheet1", "E1", "邮件内容")
  427. f.SetCellValue("Sheet1", "F1", "邮件备注")
  428. f.SetCellValue("Sheet1", "G1", "发送道具")
  429. f.SetCellValue("Sheet1", "H1", "预计发送时间")
  430. f.SetCellValue("Sheet1", "I1", "发送状态")
  431. f.SetCellValue("Sheet1", "J1", "创建人")
  432. f.SetCellValue("Sheet1", "K1", "创建时间")
  433. //获取道具和装备信息
  434. materials := gmdata.MaterialsDict //道具
  435. equipments := gmdata.EquipmentDict //装备
  436. materialMap := make(map[int64]gmdata.Material)
  437. equipmentMap := make(map[int64]gmdata.Equipment)
  438. for _, item := range materials {
  439. materialMap[int64(item.ID)] = item
  440. }
  441. for _, item := range equipments {
  442. equipmentMap[item.LevelId] = item
  443. }
  444. data := make([]Ext, 0)
  445. sendStatus := map[int32]string{1: "已发送", 2: "等待发送", 3: "待审核", 4: "已取消", 5: "发送中", 6: "发送出错"}
  446. uIds := make([]int64, len(list))
  447. uMap := make(map[int64]*model.AdminUser)
  448. for i, v := range list {
  449. if v.OperatorID > 0 {
  450. uIds[i] = v.OperatorID
  451. }
  452. }
  453. u := query.Use(config.DB).AdminUser
  454. usrs, err := u.WithContext(ctx).Where(u.ID.In(uIds...)).Find()
  455. if err != nil {
  456. return err
  457. }
  458. for _, usr := range usrs {
  459. uMap[usr.ID] = usr
  460. }
  461. type props struct {
  462. Name string `json:"name"`
  463. Count string `json:"count"`
  464. }
  465. for i, v := range list {
  466. f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i+2), v.ID)
  467. f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+2), v.Title)
  468. if v.ReceiptType == 1 {
  469. f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), "全服")
  470. } else if v.ReceiptType == 2 {
  471. f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), fmt.Sprintf("指定服%s", v.ServerIds))
  472. } else if v.ReceiptType == 3 {
  473. f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), fmt.Sprintf("指定玩家%s", v.PlayerIds))
  474. }
  475. f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i+2), fmt.Sprintf("%d天", v.Expired))
  476. f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i+2), v.Content)
  477. f.SetCellValue("Sheet1", fmt.Sprintf("F%d", i+2), v.Remark)
  478. //装备或者道具
  479. err := json.Unmarshal([]byte(v.Ext), &data)
  480. if err != nil {
  481. return err
  482. }
  483. propsArr := make([]props, 0)
  484. propsStr := ""
  485. for _, item := range data {
  486. //装备id大于10000000
  487. id, _ := strconv.ParseInt(item.Id, 10, 64)
  488. if id > 1000000 {
  489. if equipment, ok := equipmentMap[id]; ok {
  490. propsArr = append(propsArr, props{Name: equipment.Name, Count: item.Count})
  491. }
  492. } else if id > 0 && id < 1000000 {
  493. if material, ok := materialMap[id]; ok {
  494. propsArr = append(propsArr, props{
  495. Name: material.Name,
  496. Count: item.Count,
  497. })
  498. }
  499. }
  500. marshal, err := json.Marshal(propsArr)
  501. if err != nil {
  502. return err
  503. }
  504. propsStr = string(marshal)
  505. }
  506. f.SetCellValue("Sheet1", fmt.Sprintf("G%d", i+2), propsStr)
  507. if v.SendAt < 1 {
  508. f.SetCellValue("Sheet1", fmt.Sprintf("H%d", i+2), "-")
  509. } else {
  510. f.SetCellValue("Sheet1", fmt.Sprintf("H%d", i+2), utility.FormatSecond(time.Unix(v.SendAt, 0)))
  511. }
  512. f.SetCellValue("Sheet1", fmt.Sprintf("I%d", i+2), sendStatus[v.Status])
  513. f.SetCellValue("Sheet1", fmt.Sprintf("J%d", i+2), uMap[v.OperatorID].Nickname)
  514. f.SetCellValue("Sheet1", fmt.Sprintf("K%d", i+2), utility.FormatSecond(time.Unix(v.CreatedAt, 0)))
  515. }
  516. // 设置工作簿的默认工作表
  517. f.SetActiveSheet(1)
  518. ctx.Header("Content-Type", "application/vnd.ms-excel")
  519. ctx.Header("Content-Disposition", fmt.Sprintf("attachment;filename=订单记录导出%s.xlsx", time.Now().Format("20060102150405")))
  520. f.WriteTo(ctx.Writer)
  521. return nil
  522. }