🔧 chore: 初始化番茄小说 API 项目结构
新增项目基础框架,包含核心 API 实现、算法模块、HTTP 处理器等完整功能模块
This commit is contained in:
commit
6981148727
107 files changed
+12463
No files matched your search
@@ -0,0 +1,174 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fanqie/internal/config"
|
||||
"fanqie/internal/fqapi"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
startTime = time.Now()
|
||||
deviceHeader string
|
||||
|
||||
// 请求统计
|
||||
totalRequests int64
|
||||
searchRequests int64
|
||||
detailRequests int64
|
||||
catalogRequests int64
|
||||
contentRequests int64
|
||||
mangaRequests int64
|
||||
audioRequests int64
|
||||
videoRequests int64
|
||||
categoryRequests int64
|
||||
|
||||
// 统计保存定时器
|
||||
statsSaveTicker *time.Ticker
|
||||
)
|
||||
|
||||
// 统计函数
|
||||
func incrementTotal() { atomic.AddInt64(&totalRequests, 1) }
|
||||
func incrementSearch() { atomic.AddInt64(&searchRequests, 1) }
|
||||
func incrementDetail() { atomic.AddInt64(&detailRequests, 1) }
|
||||
func incrementCatalog() { atomic.AddInt64(&catalogRequests, 1) }
|
||||
func incrementContent() { atomic.AddInt64(&contentRequests, 1) }
|
||||
func incrementManga() { atomic.AddInt64(&mangaRequests, 1) }
|
||||
func incrementAudio() { atomic.AddInt64(&audioRequests, 1) }
|
||||
func incrementVideo() { atomic.AddInt64(&videoRequests, 1) }
|
||||
func incrementCategory() { atomic.AddInt64(&categoryRequests, 1) }
|
||||
|
||||
func getTotal() int64 { return atomic.LoadInt64(&totalRequests) }
|
||||
func getSearch() int64 { return atomic.LoadInt64(&searchRequests) }
|
||||
func getDetail() int64 { return atomic.LoadInt64(&detailRequests) }
|
||||
func getCatalog() int64 { return atomic.LoadInt64(&catalogRequests) }
|
||||
func getContent() int64 { return atomic.LoadInt64(&contentRequests) }
|
||||
func getManga() int64 { return atomic.LoadInt64(&mangaRequests) }
|
||||
func getAudio() int64 { return atomic.LoadInt64(&audioRequests) }
|
||||
func getVideo() int64 { return atomic.LoadInt64(&videoRequests) }
|
||||
func getCategory() int64 { return atomic.LoadInt64(&categoryRequests) }
|
||||
|
||||
// SetDeviceHeader 设置设备header
|
||||
func SetDeviceHeader(header string) {
|
||||
deviceHeader = header
|
||||
}
|
||||
|
||||
// LoadStatistics 从数据库加载统计数据
|
||||
func LoadStatistics() {
|
||||
stats, err := fqapi.LoadStatistics()
|
||||
if err != nil {
|
||||
fmt.Printf("加载统计数据失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
atomic.StoreInt64(&totalRequests, stats["total"])
|
||||
atomic.StoreInt64(&searchRequests, stats["search"])
|
||||
atomic.StoreInt64(&detailRequests, stats["detail"])
|
||||
atomic.StoreInt64(&catalogRequests, stats["catalog"])
|
||||
atomic.StoreInt64(&contentRequests, stats["content"])
|
||||
atomic.StoreInt64(&mangaRequests, stats["manga"])
|
||||
atomic.StoreInt64(&audioRequests, stats["audio"])
|
||||
atomic.StoreInt64(&videoRequests, stats["video"])
|
||||
atomic.StoreInt64(&categoryRequests, stats["category"])
|
||||
}
|
||||
|
||||
// SaveStatistics 保存统计数据到数据库
|
||||
func SaveStatistics() {
|
||||
stats := map[string]int64{
|
||||
"total": atomic.LoadInt64(&totalRequests),
|
||||
"search": atomic.LoadInt64(&searchRequests),
|
||||
"detail": atomic.LoadInt64(&detailRequests),
|
||||
"catalog": atomic.LoadInt64(&catalogRequests),
|
||||
"content": atomic.LoadInt64(&contentRequests),
|
||||
"manga": atomic.LoadInt64(&mangaRequests),
|
||||
"audio": atomic.LoadInt64(&audioRequests),
|
||||
"video": atomic.LoadInt64(&videoRequests),
|
||||
"category": atomic.LoadInt64(&categoryRequests),
|
||||
}
|
||||
|
||||
if err := fqapi.SaveStatistics(stats); err != nil {
|
||||
fmt.Printf("保存统计数据失败: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// StartStatisticsSaver 启动统计数据定期保存
|
||||
func StartStatisticsSaver() {
|
||||
// 每30秒保存一次统计数据
|
||||
statsSaveTicker = time.NewTicker(30 * time.Second)
|
||||
go func() {
|
||||
for range statsSaveTicker.C {
|
||||
SaveStatistics()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// StopStatisticsSaver 停止统计数据定期保存
|
||||
func StopStatisticsSaver() {
|
||||
if statsSaveTicker != nil {
|
||||
statsSaveTicker.Stop()
|
||||
// 最后保存一次
|
||||
SaveStatistics()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireAccessKey 验证访问密钥中间件
|
||||
func RequireAccessKey(handler http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
cfg := config.GetConfig()
|
||||
|
||||
// 如果未配置 access_key 或为空,则不启用验证
|
||||
if cfg == nil || cfg.Server.AccessKey == "" {
|
||||
handler(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// 从查询参数或 Header 中获取 key
|
||||
key := r.URL.Query().Get("key")
|
||||
if key == "" {
|
||||
key = r.Header.Get("X-Access-Key")
|
||||
}
|
||||
|
||||
// 验证密钥
|
||||
if key != cfg.Server.AccessKey {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "访问被拒绝:无效的访问密钥"}`)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证通过,继续处理请求
|
||||
handler(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleMain 主页处理
|
||||
func HandleMain(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
response := map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": nil,
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// HandleSelfInfo 设备信息处理
|
||||
func HandleSelfInfo(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
// 获取设备池统计信息
|
||||
pool := fqapi.GetDeviceKeyPool()
|
||||
poolStats := pool.GetStats()
|
||||
|
||||
response := map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "SUCCESS",
|
||||
"data": map[string]interface{}{
|
||||
"device_pool_enabled": pool.IsEnabled(),
|
||||
"device_pool_stats": poolStats,
|
||||
},
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
Reference in new issue
Block a user