|
|
@@ -0,0 +1,243 @@
|
|
|
+package services
|
|
|
+
|
|
|
+import (
|
|
|
+ "app/commons/config"
|
|
|
+ "app/commons/core"
|
|
|
+ "app/commons/core/redisclient"
|
|
|
+ "app/commons/model/entity"
|
|
|
+ "bytes"
|
|
|
+ "context"
|
|
|
+ "encoding/json"
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "io"
|
|
|
+ "net/http"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+// TgPlatformService 平台API客户端服务(调用Vitiens平台接口)
|
|
|
+type TgPlatformService struct {
|
|
|
+ CommonService
|
|
|
+}
|
|
|
+
|
|
|
+// ---- 请求/响应结构体 ----
|
|
|
+
|
|
|
+// oauthLoginRequest OAuth登录请求
|
|
|
+type oauthLoginRequest struct {
|
|
|
+ Provider string `json:"provider"`
|
|
|
+ OpenId string `json:"openId"`
|
|
|
+ Nickname string `json:"nickname"`
|
|
|
+ Avatar string `json:"avatar"`
|
|
|
+ InviteCode string `json:"inviteCode,omitempty"`
|
|
|
+}
|
|
|
+
|
|
|
+// platformAPIResponse 平台API通用响应
|
|
|
+type platformAPIResponse struct {
|
|
|
+ Code int32 `json:"code"`
|
|
|
+ Msg string `json:"msg"`
|
|
|
+ Data json.RawMessage `json:"data"`
|
|
|
+ Time int64 `json:"time"`
|
|
|
+}
|
|
|
+
|
|
|
+// oauthLoginData OAuth登录响应data
|
|
|
+type oauthLoginData struct {
|
|
|
+ Token string `json:"token"`
|
|
|
+ User oauthUserInfo `json:"user"`
|
|
|
+ IsNew bool `json:"isNew"`
|
|
|
+}
|
|
|
+
|
|
|
+// oauthUserInfo 用户信息
|
|
|
+type oauthUserInfo struct {
|
|
|
+ Id int64 `json:"id"`
|
|
|
+ Uid string `json:"uid"`
|
|
|
+ Nickname string `json:"nickname"`
|
|
|
+ Avatar string `json:"avatar"`
|
|
|
+ Phone string `json:"phone"`
|
|
|
+ Email string `json:"email"`
|
|
|
+}
|
|
|
+
|
|
|
+// walletInfoData 钱包信息响应data
|
|
|
+type walletInfoData struct {
|
|
|
+ Balance float64 `json:"balance"`
|
|
|
+ FrozenBalance float64 `json:"frozenBalance"`
|
|
|
+ TotalRecharge float64 `json:"totalRecharge"`
|
|
|
+ TotalWithdraw float64 `json:"totalWithdraw"`
|
|
|
+ TotalTaskIncome float64 `json:"totalTaskIncome"`
|
|
|
+}
|
|
|
+
|
|
|
+// ---- 核心方法 ----
|
|
|
+
|
|
|
+// RegisterAndBind 一键注册并绑定
|
|
|
+// 1. 检查是否已绑定 → 已绑定直接返回
|
|
|
+// 2. 调用Vitiens平台OAuth接口注册/登录
|
|
|
+// 3. 在本地创建绑定记录
|
|
|
+func (s *TgPlatformService) RegisterAndBind(telegramId int64, username string, firstName string) (userId int64, isNew bool, err error) {
|
|
|
+ bindService := &TgBindService{}
|
|
|
+
|
|
|
+ // 检查是否已绑定
|
|
|
+ bound, existUserId := bindService.IsUserBound(telegramId, username)
|
|
|
+ if bound {
|
|
|
+ return existUserId, false, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调用平台OAuth接口
|
|
|
+ apiURL := s.getPlatformAPIURL()
|
|
|
+ if apiURL == "" {
|
|
|
+ return 0, false, errors.New("平台API地址未配置")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构造请求
|
|
|
+ nickname := firstName
|
|
|
+ if nickname == "" && username != "" {
|
|
|
+ nickname = username
|
|
|
+ }
|
|
|
+ if nickname == "" {
|
|
|
+ nickname = fmt.Sprintf("TG_%d", telegramId)
|
|
|
+ }
|
|
|
+
|
|
|
+ reqBody := oauthLoginRequest{
|
|
|
+ Provider: "telegram",
|
|
|
+ OpenId: fmt.Sprintf("%d", telegramId),
|
|
|
+ Nickname: nickname,
|
|
|
+ }
|
|
|
+
|
|
|
+ reqBytes, err := json.Marshal(reqBody)
|
|
|
+ if err != nil {
|
|
|
+ return 0, false, fmt.Errorf("序列化请求失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发起HTTP请求
|
|
|
+ url := fmt.Sprintf("%s/dt/auth/oauth/login", apiURL)
|
|
|
+ core.Log.Infof("一键注册 - 调用平台API: %s, telegramId: %d", url, telegramId)
|
|
|
+
|
|
|
+ httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBytes))
|
|
|
+ if err != nil {
|
|
|
+ return 0, false, fmt.Errorf("创建请求失败: %w", err)
|
|
|
+ }
|
|
|
+ httpReq.Header.Set("Content-Type", "application/json")
|
|
|
+
|
|
|
+ client := &http.Client{Timeout: 10 * time.Second}
|
|
|
+ resp, err := client.Do(httpReq)
|
|
|
+ if err != nil {
|
|
|
+ return 0, false, fmt.Errorf("请求平台API失败: %w", err)
|
|
|
+ }
|
|
|
+ defer resp.Body.Close()
|
|
|
+
|
|
|
+ body, err := io.ReadAll(resp.Body)
|
|
|
+ if err != nil {
|
|
|
+ return 0, false, fmt.Errorf("读取响应失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ core.Log.Infof("一键注册 - 平台API响应: %s", string(body))
|
|
|
+
|
|
|
+ // 解析响应
|
|
|
+ var apiResp platformAPIResponse
|
|
|
+ if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
|
+ return 0, false, fmt.Errorf("解析响应失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if apiResp.Code != 200 {
|
|
|
+ return 0, false, fmt.Errorf("平台注册失败: %s", apiResp.Msg)
|
|
|
+ }
|
|
|
+
|
|
|
+ var loginData oauthLoginData
|
|
|
+ if err := json.Unmarshal(apiResp.Data, &loginData); err != nil {
|
|
|
+ return 0, false, fmt.Errorf("解析用户数据失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if loginData.User.Id <= 0 {
|
|
|
+ return 0, false, errors.New("平台返回的用户ID无效")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建本地绑定记录
|
|
|
+ bind := &entity.TgUserBind{
|
|
|
+ UserId: loginData.User.Id,
|
|
|
+ TelegramId: telegramId,
|
|
|
+ TelegramUsername: username,
|
|
|
+ TelegramFirstName: firstName,
|
|
|
+ BindStatus: 1,
|
|
|
+ BindTime: time.Now(),
|
|
|
+ }
|
|
|
+ if err := s.DB().Create(bind).Error; err != nil {
|
|
|
+ core.Log.Errorf("一键注册 - 创建绑定记录失败: %v", err)
|
|
|
+ return 0, false, fmt.Errorf("绑定失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置Redis绑定缓存
|
|
|
+ ctx := context.Background()
|
|
|
+ rdb := redisclient.DefaultClient()
|
|
|
+ cacheKey := fmt.Sprintf("tg_bind_cache:%d", telegramId)
|
|
|
+ rdb.Set(ctx, cacheKey, fmt.Sprintf("%d", loginData.User.Id), 1*time.Hour)
|
|
|
+
|
|
|
+ core.Log.Infof("一键注册 - 成功, telegramId: %d, userId: %d, isNew: %v",
|
|
|
+ telegramId, loginData.User.Id, loginData.IsNew)
|
|
|
+
|
|
|
+ return loginData.User.Id, loginData.IsNew, nil
|
|
|
+}
|
|
|
+
|
|
|
+// GetUserBalance 查询用户余额(通过平台API)
|
|
|
+func (s *TgPlatformService) GetUserBalance(platformToken string) (*walletInfoData, error) {
|
|
|
+ apiURL := s.getPlatformAPIURL()
|
|
|
+ if apiURL == "" {
|
|
|
+ return nil, errors.New("平台API地址未配置")
|
|
|
+ }
|
|
|
+
|
|
|
+ url := fmt.Sprintf("%s/dt/wallet/info", apiURL)
|
|
|
+ httpReq, err := http.NewRequest("GET", url, nil)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("创建请求失败: %w", err)
|
|
|
+ }
|
|
|
+ httpReq.Header.Set("Authorization", "Bearer "+platformToken)
|
|
|
+
|
|
|
+ client := &http.Client{Timeout: 10 * time.Second}
|
|
|
+ resp, err := client.Do(httpReq)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("请求失败: %w", err)
|
|
|
+ }
|
|
|
+ defer resp.Body.Close()
|
|
|
+
|
|
|
+ body, err := io.ReadAll(resp.Body)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("读取响应失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ var apiResp platformAPIResponse
|
|
|
+ if err := json.Unmarshal(body, &apiResp); err != nil {
|
|
|
+ return nil, fmt.Errorf("解析响应失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if apiResp.Code != 200 {
|
|
|
+ return nil, fmt.Errorf("查询失败: %s", apiResp.Msg)
|
|
|
+ }
|
|
|
+
|
|
|
+ var walletData walletInfoData
|
|
|
+ if err := json.Unmarshal(apiResp.Data, &walletData); err != nil {
|
|
|
+ return nil, fmt.Errorf("解析钱包数据失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ return &walletData, nil
|
|
|
+}
|
|
|
+
|
|
|
+// GetUserGrabRecords 查询用户抢红包记录(本地数据库)
|
|
|
+// limit <= 0 时返回全部记录
|
|
|
+func (s *TgPlatformService) GetUserGrabRecords(telegramId int64, limit int) ([]entity.TgRedPacketRecord, error) {
|
|
|
+ var records []entity.TgRedPacketRecord
|
|
|
+ query := s.DB().Where("telegram_id = ?", telegramId).Order("grabbed_at DESC")
|
|
|
+ if limit > 0 {
|
|
|
+ query = query.Limit(limit)
|
|
|
+ }
|
|
|
+ err := query.Find(&records).Error
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return records, nil
|
|
|
+}
|
|
|
+
|
|
|
+// getPlatformAPIURL 获取平台API地址
|
|
|
+func (s *TgPlatformService) getPlatformAPIURL() string {
|
|
|
+ telegramConfig := config.EnvConf().Telegram
|
|
|
+ if telegramConfig != nil && telegramConfig.PlatformAPIURL != "" {
|
|
|
+ return telegramConfig.PlatformAPIURL
|
|
|
+ }
|
|
|
+ return ""
|
|
|
+}
|