From 9622baf76e5d5af7306c7abd960e55df48ade148 Mon Sep 17 00:00:00 2001 From: yueli Date: Wed, 9 Sep 2026 09:31:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(borrow,outbound):=20=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E6=9F=A5=E7=9C=8B=E6=8C=89=E8=A7=92=E8=89=B2=E6=94=B6=E7=AA=84?= =?UTF-8?q?=E2=80=94=E2=80=94=E6=99=AE=E9=80=9A=E5=8F=AA=E7=9C=8B=E8=87=AA?= =?UTF-8?q?=E5=B7=B1=EF=BC=8C=E5=BA=93=E7=AE=A1/=E4=B8=BB=E7=AE=A1/?= =?UTF-8?q?=E8=B6=85=E7=AE=A1/=E8=B7=A8=E5=9F=9F=E7=9C=8B=E5=85=A8?= =?UTF-8?q?=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - decorators 新增 is_privileged_viewer(SUPER_ADMIN/SUPERVISOR/WAREHOUSE_MGR 或 crossDomain) - 借库记录列表:非管理者强制 applicant_id=当前用户 - 出库记录列表+详情:非管理者强制只看自己/无权访问他人单(403) --- inventory-backend/app/api/v1/outbound.py | 13 +++++++++++- inventory-backend/app/api/v1/transactions.py | 7 ++++++- inventory-backend/app/utils/decorators.py | 21 ++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/inventory-backend/app/api/v1/outbound.py b/inventory-backend/app/api/v1/outbound.py index ee8690e..c8b89e4 100644 --- a/inventory-backend/app/api/v1/outbound.py +++ b/inventory-backend/app/api/v1/outbound.py @@ -1,7 +1,7 @@ from flask import Blueprint, request, jsonify, current_app from app.services.outbound_service import OutboundService from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt -from app.utils.decorators import permission_required, audit_log, prevent_double_submit +from app.utils.decorators import permission_required, audit_log, prevent_double_submit, is_privileged_viewer from app.services.auth_service import AuthService import traceback @@ -551,6 +551,11 @@ def get_outbound_request_list(): if status is not None: status = int(status) + # ★ 数据权限:普通申请人只能看“自己的”出库记录;库管/主管/超管(或跨域)才可看他人 + if not is_privileged_viewer(): + identity = get_jwt_identity() + applicant_id = int(identity) if identity else None + result = OutboundApprovalService.get_request_list( page=page, per_page=limit, @@ -584,6 +589,12 @@ def get_outbound_request_detail(request_id): if not approval: return jsonify({'code': 404, 'msg': '审批单不存在'}), 404 + # ★ 数据权限:普通申请人只能看自己的单;库管/主管/超管(或跨域)可看任意 + if not is_privileged_viewer(): + identity = get_jwt_identity() + if int(approval.applicant_id or 0) != int(identity or 0): + return jsonify({'code': 403, 'msg': '无权查看他人的出库记录'}), 403 + return jsonify({ 'code': 200, 'msg': '获取成功', diff --git a/inventory-backend/app/api/v1/transactions.py b/inventory-backend/app/api/v1/transactions.py index 900ab22..85b5ab8 100644 --- a/inventory-backend/app/api/v1/transactions.py +++ b/inventory-backend/app/api/v1/transactions.py @@ -1,6 +1,6 @@ from flask import Blueprint, jsonify, request # .material -> .base refactor checked from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt -from app.utils.decorators import permission_required, audit_log, prevent_double_submit +from app.utils.decorators import permission_required, audit_log, prevent_double_submit, is_privileged_viewer from app.services.auth_service import AuthService from app.services.trans_service import TransService from app.services.borrow_service import BorrowApprovalService @@ -330,6 +330,11 @@ def get_borrow_request_list(): if status is not None: status = int(status) + # ★ 数据权限:普通申请人只能看“自己的”借还记录;库管/主管/超管(或跨域)才可看他人 + if not is_privileged_viewer(): + identity = get_jwt_identity() + applicant_id = int(identity) if identity else None + result = BorrowApprovalService.get_request_list( page=page, per_page=limit, applicant_id=applicant_id, status=status ) diff --git a/inventory-backend/app/utils/decorators.py b/inventory-backend/app/utils/decorators.py index 73cc2bc..c9d4ffd 100644 --- a/inventory-backend/app/utils/decorators.py +++ b/inventory-backend/app/utils/decorators.py @@ -226,6 +226,27 @@ def _has_cross_domain_permission(user_role, user_company=''): return False +# 能查看“他人借还/出库记录”的管理者角色(库管/主管/超管)。普通申请人只能看自己的记录。 +PRIVILEGED_VIEWER_ROLES = ('SUPER_ADMIN', 'SUPERVISOR', 'WAREHOUSE_MGR') + + +def is_privileged_viewer(): + """ + 判断当前登录用户是否属于“管理者视角”(可查看他人借还/出库记录)。 + 规则:角色为 SUPER_ADMIN/SUPERVISOR/WAREHOUSE_MGR,或拥有 crossDomain 权限。 + 普通申请人(其余角色)只能看自己的记录(由各列表 handler 强制 applicant_id=当前用户)。 + """ + from flask_jwt_extended import get_jwt + claims = get_jwt() + role = (claims.get('role') or '').upper() + if role in PRIVILEGED_VIEWER_ROLES: + return True + user_company = claims.get('company_name', '') + if _has_cross_domain_permission(role, user_company): + return True + return False + + def get_current_company_filter(): """ 多租户数据权限隔离工具函数。