整理了路由,添加了前缀/api/rank

This commit is contained in:
xbh 2025-10-18 00:04:53 +08:00
parent 93928b8a66
commit 2c0f891a89
3 changed files with 472 additions and 541 deletions

73
app.py
View File

@ -1,4 +1,4 @@
from flask import Flask
from flask import Flask, jsonify
from flask_cors import CORS
import logging
import os
@ -20,74 +20,13 @@ logging.basicConfig(
]
)
# 导入路由
from routers.rank_api_routes import api
# 导入并注册蓝图
from routers.rank_api_routes import rank_bp
app.register_blueprint(rank_bp)
# 注册路由
@app.route('/')
def index():
"""API首页"""
from flask import jsonify
return jsonify({
"name": "抖音播放量数据API服务",
"version": "2.0",
"description": "主程序服务 - 整合小程序API功能",
"endpoints": {
"/api/videos": "获取视频列表 (支持分页和排序)",
"/api/top": "获取热门视频榜单",
"/api/search": "搜索视频",
"/api/detail": "获取视频详情",
"/api/stats": "获取统计信息",
"/api/health": "健康检查"
},
"features": [
"分页支持",
"多种排序方式",
"搜索功能",
"详情查看",
"统计分析",
"小程序优化"
]
})
# 注册小程序API路由
@app.route('/api/videos')
def get_videos():
return api.get_videos()
@app.route('/api/top')
def get_top():
return api.get_top()
@app.route('/api/search')
def search():
return api.search()
@app.route('/api/detail')
def get_detail():
return api.get_detail()
@app.route('/api/stats')
def get_stats():
return api.get_stats()
@app.route('/api/health')
def health_check():
return api.health_check()
if __name__ == '__main__':
print("启动主程序服务...")
print("服务地址: http://localhost:5000")
print("API接口列表:")
print(" - GET / 显示API信息")
print(" - GET /api/videos?page=1&limit=20&sort=playcount 获取视频列表(总播放量排序)")
print(" - GET /api/videos?page=1&limit=20&sort=growth 获取视频列表(增长排序,默认昨天到今天的差值)")
print(" - GET /api/videos?page=1&limit=20&sort=growth&start_date=2025-10-16&end_date=2025-10-17 获取视频列表(自定义日期范围增长排序)")
print(" - GET /api/top?limit=10 获取热门榜单")
print(" - GET /api/search?q=关键词&page=1&limit=10 搜索视频")
print(" - GET /api/detail?id=视频ID 获取视频详情")
print(" - GET /api/stats 获取统计信息")
print(" - GET /api/health 健康检查")
print("专为小程序优化:分页、搜索、详情、统计、增长排序、自定义日期范围")
print("服务地址: http://localhost:5001")
app.run(host='0.0.0.0', port=5000, debug=True)
app.run(host='0.0.0.0', port=5001, debug=True)

View File

@ -2,8 +2,8 @@ import os
import importlib
# 数据库配置
# MONGO_URI = "mongodb://localhost:27017"
MONGO_URI = "mongodb://mongouser:Jdei2243afN@172.16.0.6:27017,172.16.0.4:27017/test?replicaSet=cmgo-r6qkaern_0&authSource=admin"
MONGO_URI = "mongodb://localhost:27017"
# MONGO_URI = "mongodb://mongouser:Jdei2243afN@172.16.0.6:27017,172.16.0.4:27017/test?replicaSet=cmgo-r6qkaern_0&authSource=admin"
MONGO_DB_NAME = "kemeng_media"
# 应用配置
@ -15,6 +15,6 @@ LOG_LEVEL = 'INFO'
LOG_DIR = 'logs'
# 定时器配置
SCHEDULER_TIME = "20:23" # 定时器执行时间,格式为 HH:MM (24小时制)
SCHEDULER_TIME = "00:01" # 定时器执行时间,格式为 HH:MM (24小时制)
print(f"Successfully loaded configuration for environment: {APP_ENV}")

View File

