tg_red_packet_send.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package app
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "time"
  9. "github.com/gin-gonic/gin"
  10. "github.com/shopspring/decimal"
  11. "go_server/base/core"
  12. model "go_server/model/biz_modules/app"
  13. "go_server/model/common/response"
  14. "go_server/service/base"
  15. "go_server/utils"
  16. )
  17. type TgRedPacketSendService struct {
  18. base.BizCommonService
  19. }
  20. // SendManual 手动发送红包(基于配置ID)
  21. func (s *TgRedPacketSendService) SendManual(c *gin.Context) {
  22. s.SetDbAlias("app")
  23. type request struct {
  24. ConfigId int64 `json:"configId" binding:"required"`
  25. }
  26. var req request
  27. if err := c.ShouldBindJSON(&req); err != nil {
  28. response.Resp(c, err.Error())
  29. return
  30. }
  31. // 查询配置
  32. var config model.TgRedPacketConfig
  33. if err := s.DB().Where("id = ? AND status = 1", req.ConfigId).First(&config).Error; err != nil {
  34. response.Resp(c, "配置不存在或已禁用")
  35. return
  36. }
  37. // 执行发送
  38. packetNo, err := s.ExecuteSendRedPacket(&config)
  39. if err != nil {
  40. core.Log.Errorf("发送红包失败: %v", err)
  41. response.Resp(c, fmt.Sprintf("发送失败: %v", err))
  42. return
  43. }
  44. // 更新配置的执行统计
  45. now := time.Now().Unix()
  46. s.DB().Model(&model.TgRedPacketConfig{}).Where("id = ?", config.Id).Updates(map[string]interface{}{
  47. "last_exec_time": now,
  48. "exec_count": config.ExecCount + 1,
  49. })
  50. response.Resp(c, map[string]interface{}{
  51. "message": "红包发送成功",
  52. "packetNo": packetNo,
  53. })
  54. }
  55. // SendDirect 直接发送红包(无需配置,直接传参数)
  56. func (s *TgRedPacketSendService) SendDirect(c *gin.Context) {
  57. s.SetDbAlias("app")
  58. type request struct {
  59. GroupId int64 `json:"groupId" binding:"required"`
  60. GroupName string `json:"groupName"`
  61. PacketType int8 `json:"packetType" binding:"required"`
  62. TotalAmount decimal.Decimal `json:"totalAmount" binding:"required"`
  63. TotalCount int `json:"totalCount" binding:"required"`
  64. Symbol string `json:"symbol"`
  65. BlessingWords string `json:"blessingWords"`
  66. }
  67. var req request
  68. if err := c.ShouldBindJSON(&req); err != nil {
  69. response.Resp(c, err.Error())
  70. return
  71. }
  72. // 验证
  73. if req.PacketType != 1 && req.PacketType != 2 {
  74. response.Resp(c, "红包类型错误")
  75. return
  76. }
  77. if req.TotalCount <= 0 {
  78. response.Resp(c, "红包个数必须大于0")
  79. return
  80. }
  81. if req.TotalAmount.LessThanOrEqual(decimal.Zero) {
  82. response.Resp(c, "红包金额必须大于0")
  83. return
  84. }
  85. // 设置默认值
  86. if req.Symbol == "" {
  87. req.Symbol = "VND"
  88. }
  89. if req.BlessingWords == "" {
  90. if req.PacketType == 1 {
  91. req.BlessingWords = "恭喜发财,大吉大利"
  92. } else {
  93. req.BlessingWords = "拼手气红包,快来抢!"
  94. }
  95. }
  96. // 构建临时配置
  97. config := &model.TgRedPacketConfig{
  98. GroupId: fmt.Sprintf("%d", req.GroupId),
  99. GroupName: req.GroupName,
  100. PacketType: req.PacketType,
  101. TotalAmount: req.TotalAmount,
  102. TotalCount: req.TotalCount,
  103. Symbol: req.Symbol,
  104. BlessingWords: req.BlessingWords,
  105. }
  106. // 执行发送
  107. packetNo, err := s.ExecuteSendRedPacket(config)
  108. if err != nil {
  109. core.Log.Errorf("发送红包失败: %v", err)
  110. response.Resp(c, fmt.Sprintf("发送失败: %v", err))
  111. return
  112. }
  113. response.Resp(c, map[string]interface{}{
  114. "message": "红包发送成功",
  115. "packetNo": packetNo,
  116. })
  117. }
  118. // ExecuteSendRedPacket 执行发送红包的核心逻辑
  119. func (s *TgRedPacketSendService) ExecuteSendRedPacket(config *model.TgRedPacketConfig) (string, error) {
  120. // 调用 magic_server API 创建红包并发送到 Telegram
  121. packetNo, err := s.callMagicServerSendRedPacket(config)
  122. if err != nil {
  123. core.Log.Errorf("创建并发送红包失败: %v", err)
  124. return "", err
  125. }
  126. core.Log.Infof("成功发送红包: 群组=%s, 类型=%d, 金额=%v, 个数=%d, 编号=%s",
  127. config.GroupId, config.PacketType, config.TotalAmount, config.TotalCount, packetNo)
  128. return packetNo, nil
  129. }
  130. // callMagicServerSendRedPacket 调用 magic_server API 发送红包
  131. func (s *TgRedPacketSendService) callMagicServerSendRedPacket(config *model.TgRedPacketConfig) (string, error) {
  132. // magic_server API 地址
  133. apiURL := "http://localhost:2011/api/v1/adi/telegram/redpacket/send"
  134. // 构造请求参数
  135. payload := map[string]interface{}{
  136. "groupId": config.GroupId,
  137. "totalAmount": config.TotalAmount,
  138. "totalCount": config.TotalCount,
  139. "packetType": config.PacketType,
  140. "symbol": config.Symbol,
  141. "blessingWords": config.BlessingWords,
  142. }
  143. jsonData, err := json.Marshal(payload)
  144. if err != nil {
  145. return "", fmt.Errorf("序列化请求参数失败: %v", err)
  146. }
  147. // 发送 HTTP 请求
  148. req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
  149. if err != nil {
  150. return "", fmt.Errorf("创建 HTTP 请求失败: %v", err)
  151. }
  152. signMessage, _ := utils.BuildSignMessage()
  153. req.Header.Set("Content-Type", "application/json")
  154. req.Header.Set("sign", signMessage)
  155. // TODO: 添加认证 Token
  156. // req.Header.Set("Authorization", "Bearer "+token)
  157. client := &http.Client{Timeout: 10 * time.Second}
  158. resp, err := client.Do(req)
  159. if err != nil {
  160. return "", fmt.Errorf("发送 HTTP 请求失败: %v", err)
  161. }
  162. defer resp.Body.Close()
  163. // 解析响应
  164. var result struct {
  165. Code int `json:"code"`
  166. Message string `json:"message"`
  167. Data struct {
  168. PacketNo string `json:"packetNo"`
  169. PacketId int64 `json:"packetId"`
  170. } `json:"data"`
  171. }
  172. body, err := io.ReadAll(resp.Body)
  173. if err != nil {
  174. return "", fmt.Errorf("读取响应失败: %v", err)
  175. }
  176. // 记录原始响应,用于调试
  177. core.Log.Infof("magic_server 原始响应: %s", string(body))
  178. if err := json.Unmarshal(body, &result); err != nil {
  179. return "", fmt.Errorf("解析响应失败: %v (响应内容: %s)", err, string(body))
  180. }
  181. if result.Code != 0 && result.Code != 200 {
  182. return "", fmt.Errorf("API 返回错误: %s", result.Message)
  183. }
  184. if result.Data.PacketNo == "" {
  185. return "", fmt.Errorf("红包编号为空")
  186. }
  187. return result.Data.PacketNo, nil
  188. }
  189. // GetGroups 获取机器人所在的群组列表
  190. func (s *TgRedPacketSendService) GetGroups(c *gin.Context) {
  191. s.SetDbAlias("app")
  192. // 从数据库中获取已记录的群组
  193. var groups []model.TgGroup
  194. if err := s.DB().Where("status = 1").Find(&groups).Error; err != nil {
  195. response.Resp(c, err.Error())
  196. return
  197. }
  198. response.Resp(c, map[string]interface{}{
  199. "message": "获取群组列表成功",
  200. "groups": groups,
  201. })
  202. }