""" Community APIs - 指标社区接口 提供指标市场、购买、评论等功能的 REST API。 """ from flask import Blueprint, jsonify, request, g from app.utils.auth import login_required from app.utils.logger import get_logger from app.services.community_service import get_community_service logger = get_logger(__name__) community_bp = Blueprint("community", __name__) # ========================================== # 指标市场 # ========================================== @community_bp.route("/indicators", methods=["GET"]) @login_required def get_market_indicators(): """ 获取市场指标列表 Query params: page: 页码 (default 1) page_size: 每页数量 (default 12) keyword: 搜索关键词 pricing_type: 'free' / 'paid' / 空(全部) sort_by: 'newest' / 'hot' / 'price_asc' / 'price_desc' / 'rating' """ try: page = int(request.args.get('page', 1)) page_size = int(request.args.get('page_size', 12)) keyword = request.args.get('keyword', '').strip() pricing_type = request.args.get('pricing_type', '').strip() or None sort_by = request.args.get('sort_by', 'newest').strip() # 限制每页数量 page_size = min(max(page_size, 1), 50) service = get_community_service() result = service.get_market_indicators( page=page, page_size=page_size, keyword=keyword if keyword else None, pricing_type=pricing_type, sort_by=sort_by, user_id=g.user_id ) return jsonify({'code': 1, 'msg': 'success', 'data': result}) except Exception as e: logger.error(f"get_market_indicators failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 @community_bp.route("/indicators/", methods=["GET"]) @login_required def get_indicator_detail(indicator_id: int): """获取指标详情""" try: service = get_community_service() result = service.get_indicator_detail(indicator_id, user_id=g.user_id) if not result: return jsonify({'code': 0, 'msg': 'indicator_not_found', 'data': None}), 404 return jsonify({'code': 1, 'msg': 'success', 'data': result}) except Exception as e: logger.error(f"get_indicator_detail failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 # ========================================== # 购买功能 # ========================================== @community_bp.route("/indicators//purchase", methods=["POST"]) @login_required def purchase_indicator(indicator_id: int): """ 购买指标 会自动: 1. 检查积分是否充足 2. 扣除买家积分,增加卖家积分 3. 创建购买记录 4. 复制指标到买家账户 """ try: service = get_community_service() success, message, data = service.purchase_indicator( buyer_id=g.user_id, indicator_id=indicator_id ) if success: return jsonify({'code': 1, 'msg': message, 'data': data}) else: return jsonify({'code': 0, 'msg': message, 'data': data}), 400 except Exception as e: logger.error(f"purchase_indicator failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 @community_bp.route("/my-purchases", methods=["GET"]) @login_required def get_my_purchases(): """获取我购买的指标列表""" try: page = int(request.args.get('page', 1)) page_size = int(request.args.get('page_size', 20)) page_size = min(max(page_size, 1), 50) service = get_community_service() result = service.get_my_purchases( user_id=g.user_id, page=page, page_size=page_size ) return jsonify({'code': 1, 'msg': 'success', 'data': result}) except Exception as e: logger.error(f"get_my_purchases failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 # ========================================== # 评论功能 # ========================================== @community_bp.route("/indicators//comments", methods=["GET"]) @login_required def get_comments(indicator_id: int): """获取指标评论列表""" try: page = int(request.args.get('page', 1)) page_size = int(request.args.get('page_size', 20)) page_size = min(max(page_size, 1), 50) service = get_community_service() result = service.get_comments( indicator_id=indicator_id, page=page, page_size=page_size ) return jsonify({'code': 1, 'msg': 'success', 'data': result}) except Exception as e: logger.error(f"get_comments failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 @community_bp.route("/indicators//comments", methods=["POST"]) @login_required def add_comment(indicator_id: int): """ 添加评论 Request body: rating: 1-5 星评分 content: 评论内容(可选,最多500字) 注意:只有购买过的用户可以评论,且只能评论一次 """ try: data = request.get_json() or {} rating = int(data.get('rating', 5)) content = (data.get('content') or '').strip() service = get_community_service() success, message, result = service.add_comment( user_id=g.user_id, indicator_id=indicator_id, rating=rating, content=content ) if success: return jsonify({'code': 1, 'msg': message, 'data': result}) else: return jsonify({'code': 0, 'msg': message, 'data': result}), 400 except Exception as e: logger.error(f"add_comment failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 @community_bp.route("/indicators//comments/", methods=["PUT"]) @login_required def update_comment(indicator_id: int, comment_id: int): """ 更新评论(只能修改自己的评论) Request body: rating: 1-5 星评分 content: 评论内容(最多500字) """ try: data = request.get_json() or {} rating = int(data.get('rating', 5)) content = (data.get('content') or '').strip() service = get_community_service() success, message, result = service.update_comment( user_id=g.user_id, comment_id=comment_id, indicator_id=indicator_id, rating=rating, content=content ) if success: return jsonify({'code': 1, 'msg': message, 'data': result}) else: return jsonify({'code': 0, 'msg': message, 'data': result}), 400 except Exception as e: logger.error(f"update_comment failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 @community_bp.route("/indicators//my-comment", methods=["GET"]) @login_required def get_my_comment(indicator_id: int): """获取当前用户对指定指标的评论(用于编辑)""" try: service = get_community_service() result = service.get_user_comment( user_id=g.user_id, indicator_id=indicator_id ) return jsonify({'code': 1, 'msg': 'success', 'data': result}) except Exception as e: logger.error(f"get_my_comment failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 # ========================================== # 实盘表现 # ========================================== @community_bp.route("/indicators//performance", methods=["GET"]) @login_required def get_indicator_performance(indicator_id: int): """获取指标的实盘表现统计""" try: service = get_community_service() result = service.get_indicator_performance(indicator_id) return jsonify({'code': 1, 'msg': 'success', 'data': result}) except Exception as e: logger.error(f"get_indicator_performance failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 # ========================================== # 管理员审核功能 # ========================================== def _is_admin(): """检查当前用户是否是管理员""" role = getattr(g, 'user_role', None) return role == 'admin' @community_bp.route("/admin/pending-indicators", methods=["GET"]) @login_required def get_pending_indicators(): """ 获取待审核的指标列表(管理员专用) Query params: page: 页码 (default 1) page_size: 每页数量 (default 20) review_status: 'pending' / 'approved' / 'rejected' / 'all' """ try: if not _is_admin(): return jsonify({'code': 0, 'msg': 'admin_required', 'data': None}), 403 page = int(request.args.get('page', 1)) page_size = int(request.args.get('page_size', 20)) review_status = request.args.get('review_status', 'pending').strip() or 'pending' page_size = min(max(page_size, 1), 100) service = get_community_service() result = service.get_pending_indicators( page=page, page_size=page_size, review_status=review_status ) return jsonify({'code': 1, 'msg': 'success', 'data': result}) except Exception as e: logger.error(f"get_pending_indicators failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 @community_bp.route("/admin/review-stats", methods=["GET"]) @login_required def get_review_stats(): """获取审核统计数据(管理员专用)""" try: if not _is_admin(): return jsonify({'code': 0, 'msg': 'admin_required', 'data': None}), 403 service = get_community_service() result = service.get_review_stats() return jsonify({'code': 1, 'msg': 'success', 'data': result}) except Exception as e: logger.error(f"get_review_stats failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 @community_bp.route("/admin/indicators//review", methods=["POST"]) @login_required def review_indicator(indicator_id: int): """ 审核指标(管理员专用) Request body: action: 'approve' / 'reject' note: 审核备注(可选) """ try: if not _is_admin(): return jsonify({'code': 0, 'msg': 'admin_required', 'data': None}), 403 data = request.get_json() or {} action = data.get('action', '').strip() note = data.get('note', '').strip() if action not in ('approve', 'reject'): return jsonify({'code': 0, 'msg': 'invalid_action', 'data': None}), 400 service = get_community_service() success, message = service.review_indicator( admin_id=g.user_id, indicator_id=indicator_id, action=action, note=note ) if success: return jsonify({'code': 1, 'msg': message, 'data': None}) else: return jsonify({'code': 0, 'msg': message, 'data': None}), 400 except Exception as e: logger.error(f"review_indicator failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 @community_bp.route("/admin/indicators//unpublish", methods=["POST"]) @login_required def unpublish_indicator(indicator_id: int): """ 下架指标(管理员专用) Request body: note: 下架原因(可选) """ try: if not _is_admin(): return jsonify({'code': 0, 'msg': 'admin_required', 'data': None}), 403 data = request.get_json() or {} note = data.get('note', '').strip() service = get_community_service() success, message = service.unpublish_indicator( admin_id=g.user_id, indicator_id=indicator_id, note=note ) if success: return jsonify({'code': 1, 'msg': message, 'data': None}) else: return jsonify({'code': 0, 'msg': message, 'data': None}), 400 except Exception as e: logger.error(f"unpublish_indicator failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500 @community_bp.route("/admin/indicators/", methods=["DELETE"]) @login_required def admin_delete_indicator(indicator_id: int): """删除指标(管理员专用)""" try: if not _is_admin(): return jsonify({'code': 0, 'msg': 'admin_required', 'data': None}), 403 service = get_community_service() success, message = service.admin_delete_indicator( admin_id=g.user_id, indicator_id=indicator_id ) if success: return jsonify({'code': 1, 'msg': message, 'data': None}) else: return jsonify({'code': 0, 'msg': message, 'data': None}), 400 except Exception as e: logger.error(f"admin_delete_indicator failed: {e}") return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500