feat: 添加 JD-R 理论分析模块与 SHAP 可解释性分析功能
- 后端新增 JD-R(工作要求-资源)理论维度数据生成,包含工作要求、工作资源、
个人资源、中介变量共 16 个新特征列
- 新增 JD-R 分析服务与 API(维度统计、倦怠投入分析、双路径中介分析、
分组轮廓、风险分布)
- 新增 SHAP 可解释性分析模块(全局重要性、局部解释、特征交互、依赖图)
- 预测服务增加风险分类模型加载与概率预测能力
- 前端新增 JD-R 分析页面(JDRAnalysis.vue),含雷达图、散点图、路径分析等可视化
- 预测页面增加风险概率展示与 SHAP 特征解释
- 路由与导航菜单同步更新
This commit is contained in:
@@ -2,6 +2,8 @@ from .overview_routes import overview_bp
|
||||
from .analysis_routes import analysis_bp
|
||||
from .predict_routes import predict_bp
|
||||
from .cluster_routes import cluster_bp
|
||||
from .jdr_routes import jdr_bp
|
||||
from .shap_routes import shap_bp
|
||||
|
||||
|
||||
def register_blueprints(app):
|
||||
@@ -9,3 +11,5 @@ def register_blueprints(app):
|
||||
app.register_blueprint(analysis_bp)
|
||||
app.register_blueprint(predict_bp)
|
||||
app.register_blueprint(cluster_bp)
|
||||
app.register_blueprint(jdr_bp)
|
||||
app.register_blueprint(shap_bp)
|
||||
|
||||
51
backend/api/jdr_routes.py
Normal file
51
backend/api/jdr_routes.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from flask import Blueprint, jsonify, request
|
||||
|
||||
from services.jdr_service import jdr_service
|
||||
|
||||
jdr_bp = Blueprint('jdr', __name__, url_prefix='/api/jdr')
|
||||
|
||||
|
||||
@jdr_bp.route('/dimensions', methods=['GET'])
|
||||
def get_dimensions():
|
||||
try:
|
||||
result = jdr_service.get_dimension_scores()
|
||||
return jsonify({'code': 200, 'message': 'success', 'data': result})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'message': str(e), 'data': None}), 500
|
||||
|
||||
|
||||
@jdr_bp.route('/burnout-engagement', methods=['GET'])
|
||||
def get_burnout_engagement():
|
||||
try:
|
||||
result = jdr_service.get_burnout_engagement_analysis()
|
||||
return jsonify({'code': 200, 'message': 'success', 'data': result})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'message': str(e), 'data': None}), 500
|
||||
|
||||
|
||||
@jdr_bp.route('/path-analysis', methods=['GET'])
|
||||
def get_path_analysis():
|
||||
try:
|
||||
result = jdr_service.get_jdr_path_analysis()
|
||||
return jsonify({'code': 200, 'message': 'success', 'data': result})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'message': str(e), 'data': None}), 500
|
||||
|
||||
|
||||
@jdr_bp.route('/profile', methods=['GET'])
|
||||
def get_profile():
|
||||
try:
|
||||
dimension = request.args.get('dimension', '所属行业')
|
||||
result = jdr_service.get_jdr_profile(dimension)
|
||||
return jsonify({'code': 200, 'message': 'success', 'data': result})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'message': str(e), 'data': None}), 500
|
||||
|
||||
|
||||
@jdr_bp.route('/risk-distribution', methods=['GET'])
|
||||
def get_risk_distribution():
|
||||
try:
|
||||
result = jdr_service.get_risk_distribution()
|
||||
return jsonify({'code': 200, 'message': 'success', 'data': result})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'message': str(e), 'data': None}), 500
|
||||
@@ -100,3 +100,18 @@ def get_model_info():
|
||||
'message': str(e),
|
||||
'data': None
|
||||
}), 500
|
||||
|
||||
|
||||
@predict_bp.route('/risk-classify', methods=['POST'])
|
||||
def risk_classify():
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'code': 400, 'message': 'Request body is required', 'data': None}), 400
|
||||
model_type = data.get('model_type')
|
||||
result = predict_service.predict_risk_classification(data, model_type)
|
||||
if result is None:
|
||||
return jsonify({'code': 404, 'message': 'No classifier available', 'data': None}), 404
|
||||
return jsonify({'code': 200, 'message': 'success', 'data': result})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'message': str(e), 'data': None}), 500
|
||||
|
||||
50
backend/api/shap_routes.py
Normal file
50
backend/api/shap_routes.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from flask import Blueprint, jsonify, request
|
||||
|
||||
from services.shap_service import shap_service
|
||||
|
||||
shap_bp = Blueprint('shap', __name__, url_prefix='/api/shap')
|
||||
|
||||
|
||||
@shap_bp.route('/global', methods=['GET'])
|
||||
def get_global_importance():
|
||||
try:
|
||||
model_type = request.args.get('model', 'random_forest')
|
||||
result = shap_service.get_global_importance(model_type)
|
||||
return jsonify({'code': 200, 'message': 'success', 'data': result})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'message': str(e), 'data': None}), 500
|
||||
|
||||
|
||||
@shap_bp.route('/local', methods=['POST'])
|
||||
def get_local_explanation():
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'code': 400, 'message': 'Request body is required', 'data': None}), 400
|
||||
model_type = data.get('model_type', 'random_forest')
|
||||
result = shap_service.get_local_explanation(data, model_type)
|
||||
return jsonify({'code': 200, 'message': 'success', 'data': result})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'message': str(e), 'data': None}), 500
|
||||
|
||||
|
||||
@shap_bp.route('/interaction', methods=['GET'])
|
||||
def get_interactions():
|
||||
try:
|
||||
model_type = request.args.get('model', 'random_forest')
|
||||
top_n = int(request.args.get('top_n', 10))
|
||||
result = shap_service.get_interactions(model_type, top_n)
|
||||
return jsonify({'code': 200, 'message': 'success', 'data': result})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'message': str(e), 'data': None}), 500
|
||||
|
||||
|
||||
@shap_bp.route('/dependence', methods=['GET'])
|
||||
def get_dependence():
|
||||
try:
|
||||
feature = request.args.get('feature', '月均加班时长')
|
||||
model_type = request.args.get('model', 'random_forest')
|
||||
result = shap_service.get_dependence(feature, model_type)
|
||||
return jsonify({'code': 200, 'message': 'success', 'data': result})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'message': str(e), 'data': None}), 500
|
||||
Reference in New Issue
Block a user