@ -5,34 +5,19 @@
优化的数据格式和接口设计专为小程序使用
"""
from pymongo import MongoClient
from flask import Blueprint, request, jsonify
from datetime import datetime, timedelta
import logging
import re
from database import db
class MiniprogramAPI:
def __init__(self):
self.client = None
self.db = None
self.collection = None
self.connect_mongodb()
# 创建蓝图
rank_bp = Blueprint('rank', __name__, url_prefix='/api/rank')
def connect_mongodb(self):
"""连接MongoDB数据库"""
try:
self.client = MongoClient('mongodb://localhost:27017/')
# 测试连接
self.client.admin.command('ping')
# 使用数据库与集合
self.db = self.client['Rankings']
self.collection = self.db['Rankings_list']
logging.info("MongoDB连接成功")
return True
except Exception as e:
logging.error(f"MongoDB连接失败: {e}")
return False
# 获取数据库集合
collection = db['Rankings_list']
def format_playcount(self, playcount_str):
def format_playcount(playcount_str):
"""格式化播放量字符串为数字"""
if not playcount_str:
return 0
@ -56,7 +41,7 @@ class MiniprogramAPI:
except:
return 0
def format_cover_url(self, cover_data):
def format_cover_url(cover_data):
"""格式化封面图片URL"""
if not cover_data:
return ""
@ -68,7 +53,7 @@ class MiniprogramAPI:
else:
return ""
def format_time(self, time_obj):
def format_time(time_obj):
"""格式化时间"""
if not time_obj:
return ""
@ -78,11 +63,11 @@ class MiniprogramAPI:
else:
return str(time_obj)
def format_mix_item(self, doc):
def format_mix_item(doc):
"""格式化合集数据项 - 完全按照数据库原始字段返回"""
return {
"_id": str(doc.get("_id", "")),
"batch_time": self.format_time(doc.get("batch_time")),
"batch_time": format_time(doc.get("batch_time")),
"mix_name": doc.get("mix_name", ""),
"video_url": doc.get("video_url", ""),
"playcount": doc.get("playcount", ""),
@ -93,7 +78,7 @@ class MiniprogramAPI:
"cover_backup_urls": doc.get("cover_backup_urls", [])
}
def get_mix_list(self, page=1, limit=20, sort_by="playcount"):
def get_mix_list(page=1, limit=20, sort_by="playcount"):
"""获取合集列表(分页)"""
try:
# 计算跳过的数量
@ -102,7 +87,7 @@ class MiniprogramAPI:
# 设置排序字段
if sort_by == "growth":
# 按增长排序需要特殊处理
return self.get_growth_mixes(page, limit)
return get_growth_mixes(page, limit)
else:
sort_field = "play_vv" if sort_by == "playcount" else "batch_time"
sort_order = -1 # 降序
@ -132,7 +117,7 @@ class MiniprogramAPI:
{"$limit": limit}
]
docs = list(self.collection.aggregate(pipeline))
docs = list(collection.aggregate(pipeline))
# 获取总数
total_pipeline = [
@ -141,13 +126,13 @@ class MiniprogramAPI:
{"$group": {"_id": "$mix_name"}},
{"$count": "total"}
]
total_result = list(self.collection.aggregate(total_pipeline))
total_result = list(collection.aggregate(total_pipeline))
total = total_result[0]["total"] if total_result else 0
# 格式化数据
mix_list = []
for doc in docs:
item = self.format_mix_item(doc)
item = format_mix_item(doc)
mix_list.append(item)
return {
@ -169,7 +154,7 @@ class MiniprogramAPI:
logging.error(f"获取合集列表失败: {e}")
return {"success": False, "message": f"获取数据失败: {str(e)}"}
def get_growth_mixes(self, page=1, limit=20, start_date=None, end_date=None):
def get_growth_mixes(page=1, limit=20, start_date=None, end_date=None):
"""获取按播放量增长排序的合集列表"""
try:
# 计算跳过的数量
@ -187,7 +172,7 @@ class MiniprogramAPI:
end_date = datetime.strptime(end_date, "%Y-%m-%d").date()
# 查询结束日期的数据
end_cursor = self.collection.find({
end_cursor = collection.find({
"batch_time": {
"$gte": datetime(end_date.year, end_date.month, end_date.day),
"$lt": datetime(end_date.year, end_date.month, end_date.day) + timedelta(days=1)
@ -196,7 +181,7 @@ class MiniprogramAPI:
end_data = list(end_cursor)
# 查询开始日期的数据
start_cursor = self.collection.find({
start_cursor = collection.find({
"batch_time": {
"$gte": datetime(start_date.year, start_date.month, start_date.day),
"$lt": datetime(start_date.year, start_date.month, start_date.day) + timedelta(days=1)
@ -217,14 +202,14 @@ class MiniprogramAPI:
# 只保留增长为正的数据
if growth > 0:
item = self.format_mix_item(end_item)
item = format_mix_item(end_item)
item["growth"] = growth
item["start_date"] = start_date.strftime("%Y-%m-%d")
item["end_date"] = end_date.strftime("%Y-%m-%d")
growth_data.append(item)
else:
# 如果开始日期没有数据,但结束日期有,也认为是新增长
item = self.format_mix_item(end_item)
item = format_mix_item(end_item)
item["growth"] = end_item.get("play_vv", 0)
item["start_date"] = start_date.strftime("%Y-%m-%d")
item["end_date"] = end_date.strftime("%Y-%m-%d")
@ -263,13 +248,13 @@ class MiniprogramAPI:
except Exception as e:
logging.error(f"获取增长合集列表失败: {e}")
# 如果增长计算失败,返回按播放量排序的数据作为备选
return self.get_mix_list(page, limit, "playcount")
return get_mix_list(page, limit, "playcount")
def get_top_mixes(self, limit=10):
def get_top_mixes(limit=10):
"""获取热门合集TOP榜单"""
try:
# 按播放量排序获取热门合集
cursor = self.collection.find().sort("play_vv", -1).limit(limit)
cursor = collection.find().sort("play_vv", -1).limit(limit)
docs = list(cursor)
if not docs:
@ -278,21 +263,21 @@ class MiniprogramAPI:
# 格式化数据
top_list = []
for doc in docs:
item = self.format_mix_item(doc)
item = format_mix_item(doc)
top_list.append(item)
return {
"success": True,
"data": top_list,
"total": len(top_list),
"update_time": self.format_time(docs[0].get("batch_time")) if docs else ""
"update_time": format_time(docs[0].get("batch_time")) if docs else ""
}
except Exception as e:
logging.error(f"获取热门合集失败: {e}")
return {"success": False, "message": f"获取数据失败: {str(e)}"}
def search_mixes(self, keyword, page=1, limit=10):
def search_mixes(keyword, page=1, limit=10):
"""搜索合集"""
try:
if not keyword:
@ -307,16 +292,16 @@ class MiniprogramAPI:
}
# 查询数据
cursor = self.collection.find(search_condition).sort("play_vv", -1).skip(skip).limit(limit)
cursor = collection.find(search_condition).sort("play_vv", -1).skip(skip).limit(limit)
docs = list(cursor)
# 获取搜索结果总数
total = self.collection.count_documents(search_condition)
total = collection.count_documents(search_condition)
# 格式化数据
search_results = []
for doc in docs:
item = self.format_mix_item(doc)
item = format_mix_item(doc)
search_results.append(item)
return {
@ -338,17 +323,17 @@ class MiniprogramAPI:
logging.error(f"搜索合集失败: {e}")
return {"success": False, "message": f"搜索失败: {str(e)}"}
def get_mix_detail(self, mix_id):
def get_mix_detail(mix_id):
"""获取合集详情"""
try:
from bson import ObjectId
# 尝试通过ObjectId查找
try:
doc = self.collection.find_one({"_id": ObjectId(mix_id)})
doc = collection.find_one({"_id": ObjectId(mix_id)})
except:
# 如果ObjectId无效尝试其他字段
doc = self.collection.find_one({
doc = collection.find_one({
"$or": [
{"mix_name": mix_id},
{"request_id": mix_id}
@ -359,7 +344,7 @@ class MiniprogramAPI:
return {"success": False, "message": "未找到合集信息"}
# 格式化详细信息 - 只返回数据库原始字段
detail = self.format_mix_item(doc)
detail = format_mix_item(doc)
return {
"success": True,
@ -371,11 +356,11 @@ class MiniprogramAPI:
logging.error(f"获取合集详情失败: {e}")
return {"success": False, "message": f"获取详情失败: {str(e)}"}
def get_statistics(self):
def get_statistics():
"""获取统计信息"""
try:
# 基本统计
total_mixes = self.collection.count_documents({})
total_mixes = collection.count_documents({})
if total_mixes == 0:
return {"success": False, "message": "暂无数据"}
@ -393,16 +378,16 @@ class MiniprogramAPI:
}
]
stats_result = list(self.collection.aggregate(pipeline))
stats_result = list(collection.aggregate(pipeline))
stats = stats_result[0] if stats_result else {}
# 获取最新更新时间
latest_doc = self.collection.find().sort("batch_time", -1).limit(1)
latest_doc = collection.find().sort("batch_time", -1).limit(1)
latest_time = ""
if latest_doc:
latest_list = list(latest_doc)
if latest_list:
latest_time = self.format_time(latest_list[0].get("batch_time"))
latest_time = format_time(latest_list[0].get("batch_time"))
# 热门分类统计(按播放量区间)
categories = [
@ -414,11 +399,11 @@ class MiniprogramAPI:
for category in categories:
if "max" in category:
count = self.collection.count_documents({
count = collection.count_documents({
"play_vv": {"$gte": category["min"], "$lte": category["max"]}
})
else:
count = self.collection.count_documents({
count = collection.count_documents({
"play_vv": {"$gte": category["min"]}
})
category["count"] = count
@ -441,10 +426,10 @@ class MiniprogramAPI:
logging.error(f"获取统计信息失败: {e}")
return {"success": False, "message": f"获取统计失败: {str(e)}"}
def get_videos(self):
# 路由定义
@rank_bp.route('/videos')
def get_videos():
"""获取合集列表 - 兼容app.py调用"""
from flask import request
page = int(request.args.get('page', 1))
limit = int(request.args.get('limit', 20))
sort_by = request.args.get('sort', 'playcount')
@ -452,48 +437,58 @@ class MiniprogramAPI:
if sort_by == 'growth':
start_date = request.args.get('start_date')
end_date = request.args.get('end_date')
return self.get_growth_mixes(page, limit, start_date, end_date)
result = get_growth_mixes(page, limit, start_date, end_date)
else:
return self.get_mix_list(page, limit, sort_by)
result = get_mix_list(page, limit, sort_by)
def get_top(self):
return jsonify(result)
@rank_bp.route('/top')
def get_top():
"""获取热门榜单 - 兼容app.py调用"""
from flask import request
limit = int(request.args.get('limit', 10))
return self.get_top_mixes(limit)
result = get_top_mixes(limit)
return jsonify(result)
def search(self):
@rank_bp.route('/search')
def search():
"""搜索合集 - 兼容app.py调用"""
from flask import request
keyword = request.args.get('q', '')
page = int(request.args.get('page', 1))
limit = int(request.args.get('limit', 10))
return self.search_mixes(keyword, page, limit)
result = search_mixes(keyword, page, limit)
return jsonify(result)
def get_detail(self):
@rank_bp.route('/detail')
def get_detail():
"""获取合集详情 - 兼容app.py调用"""
from flask import request
mix_id = request.args.get('id', '')
return self.get_mix_detail(mix_id)
result = get_mix_detail(mix_id)
return jsonify(result)
def get_stats(self):
@rank_bp.route('/stats')
def get_stats():
"""获取统计信息 - 兼容app.py调用"""
return self.get_statistics()
result = get_statistics()
return jsonify(result)
def health_check(self):
@rank_bp.route('/health')
def health_check():
"""健康检查 - 兼容app.py调用"""
try:
from database import client
# 检查数据库连接
if not self.client:
return {"success": False, "message": "数据库未连接"}
if not client:
return jsonify({"success": False, "message": "数据库未连接"})
# 测试数据库连接
self.client.admin.command('ping')
client.admin.command('ping')
# 获取数据统计
total_count = self.collection.count_documents({})
total_count = collection.count_documents({})
return {
return jsonify({
"success": True,
"message": "服务正常",
"data": {
@ -501,10 +496,7 @@ class MiniprogramAPI:
"total_records": total_count,
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
}
})
except Exception as e:
logging.error(f"健康检查失败: {e}")
return {"success": False, "message": f"服务异常: {str(e)}"}
# 创建API实例
api = MiniprogramAPI()
return jsonify({"success": False, "message": f"服务异常: {str(e)}"})