Files
fanqie-go/internal/handlers/common.go
T
chenxin 6981148727 🔧 chore: 初始化番茄小说 API 项目结构
新增项目基础框架,包含核心 API 实现、算法模块、HTTP 处理器等完整功能模块
2026-08-28 15:22:22 +08:00

175 lines
5.1 KiB
Go

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