update: pan

This commit is contained in:
ctwj
2025-07-13 09:57:36 +08:00
parent 08be8228e0
commit b692b45e24
17 changed files with 321 additions and 49 deletions

View File

@@ -90,15 +90,6 @@ func (q *QuarkPanService) Transfer(shareID string) (*TransferResult, error) {
return ErrorResult(fmt.Sprintf("获取stoken失败: %v", err)), nil
}
if config.IsType == 1 {
// 直接返回资源信息
return SuccessResult("检验成功", map[string]interface{}{
"title": stokenResult.Title,
"shareUrl": config.URL,
"stoken": stokenResult.Stoken,
}), nil
}
stoken = strings.ReplaceAll(stokenResult.Stoken, " ", "+")
} else {
stoken = strings.ReplaceAll(config.Stoken, " ", "+")
@@ -110,6 +101,42 @@ func (q *QuarkPanService) Transfer(shareID string) (*TransferResult, error) {
return ErrorResult(fmt.Sprintf("获取分享详情失败: %v", err)), nil
}
if config.IsType == 1 {
// 遍历 资源目录结构
for _, item := range shareResult.List {
// 获取文件信息
fileList, err := q.getDirFile(item.Fid)
if err != nil {
log.Printf("获取目录文件失败: %v", err)
continue
}
// 处理文件列表
if fileList != nil {
log.Printf("目录 %s 包含 %d 个文件/文件夹", item.Fid, len(fileList))
// 遍历所有文件,可以在这里添加具体的处理逻辑
for _, file := range fileList {
if fileName, ok := file["file_name"].(string); ok {
if fileType, ok := file["file_type"].(float64); ok {
fileTypeStr := "文件"
if fileType == 1 {
fileTypeStr = "目录"
}
log.Printf(" - %s (%s)", fileName, fileTypeStr)
}
}
}
}
}
// 直接返回资源信息
return SuccessResult("检验成功", map[string]interface{}{
"title": shareResult.Share.Title,
"shareUrl": config.URL,
}), nil
}
// 提取文件信息
fidList := make([]string, 0)
fidTokenList := make([]string, 0)
@@ -280,18 +307,23 @@ func (q *QuarkPanService) getStoken(shareID string) (*StokenResult, error) {
// getShare 获取分享详情
func (q *QuarkPanService) getShare(shareID, stoken string) (*ShareResult, error) {
data := map[string]interface{}{
"pwd_id": shareID,
"stoken": stoken,
}
queryParams := map[string]string{
"pr": "ucpro",
"fr": "pc",
"uc_param_str": "",
"pr": "ucpro",
"fr": "pc",
"uc_param_str": "",
"pwd_id": shareID,
"stoken": stoken,
"pdir_fid": "0",
"force": "0",
"_page": "1",
"_size": "100",
"_fetch_banner": "1",
"_fetch_share": "1",
"_fetch_total": "1",
"_sort": "file_type:asc,updated_at:desc",
}
respData, err := q.HTTPPost("https://drive-pc.quark.cn/1/clouddrive/share/sharepage/detail", data, queryParams)
respData, err := q.HTTPGet("https://drive-pc.quark.cn/1/clouddrive/share/sharepage/detail", queryParams)
if err != nil {
return nil, err
}
@@ -481,6 +513,67 @@ func (q *QuarkPanService) deleteAdFiles(pdirFid string) error {
return nil
}
// getDirFile 获取指定文件夹的文件列表
func (q *QuarkPanService) getDirFile(pdirFid string) ([]map[string]interface{}, error) {
log.Printf("正在遍历父文件夹: %s", pdirFid)
queryParams := map[string]string{
"pr": "ucpro",
"fr": "pc",
"uc_param_str": "",
"pdir_fid": pdirFid,
"_page": "1",
"_size": "50",
"_fetch_total": "1",
"_fetch_sub_dirs": "0",
"_sort": "updated_at:desc",
}
respData, err := q.HTTPGet("https://drive-pc.quark.cn/1/clouddrive/file/sort", queryParams)
if err != nil {
log.Printf("获取目录文件失败: %v", err)
return nil, err
}
var response struct {
Status int `json:"status"`
Message string `json:"message"`
Data struct {
List []map[string]interface{} `json:"list"`
} `json:"data"`
}
if err := json.Unmarshal(respData, &response); err != nil {
log.Printf("解析目录文件响应失败: %v", err)
return nil, err
}
if response.Status != 200 {
return nil, fmt.Errorf(response.Message)
}
// 递归处理子目录
var allFiles []map[string]interface{}
for _, item := range response.Data.List {
// 添加当前文件/目录
allFiles = append(allFiles, item)
// 如果是目录,递归获取子目录内容
if fileType, ok := item["file_type"].(float64); ok && fileType == 1 { // 1表示目录
if fid, ok := item["fid"].(string); ok {
subFiles, err := q.getDirFile(fid)
if err != nil {
log.Printf("获取子目录 %s 失败: %v", fid, err)
continue
}
allFiles = append(allFiles, subFiles...)
}
}
}
return allFiles, nil
}
// 定义各种结果结构体
type StokenResult struct {
Stoken string `json:"stoken"`

View File

@@ -1,4 +1,4 @@
package utils
package common
import (
"encoding/json"

166
doc/QUARK_GETSHARE_FIX.md Normal file
View File

@@ -0,0 +1,166 @@
# 夸克网盘 getShare 方法修复
## 问题描述
在夸克网盘的 `getShare` 函数中遇到了 HTTP 405 错误:
```
HTTP请求失败: 405, {"timestamp":1752368941648,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/1/clouddrive/share/sharepage/detail"}
```
## 问题分析
### 错误原因
- 当前Go代码使用 `HTTPPost` 方法请求 `/1/clouddrive/share/sharepage/detail` 接口
- 但服务器返回 405 错误,表示不支持 POST 方法
- 需要改为 GET 请求
### 对比原始PHP代码
通过对比 `demo/pan/QuarkPan.php` 中的 `getShare` 方法:
```php
public function getShare($pwd_id,$stoken)
{
$urlData = array();
$queryParams = [
"pr" => "ucpro",
"fr" => "pc",
"uc_param_str" => "",
"pwd_id" => $pwd_id,
"stoken" => $stoken,
"pdir_fid" => "0",
"force" => "0",
"_page" => "1",
"_size" => "100",
"_fetch_banner" => "1",
"_fetch_share" => "1",
"_fetch_total" => "1",
"_sort" => "file_type:asc,updated_at:desc"
];
return $this->executeApiRequest(
"https://drive-pc.quark.cn/1/clouddrive/share/sharepage/detail",
"GET", // 使用 GET 方法
$urlData,
$queryParams
);
}
```
## 修复方案
### 修复前的代码
```go
// 修复前:使用 POST 请求
func (q *QuarkPanService) getShare(shareID, stoken string) (*ShareResult, error) {
data := map[string]interface{}{
"pwd_id": shareID,
"stoken": stoken,
}
queryParams := map[string]string{
"pr": "ucpro",
"fr": "pc",
"uc_param_str": "",
}
respData, err := q.HTTPPost("https://drive-pc.quark.cn/1/clouddrive/share/sharepage/detail", data, queryParams)
// ...
}
```
### 修复后的代码
```go
// 修复后:使用 GET 请求,参数放在 URL 中
func (q *QuarkPanService) getShare(shareID, stoken string) (*ShareResult, error) {
queryParams := map[string]string{
"pr": "ucpro",
"fr": "pc",
"uc_param_str": "",
"pwd_id": shareID,
"stoken": stoken,
"pdir_fid": "0",
"force": "0",
"_page": "1",
"_size": "100",
"_fetch_banner": "1",
"_fetch_share": "1",
"_fetch_total": "1",
"_sort": "file_type:asc,updated_at:desc",
}
respData, err := q.HTTPGet("https://drive-pc.quark.cn/1/clouddrive/share/sharepage/detail", queryParams)
// ...
}
```
## 修复内容
### 1. 请求方法修改
- **从 POST 改为 GET**`HTTPPost``HTTPGet`
- **参数传递方式**:从请求体改为 URL 查询参数
### 2. 参数结构调整
- **移除请求体数据**:不再使用 `data` 参数
- **添加完整查询参数**按照PHP代码添加所有必要的查询参数
- **参数顺序**保持与PHP代码一致的参数顺序
### 3. 参数说明
| 参数 | 说明 | 值 |
|------|------|-----|
| `pr` | 产品标识 | `ucpro` |
| `fr` | 来源标识 | `pc` |
| `uc_param_str` | UC参数 | 空字符串 |
| `pwd_id` | 分享ID | 从URL提取 |
| `stoken` | 安全令牌 | 从getStoken获取 |
| `pdir_fid` | 父目录ID | `0` |
| `force` | 强制标志 | `0` |
| `_page` | 页码 | `1` |
| `_size` | 页面大小 | `100` |
| `_fetch_banner` | 获取横幅 | `1` |
| `_fetch_share` | 获取分享信息 | `1` |
| `_fetch_total` | 获取总数 | `1` |
| `_sort` | 排序方式 | `file_type:asc,updated_at:desc` |
## 验证结果
### 编译测试
```bash
go build -o res_db .
# ✅ 编译成功
```
### 功能测试
```bash
go test ./utils/pan -v
# ✅ 所有测试通过
```
### 预期效果
- **解决 405 错误**:使用正确的 GET 请求方法
- **保持功能完整**:所有参数都正确传递
- **兼容性良好**与原始PHP代码行为一致
## 经验总结
### 1. API 兼容性
- 在翻译代码时,需要仔细对比原始实现的请求方法
- 不同语言的HTTP客户端可能有不同的默认行为
- 需要确保请求方法、参数位置、参数名称都完全一致
### 2. 错误排查
- HTTP 405 错误通常表示请求方法不正确
- 对比原始代码是排查此类问题的最佳方法
- 需要检查请求方法、URL、参数等多个方面
### 3. 最佳实践
- **保持一致性**:与原始实现保持完全一致
- **完整参数**:不要遗漏任何必要的参数
- **测试验证**:修复后要进行充分的测试
## 相关文件
- `utils/pan/quark_pan.go` - 修复的文件
- `demo/pan/QuarkPan.php` - 原始PHP实现参考
- `utils/pan/base_pan.go` - 基础HTTP方法实现
这次修复确保了夸克网盘的 `getShare` 方法与原始PHP实现完全一致解决了HTTP 405错误问题。

View File

@@ -2,9 +2,10 @@ package utils
import (
"log"
panutils "res_db/common"
commonutils "res_db/common/utils"
"res_db/db/entity"
"res_db/db/repo"
panutils "res_db/utils/pan"
"strings"
"sync"
"time"
@@ -315,11 +316,23 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
log.Printf("检测到服务类型: %s, 分享ID: %s", serviceType.String(), shareID)
// 不是夸克,直接保存,
if serviceType != panutils.Quark {
// 检测是否有效
checkResult, _ := commonutils.CheckURL(readyResource.URL)
if !checkResult.Status {
log.Printf("链接无效: %s", readyResource.URL)
return nil
}
// 入库
}
// 准备配置
config := &panutils.PanConfig{
URL: readyResource.URL,
Code: "", // 可以从readyResource中获取
IsType: 0, // 转存并分享后的资源信息
IsType: 1, // 转存并分享后的资源信息 0 转存后分享, 1 只获取基本信息
ExpiredType: 1, // 永久分享
AdFid: "",
Stoken: "",
@@ -333,45 +346,45 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
}
// 阿里云盘特殊处理检查URL有效性
if serviceType == panutils.Alipan {
checkResult, _ := CheckURL(readyResource.URL)
if !checkResult.Status {
log.Printf("阿里云盘链接无效: %s", readyResource.URL)
return nil
}
// if serviceType == panutils.Alipan {
// checkResult, _ := CheckURL(readyResource.URL)
// if !checkResult.Status {
// log.Printf("阿里云盘链接无效: %s", readyResource.URL)
// return nil
// }
// 如果有标题,直接创建资源
if readyResource.Title != nil && *readyResource.Title != "" {
resource := &entity.Resource{
Title: *readyResource.Title,
Description: readyResource.Description,
URL: readyResource.URL,
PanID: s.determinePanID(readyResource.URL),
IsValid: true,
IsPublic: true,
}
// // 如果有标题,直接创建资源
// if readyResource.Title != nil && *readyResource.Title != "" {
// resource := &entity.Resource{
// Title: *readyResource.Title,
// Description: readyResource.Description,
// URL: readyResource.URL,
// PanID: s.determinePanID(readyResource.URL),
// IsValid: true,
// IsPublic: true,
// }
// 如果有分类信息,尝试查找或创建分类
if readyResource.Category != "" {
categoryID, err := s.getOrCreateCategory(readyResource.Category)
if err == nil {
resource.CategoryID = &categoryID
}
}
// // 如果有分类信息,尝试查找或创建分类
// if readyResource.Category != "" {
// categoryID, err := s.getOrCreateCategory(readyResource.Category)
// if err == nil {
// resource.CategoryID = &categoryID
// }
// }
return s.resourceRepo.Create(resource)
}
}
// return s.resourceRepo.Create(resource)
// }
// }
// 统一处理:尝试转存获取标题
result, err := panService.Transfer(shareID)
if err != nil {
log.Printf("网盘转存失败: %v", err)
log.Printf("网盘信息获取失败: %v", err)
return err
}
if !result.Success {
log.Printf("网盘转存失败: %s", result.Message)
log.Printf("网盘信息获取失败: %s", result.Message)
return nil
}