""" 文章内容库管理 API """ from flask import Blueprint, request, jsonify from models.database import db from services.search_service import search_service bp = Blueprint('articles', __name__, url_prefix='/api/articles') @bp.route('', methods=['GET']) def list_articles(): """获取文章列表""" limit = request.args.get('limit', 100, type=int) offset = request.args.get('offset', 0, type=int) articles = db.get_all_articles(limit=limit, offset=offset) # 解析JSON字段 for article in articles: article['product_names'] = __import__('json').loads(article.get('product_names', '[]')) article['keywords'] = __import__('json').loads(article.get('keywords', '[]')) return jsonify({ 'success': True, 'articles': articles, 'count': len(articles) }) @bp.route('/search', methods=['GET']) def search_articles(): """搜索文章""" keyword = request.args.get('q', '') category = request.args.get('category') if not keyword: return jsonify({'error': '请提供搜索关键词'}), 400 articles = db.search_articles(keyword, category) # 解析JSON字段 for article in articles: article['product_names'] = __import__('json').loads(article.get('product_names', '[]')) article['keywords'] = __import__('json').loads(article.get('keywords', '[]')) return jsonify({ 'success': True, 'articles': articles, 'count': len(articles) }) @bp.route('/', methods=['GET']) def get_article(article_id): """获取文章详情""" article = db.get_article_by_id(article_id) if not article: return jsonify({'error': '文章不存在'}), 404 article['product_names'] = __import__('json').loads(article.get('product_names', '[]')) article['keywords'] = __import__('json').loads(article.get('keywords', '[]')) return jsonify({ 'success': True, 'article': article }) @bp.route('', methods=['POST']) def create_article(): """创建文章(手动添加)""" data = request.get_json() required_fields = ['product_names', 'summary', 'content', 'source'] for field in required_fields: if field not in data: return jsonify({'error': f'缺少必填字段: {field}'}), 400 article_id = search_service.save_to_articles( product_names=data['product_names'], category=data.get('category'), keywords=data.get('keywords', []), summary=data['summary'], content=data['content'], source=data['source'], url=data.get('url') ) return jsonify({ 'success': True, 'article_id': article_id, 'message': '文章创建成功' }) @bp.route('/', methods=['DELETE']) def delete_article(article_id): """删除文章""" success = db.delete_article(article_id) if success: return jsonify({ 'success': True, 'message': '文章已删除' }) else: return jsonify({'error': '文章不存在或删除失败'}), 404 @bp.route('/fetch', methods=['POST']) def fetch_article(): """从URL抓取文章""" data = request.get_json() url = data.get('url') if not url: return jsonify({'error': '请提供URL'}), 400 result = search_service.fetch_url_content(url) if result: # 自动保存到内容库 article_id = search_service.save_to_articles( product_names=data.get('product_names', [result['title']]), category=data.get('category'), keywords=data.get('keywords', []), summary=result.get('description', ''), content=result['content'], source=url, url=url ) return jsonify({ 'success': True, 'article_id': article_id, 'data': result }) else: return jsonify({'error': '抓取失败'}), 500 @bp.route('/internet-search', methods=['POST']) def internet_search(): """互联网搜索(浏览器方式)""" data = request.get_json() keyword = data.get('keyword', '') max_results = data.get('max_results', 10) engine = data.get('engine', 'bing_cn') # 默认 Bing 中国 use_cache = data.get('use_cache', True) # 默认使用缓存 cache_days = data.get('cache_days', 7) # 默认缓存7天 if not keyword: return jsonify({'error': '请提供搜索关键词'}), 400 # 检查是否有缓存 cached = None if use_cache: cached = db.get_search_cache(keyword, engine) if cached: return jsonify({ 'success': True, 'keyword': keyword, 'engine': engine, 'results': cached['results'], 'count': cached['count'], 'cached': True, 'cached_at': cached['cached_at'], 'expires_at': cached['expires_at'] }) # 执行互联网搜索 results = search_service.search_internet(keyword, max_results, engine, use_cache, cache_days) return jsonify({ 'success': True, 'keyword': keyword, 'engine': engine, 'results': results, 'count': len(results), 'cached': False }) @bp.route('/internet-search-and-fetch', methods=['POST']) def internet_search_and_fetch(): """互联网搜索并抓取内容""" data = request.get_json() keyword = data.get('keyword', '') max_results = data.get('max_results', 5) category = data.get('category') if not keyword: return jsonify({'error': '请提供搜索关键词'}), 400 # 执行互联网搜索 results = search_service.search_internet(keyword, max_results) # 抓取每个结果的详细内容 fetched_results = [] for r in results: url = r.get('url') if url: content = search_service.fetch_url_content(url) if content: fetched_results.append({ 'title': r['title'], 'url': url, 'source': r['source'], 'fetched_content': content }) return jsonify({ 'success': True, 'keyword': keyword, 'results': fetched_results, 'count': len(fetched_results) }) # ========== 失败URL管理 ========== @bp.route('/failed-urls', methods=['GET']) def get_failed_urls(): """获取失败的URL列表""" limit = request.args.get('limit', 100, type=int) urls = db.get_failed_urls(limit=limit) return jsonify({ 'success': True, 'urls': urls, 'count': len(urls) }) @bp.route('/failed-urls', methods=['POST']) def add_failed_url(): """记录失败的URL""" data = request.get_json() url = data.get('url') title = data.get('title') error_message = data.get('error_message') source = data.get('source', 'search') if not url: return jsonify({'error': '请提供URL'}), 400 url_id = db.add_failed_url(url, title, error_message, source) return jsonify({ 'success': True, 'url_id': url_id, 'message': '失败URL已记录' }) @bp.route('/failed-urls/count', methods=['GET']) def get_failed_url_count(): """获取失败URL数量""" count = db.get_failed_url_count() return jsonify({ 'success': True, 'count': count }) @bp.route('/failed-urls/', methods=['DELETE']) def delete_failed_url(url_id): """删除失败URL记录""" success = db.delete_failed_url(url_id) if success: return jsonify({'success': True, 'message': '记录已删除'}) else: return jsonify({'error': '记录不存在'}), 404 @bp.route('/failed-urls/clear', methods=['POST']) def clear_failed_urls(): """清空所有失败URL记录""" count = db.clear_failed_urls() return jsonify({ 'success': True, 'message': f'已清空 {count} 条记录' }) @bp.route('/failed-urls/retry', methods=['POST']) def retry_failed_url(): """重试失败的URL""" data = request.get_json() url = data.get('url') if not url: return jsonify({'error': '请提供URL'}), 400 # 尝试抓取 result = search_service.fetch_url_content(url) if result and result.get('content'): # 成功,标记为已处理 db.mark_url_success(url) return jsonify({ 'success': True, 'message': '抓取成功', 'data': result }) else: # 仍然失败 db.add_failed_url(url, error_message='重试失败') return jsonify({'error': '抓取仍然失败'}), 500