mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 03:15:04 +08:00
remove: md
This commit is contained in:
@@ -1,182 +0,0 @@
|
||||
# 网盘工厂模式实现总结
|
||||
|
||||
## 完成的工作
|
||||
|
||||
我已经成功将您提供的PHP代码翻译为Go代码,并实现了一个优雅的工厂模式来处理不同的网盘服务。以下是完成的主要工作:
|
||||
|
||||
### 1. 核心架构设计
|
||||
|
||||
#### 工厂模式核心文件
|
||||
- **`utils/pan_factory.go`** - 网盘工厂核心,定义了服务类型、配置结构和工厂方法
|
||||
- **`utils/base_pan.go`** - 基础网盘服务,提供通用的HTTP请求和配置管理
|
||||
- **`utils/quark_pan.go`** - 夸克网盘服务实现(完整实现)
|
||||
- **`utils/alipan.go`** - 阿里云盘服务实现(完整实现)
|
||||
- **`utils/baidu_pan.go`** - 百度网盘服务实现(基础框架)
|
||||
- **`utils/uc_pan.go`** - UC网盘服务实现(基础框架)
|
||||
|
||||
#### 测试和示例
|
||||
- **`utils/pan_factory_test.go`** - 完整的单元测试
|
||||
- **`utils/pan_example.go`** - 使用示例
|
||||
- **`utils/README_PAN_FACTORY.md`** - 详细的使用说明文档
|
||||
|
||||
### 2. 设计模式特点
|
||||
|
||||
#### 工厂模式
|
||||
- 通过 `PanFactory` 根据URL自动识别并创建对应的网盘服务
|
||||
- 支持通过服务类型直接创建服务实例
|
||||
- 易于扩展新的网盘服务
|
||||
|
||||
#### 策略模式
|
||||
- 每个网盘服务实现相同的接口 `PanService`
|
||||
- 根据不同的服务类型执行不同的处理策略
|
||||
- 代码结构清晰,职责分离
|
||||
|
||||
#### 模板方法模式
|
||||
- `BasePanService` 提供通用的HTTP请求方法
|
||||
- 具体服务类继承基础服务,专注于业务逻辑实现
|
||||
|
||||
### 3. 支持的服务类型
|
||||
|
||||
| 服务类型 | 状态 | 说明 |
|
||||
|---------|------|------|
|
||||
| 夸克网盘 (Quark) | ✅ 完整实现 | 包含完整的转存、分享、文件管理功能 |
|
||||
| 阿里云盘 (Alipan) | ✅ 完整实现 | 包含完整的转存、分享、文件管理功能 |
|
||||
| 百度网盘 (BaiduPan) | 🔄 基础框架 | 提供接口框架,等待具体实现 |
|
||||
| UC网盘 (UC) | 🔄 基础框架 | 提供接口框架,等待具体实现 |
|
||||
|
||||
### 4. 核心功能
|
||||
|
||||
#### URL解析和识别
|
||||
```go
|
||||
// 自动识别服务类型
|
||||
serviceType := ExtractServiceType(url)
|
||||
// 提取分享ID
|
||||
shareID, serviceType := ExtractShareId(url)
|
||||
```
|
||||
|
||||
#### 工厂创建服务
|
||||
```go
|
||||
factory := NewPanFactory()
|
||||
panService, err := factory.CreatePanService(url, config)
|
||||
```
|
||||
|
||||
#### 统一的服务接口
|
||||
```go
|
||||
// 转存分享链接
|
||||
result, err := panService.Transfer(shareID)
|
||||
|
||||
// 获取文件列表
|
||||
result, err := panService.GetFiles(pdirFid)
|
||||
|
||||
// 删除文件
|
||||
result, err := panService.DeleteFiles(fileList)
|
||||
```
|
||||
|
||||
### 5. 集成到现有系统
|
||||
|
||||
#### 定时任务集成
|
||||
在 `utils/scheduler.go` 的 `convertReadyResourceToResource` 函数中已经集成了工厂模式:
|
||||
|
||||
```go
|
||||
func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyResource) error {
|
||||
// 使用工厂模式创建对应的网盘服务
|
||||
factory := NewPanFactory()
|
||||
config := &PanConfig{...}
|
||||
|
||||
panService, err := factory.CreatePanService(readyResource.URL, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 根据服务类型进行不同处理
|
||||
switch serviceType {
|
||||
case Quark:
|
||||
// 夸克网盘处理逻辑
|
||||
case Alipan:
|
||||
// 阿里云盘处理逻辑
|
||||
// ... 其他服务类型
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 错误处理和兼容性
|
||||
|
||||
#### 解决冲突
|
||||
- 修复了与现有 `url_checker.go` 的常量冲突
|
||||
- 重命名了重复的函数和常量
|
||||
- 保持了向后兼容性
|
||||
|
||||
#### 错误处理
|
||||
- 统一的错误返回格式 `*TransferResult`
|
||||
- 详细的错误信息和状态码
|
||||
- 支持重试机制和超时控制
|
||||
|
||||
### 7. 测试验证
|
||||
|
||||
#### 单元测试
|
||||
- ✅ 服务类型识别测试
|
||||
- ✅ 分享ID提取测试
|
||||
- ✅ 工厂创建测试
|
||||
- ✅ 服务类型字符串转换测试
|
||||
|
||||
#### 编译测试
|
||||
- ✅ 项目编译成功
|
||||
- ✅ 无语法错误
|
||||
- ✅ 无依赖冲突
|
||||
|
||||
### 8. 扩展性
|
||||
|
||||
#### 添加新服务
|
||||
要添加新的网盘服务,只需要:
|
||||
|
||||
1. 创建新的服务实现文件(如 `utils/new_pan.go`)
|
||||
2. 实现 `PanService` 接口
|
||||
3. 在工厂中添加新的服务类型
|
||||
4. 更新URL模式匹配
|
||||
|
||||
#### 配置管理
|
||||
- 支持通过 `PanConfig` 进行灵活配置
|
||||
- 可以轻松添加新的配置项
|
||||
- 支持环境变量和配置文件
|
||||
|
||||
### 9. 性能优化
|
||||
|
||||
#### HTTP客户端优化
|
||||
- 连接池管理
|
||||
- 超时控制
|
||||
- 重试机制
|
||||
- 请求头缓存
|
||||
|
||||
#### 内存管理
|
||||
- 合理的对象生命周期
|
||||
- 避免内存泄漏
|
||||
- 高效的字符串处理
|
||||
|
||||
### 10. 使用建议
|
||||
|
||||
#### 生产环境使用
|
||||
1. 配置适当的超时时间
|
||||
2. 实现token缓存机制
|
||||
3. 添加监控和日志
|
||||
4. 配置错误告警
|
||||
|
||||
#### 开发环境使用
|
||||
1. 使用测试用例验证功能
|
||||
2. 模拟网络异常情况
|
||||
3. 测试不同URL格式
|
||||
4. 验证错误处理逻辑
|
||||
|
||||
## 总结
|
||||
|
||||
这个工厂模式实现具有以下优势:
|
||||
|
||||
1. **优雅的设计** - 使用工厂模式和策略模式,代码结构清晰
|
||||
2. **易于扩展** - 可以轻松添加新的网盘服务支持
|
||||
3. **统一接口** - 所有服务使用相同的接口,便于维护
|
||||
4. **错误处理** - 完善的错误处理和重试机制
|
||||
5. **测试覆盖** - 完整的单元测试确保代码质量
|
||||
6. **向后兼容** - 不影响现有功能,平滑集成
|
||||
|
||||
这个实现为您的网盘资源管理系统提供了一个强大、灵活、可扩展的基础架构。
|
||||
@@ -1,284 +0,0 @@
|
||||
# 网盘工厂模式使用说明
|
||||
|
||||
## 概述
|
||||
|
||||
本项目实现了一个优雅的网盘服务工厂模式,用于处理不同网盘平台的资源转存和文件管理。该设计采用了工厂模式和策略模式,使得代码结构清晰、易于扩展。
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 核心组件
|
||||
|
||||
1. **PanFactory** - 网盘工厂,负责创建对应的网盘服务实例
|
||||
2. **PanService** - 网盘服务接口,定义所有网盘服务必须实现的方法
|
||||
3. **BasePanService** - 基础网盘服务,提供通用的HTTP请求和配置管理
|
||||
4. **具体实现类** - 各网盘平台的具体实现
|
||||
|
||||
### 支持的服务类型
|
||||
|
||||
- **Quark** - 夸克网盘
|
||||
- **Alipan** - 阿里云盘
|
||||
- **BaiduPan** - 百度网盘(基础框架)
|
||||
- **UC** - UC网盘(基础框架)
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 基本使用
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"res_db/utils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 创建工厂实例
|
||||
factory := utils.NewPanFactory()
|
||||
|
||||
// 准备配置
|
||||
config := &utils.PanConfig{
|
||||
URL: "https://pan.quark.cn/s/123456789",
|
||||
Code: "",
|
||||
IsType: 0, // 0: 转存并分享后的资源信息, 1: 直接获取资源信息
|
||||
ExpiredType: 1, // 1: 分享永久, 2: 临时
|
||||
AdFid: "",
|
||||
Stoken: "",
|
||||
}
|
||||
|
||||
// 创建对应的网盘服务
|
||||
panService, err := factory.CreatePanService(config.URL, config)
|
||||
if err != nil {
|
||||
log.Fatalf("创建网盘服务失败: %v", err)
|
||||
}
|
||||
|
||||
// 提取分享ID
|
||||
shareID, serviceType := utils.ExtractShareId(config.URL)
|
||||
if serviceType == utils.NotFound {
|
||||
log.Fatal("不支持的链接格式")
|
||||
}
|
||||
|
||||
// 执行转存
|
||||
result, err := panService.Transfer(shareID)
|
||||
if err != nil {
|
||||
log.Fatalf("转存失败: %v", err)
|
||||
}
|
||||
|
||||
if result.Success {
|
||||
log.Printf("转存成功: %s", result.Message)
|
||||
// 处理转存结果
|
||||
if resultData, ok := result.Data.(map[string]interface{}); ok {
|
||||
title := resultData["title"].(string)
|
||||
shareURL := resultData["shareUrl"].(string)
|
||||
log.Printf("标题: %s", title)
|
||||
log.Printf("分享链接: %s", shareURL)
|
||||
}
|
||||
} else {
|
||||
log.Printf("转存失败: %s", result.Message)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 在定时任务中使用
|
||||
|
||||
在 `utils/scheduler.go` 的 `convertReadyResourceToResource` 函数中已经集成了工厂模式:
|
||||
|
||||
```go
|
||||
func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyResource) error {
|
||||
// 使用工厂模式创建对应的网盘服务
|
||||
factory := NewPanFactory()
|
||||
config := &PanConfig{
|
||||
URL: readyResource.URL,
|
||||
Code: "",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
AdFid: "",
|
||||
Stoken: "",
|
||||
}
|
||||
|
||||
panService, err := factory.CreatePanService(readyResource.URL, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 根据服务类型进行不同处理
|
||||
shareID, serviceType := ExtractShareId(readyResource.URL)
|
||||
switch serviceType {
|
||||
case Quark:
|
||||
// 夸克网盘处理逻辑
|
||||
case Alipan:
|
||||
// 阿里云盘处理逻辑
|
||||
// ... 其他服务类型
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## 扩展新的网盘服务
|
||||
|
||||
### 1. 创建新的服务实现
|
||||
|
||||
```go
|
||||
// 在 utils/ 目录下创建新文件,例如 new_pan.go
|
||||
package utils
|
||||
|
||||
// NewPanService 新网盘服务
|
||||
type NewPanService struct {
|
||||
*BasePanService
|
||||
}
|
||||
|
||||
// NewNewPanService 创建新网盘服务
|
||||
func NewNewPanService(config *PanConfig) *NewPanService {
|
||||
service := &NewPanService{
|
||||
BasePanService: NewBasePanService(config),
|
||||
}
|
||||
|
||||
// 设置请求头
|
||||
service.SetHeaders(map[string]string{
|
||||
"User-Agent": "Mozilla/5.0 ...",
|
||||
// 其他必要的请求头
|
||||
})
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
// GetServiceType 获取服务类型
|
||||
func (n *NewPanService) GetServiceType() ServiceType {
|
||||
return NewPan // 需要在 ServiceType 中定义新的类型
|
||||
}
|
||||
|
||||
// Transfer 转存分享链接
|
||||
func (n *NewPanService) Transfer(shareID string) (*TransferResult, error) {
|
||||
// 实现转存逻辑
|
||||
return SuccessResult("转存成功", data), nil
|
||||
}
|
||||
|
||||
// GetFiles 获取文件列表
|
||||
func (n *NewPanService) GetFiles(pdirFid string) (*TransferResult, error) {
|
||||
// 实现文件列表获取逻辑
|
||||
return SuccessResult("获取成功", files), nil
|
||||
}
|
||||
|
||||
// DeleteFiles 删除文件
|
||||
func (n *NewPanService) DeleteFiles(fileList []string) (*TransferResult, error) {
|
||||
// 实现文件删除逻辑
|
||||
return SuccessResult("删除成功", nil), nil
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 更新工厂
|
||||
|
||||
在 `utils/pan_factory.go` 中添加新的服务类型:
|
||||
|
||||
```go
|
||||
const (
|
||||
Quark ServiceType = iota
|
||||
Alipan
|
||||
BaiduPan
|
||||
UC
|
||||
NewPan // 添加新的服务类型
|
||||
NotFound
|
||||
)
|
||||
|
||||
// 在 String 方法中添加
|
||||
func (s ServiceType) String() string {
|
||||
switch s {
|
||||
// ... 现有case
|
||||
case NewPan:
|
||||
return "newpan"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// 在 CreatePanService 方法中添加
|
||||
func (f *PanFactory) CreatePanService(url string, config *PanConfig) (PanService, error) {
|
||||
serviceType := ExtractServiceType(url)
|
||||
|
||||
switch serviceType {
|
||||
// ... 现有case
|
||||
case NewPan:
|
||||
return NewNewPanService(config), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的服务类型: %s", url)
|
||||
}
|
||||
}
|
||||
|
||||
// 在 ExtractServiceType 方法中添加URL模式
|
||||
func ExtractServiceType(url string) ServiceType {
|
||||
url = strings.ToLower(url)
|
||||
|
||||
patterns := map[string]ServiceType{
|
||||
// ... 现有模式
|
||||
"newpan.example.com": NewPan,
|
||||
}
|
||||
|
||||
for pattern, serviceType := range patterns {
|
||||
if strings.Contains(url, pattern) {
|
||||
return serviceType
|
||||
}
|
||||
}
|
||||
|
||||
return NotFound
|
||||
}
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
### PanConfig 配置项
|
||||
|
||||
- **URL** - 分享链接地址
|
||||
- **Code** - 分享码(如果需要)
|
||||
- **IsType** - 处理类型:0=转存并分享后的资源信息,1=直接获取资源信息
|
||||
- **ExpiredType** - 有效期类型:1=分享永久,2=临时
|
||||
- **AdFid** - 夸克专用,分享时带上这个文件的fid
|
||||
- **Stoken** - 夸克专用,分享token
|
||||
|
||||
### 环境配置
|
||||
|
||||
某些网盘服务可能需要特定的配置,如:
|
||||
|
||||
- **夸克网盘** - 需要配置cookie
|
||||
- **阿里云盘** - 需要配置access_token
|
||||
- **百度网盘** - 需要配置相关认证信息
|
||||
|
||||
## 错误处理
|
||||
|
||||
所有服务方法都返回 `*TransferResult` 和 `error`:
|
||||
|
||||
```go
|
||||
type TransferResult struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
ShareURL string `json:"shareUrl,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Fid string `json:"fid,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
运行测试:
|
||||
|
||||
```bash
|
||||
cd utils
|
||||
go test -v
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **并发安全** - 每个服务实例都是独立的,可以安全地在多个goroutine中使用
|
||||
2. **错误重试** - 基础服务提供了带重试的HTTP请求方法
|
||||
3. **资源管理** - 记得及时清理不需要的资源
|
||||
4. **配置管理** - 敏感配置(如token)应该通过环境变量或配置文件管理
|
||||
|
||||
## 性能优化
|
||||
|
||||
1. **连接池** - 基础服务使用HTTP客户端连接池
|
||||
2. **超时控制** - 设置了合理的请求超时时间
|
||||
3. **重试机制** - 提供了可配置的重试机制
|
||||
4. **缓存** - 可以实现token缓存等优化
|
||||
|
||||
这个工厂模式设计使得代码具有良好的可维护性和可扩展性,可以轻松添加新的网盘服务支持。
|
||||
@@ -1,212 +0,0 @@
|
||||
# 网盘服务单例模式分析
|
||||
|
||||
## 为什么需要服务单例模式?
|
||||
|
||||
### 1. 当前问题分析
|
||||
|
||||
在原来的实现中,每次调用 `CreatePanService` 都会创建新的服务实例:
|
||||
|
||||
```go
|
||||
// 原来的实现
|
||||
func (f *PanFactory) CreatePanService(url string, config *PanConfig) (PanService, error) {
|
||||
serviceType := ExtractServiceType(url)
|
||||
|
||||
switch serviceType {
|
||||
case Quark:
|
||||
return NewQuarkPanService(config), nil // 每次都创建新实例
|
||||
case Alipan:
|
||||
return NewAlipanService(config), nil // 每次都创建新实例
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 服务实例的开销分析
|
||||
|
||||
#### HTTP客户端开销
|
||||
```go
|
||||
// 每个服务实例都包含一个HTTP客户端
|
||||
httpClient: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
```
|
||||
- **内存占用**:每个HTTP客户端约占用几KB内存
|
||||
- **创建时间**:毫秒级别
|
||||
- **连接池**:每个客户端都有自己的连接池
|
||||
|
||||
#### 请求头设置开销
|
||||
```go
|
||||
// 每个服务实例都设置相同的请求头
|
||||
headers: map[string]string{
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"User-Agent": "Mozilla/5.0 ...",
|
||||
// ... 更多请求头
|
||||
}
|
||||
```
|
||||
- **内存占用**:每个map约占用几百字节
|
||||
- **初始化时间**:微秒级别
|
||||
|
||||
#### 配置对象开销
|
||||
```go
|
||||
// 每次创建服务都传递配置
|
||||
config := &PanConfig{
|
||||
URL: readyResource.URL,
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 单例模式的优势
|
||||
|
||||
#### 内存优化
|
||||
- **避免重复创建HTTP客户端**:每个服务类型只有一个HTTP客户端
|
||||
- **减少请求头重复设置**:请求头只设置一次
|
||||
- **降低内存占用**:特别是在处理大量资源时
|
||||
|
||||
#### 性能优化
|
||||
- **减少初始化开销**:避免重复的HTTP客户端创建
|
||||
- **提高响应速度**:复用已建立的连接
|
||||
- **减少GC压力**:减少对象创建和销毁
|
||||
|
||||
#### 资源管理
|
||||
- **连接池复用**:HTTP连接池可以更好地复用
|
||||
- **配置统一管理**:可以在服务实例中维护全局配置
|
||||
- **状态一致性**:避免多个实例状态不一致
|
||||
|
||||
### 4. 实现细节
|
||||
|
||||
#### 线程安全的单例实现
|
||||
```go
|
||||
// 夸克网盘服务单例
|
||||
var (
|
||||
quarkInstance *QuarkPanService
|
||||
quarkOnce sync.Once
|
||||
)
|
||||
|
||||
func NewQuarkPanService(config *PanConfig) *QuarkPanService {
|
||||
quarkOnce.Do(func() {
|
||||
quarkInstance = &QuarkPanService{
|
||||
BasePanService: NewBasePanService(config),
|
||||
}
|
||||
// 设置固定的请求头
|
||||
quarkInstance.SetHeaders(map[string]string{...})
|
||||
})
|
||||
|
||||
// 更新配置(线程安全)
|
||||
quarkInstance.UpdateConfig(config)
|
||||
|
||||
return quarkInstance
|
||||
}
|
||||
```
|
||||
|
||||
#### 配置更新机制
|
||||
```go
|
||||
// 线程安全的配置更新
|
||||
func (q *QuarkPanService) UpdateConfig(config *PanConfig) {
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
|
||||
q.configMutex.Lock()
|
||||
defer q.configMutex.Unlock()
|
||||
|
||||
q.config = config
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 性能测试结果
|
||||
|
||||
#### 服务创建性能对比
|
||||
```bash
|
||||
# 单例模式服务创建
|
||||
BenchmarkQuarkServiceCreation-8 100000000 2.1 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAlipanServiceCreation-8 100000000 2.3 ns/op 0 B/op 0 allocs/op
|
||||
|
||||
# 工厂方法获取服务
|
||||
BenchmarkFactoryGetQuarkService-8 100000000 2.5 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFactoryGetAlipanService-8 100000000 2.7 ns/op 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
#### 内存使用对比
|
||||
- **单例模式**:每个服务类型固定内存占用
|
||||
- **普通创建**:每次创建都增加内存占用
|
||||
|
||||
### 6. 使用建议
|
||||
|
||||
#### 推荐使用方式
|
||||
```go
|
||||
// ✅ 推荐:使用工厂获取单例服务
|
||||
factory := panutils.GetInstance()
|
||||
quarkService := factory.GetQuarkService(config)
|
||||
alipanService := factory.GetAlipanService(config)
|
||||
|
||||
// ✅ 推荐:直接使用单例服务
|
||||
quarkService := panutils.NewQuarkPanService(config)
|
||||
alipanService := panutils.NewAlipanService(config)
|
||||
```
|
||||
|
||||
#### 在定时任务中的使用
|
||||
```go
|
||||
func (s *Scheduler) processReadyResources() {
|
||||
factory := panutils.GetInstance()
|
||||
|
||||
for _, readyResource := range readyResources {
|
||||
// 根据URL类型获取对应的单例服务
|
||||
serviceType := panutils.ExtractServiceType(readyResource.URL)
|
||||
config := &panutils.PanConfig{...}
|
||||
|
||||
var panService panutils.PanService
|
||||
switch serviceType {
|
||||
case panutils.Quark:
|
||||
panService = factory.GetQuarkService(config)
|
||||
case panutils.Alipan:
|
||||
panService = factory.GetAlipanService(config)
|
||||
}
|
||||
|
||||
// 使用服务处理资源
|
||||
result, err := panService.Transfer(shareID)
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 扩展性考虑
|
||||
|
||||
#### 未来可能的扩展
|
||||
1. **服务实例缓存**:缓存已创建的服务实例
|
||||
2. **配置缓存**:缓存常用配置
|
||||
3. **连接池优化**:优化HTTP连接池管理
|
||||
4. **监控和统计**:在服务中添加使用统计
|
||||
|
||||
#### 单例模式的适用性
|
||||
- ✅ **适合**:无状态服务、HTTP客户端、配置管理
|
||||
- ❌ **不适合**:有状态的对象、需要隔离的实例
|
||||
|
||||
### 8. 与工厂模式的结合
|
||||
|
||||
#### 双重单例模式
|
||||
```go
|
||||
// 工厂单例 + 服务单例
|
||||
factory := panutils.GetInstance() // 工厂单例
|
||||
quarkService := factory.GetQuarkService(config) // 服务单例
|
||||
```
|
||||
|
||||
#### 优势
|
||||
1. **工厂单例**:避免重复创建工厂实例
|
||||
2. **服务单例**:避免重复创建服务实例
|
||||
3. **配置更新**:支持动态配置更新
|
||||
4. **线程安全**:保证并发环境下的正确性
|
||||
|
||||
### 9. 总结
|
||||
|
||||
对于网盘服务使用单例模式是**强烈推荐的做法**,原因:
|
||||
|
||||
1. **显著性能提升**:减少HTTP客户端创建开销
|
||||
2. **内存优化**:降低内存占用和GC压力
|
||||
3. **资源复用**:HTTP连接池和请求头可以复用
|
||||
4. **配置灵活**:支持动态配置更新
|
||||
5. **线程安全**:保证并发环境下的正确性
|
||||
6. **扩展性好**:为未来功能扩展提供基础
|
||||
|
||||
特别是在高频调用的场景下(如定时任务处理大量资源),服务单例模式能带来显著的性能提升和资源优化。
|
||||
@@ -1,111 +0,0 @@
|
||||
package pan
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
// PanExample 网盘工厂模式使用示例
|
||||
func PanExample() {
|
||||
// 创建工厂实例
|
||||
factory := NewPanFactory()
|
||||
|
||||
// 示例URL
|
||||
urls := []string{
|
||||
"https://pan.quark.cn/s/123456789",
|
||||
"https://www.alipan.com/s/abcdef123",
|
||||
"https://pan.baidu.com/s/xyz789",
|
||||
"https://drive.uc.cn/s/uc123456",
|
||||
}
|
||||
|
||||
for _, url := range urls {
|
||||
log.Printf("处理URL: %s", url)
|
||||
|
||||
// 创建配置
|
||||
config := &PanConfig{
|
||||
URL: url,
|
||||
Code: "",
|
||||
IsType: 0, // 转存并分享后的资源信息
|
||||
ExpiredType: 1, // 永久分享
|
||||
AdFid: "",
|
||||
Stoken: "",
|
||||
}
|
||||
|
||||
// 使用工厂创建对应的网盘服务
|
||||
panService, err := factory.CreatePanService(url, config)
|
||||
if err != nil {
|
||||
log.Printf("创建网盘服务失败: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取服务类型
|
||||
serviceType := panService.GetServiceType()
|
||||
log.Printf("检测到服务类型: %s", serviceType.String())
|
||||
|
||||
// 提取分享ID
|
||||
shareID, extractedType := ExtractShareId(url)
|
||||
if extractedType == NotFound {
|
||||
log.Printf("不支持的链接格式: %s", url)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("分享ID: %s", shareID)
|
||||
|
||||
// 根据服务类型进行不同处理
|
||||
switch serviceType {
|
||||
case Quark:
|
||||
log.Println("使用夸克网盘服务")
|
||||
// 这里可以调用 panService.Transfer(shareID)
|
||||
|
||||
case Alipan:
|
||||
log.Println("使用阿里云盘服务")
|
||||
// 这里可以调用 panService.Transfer(shareID)
|
||||
|
||||
case BaiduPan:
|
||||
log.Println("使用百度网盘服务")
|
||||
// 这里可以调用 panService.Transfer(shareID)
|
||||
|
||||
case UC:
|
||||
log.Println("使用UC网盘服务")
|
||||
// 这里可以调用 panService.Transfer(shareID)
|
||||
|
||||
default:
|
||||
log.Printf("暂不支持的服务类型: %s", serviceType.String())
|
||||
}
|
||||
|
||||
log.Println("---")
|
||||
}
|
||||
}
|
||||
|
||||
// TransferExample 转存示例
|
||||
func TransferExample(url string) (*TransferResult, error) {
|
||||
factory := NewPanFactory()
|
||||
|
||||
config := &PanConfig{
|
||||
URL: url,
|
||||
Code: "",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
AdFid: "",
|
||||
Stoken: "",
|
||||
}
|
||||
|
||||
// 创建服务
|
||||
panService, err := factory.CreatePanService(url, config)
|
||||
if err != nil {
|
||||
return ErrorResult("创建网盘服务失败"), err
|
||||
}
|
||||
|
||||
// 提取分享ID
|
||||
shareID, serviceType := ExtractShareId(url)
|
||||
if serviceType == NotFound {
|
||||
return ErrorResult("不支持的链接格式"), nil
|
||||
}
|
||||
|
||||
// 执行转存
|
||||
result, err := panService.Transfer(shareID)
|
||||
if err != nil {
|
||||
return ErrorResult("转存失败"), err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
package pan
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExtractServiceType(t *testing.T) {
|
||||
tests := []struct {
|
||||
url string
|
||||
expected ServiceType
|
||||
description string
|
||||
}{
|
||||
{
|
||||
url: "https://pan.quark.cn/s/123456",
|
||||
expected: Quark,
|
||||
description: "夸克网盘",
|
||||
},
|
||||
{
|
||||
url: "https://www.alipan.com/s/123456",
|
||||
expected: Alipan,
|
||||
description: "阿里云盘",
|
||||
},
|
||||
{
|
||||
url: "https://www.aliyundrive.com/s/123456",
|
||||
expected: Alipan,
|
||||
description: "阿里云盘别名",
|
||||
},
|
||||
{
|
||||
url: "https://pan.baidu.com/s/123456",
|
||||
expected: BaiduPan,
|
||||
description: "百度网盘",
|
||||
},
|
||||
{
|
||||
url: "https://drive.uc.cn/s/123456",
|
||||
expected: UC,
|
||||
description: "UC网盘",
|
||||
},
|
||||
{
|
||||
url: "https://fast.uc.cn/s/123456",
|
||||
expected: UC,
|
||||
description: "UC网盘别名",
|
||||
},
|
||||
{
|
||||
url: "https://example.com/s/123456",
|
||||
expected: NotFound,
|
||||
description: "不支持的链接",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
result := ExtractServiceType(test.url)
|
||||
if result != test.expected {
|
||||
t.Errorf("期望 %v, 实际 %v", test.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractShareId(t *testing.T) {
|
||||
tests := []struct {
|
||||
url string
|
||||
expectedID string
|
||||
expectedType ServiceType
|
||||
description string
|
||||
}{
|
||||
{
|
||||
url: "https://pan.quark.cn/s/123456",
|
||||
expectedID: "123456",
|
||||
expectedType: Quark,
|
||||
description: "夸克网盘",
|
||||
},
|
||||
{
|
||||
url: "https://www.alipan.com/s/123456?entry=abc",
|
||||
expectedID: "123456",
|
||||
expectedType: Alipan,
|
||||
description: "阿里云盘带参数",
|
||||
},
|
||||
{
|
||||
url: "https://pan.baidu.com/s/123456#section",
|
||||
expectedID: "123456",
|
||||
expectedType: BaiduPan,
|
||||
description: "百度网盘带锚点",
|
||||
},
|
||||
{
|
||||
url: "https://example.com/s/123456",
|
||||
expectedID: "123456",
|
||||
expectedType: NotFound,
|
||||
description: "不支持的链接",
|
||||
},
|
||||
{
|
||||
url: "https://pan.quark.cn/other/123456",
|
||||
expectedID: "",
|
||||
expectedType: NotFound,
|
||||
description: "无效格式",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
shareID, serviceType := ExtractShareId(test.url)
|
||||
if shareID != test.expectedID {
|
||||
t.Errorf("期望分享ID %s, 实际 %s", test.expectedID, shareID)
|
||||
}
|
||||
if serviceType != test.expectedType {
|
||||
t.Errorf("期望服务类型 %v, 实际 %v", test.expectedType, serviceType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPanFactory(t *testing.T) {
|
||||
factory := NewPanFactory()
|
||||
|
||||
tests := []struct {
|
||||
url string
|
||||
config *PanConfig
|
||||
shouldError bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
url: "https://pan.quark.cn/s/123456",
|
||||
config: &PanConfig{
|
||||
URL: "https://pan.quark.cn/s/123456",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
},
|
||||
shouldError: false,
|
||||
description: "夸克网盘",
|
||||
},
|
||||
{
|
||||
url: "https://www.alipan.com/s/123456",
|
||||
config: &PanConfig{
|
||||
URL: "https://www.alipan.com/s/123456",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
},
|
||||
shouldError: false,
|
||||
description: "阿里云盘",
|
||||
},
|
||||
{
|
||||
url: "https://example.com/s/123456",
|
||||
config: &PanConfig{
|
||||
URL: "https://example.com/s/123456",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
},
|
||||
shouldError: true,
|
||||
description: "不支持的链接",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
service, err := factory.CreatePanService(test.url, test.config)
|
||||
|
||||
if test.shouldError {
|
||||
if err == nil {
|
||||
t.Error("期望错误,但没有错误")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("不期望错误,但得到错误: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if service == nil {
|
||||
t.Error("服务不应该为nil")
|
||||
return
|
||||
}
|
||||
|
||||
// 测试服务类型
|
||||
serviceType := service.GetServiceType()
|
||||
expectedType := ExtractServiceType(test.url)
|
||||
if serviceType != expectedType {
|
||||
t.Errorf("期望服务类型 %v, 实际 %v", expectedType, serviceType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceTypeString(t *testing.T) {
|
||||
tests := []struct {
|
||||
serviceType ServiceType
|
||||
expected string
|
||||
}{
|
||||
{Quark, "quark"},
|
||||
{Alipan, "alipan"},
|
||||
{BaiduPan, "baidu"},
|
||||
{UC, "uc"},
|
||||
{NotFound, "unknown"},
|
||||
{ServiceType(999), "unknown"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.expected, func(t *testing.T) {
|
||||
result := test.serviceType.String()
|
||||
if result != test.expected {
|
||||
t.Errorf("期望 %s, 实际 %s", test.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package pan
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestQuarkGetUserInfo 测试夸克网盘GetUserInfo功能
|
||||
func TestQuarkGetUserInfo(t *testing.T) {
|
||||
// 创建夸克网盘服务实例
|
||||
service := NewQuarkPanService(&PanConfig{})
|
||||
|
||||
// 测试无效cookie
|
||||
_, err := service.GetUserInfo("invalid_cookie")
|
||||
if err == nil {
|
||||
t.Error("期望返回错误,但没有返回")
|
||||
} else {
|
||||
t.Logf("正确返回错误: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestQuarkGetUserInfoWithValidCookie 测试有效cookie的情况
|
||||
func TestQuarkGetUserInfoWithValidCookie(t *testing.T) {
|
||||
// 创建夸克网盘服务实例
|
||||
service := NewQuarkPanService(&PanConfig{})
|
||||
|
||||
// 使用测试cookie(需要替换为有效的cookie)
|
||||
testCookie := "your_test_cookie_here"
|
||||
if testCookie == "your_test_cookie_here" {
|
||||
t.Skip("跳过测试,需要提供有效的cookie")
|
||||
}
|
||||
|
||||
userInfo, err := service.GetUserInfo(testCookie)
|
||||
if err != nil {
|
||||
t.Logf("获取用户信息失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证返回的用户信息
|
||||
if userInfo == nil {
|
||||
t.Fatal("用户信息为空")
|
||||
}
|
||||
|
||||
t.Logf("用户名: %s", userInfo.Username)
|
||||
t.Logf("VIP状态: %t", userInfo.VIPStatus)
|
||||
t.Logf("容量信息: %s", userInfo.Capacity)
|
||||
t.Logf("服务类型: %s", userInfo.ServiceType)
|
||||
|
||||
// 基本验证
|
||||
if userInfo.ServiceType != "quark" {
|
||||
t.Errorf("服务类型不匹配,期望: quark, 实际: %s", userInfo.ServiceType)
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
package pan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQuarkServiceSingleton(t *testing.T) {
|
||||
// 测试夸克服务单例模式
|
||||
config1 := &PanConfig{
|
||||
URL: "https://pan.quark.cn/s/123456",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
}
|
||||
|
||||
config2 := &PanConfig{
|
||||
URL: "https://pan.quark.cn/s/789012",
|
||||
IsType: 1,
|
||||
ExpiredType: 2,
|
||||
}
|
||||
|
||||
service1 := NewQuarkPanService(config1)
|
||||
service2 := NewQuarkPanService(config2)
|
||||
|
||||
// 应该返回相同的实例
|
||||
if service1 != service2 {
|
||||
t.Error("QuarkPanService 应该返回相同的单例实例")
|
||||
}
|
||||
|
||||
// 验证服务类型
|
||||
if service1.GetServiceType() != Quark {
|
||||
t.Error("服务类型应该是 Quark")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlipanServiceSingleton(t *testing.T) {
|
||||
// 测试阿里云盘服务单例模式
|
||||
config1 := &PanConfig{
|
||||
URL: "https://www.alipan.com/s/123456",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
}
|
||||
|
||||
config2 := &PanConfig{
|
||||
URL: "https://www.alipan.com/s/789012",
|
||||
IsType: 1,
|
||||
ExpiredType: 2,
|
||||
}
|
||||
|
||||
service1 := NewAlipanService(config1)
|
||||
service2 := NewAlipanService(config2)
|
||||
|
||||
// 应该返回相同的实例
|
||||
if service1 != service2 {
|
||||
t.Error("AlipanService 应该返回相同的单例实例")
|
||||
}
|
||||
|
||||
// 验证服务类型
|
||||
if service1.GetServiceType() != Alipan {
|
||||
t.Error("服务类型应该是 Alipan")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceConcurrency(t *testing.T) {
|
||||
// 测试并发情况下的服务单例
|
||||
done := make(chan bool, 10)
|
||||
quarkServices := make([]PanService, 10)
|
||||
alipanServices := make([]PanService, 10)
|
||||
|
||||
// 并发创建夸克服务
|
||||
for i := 0; i < 10; i++ {
|
||||
go func(index int) {
|
||||
config := &PanConfig{
|
||||
URL: fmt.Sprintf("https://pan.quark.cn/s/%d", index),
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
}
|
||||
service := NewQuarkPanService(config)
|
||||
quarkServices[index] = service
|
||||
done <- true
|
||||
}(i)
|
||||
}
|
||||
|
||||
// 并发创建阿里云盘服务
|
||||
for i := 0; i < 10; i++ {
|
||||
go func(index int) {
|
||||
config := &PanConfig{
|
||||
URL: fmt.Sprintf("https://www.alipan.com/s/%d", index),
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
}
|
||||
service := NewAlipanService(config)
|
||||
alipanServices[index] = service
|
||||
done <- true
|
||||
}(i)
|
||||
}
|
||||
|
||||
// 等待所有goroutine完成
|
||||
for i := 0; i < 20; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
// 验证夸克服务单例
|
||||
firstQuarkService := quarkServices[0]
|
||||
for i := 1; i < 10; i++ {
|
||||
if quarkServices[i] != firstQuarkService {
|
||||
t.Errorf("夸克服务并发测试失败:实例 %d 与第一个实例不同", i)
|
||||
}
|
||||
}
|
||||
|
||||
// 验证阿里云盘服务单例
|
||||
firstAlipanService := alipanServices[0]
|
||||
for i := 1; i < 10; i++ {
|
||||
if alipanServices[i] != firstAlipanService {
|
||||
t.Errorf("阿里云盘服务并发测试失败:实例 %d 与第一个实例不同", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkQuarkServiceCreation(b *testing.B) {
|
||||
// 测试夸克服务创建性能
|
||||
config := &PanConfig{
|
||||
URL: "https://pan.quark.cn/s/123456",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = NewQuarkPanService(config)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAlipanServiceCreation(b *testing.B) {
|
||||
// 测试阿里云盘服务创建性能
|
||||
config := &PanConfig{
|
||||
URL: "https://www.alipan.com/s/123456",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = NewAlipanService(config)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFactoryGetQuarkService(b *testing.B) {
|
||||
// 测试工厂获取夸克服务性能
|
||||
factory := GetInstance()
|
||||
config := &PanConfig{
|
||||
URL: "https://pan.quark.cn/s/123456",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = factory.GetQuarkService(config)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFactoryGetAlipanService(b *testing.B) {
|
||||
// 测试工厂获取阿里云盘服务性能
|
||||
factory := GetInstance()
|
||||
config := &PanConfig{
|
||||
URL: "https://www.alipan.com/s/123456",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = factory.GetAlipanService(config)
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package pan
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSingletonPattern(t *testing.T) {
|
||||
// 测试多次调用NewPanFactory()返回相同实例
|
||||
factory1 := NewPanFactory()
|
||||
factory2 := NewPanFactory()
|
||||
factory3 := GetInstance()
|
||||
|
||||
if factory1 != factory2 {
|
||||
t.Error("NewPanFactory() 应该返回相同的实例")
|
||||
}
|
||||
|
||||
if factory1 != factory3 {
|
||||
t.Error("GetInstance() 应该返回相同的实例")
|
||||
}
|
||||
|
||||
if factory2 != factory3 {
|
||||
t.Error("所有获取方法应该返回相同的实例")
|
||||
}
|
||||
|
||||
// 验证实例不为nil
|
||||
if factory1 == nil {
|
||||
t.Error("工厂实例不应该为nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSingletonConcurrency(t *testing.T) {
|
||||
// 测试并发情况下的单例模式
|
||||
done := make(chan bool, 10)
|
||||
instances := make([]*PanFactory, 10)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go func(index int) {
|
||||
instances[index] = GetInstance()
|
||||
done <- true
|
||||
}(i)
|
||||
}
|
||||
|
||||
// 等待所有goroutine完成
|
||||
for i := 0; i < 10; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
// 验证所有实例都是相同的
|
||||
firstInstance := instances[0]
|
||||
for i := 1; i < 10; i++ {
|
||||
if instances[i] != firstInstance {
|
||||
t.Errorf("并发测试失败:实例 %d 与第一个实例不同", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSingletonCreation(b *testing.B) {
|
||||
// 测试单例创建的性能
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = GetInstance()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNewPanFactory(b *testing.B) {
|
||||
// 测试NewPanFactory的性能
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = NewPanFactory()
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
# API 响应格式标准化
|
||||
|
||||
## 概述
|
||||
|
||||
为了统一API响应格式,提高前后端协作效率,所有API接口都使用标准化的响应格式。
|
||||
|
||||
## 标准响应格式
|
||||
|
||||
### 基础响应结构
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "操作成功",
|
||||
"data": {},
|
||||
"error": "",
|
||||
"pagination": {}
|
||||
}
|
||||
```
|
||||
|
||||
### 字段说明
|
||||
|
||||
- `success`: 布尔值,表示操作是否成功
|
||||
- `message`: 字符串,成功时的提示信息(可选)
|
||||
- `data`: 对象/数组,返回的数据内容(可选)
|
||||
- `error`: 字符串,错误信息(仅在失败时返回)
|
||||
- `pagination`: 对象,分页信息(仅在分页接口时返回)
|
||||
|
||||
## 分页响应格式
|
||||
|
||||
### 分页信息结构
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"page_size": 100,
|
||||
"total": 1002,
|
||||
"total_pages": 11
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 分页参数
|
||||
|
||||
- `page`: 当前页码(从1开始)
|
||||
- `page_size`: 每页条数
|
||||
- `total`: 总记录数
|
||||
- `total_pages`: 总页数
|
||||
|
||||
## 响应类型
|
||||
|
||||
### 1. 成功响应
|
||||
|
||||
```go
|
||||
// 普通成功响应
|
||||
SuccessResponse(c, data, "操作成功")
|
||||
|
||||
// 简单成功响应(无数据)
|
||||
SimpleSuccessResponse(c, "操作成功")
|
||||
|
||||
// 创建成功响应
|
||||
CreatedResponse(c, data, "创建成功")
|
||||
```
|
||||
|
||||
### 2. 错误响应
|
||||
|
||||
```go
|
||||
// 错误响应
|
||||
ErrorResponse(c, http.StatusBadRequest, "参数错误")
|
||||
```
|
||||
|
||||
### 3. 分页响应
|
||||
|
||||
```go
|
||||
// 分页响应
|
||||
PaginatedResponse(c, data, page, pageSize, total)
|
||||
```
|
||||
|
||||
## 接口示例
|
||||
|
||||
### 获取待处理资源列表
|
||||
|
||||
**请求:**
|
||||
```
|
||||
GET /api/ready-resources?page=1&page_size=100
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "示例资源",
|
||||
"url": "https://example.com",
|
||||
"create_time": "2024-01-01T00:00:00Z",
|
||||
"ip": "127.0.0.1"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"page_size": 100,
|
||||
"total": 1002,
|
||||
"total_pages": 11
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 创建待处理资源
|
||||
|
||||
**请求:**
|
||||
```
|
||||
POST /api/ready-resources
|
||||
{
|
||||
"title": "新资源",
|
||||
"url": "https://example.com"
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "待处理资源创建成功",
|
||||
"data": {
|
||||
"id": 1003
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应示例
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "参数错误:标题不能为空"
|
||||
}
|
||||
```
|
||||
|
||||
## 前端调用示例
|
||||
|
||||
### 获取分页数据
|
||||
|
||||
```typescript
|
||||
const response = await api.getReadyResources({
|
||||
page: 1,
|
||||
page_size: 100
|
||||
})
|
||||
|
||||
if (response.success) {
|
||||
const resources = response.data
|
||||
const pagination = response.pagination
|
||||
// 处理数据
|
||||
}
|
||||
```
|
||||
|
||||
### 处理错误
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const response = await api.createResource(data)
|
||||
if (response.success) {
|
||||
// 成功处理
|
||||
}
|
||||
} catch (error) {
|
||||
// 网络错误等
|
||||
}
|
||||
```
|
||||
|
||||
## 实施规范
|
||||
|
||||
1. **所有新接口**必须使用标准化响应格式
|
||||
2. **现有接口**逐步迁移到标准化格式
|
||||
3. **错误处理**统一使用ErrorResponse
|
||||
4. **分页接口**必须使用PaginatedResponse
|
||||
5. **前端调用**统一处理success字段
|
||||
|
||||
## 迁移计划
|
||||
|
||||
1. ✅ 待处理资源接口(ready-resources)
|
||||
2. 🔄 资源管理接口(resources)
|
||||
3. 🔄 分类管理接口(categories)
|
||||
4. 🔄 用户管理接口(users)
|
||||
5. 🔄 统计接口(stats)
|
||||
@@ -1,116 +0,0 @@
|
||||
# 用户认证系统
|
||||
|
||||
## 概述
|
||||
|
||||
本项目已成功集成了完整的用户认证系统,包括用户注册、登录、权限管理等功能。
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 1. 用户管理
|
||||
- **用户注册**: 支持新用户注册
|
||||
- **用户登录**: JWT令牌认证
|
||||
- **用户管理**: 管理员可以创建、编辑、删除用户
|
||||
- **角色管理**: 支持用户(user)和管理员(admin)角色
|
||||
- **状态管理**: 支持用户激活/禁用状态
|
||||
|
||||
### 2. 认证机制
|
||||
- **JWT令牌**: 使用JWT进行身份验证
|
||||
- **密码加密**: 使用bcrypt进行密码哈希
|
||||
- **中间件保护**: 路由级别的权限控制
|
||||
|
||||
### 3. 权限控制
|
||||
- **公开接口**: 资源查看、搜索等
|
||||
- **用户接口**: 个人资料查看
|
||||
- **管理员接口**: 所有管理功能需要管理员权限
|
||||
|
||||
## 数据库结构
|
||||
|
||||
### users表
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(100),
|
||||
role VARCHAR(20) DEFAULT 'user',
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
last_login TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
## API接口
|
||||
|
||||
### 认证接口
|
||||
- `POST /api/auth/login` - 用户登录
|
||||
- `POST /api/auth/register` - 用户注册
|
||||
- `GET /api/auth/profile` - 获取用户信息
|
||||
|
||||
### 用户管理接口(管理员)
|
||||
- `GET /api/users` - 获取用户列表
|
||||
- `POST /api/users` - 创建用户
|
||||
- `PUT /api/users/:id` - 更新用户
|
||||
- `DELETE /api/users/:id` - 删除用户
|
||||
|
||||
## 前端页面
|
||||
|
||||
### 登录页面 (`/login`)
|
||||
- 用户名/密码登录
|
||||
- 默认管理员账户: admin / password
|
||||
- 登录成功后跳转到管理页面
|
||||
|
||||
### 注册页面 (`/register`)
|
||||
- 新用户注册
|
||||
- 注册成功后跳转到登录页面
|
||||
|
||||
### 管理页面 (`/admin`)
|
||||
- 需要登录才能访问
|
||||
- 显示用户信息和退出登录按钮
|
||||
- 各种管理功能的入口
|
||||
|
||||
### 用户管理页面 (`/users`)
|
||||
- 仅管理员可访问
|
||||
- 用户列表展示
|
||||
- 创建、编辑、删除用户功能
|
||||
|
||||
## 中间件
|
||||
|
||||
### AuthMiddleware
|
||||
- 验证JWT令牌
|
||||
- 将用户信息存储到上下文中
|
||||
|
||||
### AdminMiddleware
|
||||
- 检查用户角色是否为管理员
|
||||
- 保护管理员专用接口
|
||||
|
||||
## 默认数据
|
||||
|
||||
系统启动时会自动创建默认管理员账户:
|
||||
- 用户名: admin
|
||||
- 密码: password
|
||||
- 角色: admin
|
||||
- 邮箱: admin@example.com
|
||||
|
||||
## 安全特性
|
||||
|
||||
1. **密码加密**: 使用bcrypt进行密码哈希
|
||||
2. **JWT令牌**: 24小时有效期的JWT令牌
|
||||
3. **角色权限**: 基于角色的访问控制
|
||||
4. **输入验证**: 服务器端数据验证
|
||||
5. **SQL注入防护**: 使用GORM进行参数化查询
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. 启动服务器后,访问 `/login` 页面
|
||||
2. 使用默认管理员账户登录: admin / password
|
||||
3. 登录成功后可以访问管理功能
|
||||
4. 在用户管理页面可以创建新用户或修改现有用户
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **后端**: Go + Gin + GORM + JWT
|
||||
- **前端**: Nuxt.js + Tailwind CSS
|
||||
- **数据库**: PostgreSQL
|
||||
- **认证**: JWT + bcrypt
|
||||
@@ -1,269 +0,0 @@
|
||||
# 待处理资源自动处理功能修复说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
在管理后台开启"待处理资源自动处理"功能后,系统没有自动开始任务,并且出现外键约束错误:
|
||||
```
|
||||
ERROR: insert or update on table "resources" violates foreign key constraint "fk_resources_pan" (SQLSTATE 23503)
|
||||
```
|
||||
|
||||
## 问题原因
|
||||
|
||||
1. **UpdateSchedulerStatus方法参数不完整**:`utils/global_scheduler.go` 中的 `UpdateSchedulerStatus` 方法只接收了 `autoFetchHotDramaEnabled` 参数,没有处理 `autoProcessReadyResources` 参数。
|
||||
|
||||
2. **UpdateSystemConfig调用参数不完整**:`handlers/system_config_handler.go` 中的 `UpdateSystemConfig` 函数只传递了热播剧相关的参数,没有传递待处理资源相关的参数。
|
||||
|
||||
3. **调度器间隔时间硬编码**:`utils/scheduler.go` 中的 `StartReadyResourceScheduler` 方法使用了硬编码的5分钟间隔,而不是使用系统配置中的 `AutoProcessInterval`。
|
||||
|
||||
4. **平台匹配机制优化**:使用 `serviceType` 来匹配平台,并添加平台映射缓存提高性能。
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 修复 UpdateSchedulerStatus 方法
|
||||
|
||||
**文件**: `utils/global_scheduler.go`
|
||||
|
||||
**修改前**:
|
||||
```go
|
||||
func (gs *GlobalScheduler) UpdateSchedulerStatus(autoFetchHotDramaEnabled bool) {
|
||||
// 只处理热播剧功能
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```go
|
||||
func (gs *GlobalScheduler) UpdateSchedulerStatus(autoFetchHotDramaEnabled bool, autoProcessReadyResources bool) {
|
||||
// 处理热播剧自动拉取功能
|
||||
if autoFetchHotDramaEnabled {
|
||||
if !gs.scheduler.IsRunning() {
|
||||
log.Println("系统配置启用自动拉取热播剧,启动定时任务")
|
||||
gs.scheduler.StartHotDramaScheduler()
|
||||
}
|
||||
} else {
|
||||
if gs.scheduler.IsRunning() {
|
||||
log.Println("系统配置禁用自动拉取热播剧,停止定时任务")
|
||||
gs.scheduler.StopHotDramaScheduler()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理待处理资源自动处理功能
|
||||
if autoProcessReadyResources {
|
||||
if !gs.scheduler.IsReadyResourceRunning() {
|
||||
log.Println("系统配置启用自动处理待处理资源,启动定时任务")
|
||||
gs.scheduler.StartReadyResourceScheduler()
|
||||
}
|
||||
} else {
|
||||
if gs.scheduler.IsReadyResourceRunning() {
|
||||
log.Println("系统配置禁用自动处理待处理资源,停止定时任务")
|
||||
gs.scheduler.StopReadyResourceScheduler()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 修复 UpdateSystemConfig 函数
|
||||
|
||||
**文件**: `handlers/system_config_handler.go`
|
||||
|
||||
**修改前**:
|
||||
```go
|
||||
scheduler.UpdateSchedulerStatus(req.AutoFetchHotDramaEnabled)
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```go
|
||||
scheduler.UpdateSchedulerStatus(req.AutoFetchHotDramaEnabled, req.AutoProcessReadyResources)
|
||||
```
|
||||
|
||||
### 3. 修复调度器间隔时间配置
|
||||
|
||||
**文件**: `utils/scheduler.go`
|
||||
|
||||
**修改前**:
|
||||
```go
|
||||
ticker := time.NewTicker(5 * time.Minute) // 每5分钟检查一次
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```go
|
||||
// 获取系统配置中的间隔时间
|
||||
config, err := s.systemConfigRepo.GetOrCreateDefault()
|
||||
interval := 5 * time.Minute // 默认5分钟
|
||||
if err == nil && config.AutoProcessInterval > 0 {
|
||||
interval = time.Duration(config.AutoProcessInterval) * time.Minute
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
log.Printf("待处理资源自动处理任务已启动,间隔时间: %v", interval)
|
||||
```
|
||||
|
||||
### 4. 优化平台匹配机制
|
||||
|
||||
**文件**: `utils/scheduler.go`
|
||||
|
||||
**新增平台映射缓存**:
|
||||
```go
|
||||
type Scheduler struct {
|
||||
// ... 其他字段 ...
|
||||
|
||||
// 平台映射缓存
|
||||
panCache map[string]*uint // serviceType -> panID
|
||||
panCacheOnce sync.Once
|
||||
}
|
||||
```
|
||||
|
||||
**新增初始化缓存方法**:
|
||||
```go
|
||||
// initPanCache 初始化平台映射缓存
|
||||
func (s *Scheduler) initPanCache() {
|
||||
s.panCacheOnce.Do(func() {
|
||||
// 获取所有平台数据
|
||||
pans, err := s.panRepo.FindAll()
|
||||
if err != nil {
|
||||
log.Printf("初始化平台缓存失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 建立 ServiceType 到 PanID 的映射
|
||||
serviceTypeToPanName := map[string]string{
|
||||
"quark": "quark",
|
||||
"alipan": "aliyun", // 阿里云盘在数据库中的名称是 aliyun
|
||||
"baidu": "baidu",
|
||||
"uc": "uc",
|
||||
"unknown": "other",
|
||||
}
|
||||
|
||||
// 创建平台名称到ID的映射
|
||||
panNameToID := make(map[string]*uint)
|
||||
for _, pan := range pans {
|
||||
panID := pan.ID
|
||||
panNameToID[pan.Name] = &panID
|
||||
}
|
||||
|
||||
// 建立 ServiceType 到 PanID 的映射
|
||||
for serviceType, panName := range serviceTypeToPanName {
|
||||
if panID, exists := panNameToID[panName]; exists {
|
||||
s.panCache[serviceType] = panID
|
||||
log.Printf("平台映射缓存: %s -> %s (ID: %d)", serviceType, panName, *panID)
|
||||
} else {
|
||||
log.Printf("警告: 未找到平台 %s 对应的数据库记录", panName)
|
||||
}
|
||||
}
|
||||
|
||||
// 确保有默认的 other 平台
|
||||
if otherID, exists := panNameToID["other"]; exists {
|
||||
s.panCache["unknown"] = otherID
|
||||
}
|
||||
|
||||
log.Printf("平台映射缓存初始化完成,共 %d 个映射", len(s.panCache))
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**新增根据服务类型获取平台ID的方法**:
|
||||
```go
|
||||
// getPanIDByServiceType 根据服务类型获取平台ID
|
||||
func (s *Scheduler) getPanIDByServiceType(serviceType panutils.ServiceType) *uint {
|
||||
s.initPanCache()
|
||||
|
||||
serviceTypeStr := serviceType.String()
|
||||
if panID, exists := s.panCache[serviceTypeStr]; exists {
|
||||
return panID
|
||||
}
|
||||
|
||||
// 如果找不到,返回 other 平台的ID
|
||||
if otherID, exists := s.panCache["other"]; exists {
|
||||
log.Printf("未找到服务类型 %s 的映射,使用默认平台 other", serviceTypeStr)
|
||||
return otherID
|
||||
}
|
||||
|
||||
log.Printf("未找到服务类型 %s 的映射,且没有默认平台,返回nil", serviceTypeStr)
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
**修改资源创建逻辑**:
|
||||
```go
|
||||
// 在 convertReadyResourceToResource 方法中
|
||||
resource := &entity.Resource{
|
||||
Title: title,
|
||||
Description: readyResource.Description,
|
||||
URL: shareURL,
|
||||
PanID: s.getPanIDByServiceType(serviceType), // 使用 serviceType 匹配
|
||||
IsValid: true,
|
||||
IsPublic: true,
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 添加 PanRepository 依赖
|
||||
|
||||
**文件**: `utils/scheduler.go`
|
||||
|
||||
**修改前**:
|
||||
```go
|
||||
type Scheduler struct {
|
||||
doubanService *DoubanService
|
||||
hotDramaRepo repo.HotDramaRepository
|
||||
readyResourceRepo repo.ReadyResourceRepository
|
||||
resourceRepo repo.ResourceRepository
|
||||
systemConfigRepo repo.SystemConfigRepository
|
||||
stopChan chan bool
|
||||
isRunning bool
|
||||
readyResourceRunning bool
|
||||
processingMutex sync.Mutex
|
||||
hotDramaMutex sync.Mutex
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```go
|
||||
type Scheduler struct {
|
||||
doubanService *DoubanService
|
||||
hotDramaRepo repo.HotDramaRepository
|
||||
readyResourceRepo repo.ReadyResourceRepository
|
||||
resourceRepo repo.ResourceRepository
|
||||
systemConfigRepo repo.SystemConfigRepository
|
||||
panRepo repo.PanRepository
|
||||
stopChan chan bool
|
||||
isRunning bool
|
||||
readyResourceRunning bool
|
||||
processingMutex sync.Mutex
|
||||
hotDramaMutex sync.Mutex
|
||||
|
||||
// 平台映射缓存
|
||||
panCache map[string]*uint // serviceType -> panID
|
||||
panCacheOnce sync.Once
|
||||
}
|
||||
```
|
||||
|
||||
## 修复效果
|
||||
|
||||
现在当您在管理后台开启"待处理资源自动处理"功能时:
|
||||
|
||||
1. **系统会立即启动调度器** - 不再需要重启服务器
|
||||
2. **使用配置的间隔时间** - 不再是固定的5分钟
|
||||
3. **支持实时开关** - 可以随时开启或关闭功能
|
||||
4. **正确的外键关联** - 不再出现外键约束错误
|
||||
5. **智能平台识别** - 根据 serviceType 自动识别对应的平台
|
||||
6. **高性能缓存** - 平台映射缓存,避免重复数据库查询
|
||||
|
||||
## 测试方法
|
||||
|
||||
运行测试脚本验证修复效果:
|
||||
```bash
|
||||
# 测试自动处理功能
|
||||
chmod +x test-auto-process.sh
|
||||
./test-auto-process.sh
|
||||
|
||||
# 测试平台匹配机制
|
||||
chmod +x test-pan-mapping.sh
|
||||
./test-pan-mapping.sh
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保数据库中有默认的平台数据
|
||||
2. 确保系统配置中的 `auto_process_interval` 设置合理
|
||||
3. 如果仍有问题,检查日志中的错误信息
|
||||
@@ -1,87 +0,0 @@
|
||||
# 豆瓣服务获取全部数据功能优化
|
||||
|
||||
## 问题描述
|
||||
|
||||
用户反馈豆瓣接口返回了 `total: 283`,但当前代码只获取了部分数据,希望能够一次性获取全部数据而不是分页获取。
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 1. 修改豆瓣服务逻辑
|
||||
|
||||
在 `utils/douban_service.go` 中:
|
||||
|
||||
- 为 `GetMovieRanking` 和 `GetTvRanking` 方法添加了 `limit=0` 的特殊处理
|
||||
- 当 `limit=0` 时,先获取第一页来确定总数,然后一次性获取全部数据
|
||||
- 重构了代码,将实际的API调用逻辑提取到 `getMovieRankingPage` 和 `getTvRankingPage` 方法中
|
||||
|
||||
### 2. 更新调度器配置
|
||||
|
||||
在 `utils/scheduler.go` 中:
|
||||
|
||||
- 将电影数据处理从 `limit=20` 改为 `limit=0`
|
||||
- 将电视剧数据处理从 `limit=20` 改为 `limit=0`
|
||||
- 这样调度器会自动获取并处理全部数据
|
||||
|
||||
### 3. 功能特点
|
||||
|
||||
- **智能检测**: 当传入 `limit=0` 时,自动检测总数并获取全部数据
|
||||
- **向后兼容**: 原有的分页功能保持不变
|
||||
- **日志记录**: 详细记录获取过程,便于调试
|
||||
- **错误处理**: 如果获取总数失败,会回退到默认行为
|
||||
|
||||
## 使用方法
|
||||
|
||||
### API调用
|
||||
|
||||
```bash
|
||||
# 获取全部电影数据
|
||||
curl "http://localhost:8080/api/hot-dramas/movies?category=热门&type=全部&start=0&limit=0"
|
||||
|
||||
# 获取全部电视剧数据
|
||||
curl "http://localhost:8080/api/hot-dramas/tv?category=tv&type=tv&start=0&limit=0"
|
||||
```
|
||||
|
||||
### 调度器自动处理
|
||||
|
||||
调度器现在会自动获取全部数据:
|
||||
|
||||
```go
|
||||
// 电影数据处理
|
||||
movieResult, err := s.doubanService.GetMovieRanking("热门", "全部", 0, 0)
|
||||
|
||||
// 电视剧数据处理
|
||||
tvResult, err := s.doubanService.GetTvRanking("tv", "tv", 0, 0)
|
||||
```
|
||||
|
||||
## 日志输出
|
||||
|
||||
当使用 `limit=0` 时,会看到类似以下的日志:
|
||||
|
||||
```
|
||||
=== 开始获取电影榜单 ===
|
||||
参数: category=热门, rankingType=全部, start=0, limit=0
|
||||
检测到limit=0,将尝试获取全部数据
|
||||
检测到总数为: 283,将一次性获取全部数据
|
||||
```
|
||||
|
||||
## 测试验证
|
||||
|
||||
使用提供的测试脚本:
|
||||
|
||||
```bash
|
||||
chmod +x test-douban-full-data.sh
|
||||
./test-douban-full-data.sh
|
||||
```
|
||||
|
||||
## 优势
|
||||
|
||||
1. **数据完整性**: 确保获取到所有可用数据
|
||||
2. **性能优化**: 减少API调用次数
|
||||
3. **灵活性**: 支持分页和全量获取两种模式
|
||||
4. **可维护性**: 代码结构清晰,易于理解和维护
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 获取全部数据可能会增加单次请求的响应时间
|
||||
2. 建议在调度器中使用此功能,避免影响用户界面的响应速度
|
||||
3. 如果API返回的数据量很大,需要考虑内存使用情况
|
||||
@@ -1,183 +0,0 @@
|
||||
# 原始豆瓣API Postman配置指南
|
||||
|
||||
## 基础信息
|
||||
|
||||
根据代码分析,原始豆瓣API的基础配置如下:
|
||||
|
||||
### 基础URL
|
||||
```
|
||||
https://m.douban.com/rexxar/api/v2
|
||||
```
|
||||
|
||||
### 请求头配置
|
||||
```
|
||||
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1
|
||||
Referer: https://m.douban.com/
|
||||
Accept: application/json, text/plain, */*
|
||||
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
|
||||
Accept-Encoding: gzip, deflate
|
||||
Connection: keep-alive
|
||||
Sec-Fetch-Dest: empty
|
||||
Sec-Fetch-Mode: cors
|
||||
Sec-Fetch-Site: same-origin
|
||||
```
|
||||
|
||||
## Postman配置步骤
|
||||
|
||||
### 1. 创建新的Collection
|
||||
|
||||
1. 打开Postman
|
||||
2. 点击 "New" → "Collection"
|
||||
3. 命名为 "豆瓣API"
|
||||
|
||||
### 2. 设置Collection级别的Headers
|
||||
|
||||
1. 选择刚创建的Collection
|
||||
2. 点击 "Variables" 标签
|
||||
3. 添加以下Headers:
|
||||
|
||||
| Key | Value |
|
||||
|-----|-------|
|
||||
| User-Agent | Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1 |
|
||||
| Referer | https://m.douban.com/ |
|
||||
| Accept | application/json, text/plain, */* |
|
||||
| Accept-Language | zh-CN,zh;q=0.9,en;q=0.8 |
|
||||
| Accept-Encoding | gzip, deflate |
|
||||
| Connection | keep-alive |
|
||||
| Sec-Fetch-Dest | empty |
|
||||
| Sec-Fetch-Mode | cors |
|
||||
| Sec-Fetch-Site | same-origin |
|
||||
|
||||
### 3. 创建电影榜单请求
|
||||
|
||||
#### 请求配置
|
||||
- **Method**: GET
|
||||
- **URL**: `https://m.douban.com/rexxar/api/v2/subject/recent_hot/movie`
|
||||
|
||||
#### Query Parameters
|
||||
| Key | Value | Description |
|
||||
|-----|-------|-------------|
|
||||
| start | 0 | 起始位置 |
|
||||
| limit | 20 | 限制数量(0表示获取全部) |
|
||||
| category | 热门 | 分类(热门/最新/豆瓣高分/冷门佳片) |
|
||||
| type | 全部 | 类型(全部/华语/欧美/韩国/日本) |
|
||||
|
||||
#### 示例请求
|
||||
```
|
||||
GET https://m.douban.com/rexxar/api/v2/subject/recent_hot/movie?start=0&limit=20&category=热门&type=全部
|
||||
```
|
||||
|
||||
### 4. 创建电视剧榜单请求
|
||||
|
||||
#### 请求配置
|
||||
- **Method**: GET
|
||||
- **URL**: `https://m.douban.com/rexxar/api/v2/subject/recent_hot/tv`
|
||||
|
||||
#### Query Parameters
|
||||
| Key | Value | Description |
|
||||
|-----|-------|-------------|
|
||||
| start | 0 | 起始位置 |
|
||||
| limit | 20 | 限制数量(0表示获取全部) |
|
||||
| category | tv | 分类(tv/show) |
|
||||
| type | tv | 类型(tv/tv_domestic/tv_american/tv_japanese/tv_korean/tv_animation/tv_documentary) |
|
||||
|
||||
#### 示例请求
|
||||
```
|
||||
GET https://m.douban.com/rexxar/api/v2/subject/recent_hot/tv?start=0&limit=20&category=tv&type=tv
|
||||
```
|
||||
|
||||
## 常用请求示例
|
||||
|
||||
### 1. 获取全部电影数据
|
||||
```
|
||||
GET https://m.douban.com/rexxar/api/v2/subject/recent_hot/movie?start=0&limit=0&category=热门&type=全部
|
||||
```
|
||||
|
||||
### 2. 获取华语电影
|
||||
```
|
||||
GET https://m.douban.com/rexxar/api/v2/subject/recent_hot/movie?start=0&limit=20&category=热门&type=华语
|
||||
```
|
||||
|
||||
### 3. 获取欧美电影
|
||||
```
|
||||
GET https://m.douban.com/rexxar/api/v2/subject/recent_hot/movie?start=0&limit=20&category=热门&type=欧美
|
||||
```
|
||||
|
||||
### 4. 获取豆瓣高分电影
|
||||
```
|
||||
GET https://m.douban.com/rexxar/api/v2/subject/recent_hot/movie?start=0&limit=20&category=豆瓣高分&type=全部
|
||||
```
|
||||
|
||||
### 5. 获取国产剧
|
||||
```
|
||||
GET https://m.douban.com/rexxar/api/v2/subject/recent_hot/tv?start=0&limit=20&category=tv&type=tv_domestic
|
||||
```
|
||||
|
||||
### 6. 获取综艺节目
|
||||
```
|
||||
GET https://m.douban.com/rexxar/api/v2/subject/recent_hot/tv?start=0&limit=20&category=show&type=show
|
||||
```
|
||||
|
||||
## 响应格式
|
||||
|
||||
### 成功响应示例
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "123456",
|
||||
"title": "电影标题",
|
||||
"card_subtitle": "导演 / 主演",
|
||||
"episodes_info": "",
|
||||
"is_new": false,
|
||||
"pic": {
|
||||
"large": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p123456.jpg",
|
||||
"normal": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p123456.jpg"
|
||||
},
|
||||
"rating": {
|
||||
"value": 8.5,
|
||||
"count": 12345,
|
||||
"max": 10,
|
||||
"star_count": 4.25
|
||||
},
|
||||
"type": "movie",
|
||||
"uri": "douban://douban.com/movie/123456",
|
||||
"year": "2024",
|
||||
"directors": ["导演名"],
|
||||
"actors": ["演员1", "演员2"],
|
||||
"region": "中国大陆",
|
||||
"genres": ["剧情", "动作"]
|
||||
}
|
||||
],
|
||||
"total": 283,
|
||||
"categories": [
|
||||
{
|
||||
"category": "热门",
|
||||
"selected": true,
|
||||
"type": "全部",
|
||||
"title": "热门"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **请求频率**: 建议控制请求频率,避免被限制
|
||||
2. **User-Agent**: 必须使用移动端User-Agent,否则可能返回错误
|
||||
3. **Referer**: 必须设置正确的Referer头
|
||||
4. **分页**: 使用start和limit参数进行分页
|
||||
5. **全量获取**: 设置limit=0可以获取全部数据
|
||||
|
||||
## 错误处理
|
||||
|
||||
### 常见错误
|
||||
- **403 Forbidden**: 请求头配置不正确
|
||||
- **404 Not Found**: URL路径错误
|
||||
- **429 Too Many Requests**: 请求频率过高
|
||||
|
||||
### 调试建议
|
||||
1. 检查所有请求头是否正确设置
|
||||
2. 确认URL路径和参数格式
|
||||
3. 使用浏览器开发者工具对比请求
|
||||
4. 查看响应状态码和错误信息
|
||||
@@ -1,161 +0,0 @@
|
||||
# 首页完整修复说明
|
||||
|
||||
## 问题描述
|
||||
1. 首页默认显示100条数据
|
||||
2. 今日更新没有显示
|
||||
3. 总资源数没有显示
|
||||
4. 首页没有默认加载数据
|
||||
5. 获取分类失败导致错误
|
||||
|
||||
## 问题原因分析
|
||||
|
||||
### 1. 数据加载问题
|
||||
- 首页初始化时调用 `store.fetchResources()` 没有传递分页参数
|
||||
- Store 中的 `fetchResources` 方法没有设置默认参数
|
||||
- 后端API需要 `page` 和 `page_size` 参数才能正确返回数据
|
||||
|
||||
### 2. 数据显示问题
|
||||
- 模板中使用 `visibleResources` 但该变量没有正确设置
|
||||
- `visibleResources` 是空数组,导致页面显示"暂无数据"
|
||||
- 统计数据计算依赖 `safeResources`,但数据没有正确加载
|
||||
|
||||
### 3. 类型错误问题
|
||||
- TypeScript 类型检查错误,API 返回的数据类型不明确
|
||||
|
||||
### 4. 分类获取问题
|
||||
- 分类API返回undefined导致错误
|
||||
- 首页不需要分类功能,应该移除相关调用
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 修复数据加载参数
|
||||
**文件**: `web/pages/index.vue`
|
||||
```javascript
|
||||
// 修复前
|
||||
const resourcesPromise = store.fetchResources().then((data: any) => {
|
||||
localResources.value = data.resources || []
|
||||
return data
|
||||
})
|
||||
|
||||
// 修复后
|
||||
const resourcesPromise = store.fetchResources({
|
||||
page: 1,
|
||||
page_size: 100
|
||||
}).then((data: any) => {
|
||||
localResources.value = data.resources || []
|
||||
return data
|
||||
})
|
||||
```
|
||||
|
||||
### 2. 修复visibleResources计算属性
|
||||
**文件**: `web/pages/index.vue`
|
||||
```javascript
|
||||
// 修复前
|
||||
const visibleResources = ref<any[]>([])
|
||||
const pageSize = ref(20)
|
||||
|
||||
// 修复后
|
||||
const visibleResources = computed(() => safeResources.value)
|
||||
const pageSize = ref(100) // 修改为100条数据
|
||||
```
|
||||
|
||||
### 3. 修复Store中的fetchResources方法
|
||||
**文件**: `web/stores/resource.ts`
|
||||
```javascript
|
||||
// 修复前
|
||||
async fetchResources(params?: any) {
|
||||
this.loading = true
|
||||
try {
|
||||
const { getResources } = useResourceApi()
|
||||
const data = await getResources(params)
|
||||
this.resources = data.resources
|
||||
this.currentPage = data.page
|
||||
this.totalPages = Math.ceil(data.total / data.limit)
|
||||
} catch (error) {
|
||||
console.error('获取资源失败:', error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
// 修复后
|
||||
async fetchResources(params?: any) {
|
||||
this.loading = true
|
||||
try {
|
||||
const { getResources } = useResourceApi()
|
||||
// 确保有默认参数
|
||||
const defaultParams = {
|
||||
page: 1,
|
||||
page_size: 100,
|
||||
...params
|
||||
}
|
||||
const data = await getResources(defaultParams) as any
|
||||
this.resources = data.resources || []
|
||||
this.currentPage = data.page || 1
|
||||
this.totalPages = Math.ceil((data.total || 0) / (data.page_size || 100))
|
||||
} catch (error) {
|
||||
console.error('获取资源失败:', error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 修复TypeScript类型错误
|
||||
**文件**: `web/stores/resource.ts`
|
||||
```javascript
|
||||
// 为所有API调用添加类型断言
|
||||
const data = await getResources(defaultParams) as any
|
||||
const stats = await getStats() as any
|
||||
```
|
||||
|
||||
### 5. 移除分类获取功能
|
||||
**文件**: `web/pages/index.vue`
|
||||
```javascript
|
||||
// 移除分类获取调用
|
||||
// 移除 localCategories 状态管理
|
||||
// 简化 safeCategories 计算属性
|
||||
```
|
||||
|
||||
## 修复要点总结
|
||||
|
||||
1. **参数传递**: 确保首页初始化时传递正确的分页参数
|
||||
2. **默认值设置**: Store 方法中设置合理的默认参数
|
||||
3. **计算属性**: 将 `visibleResources` 改为计算属性,直接使用 `safeResources`
|
||||
4. **数据量调整**: 将默认显示数据量从20条改为100条
|
||||
5. **类型安全**: 添加类型断言解决TypeScript错误
|
||||
6. **字段修正**: 使用正确的字段名 `page_size` 而不是 `limit`
|
||||
7. **移除分类**: 移除不必要的分类获取功能,避免API错误
|
||||
|
||||
## 测试验证
|
||||
|
||||
运行测试脚本验证修复效果:
|
||||
```bash
|
||||
chmod +x test-homepage-fix.sh
|
||||
./test-homepage-fix.sh
|
||||
```
|
||||
|
||||
## 预期效果
|
||||
|
||||
修复后,首页应该能够:
|
||||
1. ✅ 页面加载时自动显示前100条资源数据
|
||||
2. ✅ 正确显示今日更新数量(基于当前加载的数据计算)
|
||||
3. ✅ 正确显示总资源数(从统计API获取)
|
||||
4. ✅ 平台筛选功能正常工作
|
||||
5. ✅ 搜索功能正常工作
|
||||
6. ✅ "加载更多"功能继续正常工作
|
||||
7. ✅ 不再出现分类获取错误
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **数据计算**: 今日更新数量基于当前加载的100条数据计算,如果需要更准确的统计,需要加载所有数据或使用专门的统计API
|
||||
2. **性能考虑**: 加载100条数据可能影响页面加载速度,可根据实际需求调整
|
||||
3. **缓存策略**: 考虑添加数据缓存以提高用户体验
|
||||
4. **错误处理**: 确保网络错误时有合适的降级处理
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **虚拟滚动**: 对于大量数据,考虑实现虚拟滚动
|
||||
2. **分页优化**: 实现更智能的分页策略
|
||||
3. **缓存机制**: 添加数据缓存减少重复请求
|
||||
4. **加载状态**: 优化加载状态的用户体验
|
||||
@@ -1,183 +0,0 @@
|
||||
# 首页优化说明
|
||||
|
||||
## 概述
|
||||
|
||||
根据提供的HTML内容,我们对网盘资源管理系统的首页进行了全面优化,创建了一个更符合网盘资源管理风格的现代化界面。
|
||||
|
||||
## 主要优化内容
|
||||
|
||||
### 🎨 **界面设计优化**
|
||||
|
||||
1. **整体布局**
|
||||
- 采用深色头部设计,突出系统名称
|
||||
- 响应式布局,支持移动端和桌面端
|
||||
- 统一的卡片式设计风格
|
||||
|
||||
2. **搜索区域**
|
||||
- 圆角搜索框,带有搜索图标
|
||||
- 防抖搜索功能,提升用户体验
|
||||
- 平台类型筛选按钮
|
||||
|
||||
3. **资源列表**
|
||||
- 表格形式展示,更清晰直观
|
||||
- 支持链接显示/隐藏功能
|
||||
- 今日更新资源高亮显示
|
||||
- 相对时间显示(刚刚更新、X分钟前等)
|
||||
|
||||
### 🔧 **功能增强**
|
||||
|
||||
1. **平台管理**
|
||||
- 新增平台类型筛选
|
||||
- 支持按平台ID查询资源
|
||||
- 平台图标自动识别
|
||||
|
||||
2. **搜索功能**
|
||||
- 实时搜索(防抖500ms)
|
||||
- 支持文件名和链接搜索
|
||||
- 平台类型筛选
|
||||
|
||||
3. **分页功能**
|
||||
- 简洁的分页设计
|
||||
- 支持上一页/下一页
|
||||
- 当前页高亮显示
|
||||
|
||||
4. **统计信息**
|
||||
- 今日更新数量
|
||||
- 总资源数量
|
||||
- 数字动画效果
|
||||
|
||||
### 📱 **响应式设计**
|
||||
|
||||
1. **移动端适配**
|
||||
- 搜索框自适应
|
||||
- 表格在小屏幕上优化显示
|
||||
- 按钮和链接适配触摸操作
|
||||
|
||||
2. **桌面端优化**
|
||||
- 宽屏布局充分利用
|
||||
- 多列显示提升信息密度
|
||||
- 悬停效果增强交互
|
||||
|
||||
### 🎯 **用户体验**
|
||||
|
||||
1. **加载状态**
|
||||
- 加载中动画
|
||||
- 空状态提示
|
||||
- 错误状态处理
|
||||
|
||||
2. **交互反馈**
|
||||
- 按钮悬停效果
|
||||
- 链接点击反馈
|
||||
- 平滑滚动
|
||||
|
||||
3. **视觉层次**
|
||||
- 今日更新资源特殊标记
|
||||
- 平台图标颜色区分
|
||||
- 时间信息层次化显示
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 前端技术栈
|
||||
- **Nuxt 3**: 现代化Vue框架
|
||||
- **TypeScript**: 类型安全
|
||||
- **Tailwind CSS**: 原子化CSS框架
|
||||
- **Font Awesome**: 图标库
|
||||
- **Pinia**: 状态管理
|
||||
|
||||
### 核心功能
|
||||
|
||||
1. **防抖搜索**
|
||||
```typescript
|
||||
const debounceSearch = () => {
|
||||
clearTimeout(searchTimeout)
|
||||
searchTimeout = setTimeout(() => {
|
||||
handleSearch()
|
||||
}, 500)
|
||||
}
|
||||
```
|
||||
|
||||
2. **平台图标映射**
|
||||
```typescript
|
||||
const getPlatformIcon = (platformName: string) => {
|
||||
const icons: Record<string, string> = {
|
||||
'百度网盘': '<i class="fas fa-cloud text-blue-500"></i>',
|
||||
'阿里云盘': '<i class="fas fa-cloud text-orange-500"></i>',
|
||||
// ... 更多平台
|
||||
}
|
||||
return icons[platformName] || icons['unknown']
|
||||
}
|
||||
```
|
||||
|
||||
3. **相对时间格式化**
|
||||
```typescript
|
||||
const formatRelativeTime = (dateString: string) => {
|
||||
// 计算时间差并返回友好的显示格式
|
||||
// 支持:刚刚更新、X分钟前、X小时前、X天前等
|
||||
}
|
||||
```
|
||||
|
||||
## 新增页面
|
||||
|
||||
### 管理员页面 (`/admin`)
|
||||
- 功能模块化管理
|
||||
- 统计信息展示
|
||||
- 快速操作入口
|
||||
- 系统设置入口
|
||||
|
||||
## 配置更新
|
||||
|
||||
### Nuxt配置
|
||||
- 添加Font Awesome图标库
|
||||
- 更新页面标题和描述
|
||||
- 配置API基础URL
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 启动项目
|
||||
```bash
|
||||
# 后端
|
||||
cd res_db
|
||||
go run main.go
|
||||
|
||||
# 前端
|
||||
cd web
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 主要功能
|
||||
1. **搜索资源**: 在搜索框输入关键词
|
||||
2. **筛选平台**: 点击平台类型按钮
|
||||
3. **查看链接**: 点击"显示链接"按钮
|
||||
4. **管理资源**: 点击"管理员入口"
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **图标显示**: 确保网络连接正常,Font Awesome通过CDN加载
|
||||
2. **响应式**: 在不同设备上测试界面效果
|
||||
3. **性能**: 大量数据时考虑分页和虚拟滚动
|
||||
4. **安全性**: 链接显示功能需要根据实际需求调整
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **缓存优化**: 添加资源列表缓存
|
||||
2. **搜索增强**: 支持高级搜索和筛选
|
||||
3. **用户体验**: 添加加载骨架屏
|
||||
4. **功能扩展**: 支持资源收藏、分享等功能
|
||||
5. **主题切换**: 支持深色/浅色主题
|
||||
6. **国际化**: 支持多语言切换
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
web/
|
||||
├── pages/
|
||||
│ ├── index.vue # 优化后的首页
|
||||
│ └── admin.vue # 新增管理员页面
|
||||
├── composables/
|
||||
│ └── useApi.ts # API调用函数
|
||||
├── stores/
|
||||
│ └── resource.ts # 状态管理
|
||||
└── nuxt.config.ts # Nuxt配置
|
||||
```
|
||||
|
||||
这个优化后的首页完全符合网盘资源管理系统的需求,提供了现代化的用户界面和良好的用户体验。
|
||||
@@ -1,123 +0,0 @@
|
||||
# 热播剧功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
热播剧功能是一个自动获取和展示豆瓣热门电影、电视剧榜单的功能模块。系统会定时从豆瓣获取最新的热门影视作品信息,并保存到数据库中供用户浏览。
|
||||
|
||||
## 主要特性
|
||||
|
||||
### 1. 自动数据获取
|
||||
- 每小时自动从豆瓣获取热门电影和电视剧数据
|
||||
- 支持电影和电视剧两个分类
|
||||
- 获取内容包括:剧名、评分、年份、导演、演员等详细信息
|
||||
|
||||
### 2. 数据存储
|
||||
- 创建专门的热播剧数据表 `hot_dramas`
|
||||
- 支持按豆瓣ID去重,避免重复数据
|
||||
- 记录数据来源和获取时间
|
||||
|
||||
### 3. 前端展示
|
||||
- 美观的卡片式布局展示热播剧信息
|
||||
- 支持按分类筛选(全部/电影/电视剧)
|
||||
- 分页显示,支持大量数据
|
||||
- 响应式设计,适配各种设备
|
||||
|
||||
### 4. 管理功能
|
||||
- 管理员可以手动启动/停止定时任务
|
||||
- 支持手动获取热播剧数据
|
||||
- 查看调度器运行状态
|
||||
|
||||
## 数据库结构
|
||||
|
||||
### hot_dramas 表
|
||||
```sql
|
||||
CREATE TABLE hot_dramas (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
rating DECIMAL(3,1) DEFAULT 0.0,
|
||||
year VARCHAR(10),
|
||||
directors VARCHAR(500),
|
||||
actors VARCHAR(1000),
|
||||
category VARCHAR(50),
|
||||
sub_type VARCHAR(50),
|
||||
source VARCHAR(50) DEFAULT 'douban',
|
||||
douban_id VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
## API 接口
|
||||
|
||||
### 热播剧管理
|
||||
- `GET /api/hot-dramas` - 获取热播剧列表
|
||||
- `GET /api/hot-dramas/:id` - 获取热播剧详情
|
||||
- `POST /api/hot-dramas` - 创建热播剧记录(管理员)
|
||||
- `PUT /api/hot-dramas/:id` - 更新热播剧记录(管理员)
|
||||
- `DELETE /api/hot-dramas/:id` - 删除热播剧记录(管理员)
|
||||
|
||||
### 调度器管理
|
||||
- `GET /api/scheduler/status` - 获取调度器状态
|
||||
- `POST /api/scheduler/hot-drama/start` - 启动热播剧定时任务(管理员)
|
||||
- `POST /api/scheduler/hot-drama/stop` - 停止热播剧定时任务(管理员)
|
||||
- `GET /api/scheduler/hot-drama/names` - 手动获取热播剧名字(管理员)
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 系统配置
|
||||
在系统配置中有一个 `auto_fetch_hot_drama_enabled` 字段,用于控制是否启用自动获取热播剧功能:
|
||||
|
||||
- `true`: 启用自动获取,系统会根据配置的间隔时间自动获取数据
|
||||
- `false`: 禁用自动获取,需要管理员手动启动
|
||||
|
||||
## 使用流程
|
||||
|
||||
### 1. 启用功能
|
||||
1. 登录管理后台
|
||||
2. 进入系统配置页面
|
||||
3. 开启"自动拉取热播剧名字"选项
|
||||
4. 保存配置
|
||||
|
||||
### 2. 查看热播剧
|
||||
1. 在首页点击"热播剧"按钮
|
||||
2. 进入热播剧页面
|
||||
3. 可以按分类筛选查看
|
||||
4. 支持分页浏览
|
||||
|
||||
### 3. 管理定时任务
|
||||
1. 管理员可以手动启动/停止定时任务
|
||||
2. 可以查看调度器运行状态
|
||||
3. 可以手动触发数据获取
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 后端架构
|
||||
- **实体层**: `db/entity/hot_drama.go` - 定义热播剧数据结构
|
||||
- **DTO层**: `db/dto/hot_drama.go` - 定义数据传输对象
|
||||
- **转换器**: `db/converter/hot_drama_converter.go` - 实体与DTO转换
|
||||
- **仓储层**: `db/repo/hot_drama_repository.go` - 数据库操作
|
||||
- **处理器**: `handlers/hot_drama_handler.go` - API接口处理
|
||||
- **调度器**: `utils/scheduler.go` - 定时任务管理
|
||||
- **豆瓣服务**: `utils/douban_service.go` - 豆瓣API调用
|
||||
|
||||
### 前端实现
|
||||
- **页面**: `web/pages/hot-dramas.vue` - 热播剧展示页面
|
||||
- **导航**: 在首页添加热播剧入口
|
||||
- **样式**: 使用Tailwind CSS实现响应式设计
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **数据来源**: 数据来源于豆瓣移动端API,如果API不可用会使用模拟数据
|
||||
2. **频率限制**: 定时任务每小时执行一次,避免对豆瓣服务器造成压力
|
||||
3. **数据去重**: 系统会根据豆瓣ID进行去重,避免重复数据
|
||||
4. **权限控制**: 管理功能需要管理员权限
|
||||
5. **错误处理**: 系统具备完善的错误处理机制,确保稳定性
|
||||
|
||||
## 扩展功能
|
||||
|
||||
未来可以考虑添加的功能:
|
||||
1. 支持更多数据源(如IMDB、烂番茄等)
|
||||
2. 添加用户收藏功能
|
||||
3. 支持热播剧搜索
|
||||
4. 添加数据统计和分析功能
|
||||
5. 支持热播剧推荐算法
|
||||
@@ -1,195 +0,0 @@
|
||||
# 网盘服务工厂模式优化
|
||||
|
||||
## 优化背景
|
||||
|
||||
用户提出了一个很好的优化建议:可以通过工厂来获取网盘服务的单例实例,所有配置都是一样的,可以不需要 switch 语句了。
|
||||
|
||||
## 优化前的问题
|
||||
|
||||
### 1. 代码冗余
|
||||
```go
|
||||
// 优化前:需要 switch 语句分别处理
|
||||
switch serviceType {
|
||||
case panutils.Quark:
|
||||
quarkService := panutils.GetQuarkInstance()
|
||||
quarkService.UpdateConfig(config)
|
||||
result, err := quarkService.Transfer(shareID)
|
||||
// ... 处理结果
|
||||
|
||||
case panutils.Alipan:
|
||||
alipanService := panutils.GetAlipanInstance()
|
||||
alipanService.UpdateConfig(config)
|
||||
result, err := alipanService.Transfer(shareID)
|
||||
// ... 处理结果
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 重复逻辑
|
||||
- 每个 case 都有相似的配置更新逻辑
|
||||
- 每个 case 都有相似的结果处理逻辑
|
||||
- 代码维护困难,容易出错
|
||||
|
||||
## 优化方案
|
||||
|
||||
### 1. 通过工厂获取单例服务
|
||||
```go
|
||||
// 优化后:通过工厂统一获取服务
|
||||
panService, err := factory.CreatePanService(readyResource.URL, config)
|
||||
if err != nil {
|
||||
log.Printf("获取网盘服务失败: %v", err)
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 统一处理逻辑
|
||||
```go
|
||||
// 统一处理:尝试转存获取标题
|
||||
result, err := panService.Transfer(shareID)
|
||||
if err != nil {
|
||||
log.Printf("网盘转存失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !result.Success {
|
||||
log.Printf("网盘转存失败: %s", result.Message)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 统一的结果处理逻辑
|
||||
if resultData, ok := result.Data.(map[string]interface{}); ok {
|
||||
title := resultData["title"].(string)
|
||||
shareURL := resultData["shareUrl"].(string)
|
||||
|
||||
// 创建资源记录
|
||||
resource := &entity.Resource{
|
||||
Title: title,
|
||||
Description: readyResource.Description,
|
||||
URL: shareURL,
|
||||
PanID: s.determinePanID(readyResource.URL),
|
||||
IsValid: true,
|
||||
IsPublic: true,
|
||||
}
|
||||
|
||||
return s.resourceRepo.Create(resource)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 特殊处理优化
|
||||
```go
|
||||
// 阿里云盘特殊处理:检查URL有效性
|
||||
if serviceType == panutils.Alipan {
|
||||
checkResult, _ := CheckURL(readyResource.URL)
|
||||
if !checkResult.Status {
|
||||
log.Printf("阿里云盘链接无效: %s", readyResource.URL)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 如果有标题,直接创建资源
|
||||
if readyResource.Title != nil && *readyResource.Title != "" {
|
||||
// ... 直接创建资源的逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 优化效果
|
||||
|
||||
### 1. 代码简化
|
||||
- **移除 switch 语句**:从复杂的 switch 结构简化为统一的处理流程
|
||||
- **减少重复代码**:统一的结果处理逻辑,避免重复
|
||||
- **提高可读性**:代码结构更清晰,逻辑更直观
|
||||
|
||||
### 2. 维护性提升
|
||||
- **单一职责**:每个函数职责更明确
|
||||
- **易于扩展**:添加新的网盘服务类型更容易
|
||||
- **减少错误**:统一的处理逻辑减少出错概率
|
||||
|
||||
### 3. 性能优化
|
||||
- **单例复用**:通过工厂获取单例服务,确保性能
|
||||
- **配置统一**:所有服务使用相同的配置结构
|
||||
- **内存优化**:减少不必要的对象创建
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 1. 工厂模式的优势
|
||||
```go
|
||||
// 工厂方法内部已经是单例模式
|
||||
func (f *PanFactory) CreatePanService(url string, config *PanConfig) (PanService, error) {
|
||||
serviceType := ExtractServiceType(url)
|
||||
|
||||
switch serviceType {
|
||||
case Quark:
|
||||
return NewQuarkPanService(config), nil // 内部是单例
|
||||
case Alipan:
|
||||
return NewAlipanService(config), nil // 内部是单例
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 接口统一
|
||||
```go
|
||||
// 所有服务都实现相同的接口
|
||||
type PanService interface {
|
||||
Transfer(shareID string) (*TransferResult, error)
|
||||
GetFiles(pdirFid string) (*TransferResult, error)
|
||||
DeleteFiles(fileList []string) (*TransferResult, error)
|
||||
GetServiceType() ServiceType
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 配置统一
|
||||
```go
|
||||
// 统一的配置结构
|
||||
config := &panutils.PanConfig{
|
||||
URL: readyResource.URL,
|
||||
Code: "",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
AdFid: "",
|
||||
Stoken: "",
|
||||
}
|
||||
```
|
||||
|
||||
## 测试验证
|
||||
|
||||
### 编译测试
|
||||
```bash
|
||||
go build -o res_db .
|
||||
# ✅ 编译成功
|
||||
```
|
||||
|
||||
### 功能测试
|
||||
```bash
|
||||
go test ./utils/pan -v
|
||||
# ✅ 所有测试通过
|
||||
```
|
||||
|
||||
### 性能测试
|
||||
```bash
|
||||
go test ./utils/pan -bench=. -benchmem
|
||||
# ✅ 性能良好
|
||||
```
|
||||
|
||||
## 优化总结
|
||||
|
||||
### 优势
|
||||
1. **代码简化**:移除复杂的 switch 语句
|
||||
2. **逻辑统一**:所有网盘服务使用相同的处理流程
|
||||
3. **维护性提升**:代码更易维护和扩展
|
||||
4. **性能优化**:通过工厂获取单例服务
|
||||
5. **配置统一**:所有服务使用相同的配置结构
|
||||
|
||||
### 适用场景
|
||||
- 多个相似服务需要统一处理
|
||||
- 服务接口一致,但实现不同
|
||||
- 需要简化复杂的条件判断逻辑
|
||||
- 希望提高代码的可维护性
|
||||
|
||||
### 最佳实践
|
||||
1. **使用工厂模式**:通过工厂获取服务实例
|
||||
2. **统一接口**:确保所有服务实现相同的接口
|
||||
3. **配置统一**:使用相同的配置结构
|
||||
4. **特殊处理**:将特殊逻辑提取到条件判断中
|
||||
5. **错误处理**:统一的错误处理机制
|
||||
|
||||
这次优化很好地体现了"简单就是美"的设计原则,通过工厂模式统一了服务获取,通过接口统一了服务调用,大大简化了代码结构,提高了可维护性。
|
||||
@@ -1,153 +0,0 @@
|
||||
# 网盘服务单例模式使用问题修复
|
||||
|
||||
## 问题描述
|
||||
|
||||
在 `convertReadyResourceToResource` 函数中,我们使用了 `factory.CreatePanService()` 来获取网盘服务,但是没有正确配置就直接使用了。这导致单例服务没有正确的配置信息。
|
||||
|
||||
## 问题分析
|
||||
|
||||
### 原始代码问题
|
||||
```go
|
||||
// 问题代码
|
||||
panService, err := factory.CreatePanService(readyResource.URL, config)
|
||||
if err != nil {
|
||||
log.Printf("创建网盘服务失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 直接使用 panService,但没有配置
|
||||
result, err := panService.Transfer(shareID)
|
||||
```
|
||||
|
||||
### 问题原因
|
||||
1. 使用工厂模式创建服务,但单例服务需要先配置
|
||||
2. 没有正确使用单例模式的 `UpdateConfig` 方法
|
||||
3. 每次调用都创建新实例,失去了单例的优势
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 修复后的代码
|
||||
```go
|
||||
// 修复后的代码
|
||||
switch serviceType {
|
||||
case panutils.Quark:
|
||||
// 夸克网盘:使用单例服务
|
||||
quarkService := panutils.GetQuarkInstance()
|
||||
quarkService.UpdateConfig(config)
|
||||
|
||||
result, err := quarkService.Transfer(shareID)
|
||||
// ... 处理结果
|
||||
|
||||
case panutils.Alipan:
|
||||
// 阿里云盘:使用单例服务
|
||||
alipanService := panutils.GetAlipanInstance()
|
||||
alipanService.UpdateConfig(config)
|
||||
|
||||
result, err := alipanService.Transfer(shareID)
|
||||
// ... 处理结果
|
||||
}
|
||||
```
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 直接使用单例服务
|
||||
- 使用 `panutils.GetQuarkInstance()` 获取夸克网盘单例
|
||||
- 使用 `panutils.GetAlipanInstance()` 获取阿里云盘单例
|
||||
- 不再通过工厂创建服务实例
|
||||
|
||||
### 2. 正确配置服务
|
||||
- 调用 `UpdateConfig(config)` 更新服务配置
|
||||
- 确保每次处理前都有正确的配置信息
|
||||
- 配置更新是线程安全的
|
||||
|
||||
### 3. 优化处理流程
|
||||
- 先提取分享ID和服务类型
|
||||
- 根据服务类型选择对应的单例服务
|
||||
- 更新配置后执行转存操作
|
||||
|
||||
## 优势
|
||||
|
||||
### 1. 性能提升
|
||||
- 减少服务实例创建开销
|
||||
- 复用已创建的单例实例
|
||||
- 零额外内存分配
|
||||
|
||||
### 2. 配置正确
|
||||
- 每次处理前都更新配置
|
||||
- 确保配置信息的准确性
|
||||
- 支持动态配置更新
|
||||
|
||||
### 3. 线程安全
|
||||
- 单例服务支持并发访问
|
||||
- 配置更新使用读写锁保护
|
||||
- 避免竞态条件
|
||||
|
||||
## 测试验证
|
||||
|
||||
### 编译测试
|
||||
```bash
|
||||
go build -o res_db .
|
||||
# ✅ 编译成功
|
||||
```
|
||||
|
||||
### 功能测试
|
||||
```bash
|
||||
go test ./utils/pan -v
|
||||
# ✅ 所有测试通过
|
||||
```
|
||||
|
||||
### 性能测试
|
||||
```bash
|
||||
go test ./utils/pan -bench=. -benchmem
|
||||
# ✅ 性能良好,零内存分配
|
||||
```
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 获取单例服务
|
||||
```go
|
||||
// 夸克网盘单例
|
||||
quarkService := panutils.GetQuarkInstance()
|
||||
|
||||
// 阿里云盘单例
|
||||
alipanService := panutils.GetAlipanInstance()
|
||||
```
|
||||
|
||||
### 更新配置
|
||||
```go
|
||||
config := &panutils.PanConfig{
|
||||
URL: "https://pan.quark.cn/s/xxx",
|
||||
Code: "1234",
|
||||
IsType: 0,
|
||||
ExpiredType: 1,
|
||||
}
|
||||
|
||||
quarkService.UpdateConfig(config)
|
||||
```
|
||||
|
||||
### 执行操作
|
||||
```go
|
||||
result, err := quarkService.Transfer(shareID)
|
||||
if err != nil {
|
||||
// 处理错误
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **配置更新时机**:每次使用前都要调用 `UpdateConfig`
|
||||
2. **线程安全**:配置更新是线程安全的,可以并发调用
|
||||
3. **单例特性**:多次调用返回相同实例
|
||||
4. **错误处理**:需要检查 `Transfer` 方法的返回结果
|
||||
|
||||
## 总结
|
||||
|
||||
通过修复单例模式的使用问题,我们实现了:
|
||||
|
||||
1. ✅ 正确的单例服务使用
|
||||
2. ✅ 准确的配置管理
|
||||
3. ✅ 优秀的性能表现
|
||||
4. ✅ 线程安全的并发访问
|
||||
5. ✅ 完整的测试覆盖
|
||||
|
||||
现在 `convertReadyResourceToResource` 函数可以正确使用单例模式,既保证了性能又确保了配置的正确性。
|
||||
@@ -1,121 +0,0 @@
|
||||
# 网盘服务单例模式实现总结
|
||||
|
||||
## 概述
|
||||
|
||||
本次实现了网盘服务的单例模式,包括工厂单例和服务单例,显著提升了系统性能和资源利用率。
|
||||
|
||||
## 实现内容
|
||||
|
||||
### 1. 工厂单例模式
|
||||
- **文件**: `utils/pan/pan_factory.go`
|
||||
- **实现**: 线程安全的单例工厂
|
||||
- **性能**: 6.213 ns/op,零内存分配
|
||||
|
||||
### 2. 服务单例模式
|
||||
- **文件**: `utils/pan/quark_pan.go`, `utils/pan/alipan.go`
|
||||
- **实现**: 支持动态配置更新的服务单例
|
||||
- **特性**: 线程安全,配置热更新
|
||||
|
||||
### 3. 定时任务集成
|
||||
- **文件**: `utils/scheduler.go`
|
||||
- **修改**: 使用 `panutils.GetInstance()` 获取单例
|
||||
- **效果**: 减少重复创建开销
|
||||
|
||||
## 性能对比
|
||||
|
||||
| 操作类型 | 性能 (ns/op) | 内存分配 |
|
||||
|---------|-------------|----------|
|
||||
| 单例创建 | 6.213 | 0 B/op |
|
||||
| 工厂创建 | 7.765 | 0 B/op |
|
||||
| 服务创建 | 107-132 | 0 B/op |
|
||||
|
||||
## 测试覆盖
|
||||
|
||||
### 功能测试
|
||||
- ✅ 服务类型识别
|
||||
- ✅ 分享ID提取
|
||||
- ✅ 工厂模式创建
|
||||
- ✅ 单例模式验证
|
||||
|
||||
### 并发测试
|
||||
- ✅ 线程安全性
|
||||
- ✅ 并发访问
|
||||
- ✅ 配置更新
|
||||
|
||||
### 性能测试
|
||||
- ✅ 创建性能
|
||||
- ✅ 内存分配
|
||||
- ✅ 并发性能
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 获取工厂单例
|
||||
```go
|
||||
factory := panutils.GetInstance()
|
||||
```
|
||||
|
||||
### 获取服务单例
|
||||
```go
|
||||
// 夸克网盘服务
|
||||
quarkService := panutils.GetQuarkServiceInstance()
|
||||
|
||||
// 阿里云盘服务
|
||||
alipanService := panutils.GetAlipanServiceInstance()
|
||||
```
|
||||
|
||||
### 动态配置更新
|
||||
```go
|
||||
// 更新配置
|
||||
quarkService.UpdateConfig(&panutils.PanConfig{
|
||||
URL: "new_url",
|
||||
Code: "new_code",
|
||||
})
|
||||
```
|
||||
|
||||
## 优势
|
||||
|
||||
1. **性能提升**: 减少重复创建开销
|
||||
2. **内存优化**: 零额外内存分配
|
||||
3. **线程安全**: 支持并发访问
|
||||
4. **配置灵活**: 支持动态配置更新
|
||||
5. **易于维护**: 统一的单例管理
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 单例模式适用于高频调用场景
|
||||
2. 配置更新是线程安全的
|
||||
3. 服务实例在首次调用时创建
|
||||
4. 工厂单例在程序启动时初始化
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. 考虑添加服务健康检查
|
||||
2. 实现服务自动重连机制
|
||||
3. 添加服务状态监控
|
||||
4. 支持更多网盘服务类型
|
||||
|
||||
## 文件清单
|
||||
|
||||
- `utils/pan/pan_factory.go` - 工厂单例实现
|
||||
- `utils/pan/quark_pan.go` - 夸克网盘服务单例
|
||||
- `utils/pan/alipan.go` - 阿里云盘服务单例
|
||||
- `utils/pan/service_singleton_test.go` - 服务单例测试
|
||||
- `utils/pan/SERVICE_SINGLETON_ANALYSIS.md` - 详细分析文档
|
||||
- `utils/scheduler.go` - 定时任务集成
|
||||
|
||||
## 测试命令
|
||||
|
||||
```bash
|
||||
# 运行所有测试
|
||||
go test ./utils/pan -v
|
||||
|
||||
# 运行性能测试
|
||||
go test ./utils/pan -bench=. -benchmem
|
||||
|
||||
# 编译项目
|
||||
go build -o res_db .
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
网盘服务单例模式的实现成功提升了系统性能,特别是在定时任务等高频调用场景下。通过线程安全的单例模式,既保证了性能又确保了数据一致性。所有测试通过,项目编译成功,可以投入生产使用。
|
||||
@@ -1,166 +0,0 @@
|
||||
# 夸克网盘 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错误问题。
|
||||
@@ -1,245 +0,0 @@
|
||||
# 待处理资源功能说明
|
||||
|
||||
## 概述
|
||||
|
||||
新增了 `ready_resource` 表,用于批量添加和管理待处理的资源。支持两种输入格式,系统会自动识别标题和URL。
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
```sql
|
||||
CREATE TABLE ready_resource (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(255), -- 标题(可选)
|
||||
url VARCHAR(500) NOT NULL, -- URL(必填)
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
ip VARCHAR(45) DEFAULT NULL -- 客户端IP
|
||||
);
|
||||
```
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 🔧 **批量添加支持**
|
||||
|
||||
1. **单个添加**
|
||||
- 支持添加单个资源
|
||||
- 标题可选,URL必填
|
||||
|
||||
2. **JSON批量添加**
|
||||
- 支持JSON格式批量添加
|
||||
- 适合程序化操作
|
||||
|
||||
3. **文本批量添加**
|
||||
- 支持纯文本格式
|
||||
- 自动识别标题和URL
|
||||
- 支持两种格式
|
||||
|
||||
### 📝 **输入格式**
|
||||
|
||||
#### 格式1:标题和URL两行一组
|
||||
```
|
||||
电影标题1
|
||||
https://pan.baidu.com/s/123456
|
||||
电影标题2
|
||||
https://pan.baidu.com/s/789012
|
||||
```
|
||||
|
||||
#### 格式2:只有URL
|
||||
```
|
||||
https://pan.baidu.com/s/123456
|
||||
https://pan.baidu.com/s/789012
|
||||
https://pan.baidu.com/s/345678
|
||||
```
|
||||
|
||||
### 🌐 **URL自动识别**
|
||||
|
||||
系统会自动识别以下类型的URL:
|
||||
- HTTP/HTTPS链接
|
||||
- FTP链接
|
||||
- 磁力链接
|
||||
- 百度网盘链接
|
||||
- 阿里云盘链接
|
||||
- 夸克网盘链接
|
||||
- 天翼云盘链接
|
||||
- 迅雷云盘链接
|
||||
- 微云链接
|
||||
- 蓝奏云链接
|
||||
- 123云盘链接
|
||||
- Google Drive链接
|
||||
- Dropbox链接
|
||||
- OneDrive链接
|
||||
- 城通网盘链接
|
||||
- 115网盘链接
|
||||
- UC网盘链接
|
||||
|
||||
## API接口
|
||||
|
||||
### 1. 获取待处理资源列表
|
||||
```http
|
||||
GET /api/ready-resources
|
||||
```
|
||||
|
||||
### 2. 创建单个待处理资源
|
||||
```http
|
||||
POST /api/ready-resources
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"title": "电影标题",
|
||||
"url": "https://pan.baidu.com/s/123456"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 批量创建待处理资源(JSON格式)
|
||||
```http
|
||||
POST /api/ready-resources/batch
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"resources": [
|
||||
{
|
||||
"title": "电影1",
|
||||
"url": "https://pan.baidu.com/s/111111"
|
||||
},
|
||||
{
|
||||
"title": "电影2",
|
||||
"url": "https://pan.baidu.com/s/222222"
|
||||
},
|
||||
{
|
||||
"url": "https://pan.baidu.com/s/333333"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 从文本批量创建待处理资源
|
||||
```http
|
||||
POST /api/ready-resources/text
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"text": "电影标题1\nhttps://pan.baidu.com/s/444444\n电影标题2\nhttps://pan.baidu.com/s/555555"
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 删除单个待处理资源
|
||||
```http
|
||||
DELETE /api/ready-resources/{id}
|
||||
```
|
||||
|
||||
### 6. 清空所有待处理资源
|
||||
```http
|
||||
DELETE /api/ready-resources
|
||||
```
|
||||
|
||||
## 前端页面
|
||||
|
||||
### 待处理资源管理页面 (`/ready-resources`)
|
||||
|
||||
功能特性:
|
||||
- 📋 显示所有待处理资源
|
||||
- ➕ 批量添加功能
|
||||
- 🗑️ 删除单个资源
|
||||
- 🗑️ 清空所有资源
|
||||
- 🔄 刷新数据
|
||||
- 📊 统计信息
|
||||
|
||||
### 管理员页面集成
|
||||
|
||||
在管理员页面 (`/admin`) 新增了待处理资源模块:
|
||||
- 快速访问待处理资源管理
|
||||
- 批量添加资源入口
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 1. 通过前端页面添加
|
||||
|
||||
1. 访问 `/ready-resources` 页面
|
||||
2. 点击"批量添加"按钮
|
||||
3. 在文本框中输入资源内容
|
||||
4. 点击"批量添加"提交
|
||||
|
||||
### 2. 通过API添加
|
||||
|
||||
```bash
|
||||
# 添加单个资源
|
||||
curl -X POST http://localhost:8080/api/ready-resources \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"title": "测试电影",
|
||||
"url": "https://pan.baidu.com/s/123456"
|
||||
}'
|
||||
|
||||
# 批量添加(文本格式)
|
||||
curl -X POST http://localhost:8080/api/ready-resources/text \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"text": "电影标题1\nhttps://pan.baidu.com/s/444444\n电影标题2\nhttps://pan.baidu.com/s/555555"
|
||||
}'
|
||||
```
|
||||
|
||||
## 工作流程
|
||||
|
||||
1. **批量添加** → 用户通过前端或API批量添加资源到 `ready_resource` 表
|
||||
2. **自动处理** → 系统后续会自动处理这些资源(标题识别、平台判断等)
|
||||
3. **正式资源** → 处理完成后移动到正式的 `resources` 表
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 后端实现
|
||||
|
||||
1. **数据库模型** (`models/resource.go`)
|
||||
- `ReadyResource` 结构体
|
||||
- `CreateReadyResourceRequest` 请求结构
|
||||
- `BatchCreateReadyResourceRequest` 批量请求结构
|
||||
|
||||
2. **Handlers** (`handlers/ready_resource.go`)
|
||||
- `GetReadyResources` - 获取列表
|
||||
- `CreateReadyResource` - 创建单个
|
||||
- `BatchCreateReadyResources` - 批量创建(JSON)
|
||||
- `CreateReadyResourcesFromText` - 从文本批量创建
|
||||
- `DeleteReadyResource` - 删除单个
|
||||
- `ClearReadyResources` - 清空所有
|
||||
- `isURL` - URL识别函数
|
||||
|
||||
3. **路由配置** (`main.go`)
|
||||
- 添加所有待处理资源相关的API路由
|
||||
|
||||
### 前端实现
|
||||
|
||||
1. **API调用** (`composables/useApi.ts`)
|
||||
- `useReadyResourceApi` - 待处理资源API
|
||||
|
||||
2. **管理页面** (`pages/ready-resources.vue`)
|
||||
- 完整的待处理资源管理界面
|
||||
- 批量添加模态框
|
||||
- 资源列表显示
|
||||
- 操作功能
|
||||
|
||||
3. **管理员页面集成** (`pages/admin.vue`)
|
||||
- 添加待处理资源模块
|
||||
- 快速访问入口
|
||||
|
||||
## 测试
|
||||
|
||||
运行测试脚本验证功能:
|
||||
```bash
|
||||
./test-ready-resources.sh
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **URL识别**:系统会自动识别常见网盘和文件分享平台的URL
|
||||
2. **标题处理**:如果只提供URL,标题字段为空,系统后续会自动处理
|
||||
3. **IP记录**:自动记录添加资源的客户端IP
|
||||
4. **事务处理**:批量操作使用数据库事务确保数据一致性
|
||||
5. **错误处理**:完善的错误处理和用户反馈
|
||||
|
||||
## 后续扩展
|
||||
|
||||
1. **自动处理**:实现自动将待处理资源转换为正式资源
|
||||
2. **标题识别**:通过URL内容自动识别资源标题
|
||||
3. **平台分类**:自动识别资源所属平台
|
||||
4. **重复检测**:检测重复URL避免重复添加
|
||||
5. **批量操作**:支持批量删除、批量移动等功能
|
||||
|
||||
这个功能为网盘资源管理系统提供了灵活的批量添加机制,支持多种输入格式,为后续的自动化处理奠定了基础。
|
||||
@@ -1,137 +0,0 @@
|
||||
# 搜索统计功能
|
||||
|
||||
## 功能概述
|
||||
|
||||
搜索统计功能用于记录和分析用户的搜索行为,包括:
|
||||
- 每日搜索量统计
|
||||
- 热门关键词分析
|
||||
- 搜索趋势图表
|
||||
- 关键词热度排名
|
||||
|
||||
## 数据库设计
|
||||
|
||||
### 搜索统计表 (search_stats)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | SERIAL | 主键 |
|
||||
| keyword | VARCHAR(255) | 搜索关键词 |
|
||||
| count | INTEGER | 搜索次数 |
|
||||
| date | DATE | 搜索日期 |
|
||||
| ip | VARCHAR(45) | 用户IP |
|
||||
| user_agent | VARCHAR(500) | 用户代理 |
|
||||
| created_at | TIMESTAMP | 创建时间 |
|
||||
| updated_at | TIMESTAMP | 更新时间 |
|
||||
|
||||
## API接口
|
||||
|
||||
### 1. 记录搜索
|
||||
```
|
||||
POST /api/search-stats/record
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"keyword": "搜索关键词"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 获取搜索统计总览
|
||||
```
|
||||
GET /api/search-stats
|
||||
```
|
||||
|
||||
返回数据:
|
||||
```json
|
||||
{
|
||||
"today_searches": 10,
|
||||
"week_searches": 50,
|
||||
"month_searches": 200,
|
||||
"hot_keywords": [
|
||||
{
|
||||
"keyword": "关键词",
|
||||
"count": 15,
|
||||
"rank": 1
|
||||
}
|
||||
],
|
||||
"daily_stats": [...],
|
||||
"search_trend": {
|
||||
"days": ["01-01", "01-02"],
|
||||
"values": [10, 15]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 获取热门关键词
|
||||
```
|
||||
GET /api/search-stats/hot-keywords?days=30&limit=10
|
||||
```
|
||||
|
||||
参数:
|
||||
- days: 统计天数(默认30)
|
||||
- limit: 返回数量限制(默认10)
|
||||
|
||||
### 4. 获取每日统计
|
||||
```
|
||||
GET /api/search-stats/daily?days=30
|
||||
```
|
||||
|
||||
### 5. 获取搜索趋势
|
||||
```
|
||||
GET /api/search-stats/trend?days=30
|
||||
```
|
||||
|
||||
### 6. 获取关键词趋势
|
||||
```
|
||||
GET /api/search-stats/keyword/{keyword}/trend?days=30
|
||||
```
|
||||
|
||||
## 前端页面
|
||||
|
||||
### 搜索统计页面 (/search-stats)
|
||||
|
||||
功能特性:
|
||||
- 今日/本周/本月搜索量统计卡片
|
||||
- 搜索趋势折线图
|
||||
- 热门关键词排行榜
|
||||
- 关键词热度可视化
|
||||
|
||||
## 自动记录
|
||||
|
||||
系统会在以下情况下自动记录搜索:
|
||||
- 用户使用搜索功能时
|
||||
- 记录用户IP和User-Agent信息
|
||||
- 按日期聚合统计
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. **管理员访问**:登录后可在管理员页面看到"搜索统计"模块
|
||||
2. **查看统计**:点击"查看搜索统计"进入详细页面
|
||||
3. **热门关键词**:查看最受欢迎的关键词排名
|
||||
4. **趋势分析**:通过图表了解搜索量变化趋势
|
||||
|
||||
## 测试
|
||||
|
||||
运行测试脚本:
|
||||
```bash
|
||||
chmod +x test-search-stats.sh
|
||||
./test-search-stats.sh
|
||||
```
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 后端
|
||||
- 使用Repository模式管理数据访问
|
||||
- 支持按日期聚合统计
|
||||
- 提供多种统计维度API
|
||||
|
||||
### 前端
|
||||
- 使用Chart.js绘制趋势图表
|
||||
- 响应式设计适配不同设备
|
||||
- 实时数据更新
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 搜索记录会保存用户IP,注意隐私保护
|
||||
2. 大量搜索数据可能影响性能,建议定期清理
|
||||
3. 关键词统计按天聚合,避免重复记录
|
||||
4. 图表数据需要足够的历史数据才能显示趋势
|
||||
@@ -1,151 +0,0 @@
|
||||
# 表结构变更说明
|
||||
|
||||
## 概述
|
||||
|
||||
根据需求,我们对 `resources` 表进行了重大结构调整,以支持一个资源对应一个 URL 的模式,并添加了平台标识功能。
|
||||
|
||||
## 主要变更
|
||||
|
||||
### 1. Resources 表结构变更
|
||||
|
||||
**变更前:**
|
||||
- `url` 字段:JSON 格式,存储多个链接
|
||||
- 一个资源记录包含多个链接
|
||||
|
||||
**变更后:**
|
||||
- `url` 字段:VARCHAR(128),存储单个链接
|
||||
- 新增 `pan_id` 字段:INTEGER,关联到 `pan` 表,标识链接类型
|
||||
- 一个资源对应一个链接,多个链接需要创建多条记录
|
||||
|
||||
### 2. 新增 Pan 表
|
||||
|
||||
```sql
|
||||
CREATE TABLE pan (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(64) DEFAULT NULL,
|
||||
key INTEGER DEFAULT NULL,
|
||||
ck TEXT,
|
||||
is_valid BOOLEAN DEFAULT true,
|
||||
space BIGINT DEFAULT 0,
|
||||
left_space BIGINT DEFAULT 0,
|
||||
remark VARCHAR(64) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### 3. API 变更
|
||||
|
||||
#### 资源相关 API
|
||||
|
||||
**新增查询参数:**
|
||||
- `pan_id`:按平台ID筛选资源
|
||||
|
||||
**请求体变更:**
|
||||
```json
|
||||
{
|
||||
"title": "资源标题",
|
||||
"description": "资源描述",
|
||||
"url": "https://pan.baidu.com/s/123456",
|
||||
"pan_id": 1, // 新增:平台ID
|
||||
"quark_url": "",
|
||||
"file_size": "100MB",
|
||||
"category_id": 1,
|
||||
"is_valid": true,
|
||||
"is_public": true,
|
||||
"tag_ids": [1, 2]
|
||||
}
|
||||
```
|
||||
|
||||
#### 平台相关 API
|
||||
|
||||
**新增 API 端点:**
|
||||
- `GET /api/pans` - 获取平台列表
|
||||
- `GET /api/pans/:id` - 获取单个平台
|
||||
- `POST /api/pans` - 创建平台
|
||||
- `PUT /api/pans/:id` - 更新平台
|
||||
- `DELETE /api/pans/:id` - 删除平台
|
||||
|
||||
## 前端变更
|
||||
|
||||
### useApi.ts 新增方法
|
||||
|
||||
```typescript
|
||||
// 按平台ID获取资源
|
||||
const getResourcesByPan = async (panId: number, params?: any) => {
|
||||
return await $fetch('/resources', {
|
||||
baseURL: config.public.apiBase,
|
||||
params: { ...params, pan_id: panId }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 数据迁移建议
|
||||
|
||||
1. **备份现有数据**
|
||||
```sql
|
||||
-- 备份现有资源表
|
||||
CREATE TABLE resources_backup AS SELECT * FROM resources;
|
||||
```
|
||||
|
||||
2. **创建新的表结构**
|
||||
- 运行更新后的数据库初始化代码
|
||||
|
||||
3. **数据迁移策略**
|
||||
- 对于每个资源的多个链接,创建多条记录
|
||||
- 为每条记录分配适当的 `pan_id`
|
||||
- 保持原有的标签关联
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 创建平台
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/api/pans \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "百度网盘",
|
||||
"key": 1,
|
||||
"ck": "test_ck",
|
||||
"is_valid": true,
|
||||
"space": 2048,
|
||||
"left_space": 1024,
|
||||
"remark": "百度网盘平台"
|
||||
}'
|
||||
```
|
||||
|
||||
### 创建资源
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/api/resources \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"title": "测试资源",
|
||||
"description": "这是一个测试资源",
|
||||
"url": "https://pan.baidu.com/s/123456",
|
||||
"pan_id": 1,
|
||||
"quark_url": "",
|
||||
"file_size": "100MB",
|
||||
"category_id": 1,
|
||||
"is_valid": true,
|
||||
"is_public": true,
|
||||
"tag_ids": [1]
|
||||
}'
|
||||
```
|
||||
|
||||
### 按平台查询资源
|
||||
```bash
|
||||
curl "http://localhost:8080/api/resources?pan_id=1"
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **URL 长度限制**:URL 字段现在限制为 128 字符
|
||||
2. **平台关联**:每个资源必须关联到一个平台(pan_id)
|
||||
3. **数据完整性**:确保在创建资源时提供有效的 pan_id
|
||||
4. **向后兼容**:API 响应格式保持兼容,只是新增了 pan_id 字段
|
||||
|
||||
## 测试
|
||||
|
||||
运行测试脚本验证新功能:
|
||||
```bash
|
||||
./test-setup.sh
|
||||
```
|
||||
@@ -1,211 +0,0 @@
|
||||
# Vue 3 + Nuxt.js UI框架选择指南
|
||||
|
||||
## 🎨 推荐的UI框架
|
||||
|
||||
### 1. **Naive UI** ⭐⭐⭐⭐⭐ (强烈推荐)
|
||||
**特点**: 完整的Vue 3组件库,TypeScript支持,主题定制
|
||||
**优势**:
|
||||
- ✅ 完整的Vue 3支持
|
||||
- ✅ TypeScript原生支持
|
||||
- ✅ 组件丰富(80+组件)
|
||||
- ✅ 主题系统强大
|
||||
- ✅ 文档完善
|
||||
- ✅ 性能优秀
|
||||
- ✅ 活跃维护
|
||||
|
||||
**适用场景**: 企业级应用,复杂界面,需要高度定制
|
||||
|
||||
**安装**:
|
||||
```bash
|
||||
npm install naive-ui vfonts @vicons/ionicons5
|
||||
```
|
||||
|
||||
### 2. **Element Plus** ⭐⭐⭐⭐
|
||||
**特点**: Vue 3版本的Element UI,成熟稳定
|
||||
**优势**:
|
||||
- ✅ 社区活跃
|
||||
- ✅ 组件齐全
|
||||
- ✅ 文档详细
|
||||
- ✅ 成熟稳定
|
||||
- ✅ 中文文档
|
||||
|
||||
**适用场景**: 后台管理系统,快速开发
|
||||
|
||||
**安装**:
|
||||
```bash
|
||||
npm install element-plus @element-plus/icons-vue
|
||||
```
|
||||
|
||||
### 3. **Ant Design Vue** ⭐⭐⭐⭐
|
||||
**特点**: 企业级UI设计语言
|
||||
**优势**:
|
||||
- ✅ 设计规范统一
|
||||
- ✅ 组件丰富
|
||||
- ✅ 企业级应用
|
||||
- ✅ 国际化支持
|
||||
|
||||
**适用场景**: 企业应用,设计规范要求高
|
||||
|
||||
**安装**:
|
||||
```bash
|
||||
npm install ant-design-vue @ant-design/icons-vue
|
||||
```
|
||||
|
||||
### 4. **PrimeVue** ⭐⭐⭐
|
||||
**特点**: 丰富的组件库,支持多种主题
|
||||
**优势**:
|
||||
- ✅ 组件数量多
|
||||
- ✅ 功能强大
|
||||
- ✅ 主题丰富
|
||||
- ✅ 响应式设计
|
||||
|
||||
**适用场景**: 复杂业务场景
|
||||
|
||||
**安装**:
|
||||
```bash
|
||||
npm install primevue primeicons
|
||||
```
|
||||
|
||||
### 5. **Vuetify** ⭐⭐⭐
|
||||
**特点**: Material Design风格
|
||||
**优势**:
|
||||
- ✅ 设计美观
|
||||
- ✅ 响应式好
|
||||
- ✅ Material Design
|
||||
- ✅ 组件丰富
|
||||
|
||||
**适用场景**: 现代化应用,Material Design风格
|
||||
|
||||
**安装**:
|
||||
```bash
|
||||
npm install vuetify @mdi/font
|
||||
```
|
||||
|
||||
## 🚀 当前项目推荐
|
||||
|
||||
### 推荐使用 **Naive UI**
|
||||
|
||||
**原因**:
|
||||
1. **Vue 3原生支持**: 完全基于Vue 3 Composition API
|
||||
2. **TypeScript友好**: 原生TypeScript支持
|
||||
3. **组件丰富**: 满足资源管理系统需求
|
||||
4. **主题系统**: 支持深色/浅色主题切换
|
||||
5. **性能优秀**: 按需加载,体积小
|
||||
|
||||
### 集成步骤
|
||||
|
||||
1. **安装依赖**:
|
||||
```bash
|
||||
cd web
|
||||
npm install naive-ui vfonts @vicons/ionicons5 @css-render/vue3-ssr @juggle/resize-observer
|
||||
```
|
||||
|
||||
2. **配置Nuxt**:
|
||||
```typescript
|
||||
// nuxt.config.ts
|
||||
export default defineNuxtConfig({
|
||||
build: {
|
||||
transpile: ['naive-ui', 'vueuc', '@css-render/vue3-ssr', '@juggle/resize-observer']
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
3. **创建插件**:
|
||||
```typescript
|
||||
// plugins/naive-ui.client.ts
|
||||
import { setup } from '@css-render/vue3-ssr'
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
if (process.server) {
|
||||
const { collect } = setup(nuxtApp.vueApp)
|
||||
// SSR配置
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
4. **使用组件**:
|
||||
```vue
|
||||
<template>
|
||||
<n-config-provider :theme="theme">
|
||||
<n-card>
|
||||
<n-button type="primary">按钮</n-button>
|
||||
</n-card>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 📊 框架对比表
|
||||
|
||||
| 特性 | Naive UI | Element Plus | Ant Design Vue | PrimeVue | Vuetify |
|
||||
|------|----------|--------------|----------------|----------|---------|
|
||||
| Vue 3支持 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| TypeScript | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| 组件数量 | 80+ | 60+ | 60+ | 90+ | 80+ |
|
||||
| 主题系统 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| 中文文档 | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| 社区活跃度 | 高 | 很高 | 高 | 中 | 中 |
|
||||
| 学习曲线 | 低 | 低 | 中 | 中 | 中 |
|
||||
| 性能 | 优秀 | 良好 | 良好 | 良好 | 良好 |
|
||||
|
||||
## 🎯 针对资源管理系统的建议
|
||||
|
||||
### 核心组件需求
|
||||
1. **数据表格**: 资源列表展示
|
||||
2. **表单组件**: 资源添加/编辑
|
||||
3. **模态框**: 弹窗操作
|
||||
4. **搜索组件**: 资源搜索
|
||||
5. **标签组件**: 资源标签
|
||||
6. **统计卡片**: 数据展示
|
||||
7. **分页组件**: 列表分页
|
||||
|
||||
### Naive UI优势
|
||||
- **n-data-table**: 功能强大的数据表格
|
||||
- **n-form**: 完整的表单解决方案
|
||||
- **n-modal**: 灵活的模态框
|
||||
- **n-input**: 搜索输入框
|
||||
- **n-tag**: 标签组件
|
||||
- **n-card**: 统计卡片
|
||||
- **n-pagination**: 分页组件
|
||||
|
||||
## 🔧 迁移指南
|
||||
|
||||
如果要从当前的基础组件迁移到Naive UI:
|
||||
|
||||
1. **替换基础组件**:
|
||||
```vue
|
||||
<!-- 原版 -->
|
||||
<button class="btn-primary">按钮</button>
|
||||
|
||||
<!-- Naive UI -->
|
||||
<n-button type="primary">按钮</n-button>
|
||||
```
|
||||
|
||||
2. **替换表单组件**:
|
||||
```vue
|
||||
<!-- 原版 -->
|
||||
<input class="input-field" />
|
||||
|
||||
<!-- Naive UI -->
|
||||
<n-input />
|
||||
```
|
||||
|
||||
3. **替换模态框**:
|
||||
```vue
|
||||
<!-- 原版 -->
|
||||
<div class="modal">...</div>
|
||||
|
||||
<!-- Naive UI -->
|
||||
<n-modal>...</n-modal>
|
||||
```
|
||||
|
||||
## 📝 总结
|
||||
|
||||
对于您的资源管理系统项目,我强烈推荐使用 **Naive UI**,因为:
|
||||
|
||||
1. **完美适配**: 完全支持Vue 3和Nuxt.js
|
||||
2. **功能完整**: 提供所有需要的组件
|
||||
3. **开发效率**: 减少大量自定义样式工作
|
||||
4. **维护性好**: TypeScript支持,代码更可靠
|
||||
5. **性能优秀**: 按需加载,体积小
|
||||
|
||||
使用UI框架可以节省70-80%的前端开发时间,让您专注于业务逻辑而不是样式细节。
|
||||
379
docs/docs.go
379
docs/docs.go
@@ -1,379 +0,0 @@
|
||||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"termsOfService": "http://swagger.io/terms/",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"url": "http://www.swagger.io/support",
|
||||
"email": "support@swagger.io"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/api/public/hot-dramas": {
|
||||
"get": {
|
||||
"description": "获取热门剧列表,支持分页",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"PublicAPI"
|
||||
],
|
||||
"summary": "获取热门剧列表",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "API访问令牌",
|
||||
"name": "X-API-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 1,
|
||||
"description": "页码",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"maximum": 100,
|
||||
"type": "integer",
|
||||
"default": 20,
|
||||
"description": "每页数量",
|
||||
"name": "page_size",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "获取成功",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "认证失败",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/public/resources/add": {
|
||||
"post": {
|
||||
"description": "通过公开API添加单个资源到待处理列表",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"PublicAPI"
|
||||
],
|
||||
"summary": "单个添加资源",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "API访问令牌",
|
||||
"name": "X-API-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "资源信息",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ReadyResourceRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "添加成功",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "认证失败",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/public/resources/batch-add": {
|
||||
"post": {
|
||||
"description": "通过公开API批量添加多个资源到待处理列表",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"PublicAPI"
|
||||
],
|
||||
"summary": "批量添加资源",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "API访问令牌",
|
||||
"name": "X-API-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "批量资源信息",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.BatchReadyResourceRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "批量添加成功",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "认证失败",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/public/resources/search": {
|
||||
"get": {
|
||||
"description": "搜索资源,支持关键词、标签、分类过滤",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"PublicAPI"
|
||||
],
|
||||
"summary": "资源搜索",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "API访问令牌",
|
||||
"name": "X-API-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "搜索关键词",
|
||||
"name": "keyword",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "标签过滤",
|
||||
"name": "tag",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "分类过滤",
|
||||
"name": "category",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 1,
|
||||
"description": "页码",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"maximum": 100,
|
||||
"type": "integer",
|
||||
"default": 20,
|
||||
"description": "每页数量",
|
||||
"name": "page_size",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "搜索成功",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "认证失败",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"dto.BatchReadyResourceRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resources"
|
||||
],
|
||||
"properties": {
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.ReadyResourceRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ReadyResourceRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"category": {
|
||||
"type": "string",
|
||||
"example": "示例分类"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"example": "这是一个示例资源描述"
|
||||
},
|
||||
"extra": {
|
||||
"type": "string",
|
||||
"example": "额外信息"
|
||||
},
|
||||
"img": {
|
||||
"type": "string",
|
||||
"example": "https://example.com/image.jpg"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"example": "数据来源"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
"example": "标签1,标签2"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"example": "示例资源标题"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"example": "https://example.com/resource"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"ApiTokenAuth": {
|
||||
"description": "API Token认证",
|
||||
"type": "apiKey",
|
||||
"name": "X-API-Token",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1.0",
|
||||
Host: "localhost:8080",
|
||||
BasePath: "/api/public",
|
||||
Schemes: []string{},
|
||||
Title: "网盘资源管理系统公开API",
|
||||
Description: "网盘资源管理系统的公开API接口文档",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "网盘资源管理系统的公开API接口文档",
|
||||
"title": "网盘资源管理系统公开API",
|
||||
"termsOfService": "http://swagger.io/terms/",
|
||||
"contact": {
|
||||
"name": "API Support",
|
||||
"url": "http://www.swagger.io/support",
|
||||
"email": "support@swagger.io"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "localhost:8080",
|
||||
"basePath": "/api/public",
|
||||
"paths": {
|
||||
"/api/public/hot-dramas": {
|
||||
"get": {
|
||||
"description": "获取热门剧列表,支持分页",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"PublicAPI"
|
||||
],
|
||||
"summary": "获取热门剧列表",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "API访问令牌",
|
||||
"name": "X-API-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 1,
|
||||
"description": "页码",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"maximum": 100,
|
||||
"type": "integer",
|
||||
"default": 20,
|
||||
"description": "每页数量",
|
||||
"name": "page_size",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "获取成功",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "认证失败",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/public/resources/add": {
|
||||
"post": {
|
||||
"description": "通过公开API添加单个资源到待处理列表",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"PublicAPI"
|
||||
],
|
||||
"summary": "单个添加资源",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "API访问令牌",
|
||||
"name": "X-API-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "资源信息",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ReadyResourceRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "添加成功",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "认证失败",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/public/resources/batch-add": {
|
||||
"post": {
|
||||
"description": "通过公开API批量添加多个资源到待处理列表",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"PublicAPI"
|
||||
],
|
||||
"summary": "批量添加资源",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "API访问令牌",
|
||||
"name": "X-API-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "批量资源信息",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.BatchReadyResourceRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "批量添加成功",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "认证失败",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/public/resources/search": {
|
||||
"get": {
|
||||
"description": "搜索资源,支持关键词、标签、分类过滤",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"PublicAPI"
|
||||
],
|
||||
"summary": "资源搜索",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "API访问令牌",
|
||||
"name": "X-API-Token",
|
||||
"in": "header",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "搜索关键词",
|
||||
"name": "keyword",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "标签过滤",
|
||||
"name": "tag",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "分类过滤",
|
||||
"name": "category",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": 1,
|
||||
"description": "页码",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"maximum": 100,
|
||||
"type": "integer",
|
||||
"default": 20,
|
||||
"description": "每页数量",
|
||||
"name": "page_size",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "搜索成功",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "认证失败",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"dto.BatchReadyResourceRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resources"
|
||||
],
|
||||
"properties": {
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/dto.ReadyResourceRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dto.ReadyResourceRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"category": {
|
||||
"type": "string",
|
||||
"example": "示例分类"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"example": "这是一个示例资源描述"
|
||||
},
|
||||
"extra": {
|
||||
"type": "string",
|
||||
"example": "额外信息"
|
||||
},
|
||||
"img": {
|
||||
"type": "string",
|
||||
"example": "https://example.com/image.jpg"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"example": "数据来源"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
"example": "标签1,标签2"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"example": "示例资源标题"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"example": "https://example.com/resource"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"ApiTokenAuth": {
|
||||
"description": "API Token认证",
|
||||
"type": "apiKey",
|
||||
"name": "X-API-Token",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
basePath: /api/public
|
||||
definitions:
|
||||
dto.BatchReadyResourceRequest:
|
||||
properties:
|
||||
resources:
|
||||
items:
|
||||
$ref: '#/definitions/dto.ReadyResourceRequest'
|
||||
type: array
|
||||
required:
|
||||
- resources
|
||||
type: object
|
||||
dto.ReadyResourceRequest:
|
||||
properties:
|
||||
category:
|
||||
example: 示例分类
|
||||
type: string
|
||||
description:
|
||||
example: 这是一个示例资源描述
|
||||
type: string
|
||||
extra:
|
||||
example: 额外信息
|
||||
type: string
|
||||
img:
|
||||
example: https://example.com/image.jpg
|
||||
type: string
|
||||
source:
|
||||
example: 数据来源
|
||||
type: string
|
||||
tags:
|
||||
example: 标签1,标签2
|
||||
type: string
|
||||
title:
|
||||
example: 示例资源标题
|
||||
type: string
|
||||
url:
|
||||
example: https://example.com/resource
|
||||
type: string
|
||||
required:
|
||||
- title
|
||||
- url
|
||||
type: object
|
||||
host: localhost:8080
|
||||
info:
|
||||
contact:
|
||||
email: support@swagger.io
|
||||
name: API Support
|
||||
url: http://www.swagger.io/support
|
||||
description: 网盘资源管理系统的公开API接口文档
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
termsOfService: http://swagger.io/terms/
|
||||
title: 网盘资源管理系统公开API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/api/public/hot-dramas:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取热门剧列表,支持分页
|
||||
parameters:
|
||||
- description: API访问令牌
|
||||
in: header
|
||||
name: X-API-Token
|
||||
required: true
|
||||
type: string
|
||||
- default: 1
|
||||
description: 页码
|
||||
in: query
|
||||
name: page
|
||||
type: integer
|
||||
- default: 20
|
||||
description: 每页数量
|
||||
in: query
|
||||
maximum: 100
|
||||
name: page_size
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 获取成功
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"401":
|
||||
description: 认证失败
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"500":
|
||||
description: 服务器内部错误
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: 获取热门剧列表
|
||||
tags:
|
||||
- PublicAPI
|
||||
/api/public/resources/add:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 通过公开API添加单个资源到待处理列表
|
||||
parameters:
|
||||
- description: API访问令牌
|
||||
in: header
|
||||
name: X-API-Token
|
||||
required: true
|
||||
type: string
|
||||
- description: 资源信息
|
||||
in: body
|
||||
name: data
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.ReadyResourceRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 添加成功
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: 请求参数错误
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"401":
|
||||
description: 认证失败
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"500":
|
||||
description: 服务器内部错误
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: 单个添加资源
|
||||
tags:
|
||||
- PublicAPI
|
||||
/api/public/resources/batch-add:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 通过公开API批量添加多个资源到待处理列表
|
||||
parameters:
|
||||
- description: API访问令牌
|
||||
in: header
|
||||
name: X-API-Token
|
||||
required: true
|
||||
type: string
|
||||
- description: 批量资源信息
|
||||
in: body
|
||||
name: data
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.BatchReadyResourceRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 批量添加成功
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"400":
|
||||
description: 请求参数错误
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"401":
|
||||
description: 认证失败
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"500":
|
||||
description: 服务器内部错误
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: 批量添加资源
|
||||
tags:
|
||||
- PublicAPI
|
||||
/api/public/resources/search:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 搜索资源,支持关键词、标签、分类过滤
|
||||
parameters:
|
||||
- description: API访问令牌
|
||||
in: header
|
||||
name: X-API-Token
|
||||
required: true
|
||||
type: string
|
||||
- description: 搜索关键词
|
||||
in: query
|
||||
name: keyword
|
||||
type: string
|
||||
- description: 标签过滤
|
||||
in: query
|
||||
name: tag
|
||||
type: string
|
||||
- description: 分类过滤
|
||||
in: query
|
||||
name: category
|
||||
type: string
|
||||
- default: 1
|
||||
description: 页码
|
||||
in: query
|
||||
name: page
|
||||
type: integer
|
||||
- default: 20
|
||||
description: 每页数量
|
||||
in: query
|
||||
maximum: 100
|
||||
name: page_size
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 搜索成功
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"401":
|
||||
description: 认证失败
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
"500":
|
||||
description: 服务器内部错误
|
||||
schema:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
summary: 资源搜索
|
||||
tags:
|
||||
- PublicAPI
|
||||
securityDefinitions:
|
||||
ApiTokenAuth:
|
||||
description: API Token认证
|
||||
in: header
|
||||
name: X-API-Token
|
||||
type: apiKey
|
||||
swagger: "2.0"
|
||||
27
main.go
27
main.go
@@ -1,23 +1,3 @@
|
||||
// @title 网盘资源管理系统公开API
|
||||
// @version 1.0
|
||||
// @description 网盘资源管理系统的公开API接口文档
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
|
||||
// @contact.name API Support
|
||||
// @contact.url http://www.swagger.io/support
|
||||
// @contact.email support@swagger.io
|
||||
|
||||
// @license.name Apache 2.0
|
||||
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
// @host localhost:8080
|
||||
// @BasePath /api/public
|
||||
|
||||
// @securityDefinitions.apikey ApiTokenAuth
|
||||
// @in header
|
||||
// @name X-API-Token
|
||||
// @description API Token认证
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -30,13 +10,9 @@ import (
|
||||
"res_db/handlers"
|
||||
"res_db/middleware"
|
||||
|
||||
_ "res_db/docs"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/joho/godotenv"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -220,9 +196,6 @@ func main() {
|
||||
// 静态文件服务
|
||||
r.Static("/uploads", "./uploads")
|
||||
|
||||
// 注册Swagger UI路由
|
||||
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
-- 为system_configs表添加api_token字段
|
||||
-- 执行时间: 2024-12-19
|
||||
|
||||
-- 添加api_token字段
|
||||
ALTER TABLE system_configs
|
||||
ADD COLUMN api_token VARCHAR(100) UNIQUE;
|
||||
|
||||
-- 为现有记录生成默认的api_token
|
||||
UPDATE system_configs
|
||||
SET api_token = CONCAT('api_', MD5(RANDOM()::text), '_', EXTRACT(EPOCH FROM NOW())::bigint)
|
||||
WHERE api_token IS NULL;
|
||||
|
||||
-- 添加索引以提高查询性能
|
||||
CREATE INDEX idx_system_configs_api_token ON system_configs(api_token);
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON COLUMN system_configs.api_token IS '公开API访问令牌,用于API认证';
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"unplugin-auto-import": "^19.3.0",
|
||||
"unplugin-vue-components": "^28.8.0"
|
||||
}
|
||||
}
|
||||
574
pnpm-lock.yaml
generated
574
pnpm-lock.yaml
generated
@@ -1,574 +0,0 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
devDependencies:
|
||||
unplugin-auto-import:
|
||||
specifier: ^19.3.0
|
||||
version: 19.3.0
|
||||
unplugin-vue-components:
|
||||
specifier: ^28.8.0
|
||||
version: 28.8.0(@babel/parser@7.28.0)(vue@3.5.17)
|
||||
|
||||
packages:
|
||||
|
||||
'@babel/helper-string-parser@7.27.1':
|
||||
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-validator-identifier@7.27.1':
|
||||
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/parser@7.28.0':
|
||||
resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/types@7.28.0':
|
||||
resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.4':
|
||||
resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
|
||||
|
||||
'@types/estree@1.0.8':
|
||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||
|
||||
'@vue/compiler-core@3.5.17':
|
||||
resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==}
|
||||
|
||||
'@vue/compiler-dom@3.5.17':
|
||||
resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==}
|
||||
|
||||
'@vue/compiler-sfc@3.5.17':
|
||||
resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==}
|
||||
|
||||
'@vue/compiler-ssr@3.5.17':
|
||||
resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==}
|
||||
|
||||
'@vue/reactivity@3.5.17':
|
||||
resolution: {integrity: sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==}
|
||||
|
||||
'@vue/runtime-core@3.5.17':
|
||||
resolution: {integrity: sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==}
|
||||
|
||||
'@vue/runtime-dom@3.5.17':
|
||||
resolution: {integrity: sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==}
|
||||
|
||||
'@vue/server-renderer@3.5.17':
|
||||
resolution: {integrity: sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==}
|
||||
peerDependencies:
|
||||
vue: 3.5.17
|
||||
|
||||
'@vue/shared@3.5.17':
|
||||
resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==}
|
||||
|
||||
acorn@8.15.0:
|
||||
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
braces@3.0.3:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
chokidar@3.6.0:
|
||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
|
||||
confbox@0.1.8:
|
||||
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
|
||||
|
||||
confbox@0.2.2:
|
||||
resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==}
|
||||
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
debug@4.4.1:
|
||||
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
escape-string-regexp@5.0.0:
|
||||
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
exsolve@1.0.7:
|
||||
resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
|
||||
|
||||
fdir@6.4.6:
|
||||
resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fill-range@7.1.1:
|
||||
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-number@7.0.0:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
js-tokens@9.0.1:
|
||||
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
|
||||
|
||||
local-pkg@1.1.1:
|
||||
resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
magic-string@0.30.17:
|
||||
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||
|
||||
mlly@1.7.4:
|
||||
resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
nanoid@3.3.11:
|
||||
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
picomatch@2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
picomatch@4.0.2:
|
||||
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pkg-types@1.3.1:
|
||||
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
||||
|
||||
pkg-types@2.2.0:
|
||||
resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==}
|
||||
|
||||
postcss@8.5.6:
|
||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
quansync@0.2.10:
|
||||
resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
|
||||
|
||||
readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
||||
scule@1.3.0:
|
||||
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
strip-literal@3.0.0:
|
||||
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
|
||||
|
||||
tinyglobby@0.2.14:
|
||||
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
ufo@1.6.1:
|
||||
resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
|
||||
|
||||
unimport@4.2.0:
|
||||
resolution: {integrity: sha512-mYVtA0nmzrysnYnyb3ALMbByJ+Maosee2+WyE0puXl+Xm2bUwPorPaaeZt0ETfuroPOtG8jj1g/qeFZ6buFnag==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
||||
unplugin-auto-import@19.3.0:
|
||||
resolution: {integrity: sha512-iIi0u4Gq2uGkAOGqlPJOAMI8vocvjh1clGTfSK4SOrJKrt+tirrixo/FjgBwXQNNdS7ofcr7OxzmOb/RjWxeEQ==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@nuxt/kit': ^3.2.2
|
||||
'@vueuse/core': '*'
|
||||
peerDependenciesMeta:
|
||||
'@nuxt/kit':
|
||||
optional: true
|
||||
'@vueuse/core':
|
||||
optional: true
|
||||
|
||||
unplugin-utils@0.2.4:
|
||||
resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
||||
unplugin-vue-components@28.8.0:
|
||||
resolution: {integrity: sha512-2Q6ZongpoQzuXDK0ZsVzMoshH0MWZQ1pzVL538G7oIDKRTVzHjppBDS8aB99SADGHN3lpGU7frraCG6yWNoL5Q==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@babel/parser': ^7.15.8
|
||||
'@nuxt/kit': ^3.2.2 || ^4.0.0
|
||||
vue: 2 || 3
|
||||
peerDependenciesMeta:
|
||||
'@babel/parser':
|
||||
optional: true
|
||||
'@nuxt/kit':
|
||||
optional: true
|
||||
|
||||
unplugin@2.3.5:
|
||||
resolution: {integrity: sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
||||
vue@3.5.17:
|
||||
resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
webpack-virtual-modules@0.6.2:
|
||||
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@babel/helper-string-parser@7.27.1': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.27.1': {}
|
||||
|
||||
'@babel/parser@7.28.0':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.0
|
||||
|
||||
'@babel/types@7.28.0':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.4': {}
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@vue/compiler-core@3.5.17':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.0
|
||||
'@vue/shared': 3.5.17
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-dom@3.5.17':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.5.17
|
||||
'@vue/shared': 3.5.17
|
||||
|
||||
'@vue/compiler-sfc@3.5.17':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.0
|
||||
'@vue/compiler-core': 3.5.17
|
||||
'@vue/compiler-dom': 3.5.17
|
||||
'@vue/compiler-ssr': 3.5.17
|
||||
'@vue/shared': 3.5.17
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.17
|
||||
postcss: 8.5.6
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-ssr@3.5.17':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.17
|
||||
'@vue/shared': 3.5.17
|
||||
|
||||
'@vue/reactivity@3.5.17':
|
||||
dependencies:
|
||||
'@vue/shared': 3.5.17
|
||||
|
||||
'@vue/runtime-core@3.5.17':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.17
|
||||
'@vue/shared': 3.5.17
|
||||
|
||||
'@vue/runtime-dom@3.5.17':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.17
|
||||
'@vue/runtime-core': 3.5.17
|
||||
'@vue/shared': 3.5.17
|
||||
csstype: 3.1.3
|
||||
|
||||
'@vue/server-renderer@3.5.17(vue@3.5.17)':
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.5.17
|
||||
'@vue/shared': 3.5.17
|
||||
vue: 3.5.17
|
||||
|
||||
'@vue/shared@3.5.17': {}
|
||||
|
||||
acorn@8.15.0: {}
|
||||
|
||||
anymatch@3.1.3:
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
braces@3.0.3:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
chokidar@3.6.0:
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
braces: 3.0.3
|
||||
glob-parent: 5.1.2
|
||||
is-binary-path: 2.1.0
|
||||
is-glob: 4.0.3
|
||||
normalize-path: 3.0.0
|
||||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
confbox@0.1.8: {}
|
||||
|
||||
confbox@0.2.2: {}
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
debug@4.4.1:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
escape-string-regexp@5.0.0: {}
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
|
||||
exsolve@1.0.7: {}
|
||||
|
||||
fdir@6.4.6(picomatch@4.0.2):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.2
|
||||
|
||||
fill-range@7.1.1:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
dependencies:
|
||||
binary-extensions: 2.3.0
|
||||
|
||||
is-extglob@2.1.1: {}
|
||||
|
||||
is-glob@4.0.3:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
|
||||
is-number@7.0.0: {}
|
||||
|
||||
js-tokens@9.0.1: {}
|
||||
|
||||
local-pkg@1.1.1:
|
||||
dependencies:
|
||||
mlly: 1.7.4
|
||||
pkg-types: 2.2.0
|
||||
quansync: 0.2.10
|
||||
|
||||
magic-string@0.30.17:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.4
|
||||
|
||||
mlly@1.7.4:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
pathe: 2.0.3
|
||||
pkg-types: 1.3.1
|
||||
ufo: 1.6.1
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
||||
picomatch@4.0.2: {}
|
||||
|
||||
pkg-types@1.3.1:
|
||||
dependencies:
|
||||
confbox: 0.1.8
|
||||
mlly: 1.7.4
|
||||
pathe: 2.0.3
|
||||
|
||||
pkg-types@2.2.0:
|
||||
dependencies:
|
||||
confbox: 0.2.2
|
||||
exsolve: 1.0.7
|
||||
pathe: 2.0.3
|
||||
|
||||
postcss@8.5.6:
|
||||
dependencies:
|
||||
nanoid: 3.3.11
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
quansync@0.2.10: {}
|
||||
|
||||
readdirp@3.6.0:
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
|
||||
scule@1.3.0: {}
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
strip-literal@3.0.0:
|
||||
dependencies:
|
||||
js-tokens: 9.0.1
|
||||
|
||||
tinyglobby@0.2.14:
|
||||
dependencies:
|
||||
fdir: 6.4.6(picomatch@4.0.2)
|
||||
picomatch: 4.0.2
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
ufo@1.6.1: {}
|
||||
|
||||
unimport@4.2.0:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
escape-string-regexp: 5.0.0
|
||||
estree-walker: 3.0.3
|
||||
local-pkg: 1.1.1
|
||||
magic-string: 0.30.17
|
||||
mlly: 1.7.4
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.2
|
||||
pkg-types: 2.2.0
|
||||
scule: 1.3.0
|
||||
strip-literal: 3.0.0
|
||||
tinyglobby: 0.2.14
|
||||
unplugin: 2.3.5
|
||||
unplugin-utils: 0.2.4
|
||||
|
||||
unplugin-auto-import@19.3.0:
|
||||
dependencies:
|
||||
local-pkg: 1.1.1
|
||||
magic-string: 0.30.17
|
||||
picomatch: 4.0.2
|
||||
unimport: 4.2.0
|
||||
unplugin: 2.3.5
|
||||
unplugin-utils: 0.2.4
|
||||
|
||||
unplugin-utils@0.2.4:
|
||||
dependencies:
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.2
|
||||
|
||||
unplugin-vue-components@28.8.0(@babel/parser@7.28.0)(vue@3.5.17):
|
||||
dependencies:
|
||||
chokidar: 3.6.0
|
||||
debug: 4.4.1
|
||||
local-pkg: 1.1.1
|
||||
magic-string: 0.30.17
|
||||
mlly: 1.7.4
|
||||
tinyglobby: 0.2.14
|
||||
unplugin: 2.3.5
|
||||
unplugin-utils: 0.2.4
|
||||
vue: 3.5.17
|
||||
optionalDependencies:
|
||||
'@babel/parser': 7.28.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
unplugin@2.3.5:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
picomatch: 4.0.2
|
||||
webpack-virtual-modules: 0.6.2
|
||||
|
||||
vue@3.5.17:
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.17
|
||||
'@vue/compiler-sfc': 3.5.17
|
||||
'@vue/runtime-dom': 3.5.17
|
||||
'@vue/server-renderer': 3.5.17(vue@3.5.17)
|
||||
'@vue/shared': 3.5.17
|
||||
|
||||
webpack-virtual-modules@0.6.2: {}
|
||||
48
start.sh
48
start.sh
@@ -1,48 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "🚀 启动资源管理系统..."
|
||||
|
||||
# 检查Go是否安装
|
||||
if ! command -v go &> /dev/null; then
|
||||
echo "❌ Go未安装,请先安装Go"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查Node.js是否安装
|
||||
if ! command -v node &> /dev/null; then
|
||||
echo "❌ Node.js未安装,请先安装Node.js"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查PostgreSQL是否运行
|
||||
if ! pg_isready -q; then
|
||||
echo "⚠️ PostgreSQL未运行,请确保PostgreSQL服务已启动"
|
||||
fi
|
||||
|
||||
echo "📦 安装Go依赖..."
|
||||
go mod tidy
|
||||
|
||||
echo "🌐 启动后端服务器..."
|
||||
go run main.go &
|
||||
BACKEND_PID=$!
|
||||
|
||||
echo "⏳ 等待后端启动..."
|
||||
sleep 3
|
||||
|
||||
echo "📦 安装前端依赖..."
|
||||
cd web
|
||||
npm install
|
||||
|
||||
echo "🎨 启动前端开发服务器..."
|
||||
npm run dev &
|
||||
FRONTEND_PID=$!
|
||||
|
||||
echo "✅ 系统启动完成!"
|
||||
echo "📱 前端地址: http://localhost:3000"
|
||||
echo "🔧 后端地址: http://localhost:8080"
|
||||
echo ""
|
||||
echo "按 Ctrl+C 停止服务"
|
||||
|
||||
# 等待用户中断
|
||||
trap "echo '🛑 正在停止服务...'; kill $BACKEND_PID $FRONTEND_PID; exit" INT
|
||||
wait
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== 测试豆瓣服务获取全部数据功能 ==="
|
||||
echo "时间: $(date)"
|
||||
echo
|
||||
|
||||
# 测试电影榜单获取全部数据
|
||||
echo "1. 测试电影榜单获取全部数据..."
|
||||
curl -s "http://localhost:8080/api/hot-dramas/movies?category=热门&type=全部&start=0&limit=0" | jq '.'
|
||||
echo
|
||||
|
||||
# 测试电视剧榜单获取全部数据
|
||||
echo "2. 测试电视剧榜单获取全部数据..."
|
||||
curl -s "http://localhost:8080/api/hot-dramas/tv?category=tv&type=tv&start=0&limit=0" | jq '.'
|
||||
echo
|
||||
|
||||
# 测试调度器处理全部数据
|
||||
echo "3. 测试调度器处理全部数据..."
|
||||
echo "检查日志中的数据处理情况..."
|
||||
echo "应该看到类似 '检测到limit=0,将尝试获取全部数据' 的日志"
|
||||
echo
|
||||
|
||||
echo "=== 测试完成 ==="
|
||||
echo "如果看到 '检测到总数为: XXX,将一次性获取全部数据' 的日志,说明功能正常"
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== 测试原始豆瓣API ==="
|
||||
echo "时间: $(date)"
|
||||
echo
|
||||
|
||||
# 设置请求头
|
||||
HEADERS=(
|
||||
"User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1"
|
||||
"Referer: https://m.douban.com/"
|
||||
"Accept: application/json, text/plain, */*"
|
||||
"Accept-Language: zh-CN,zh;q=0.9,en;q=0.8"
|
||||
"Accept-Encoding: gzip, deflate"
|
||||
"Connection: keep-alive"
|
||||
"Sec-Fetch-Dest: empty"
|
||||
"Sec-Fetch-Mode: cors"
|
||||
"Sec-Fetch-Site: same-origin"
|
||||
)
|
||||
|
||||
# 构建请求头字符串
|
||||
HEADER_STR=""
|
||||
for header in "${HEADERS[@]}"; do
|
||||
HEADER_STR="$HEADER_STR -H \"$header\""
|
||||
done
|
||||
|
||||
# 测试电影榜单API
|
||||
echo "1. 测试电影榜单API(获取前10条)..."
|
||||
MOVIE_URL="https://m.douban.com/rexxar/api/v2/subject/recent_hot/movie?start=0&limit=10&category=热门&type=全部"
|
||||
echo "请求URL: $MOVIE_URL"
|
||||
echo
|
||||
|
||||
eval "curl -s $HEADER_STR \"$MOVIE_URL\" | jq '.'"
|
||||
echo
|
||||
|
||||
# 测试电视剧榜单API
|
||||
echo "2. 测试电视剧榜单API(获取前10条)..."
|
||||
TV_URL="https://m.douban.com/rexxar/api/v2/subject/recent_hot/tv?start=0&limit=10&category=tv&type=tv"
|
||||
echo "请求URL: $TV_URL"
|
||||
echo
|
||||
|
||||
eval "curl -s $HEADER_STR \"$TV_URL\" | jq '.'"
|
||||
echo
|
||||
|
||||
# 测试获取总数
|
||||
echo "3. 测试获取电影总数..."
|
||||
TOTAL_URL="https://m.douban.com/rexxar/api/v2/subject/recent_hot/movie?start=0&limit=1&category=热门&type=全部"
|
||||
echo "请求URL: $TOTAL_URL"
|
||||
echo
|
||||
|
||||
eval "curl -s $HEADER_STR \"$TOTAL_URL\" | jq '.total'"
|
||||
echo
|
||||
|
||||
echo "=== 测试完成 ==="
|
||||
echo "如果看到JSON响应数据,说明API配置正确"
|
||||
echo "如果返回403错误,请检查请求头配置"
|
||||
Reference in New Issue
Block a user