package service import ( "context" "errors" "fmt" "log" "net/url" "strings" "affiliate_dash/internal/model" ) // TokenPackageLinkResult 是 Token 所属上游打包页面的定位链接。 type TokenPackageLinkResult struct { PackageID int64 `json:"package_id"` ShareCode string `json:"share_code"` URL string `json:"url"` } // TokenPackageLink 返回 Token 所属上游包页面的跳转链接。 // 只对本地已保存分享码的新包生效;历史包不向上游反查。 func (s *RockService) TokenPackageLink(ctx context.Context, tokenID uint) (*TokenPackageLinkResult, error) { var token model.RockToken if err := s.db.WithContext(ctx).First(&token, tokenID).Error; err != nil { return nil, err } if token.PackageID == nil || *token.PackageID <= 0 { return nil, errors.New("该 Token 没有关联上游打包记录,无法跳转上游") } cfg, err := s.GetConfig(ctx) if err != nil { return nil, err } shareCode := strings.TrimSpace(token.PackageShareCode) if shareCode == "" { shareCode, err = s.resolvePackageShareCode(ctx, *token.PackageID, cfg.BearerToken) if err != nil { return nil, err } // 反查成功后回填同包 Token,后续点击无需再请求上游。 if updateErr := s.db.WithContext(ctx).Model(&model.RockToken{}). Where("package_id = ? AND package_share_code = ''", *token.PackageID). Update("package_share_code", shareCode).Error; updateErr != nil { log.Printf("[RockService] 回填 package_share_code 失败 package_id=%d: %v", *token.PackageID, updateErr) } } base := strings.TrimRight(strings.TrimSpace(cfg.BaseURL), "/") if base == "" { base = model.RockDefaultBaseURL } return &TokenPackageLinkResult{ PackageID: *token.PackageID, ShareCode: shareCode, URL: fmt.Sprintf("%s/#/package/%s", base, url.PathEscape(shareCode)), }, nil } // resolvePackageShareCode 在本地没有分享码时,按 package_id 从上游包列表反查。 // 历史 Token 只落了 package_id,package_share_code 是后续版本才开始保存的。 func (s *RockService) resolvePackageShareCode(ctx context.Context, packageID int64, bearerToken string) (string, error) { if packageID <= 0 { return "", errors.New("该 Token 没有关联上游打包记录,无法跳转上游") } bearer := strings.TrimSpace(bearerToken) if bearer == "" { return "", errors.New("该 Token 缺少上游包分享码,且未配置 0666.pro Bearer Token,无法反查上游包") } // 最多翻 20 页,防止上游分页异常导致死循环。 for page := 1; page <= 20; page++ { resp, err := s.client.ListPackages(ctx, bearer, page, 100) if err != nil { return "", fmt.Errorf("反查上游包分享码失败: %w", err) } if resp == nil { break } for _, pkg := range resp.Packages { if pkg.ID != packageID { continue } code := strings.TrimSpace(pkg.ShareCode) if code == "" { return "", errors.New("上游该包未返回分享码,无法跳转上游") } return code, nil } if resp.Pagination.TotalPages > 0 { if page >= resp.Pagination.TotalPages { break } continue } if len(resp.Packages) == 0 { break } } return "", errors.New("上游未找到该 Token 所属打包记录(可能已被清理),无法跳转上游") }