184 lines
6.3 KiB
Python
184 lines
6.3 KiB
Python
"""
|
|
CPU CRUD API
|
|
"""
|
|
import uuid
|
|
import json
|
|
from datetime import datetime
|
|
from flask import Blueprint, request, jsonify, Response
|
|
from config import CPUS_FILE
|
|
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:
|
|
return 0 if key in ['cores', 'threads', 'views'] else ''
|
|
return val
|
|
|
|
|
|
@cpus_bp.route('/api/cpus')
|
|
def api_cpus():
|
|
cpus = load_data(CPUS_FILE)
|
|
hide_hidden = request.args.get('all', '0') == '0'
|
|
if hide_hidden:
|
|
cpus = [c for c in cpus if c.get('visible', True)]
|
|
keyword = request.args.get('q', '').strip().lower()
|
|
if keyword:
|
|
cpus = [c for c in cpus if keyword in c.get('name', '').lower() or
|
|
keyword in c.get('manufacturer', '').lower()]
|
|
sort_by = request.args.get('sort', 'default')
|
|
reverse = request.args.get('order', 'desc') == 'desc'
|
|
if sort_by == 'default':
|
|
cpus = sorted(cpus, key=lambda x: (
|
|
not x.get('is_pinned', False),
|
|
-(parse_date_to_timestamp(x.get('publish_date', '')) or
|
|
parse_date_to_timestamp(x.get('created_at', '')) or 0)
|
|
))
|
|
elif sort_by in ['name', 'cores', 'threads', 'price_usd', 'created_at',
|
|
'publish_date', 'views', 'updated_at']:
|
|
cpus = sorted(cpus, key=lambda x: _safe_sort_key(x, sort_by), reverse=reverse)
|
|
return jsonify(cpus)
|
|
|
|
|
|
@cpus_bp.route('/api/cpus/<cpu_id>')
|
|
def api_cpu_detail(cpu_id):
|
|
cpus = load_data(CPUS_FILE)
|
|
cpu = next((c for c in cpus if c['id'] == cpu_id), None)
|
|
if not cpu:
|
|
return jsonify({'error': 'CPU not found'}), 404
|
|
return jsonify(cpu)
|
|
|
|
|
|
@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')
|
|
data['visible'] = data.get('visible', True)
|
|
data['publish_date'] = data.get('publish_date', '')
|
|
data['views'] = data.get('views', 0)
|
|
data['is_pinned'] = data.get('is_pinned', False)
|
|
cpus.append(data)
|
|
save_data(CPUS_FILE, cpus)
|
|
return jsonify(data)
|
|
|
|
|
|
@cpus_bp.route('/api/cpus/<cpu_id>', methods=['PUT'])
|
|
def api_update_cpu(cpu_id):
|
|
data = request.get_json()
|
|
cpus = load_data(CPUS_FILE)
|
|
cpu = next((c for c in cpus if c['id'] == cpu_id), None)
|
|
if not cpu:
|
|
return jsonify({'error': 'CPU not found'}), 404
|
|
cpu.update(data)
|
|
cpu['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
save_data(CPUS_FILE, cpus)
|
|
return jsonify(cpu)
|
|
|
|
|
|
@cpus_bp.route('/api/cpus/<cpu_id>', methods=['DELETE'])
|
|
def api_delete_cpu(cpu_id):
|
|
cpus = load_data(CPUS_FILE)
|
|
cpus = [c for c in cpus if c['id'] != cpu_id]
|
|
save_data(CPUS_FILE, cpus)
|
|
return jsonify({'success': True})
|
|
|
|
|
|
@cpus_bp.route('/api/cpus/<cpu_id>/visible', methods=['POST'])
|
|
def api_toggle_cpu_visible(cpu_id):
|
|
cpus = load_data(CPUS_FILE)
|
|
cpu = next((c for c in cpus if c['id'] == cpu_id), None)
|
|
if not cpu:
|
|
return jsonify({'error': 'CPU not found'}), 404
|
|
cpu['visible'] = not cpu.get('visible', True)
|
|
save_data(CPUS_FILE, cpus)
|
|
return jsonify({'success': True, 'visible': cpu['visible']})
|
|
|
|
|
|
# ─── 导出导入 ───────────────────────────────────────────────────────
|
|
|
|
@cpus_bp.route('/api/cpus/export', methods=['GET'])
|
|
def api_export_cpus():
|
|
"""导出所有CPU数据"""
|
|
try:
|
|
cpus = load_data(CPUS_FILE)
|
|
export_data = {
|
|
'type': 'cpus',
|
|
'items': cpus,
|
|
'count': len(cpus),
|
|
'export_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
|
'version': '1.0'
|
|
}
|
|
json_str = json.dumps(export_data, ensure_ascii=False, indent=2)
|
|
response = Response(
|
|
json_str,
|
|
mimetype='application/json',
|
|
headers={'Content-Disposition': f'attachment; filename=cpus-export-{datetime.now().strftime("%Y%m%d%H%M%S")}.json'}
|
|
)
|
|
return response
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@cpus_bp.route('/api/cpus/import', methods=['GET', 'POST'])
|
|
def api_import_cpus():
|
|
"""导入CPU数据"""
|
|
try:
|
|
if request.method == 'GET':
|
|
return jsonify({'endpoint': '/api/cpus/import', 'method': 'POST', 'params': {'mode': 'merge 或 replace'}})
|
|
|
|
import_data = request.get_json()
|
|
if not import_data or 'items' not in import_data:
|
|
return jsonify({'error': '缺少 items 字段'}), 400
|
|
|
|
mode = request.args.get('mode', 'merge')
|
|
cpus = load_data(CPUS_FILE)
|
|
imported_items = import_data['items']
|
|
|
|
result = {'success': True, 'imported': 0, 'updated': 0, 'skipped': []}
|
|
|
|
for item in imported_items:
|
|
existing = next((c for c in cpus if c['id'] == item['id']), None)
|
|
if existing:
|
|
if mode == 'replace':
|
|
existing.update(item)
|
|
result['updated'] += 1
|
|
else:
|
|
result['skipped'].append(item['id'])
|
|
else:
|
|
cpus.append(item)
|
|
result['imported'] += 1
|
|
|
|
save_data(CPUS_FILE, cpus)
|
|
return jsonify(result)
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|