tg_red_packet_send.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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()
  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. ExpireMinutes int `json:"expireMinutes"` // 过期时间(分钟),默认10
  67. MaxGrabAmount decimal.Decimal `json:"maxGrabAmount"` // 单人最大可领取金额(0=不限制)
  68. Lang string `json:"lang"` // 消息语言: vi/id/en/zh
  69. }
  70. var req request
  71. if err := c.ShouldBindJSON(&req); err != nil {
  72. response.Resp(c, err.Error())
  73. return
  74. }
  75. // 验证
  76. if req.PacketType != 1 && req.PacketType != 2 {
  77. response.Resp(c, "红包类型错误")
  78. return
  79. }
  80. if req.TotalCount <= 0 {
  81. response.Resp(c, "红包个数必须大于0")
  82. return
  83. }
  84. if req.TotalAmount.LessThanOrEqual(decimal.Zero) {
  85. response.Resp(c, "红包金额必须大于0")
  86. return
  87. }
  88. // 默认语言
  89. if req.Lang == "" {
  90. req.Lang = "vi"
  91. }
  92. // 默认币种(根据语言联动)
  93. if req.Symbol == "" {
  94. langSymbol := map[string]string{"vi": "VND", "id": "IDR", "en": "USD", "zh": "CNY"}
  95. if s, ok := langSymbol[req.Lang]; ok {
  96. req.Symbol = s
  97. } else {
  98. req.Symbol = "VND"
  99. }
  100. }
  101. if req.BlessingWords == "" {
  102. if req.PacketType == 1 {
  103. req.BlessingWords = "恭喜发财,大吉大利"
  104. } else {
  105. req.BlessingWords = "拼手气红包,快来抢!"
  106. }
  107. }
  108. // 默认过期时间
  109. if req.ExpireMinutes <= 0 {
  110. req.ExpireMinutes = 10
  111. }
  112. // 构建临时配置
  113. config := &model.TgRedPacketConfig{
  114. GroupId: fmt.Sprintf("%d", req.GroupId),
  115. GroupName: req.GroupName,
  116. PacketType: req.PacketType,
  117. TotalAmount: req.TotalAmount,
  118. TotalCount: req.TotalCount,
  119. Symbol: req.Symbol,
  120. BlessingWords: req.BlessingWords,
  121. ExpireMinutes: req.ExpireMinutes,
  122. MaxGrabAmount: req.MaxGrabAmount,
  123. Lang: req.Lang,
  124. }
  125. // 执行发送
  126. packetNo, err := s.ExecuteSendRedPacket(config)
  127. if err != nil {
  128. core.Log.Errorf("发送红包失败: %v", err)
  129. response.Resp(c, fmt.Sprintf("发送失败: %v", err))
  130. return
  131. }
  132. response.Resp(c, map[string]interface{}{
  133. "message": "红包发送成功",
  134. "packetNo": packetNo,
  135. })
  136. }
  137. // ExecuteSendRedPacket 执行发送红包的核心逻辑
  138. func (s *TgRedPacketSendService) ExecuteSendRedPacket(config *model.TgRedPacketConfig) (string, error) {
  139. // 调用 magic_server API 创建红包并发送到 Telegram
  140. packetNo, err := s.callMagicServerSendRedPacket(config)
  141. if err != nil {
  142. core.Log.Errorf("创建并发送红包失败: %v", err)
  143. return "", err
  144. }
  145. core.Log.Infof("成功发送红包: 群组=%s, 类型=%d, 金额=%v, 个数=%d, 编号=%s",
  146. config.GroupId, config.PacketType, config.TotalAmount, config.TotalCount, packetNo)
  147. return packetNo, nil
  148. }
  149. // callMagicServerSendRedPacket 调用 magic_server API 发送红包
  150. func (s *TgRedPacketSendService) callMagicServerSendRedPacket(config *model.TgRedPacketConfig) (string, error) {
  151. // magic_server API 地址
  152. apiURL := "http://localhost:2011/api/v1/adi/telegram/redpacket/send"
  153. // 构造请求参数
  154. payload := map[string]interface{}{
  155. "groupId": config.GroupId,
  156. "totalAmount": config.TotalAmount,
  157. "totalCount": config.TotalCount,
  158. "packetType": config.PacketType,
  159. "symbol": config.Symbol,
  160. "blessingWords": config.BlessingWords,
  161. "expireMinutes": config.ExpireMinutes,
  162. "maxGrabAmount": config.MaxGrabAmount,
  163. "lang": config.Lang,
  164. }
  165. jsonData, err := json.Marshal(payload)
  166. if err != nil {
  167. return "", fmt.Errorf("序列化请求参数失败: %v", err)
  168. }
  169. // 发送 HTTP 请求
  170. req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
  171. if err != nil {
  172. return "", fmt.Errorf("创建 HTTP 请求失败: %v", err)
  173. }
  174. signMessage, _ := utils.BuildSignMessage()
  175. req.Header.Set("Content-Type", "application/json")
  176. req.Header.Set("sign", signMessage)
  177. // TODO: 添加认证 Token
  178. // req.Header.Set("Authorization", "Bearer "+token)
  179. client := &http.Client{Timeout: 10 * time.Second}
  180. resp, err := client.Do(req)
  181. if err != nil {
  182. return "", fmt.Errorf("发送 HTTP 请求失败: %v", err)
  183. }
  184. defer resp.Body.Close()
  185. // 解析响应
  186. var result struct {
  187. Code int `json:"code"`
  188. Message string `json:"message"`
  189. Data struct {
  190. PacketNo string `json:"packetNo"`
  191. PacketId int64 `json:"packetId"`
  192. } `json:"data"`
  193. }
  194. body, err := io.ReadAll(resp.Body)
  195. if err != nil {
  196. return "", fmt.Errorf("读取响应失败: %v", err)
  197. }
  198. // 记录原始响应,用于调试
  199. core.Log.Infof("magic_server 原始响应: %s", string(body))
  200. if err := json.Unmarshal(body, &result); err != nil {
  201. return "", fmt.Errorf("解析响应失败: %v (响应内容: %s)", err, string(body))
  202. }
  203. if result.Code != 0 && result.Code != 200 {
  204. return "", fmt.Errorf("API 返回错误: %s", result.Message)
  205. }
  206. if result.Data.PacketNo == "" {
  207. return "", fmt.Errorf("红包编号为空")
  208. }
  209. return result.Data.PacketNo, nil
  210. }
  211. // GetGroups 获取机器人所在的群组列表
  212. func (s *TgRedPacketSendService) GetGroups(c *gin.Context) {
  213. s.SetDbAlias("app")
  214. // 从数据库中获取已记录的群组
  215. var groups []model.TgGroup
  216. if err := s.DB().Where("status = 1").Find(&groups).Error; err != nil {
  217. response.Resp(c, err.Error())
  218. return
  219. }
  220. response.Resp(c, map[string]interface{}{
  221. "message": "获取群组列表成功",
  222. "groups": groups,
  223. })
  224. }