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 "" }