feat: 增加 analyst 角色——仅测试数据查询/下载+修改密码

This commit is contained in:
wangfq
2026-06-11 17:21:49 +08:00
parent 317c15aff2
commit 17e1d232e8
5 changed files with 26 additions and 1 deletions

View File

@@ -42,6 +42,23 @@ def load_user(user_id):
def init_auth(app): def init_auth(app):
login_manager.init_app(app) login_manager.init_app(app)
# analyst 角色:全局路由白名单拦截
ANALYST_ALLOWED = {
"auth.login", "auth.logout", "auth.change_password",
"test_data.test_data_page",
"test_data.api_test_data",
"test_data.api_chart_data",
"test_data.api_export",
"test_data.api_delete", # 自身有 inline 角色检查
}
@app.before_request
def _restrict_analyst():
if current_user.is_authenticated and current_user.role == "analyst":
ep = request.endpoint or ""
if ep not in ANALYST_ALLOWED and not ep.startswith("static"):
return "权限不足:当前角色为 analyst仅可访问测试数据", 403
# ─── 装饰器 ──────────────────────────────────────────────────────── # ─── 装饰器 ────────────────────────────────────────────────────────

View File

@@ -10,12 +10,14 @@ bp = Blueprint("test_data", __name__)
@bp.route("/test-data") @bp.route("/test-data")
@login_required
def test_data_page(): def test_data_page():
"""测试信息页""" """测试信息页"""
return render_template("test_data.html") return render_template("test_data.html")
@bp.route("/api/test-data") @bp.route("/api/test-data")
@login_required
def api_test_data(): def api_test_data():
"""分页查询测试数据""" """分页查询测试数据"""
page = request.args.get("page", 1, type=int) page = request.args.get("page", 1, type=int)
@@ -38,6 +40,7 @@ def api_test_data():
@bp.route("/api/test-data/chart") @bp.route("/api/test-data/chart")
@login_required
def api_chart_data(): def api_chart_data():
"""返回图表所需全部数据(不分页)""" """返回图表所需全部数据(不分页)"""
serial = request.args.get("serial", "", type=str) serial = request.args.get("serial", "", type=str)
@@ -51,6 +54,7 @@ def api_chart_data():
return jsonify({"records": records, "total": len(records)}) return jsonify({"records": records, "total": len(records)})
@bp.route("/api/test-data/export") @bp.route("/api/test-data/export")
@login_required
def api_export(): def api_export():
"""导出测试数据为 CSV""" """导出测试数据为 CSV"""
serial = request.args.get("serial", "", type=str) serial = request.args.get("serial", "", type=str)

View File

@@ -8,7 +8,9 @@
</head> </head>
<body> <body>
<nav class="top-menu"> <nav class="top-menu">
{% if current_user.is_authenticated and current_user.role != 'analyst' %}
<a href="/" class="{% if request.path == '/' %}active{% endif %}">设备</a> <a href="/" class="{% if request.path == '/' %}active{% endif %}">设备</a>
{% endif %}
<a href="/test-data" class="{% if request.path == '/test-data' %}active{% endif %}">测试信息</a> <a href="/test-data" class="{% if request.path == '/test-data' %}active{% endif %}">测试信息</a>
{% if current_user.is_authenticated and current_user.role in ('admin', 'manager') %} {% if current_user.is_authenticated and current_user.role in ('admin', 'manager') %}
<a href="/device-logs" class="{% if request.path == '/device-logs' %}active{% endif %}">设备日志</a> <a href="/device-logs" class="{% if request.path == '/device-logs' %}active{% endif %}">设备日志</a>

View File

@@ -12,6 +12,7 @@
<label>角色: <label>角色:
<select id="new-role" style="margin:0 8px;"> <select id="new-role" style="margin:0 8px;">
<option value="operator">operator</option> <option value="operator">operator</option>
<option value="analyst">analyst</option>
<option value="manager">manager</option> <option value="manager">manager</option>
<option value="admin">admin</option> <option value="admin">admin</option>
</select> </select>
@@ -52,6 +53,7 @@ async function loadUsers() {
<td> <td>
<select onchange="updateUser(${u.id}, this, 'role')" data-field="role"> <select onchange="updateUser(${u.id}, this, 'role')" data-field="role">
<option value="operator" ${u.role==='operator'?'selected':''}>operator</option> <option value="operator" ${u.role==='operator'?'selected':''}>operator</option>
<option value="analyst" ${u.role==='analyst'?'selected':''}>analyst</option>
<option value="manager" ${u.role==='manager'?'selected':''}>manager</option> <option value="manager" ${u.role==='manager'?'selected':''}>manager</option>
<option value="admin" ${u.role==='admin'?'selected':''}>admin</option> <option value="admin" ${u.role==='admin'?'selected':''}>admin</option>
</select> </select>