新增后台通知中心和产品审核发布功能
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -11,6 +11,14 @@ from utils import load_data, save_data, parse_date_to_timestamp
|
||||
cpus_bp = Blueprint('api_cpus', __name__)
|
||||
|
||||
|
||||
def is_review_required():
|
||||
try:
|
||||
import app as app_module
|
||||
return getattr(app_module, 'REQUIRE_REVIEW', False)
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def _safe_sort_key(x, key):
|
||||
val = x.get(key)
|
||||
if val is None:
|
||||
@@ -54,6 +62,23 @@ def api_cpu_detail(cpu_id):
|
||||
@cpus_bp.route('/api/cpus', methods=['POST'])
|
||||
def api_create_cpu():
|
||||
data = request.get_json()
|
||||
|
||||
# 审核模式
|
||||
if is_review_required():
|
||||
from modules.routes.api_reviews import submit_for_review
|
||||
from modules.routes.api_notifications import create_notification
|
||||
|
||||
review = submit_for_review('cpus', data, source='web')
|
||||
product_name = data.get('name', '未知')
|
||||
create_notification(
|
||||
title='新产品待审核',
|
||||
message=f'有新的CPU "{product_name}"待审核',
|
||||
level='warning',
|
||||
category='review',
|
||||
data={'review_id': review['id'], 'category': 'cpus'}
|
||||
)
|
||||
return jsonify({'success': True, 'message': '已提交审核,请等待管理员确认', 'review_id': review['id']})
|
||||
|
||||
cpus = load_data(CPUS_FILE)
|
||||
data['id'] = uuid.uuid4().hex[:12]
|
||||
data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
@@ -11,6 +11,14 @@ from utils import load_data, save_data, parse_date_to_timestamp
|
||||
gpus_bp = Blueprint('api_gpus', __name__)
|
||||
|
||||
|
||||
def is_review_required():
|
||||
try:
|
||||
import app as app_module
|
||||
return getattr(app_module, 'REQUIRE_REVIEW', False)
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def _safe_sort_key(x, key):
|
||||
val = x.get(key)
|
||||
if val is None:
|
||||
@@ -54,6 +62,23 @@ def api_gpu_detail(gpu_id):
|
||||
@gpus_bp.route('/api/gpus', methods=['POST'])
|
||||
def api_create_gpu():
|
||||
data = request.get_json()
|
||||
|
||||
# 审核模式
|
||||
if is_review_required():
|
||||
from modules.routes.api_reviews import submit_for_review
|
||||
from modules.routes.api_notifications import create_notification
|
||||
|
||||
review = submit_for_review('gpus', data, source='web')
|
||||
product_name = data.get('name', '未知')
|
||||
create_notification(
|
||||
title='新产品待审核',
|
||||
message=f'有新的GPU "{product_name}"待审核',
|
||||
level='warning',
|
||||
category='review',
|
||||
data={'review_id': review['id'], 'category': 'gpus'}
|
||||
)
|
||||
return jsonify({'success': True, 'message': '已提交审核,请等待管理员确认', 'review_id': review['id']})
|
||||
|
||||
gpus = load_data(GPUS_FILE)
|
||||
data['id'] = uuid.uuid4().hex[:12]
|
||||
data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
@@ -11,6 +11,14 @@ from utils import load_data, save_data, parse_date_to_timestamp
|
||||
items_bp = Blueprint('api_items', __name__)
|
||||
|
||||
|
||||
def is_review_required():
|
||||
try:
|
||||
import app as app_module
|
||||
return getattr(app_module, 'REQUIRE_REVIEW', False)
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
@items_bp.route('/api/items/<category_id>')
|
||||
def api_items(category_id):
|
||||
items_file = DATA_DIR / f'items_{category_id}.json'
|
||||
@@ -49,6 +57,23 @@ def api_item_detail(category_id, item_id):
|
||||
@items_bp.route('/api/items/<category_id>', methods=['POST'])
|
||||
def api_create_item(category_id):
|
||||
data = request.get_json()
|
||||
|
||||
# 审核模式
|
||||
if is_review_required():
|
||||
from modules.routes.api_reviews import submit_for_review
|
||||
from modules.routes.api_notifications import create_notification
|
||||
|
||||
review = submit_for_review(category_id, data, source='web')
|
||||
product_name = data.get('name', '未知')
|
||||
create_notification(
|
||||
title='新产品待审核',
|
||||
message=f'有新的产品"{product_name}"待审核',
|
||||
level='warning',
|
||||
category='review',
|
||||
data={'review_id': review['id'], 'category': category_id}
|
||||
)
|
||||
return jsonify({'success': True, 'message': '已提交审核,请等待管理员确认', 'review_id': review['id']})
|
||||
|
||||
items_file = DATA_DIR / f'items_{category_id}.json'
|
||||
items = load_data(items_file)
|
||||
data['id'] = uuid.uuid4().hex[:12]
|
||||
|
||||
@@ -11,6 +11,15 @@ from utils import load_data, save_data, parse_date_to_timestamp, safe_sort_key
|
||||
models_bp = Blueprint('api_models', __name__)
|
||||
|
||||
|
||||
def is_review_required():
|
||||
"""检查是否需要审核"""
|
||||
try:
|
||||
import app as app_module
|
||||
return getattr(app_module, 'REQUIRE_REVIEW', False)
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
@models_bp.route('/api/models')
|
||||
def api_models():
|
||||
models = load_data(MODELS_FILE)
|
||||
@@ -47,6 +56,24 @@ def api_model_detail(model_id):
|
||||
@models_bp.route('/api/models', methods=['POST'])
|
||||
def api_create_model():
|
||||
data = request.get_json()
|
||||
|
||||
# 审核模式:提交到审核队列
|
||||
if is_review_required():
|
||||
from modules.routes.api_reviews import submit_for_review
|
||||
from modules.routes.api_notifications import create_notification
|
||||
|
||||
review = submit_for_review('ai-models', data, source='web')
|
||||
product_name = data.get('name', '未知')
|
||||
create_notification(
|
||||
title='新产品待审核',
|
||||
message=f'有新的AI模型"{product_name}"待审核',
|
||||
level='warning',
|
||||
category='review',
|
||||
data={'review_id': review['id'], 'category': 'ai-models'}
|
||||
)
|
||||
return jsonify({'success': True, 'message': '已提交审核,请等待管理员确认', 'review_id': review['id']})
|
||||
|
||||
# 直接创建模式
|
||||
models = load_data(MODELS_FILE)
|
||||
data['id'] = uuid.uuid4().hex[:12]
|
||||
data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
通知管理 API
|
||||
"""
|
||||
import uuid
|
||||
import json
|
||||
from datetime import datetime
|
||||
from flask import Blueprint, request, jsonify
|
||||
from config import DATA_DIR
|
||||
from utils import load_data, save_data
|
||||
|
||||
notifications_bp = Blueprint('api_notifications', __name__)
|
||||
|
||||
NOTIFICATIONS_FILE = DATA_DIR / 'notifications.json'
|
||||
|
||||
|
||||
@notifications_bp.route('/api/notifications')
|
||||
def api_notifications():
|
||||
"""获取通知列表"""
|
||||
notifications = load_data(NOTIFICATIONS_FILE)
|
||||
|
||||
# 筛选参数
|
||||
unread_only = request.args.get('unread', '0') == '1'
|
||||
limit = int(request.args.get('limit', 50))
|
||||
|
||||
if unread_only:
|
||||
notifications = [n for n in notifications if not n.get('read', False)]
|
||||
|
||||
# 按时间倒序
|
||||
notifications = sorted(notifications, key=lambda x: x.get('created_at', ''), reverse=True)
|
||||
|
||||
return jsonify(notifications[:limit])
|
||||
|
||||
|
||||
@notifications_bp.route('/api/notifications/unread-count')
|
||||
def api_unread_count():
|
||||
"""获取未读通知数量"""
|
||||
notifications = load_data(NOTIFICATIONS_FILE)
|
||||
count = len([n for n in notifications if not n.get('read', False)])
|
||||
return jsonify({'count': count})
|
||||
|
||||
|
||||
@notifications_bp.route('/api/notifications/<notification_id>/read', methods=['POST'])
|
||||
def api_mark_read(notification_id):
|
||||
"""标记通知为已读"""
|
||||
notifications = load_data(NOTIFICATIONS_FILE)
|
||||
notification = next((n for n in notifications if n['id'] == notification_id), None)
|
||||
if not notification:
|
||||
return jsonify({'error': 'Notification not found'}), 404
|
||||
notification['read'] = True
|
||||
notification['read_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
save_data(NOTIFICATIONS_FILE, notifications)
|
||||
return jsonify({'success': True})
|
||||
|
||||
|
||||
@notifications_bp.route('/api/notifications/read-all', methods=['POST'])
|
||||
def api_mark_all_read():
|
||||
"""标记所有通知为已读"""
|
||||
notifications = load_data(NOTIFICATIONS_FILE)
|
||||
for n in notifications:
|
||||
if not n.get('read', False):
|
||||
n['read'] = True
|
||||
n['read_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
save_data(NOTIFICATIONS_FILE, notifications)
|
||||
return jsonify({'success': True})
|
||||
|
||||
|
||||
@notifications_bp.route('/api/notifications/<notification_id>', methods=['DELETE'])
|
||||
def api_delete_notification(notification_id):
|
||||
"""删除通知"""
|
||||
notifications = load_data(NOTIFICATIONS_FILE)
|
||||
notifications = [n for n in notifications if n['id'] != notification_id]
|
||||
save_data(NOTIFICATIONS_FILE, notifications)
|
||||
return jsonify({'success': True})
|
||||
|
||||
|
||||
# ─── 内部函数:创建通知 ───────────────────────────────────────────────────────
|
||||
|
||||
def create_notification(title, message, level='info', category='system', data=None):
|
||||
"""
|
||||
创建一条通知
|
||||
|
||||
参数:
|
||||
title: 通知标题
|
||||
message: 通知内容
|
||||
level: 级别 info/warning/error/success
|
||||
category: 分类 system/api/review/statistics
|
||||
data: 附加数据(dict)
|
||||
"""
|
||||
notifications = load_data(NOTIFICATIONS_FILE)
|
||||
|
||||
notification = {
|
||||
'id': uuid.uuid4().hex[:12],
|
||||
'title': title,
|
||||
'message': message,
|
||||
'level': level, # info, warning, error, success
|
||||
'category': category, # system, api, review, statistics
|
||||
'data': data or {},
|
||||
'read': False,
|
||||
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
|
||||
notifications.append(notification)
|
||||
|
||||
# 只保留最近500条通知
|
||||
if len(notifications) > 500:
|
||||
notifications = sorted(notifications, key=lambda x: x.get('created_at', ''), reverse=True)[:500]
|
||||
|
||||
save_data(NOTIFICATIONS_FILE, notifications)
|
||||
return notification
|
||||
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
产品审核 API
|
||||
"""
|
||||
import uuid
|
||||
import json
|
||||
from datetime import datetime
|
||||
from flask import Blueprint, request, jsonify, session
|
||||
from config import DATA_DIR, MODELS_FILE, GPUS_FILE, CPUS_FILE
|
||||
from utils import load_data, save_data
|
||||
|
||||
reviews_bp = Blueprint('api_reviews', __name__)
|
||||
|
||||
PENDING_FILE = DATA_DIR / 'pending_reviews.json'
|
||||
|
||||
|
||||
@reviews_bp.route('/api/reviews')
|
||||
def api_reviews():
|
||||
"""获取待审核列表"""
|
||||
reviews = load_data(PENDING_FILE)
|
||||
|
||||
# 筛选参数
|
||||
status = request.args.get('status', 'pending') # pending, approved, rejected, all
|
||||
limit = int(request.args.get('limit', 100))
|
||||
|
||||
if status != 'all':
|
||||
reviews = [r for r in reviews if r.get('status', 'pending') == status]
|
||||
|
||||
# 按时间倒序
|
||||
reviews = sorted(reviews, key=lambda x: x.get('created_at', ''), reverse=True)
|
||||
|
||||
return jsonify(reviews[:limit])
|
||||
|
||||
|
||||
@reviews_bp.route('/api/reviews/count')
|
||||
def api_reviews_count():
|
||||
"""获取待审核数量"""
|
||||
reviews = load_data(PENDING_FILE)
|
||||
pending_count = len([r for r in reviews if r.get('status', 'pending') == 'pending'])
|
||||
return jsonify({'count': pending_count})
|
||||
|
||||
|
||||
@reviews_bp.route('/api/reviews/<review_id>')
|
||||
def api_review_detail(review_id):
|
||||
"""获取审核详情"""
|
||||
reviews = load_data(PENDING_FILE)
|
||||
review = next((r for r in reviews if r['id'] == review_id), None)
|
||||
if not review:
|
||||
return jsonify({'error': 'Review not found'}), 404
|
||||
return jsonify(review)
|
||||
|
||||
|
||||
@reviews_bp.route('/api/reviews/<review_id>/approve', methods=['POST'])
|
||||
def api_approve_review(review_id):
|
||||
"""通过审核"""
|
||||
reviews = load_data(PENDING_FILE)
|
||||
review = next((r for r in reviews if r['id'] == review_id), None)
|
||||
if not review:
|
||||
return jsonify({'error': 'Review not found'}), 404
|
||||
|
||||
if review.get('status') != 'pending':
|
||||
return jsonify({'error': '该申请已处理'}), 400
|
||||
|
||||
# 获取数据文件
|
||||
category_id = review.get('category_id')
|
||||
data_file = get_data_file(category_id)
|
||||
if not data_file:
|
||||
return jsonify({'error': 'Unknown category'}), 400
|
||||
|
||||
# 添加数据到正式文件
|
||||
items = load_data(data_file)
|
||||
item_data = review.get('data', {})
|
||||
|
||||
# 确保有ID和时间戳
|
||||
if 'id' not in item_data or not item_data['id']:
|
||||
item_data['id'] = uuid.uuid4().hex[:12]
|
||||
item_data['created_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
item_data['visible'] = True
|
||||
item_data['approved'] = True
|
||||
item_data['approved_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
items.append(item_data)
|
||||
save_data(data_file, items)
|
||||
|
||||
# 更新审核状态
|
||||
review['status'] = 'approved'
|
||||
review['approved_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
review['approved_by'] = session.get('username', 'admin')
|
||||
save_data(PENDING_FILE, reviews)
|
||||
|
||||
return jsonify({'success': True, 'item': item_data})
|
||||
|
||||
|
||||
@reviews_bp.route('/api/reviews/<review_id>/reject', methods=['POST'])
|
||||
def api_reject_review(review_id):
|
||||
"""拒绝审核"""
|
||||
reviews = load_data(PENDING_FILE)
|
||||
review = next((r for r in reviews if r['id'] == review_id), None)
|
||||
if not review:
|
||||
return jsonify({'error': 'Review not found'}), 404
|
||||
|
||||
if review.get('status') != 'pending':
|
||||
return jsonify({'error': '该申请已处理'}), 400
|
||||
|
||||
data = request.get_json() or {}
|
||||
reason = data.get('reason', '')
|
||||
|
||||
# 更新审核状态
|
||||
review['status'] = 'rejected'
|
||||
review['rejected_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
review['rejected_by'] = session.get('username', 'admin')
|
||||
review['reject_reason'] = reason
|
||||
save_data(PENDING_FILE, reviews)
|
||||
|
||||
return jsonify({'success': True})
|
||||
|
||||
|
||||
def get_data_file(category_id):
|
||||
"""获取类别对应的数据文件"""
|
||||
builtin_map = {
|
||||
'ai-models': MODELS_FILE,
|
||||
'gpus': GPUS_FILE,
|
||||
'cpus': CPUS_FILE
|
||||
}
|
||||
|
||||
if category_id in builtin_map:
|
||||
return builtin_map[category_id]
|
||||
|
||||
# 动态分类
|
||||
return DATA_DIR / f'items_{category_id}.json'
|
||||
|
||||
|
||||
# ─── 内部函数:提交审核 ───────────────────────────────────────────────────────
|
||||
|
||||
def submit_for_review(category_id, data, source='web', submitter=None):
|
||||
"""
|
||||
提交产品到审核队列
|
||||
|
||||
参数:
|
||||
category_id: 分类ID
|
||||
data: 产品数据(dict)
|
||||
source: 来源 web/api
|
||||
submitter: 提交者
|
||||
"""
|
||||
reviews = load_data(PENDING_FILE)
|
||||
|
||||
review = {
|
||||
'id': uuid.uuid4().hex[:12],
|
||||
'category_id': category_id,
|
||||
'data': data,
|
||||
'source': source,
|
||||
'submitter': submitter or 'anonymous',
|
||||
'status': 'pending', # pending, approved, rejected
|
||||
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
|
||||
reviews.append(review)
|
||||
save_data(PENDING_FILE, reviews)
|
||||
|
||||
return review
|
||||
Reference in New Issue
Block a user