From 1ad477eda8d2a12eef5c0e67e59b3452ad48a8d3 Mon Sep 17 00:00:00 2001 From: dxc Date: Fri, 27 Feb 2026 13:12:45 +0800 Subject: [PATCH] feat: add permission management to inbound service module Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) --- .../app/api/v1/inbound/service.py | 94 +++++++++++++++---- .../src/views/stock/inbound/service.vue | 38 ++++++-- 2 files changed, 106 insertions(+), 26 deletions(-) diff --git a/inventory-backend/app/api/v1/inbound/service.py b/inventory-backend/app/api/v1/inbound/service.py index 6244e0b..91f9680 100644 --- a/inventory-backend/app/api/v1/inbound/service.py +++ b/inventory-backend/app/api/v1/inbound/service.py @@ -3,21 +3,73 @@ from flask import request, jsonify, current_app from flask_jwt_extended import jwt_required from . import inbound_bp from app.services.inbound.service_service import ServiceService -from app.utils.decorators import role_required +from app.utils.decorators import role_required, permission_required import traceback +# ============================================================================== +# 辅助函数:获取当前用户的完整权限列表(基于角色查询) +# ============================================================================== +def get_current_user_permissions(): + """ + 返回当前用户拥有的所有权限码列表(包括菜单和元素) + 此函数根据角色查询数据库得到权限。 + """ + from flask_jwt_extended import get_jwt + from app.services.auth_service import AuthService + claims = get_jwt() + user_role = claims.get('role') + if not user_role: + return [] + # 超级管理员返回所有字段权限 + if user_role == 'super_admin': + return ['inbound_service:*'] + perm_dict = AuthService.get_user_permissions(user_role) + # 合并菜单和元素权限 + perms = perm_dict.get('menus', []) + perm_dict.get('elements', []) + return perms + + +def filter_item_by_permissions(item_dict, user_permissions): + """ + 根据用户权限过滤 item 字典,无权限的字段值置为 None + """ + # 字段名到权限码的映射(与前端 permissionMap 保持一致) + field_to_perm = { + 'id': 'inbound_service:id', + 'base_id': 'inbound_service:base_id', + 'sku': 'inbound_service:sku', + 'material_name': 'inbound_service:material_name', + 'provider_name': 'inbound_service:provider_name', + 'sale_price': 'inbound_service:sale_price', + 'description': 'inbound_service:description', + 'created_at': 'inbound_service:created_at', + 'material_type': 'inbound_service:material_type', + 'category': 'inbound_service:category', + 'spec_model': 'inbound_service:spec_model', + 'unit': 'inbound_service:unit', + } + if 'inbound_service:*' in user_permissions: + return item_dict + for field, perm_code in field_to_perm.items(): + if field in item_dict and perm_code not in user_permissions: + item_dict[field] = None + return item_dict + + @inbound_bp.route('/service/search-base', methods=['GET']) -@jwt_required() +@permission_required('inbound_service') def search_base(): """搜索基础物料""" keyword = request.args.get('keyword', '') try: data = ServiceService.search_base_material(keyword) + user_permissions = get_current_user_permissions() + filtered_data = [filter_item_by_permissions(item, user_permissions) for item in data] return jsonify({ 'code': 200, 'msg': 'success', - 'data': data + 'data': filtered_data }) except Exception as e: current_app.logger.error(f'搜索基础物料失败: {str(e)}') @@ -25,7 +77,7 @@ def search_base(): @inbound_bp.route('/service', methods=['GET']) -@jwt_required() +@permission_required('inbound_service') def get_service_list(): """获取服务权益列表""" page = request.args.get('page', 1, type=int) @@ -44,6 +96,9 @@ def get_service_list(): end_date=end_date, provider_name=provider_name ) + user_permissions = get_current_user_permissions() + if result.get('items'): + result['items'] = [filter_item_by_permissions(item, user_permissions) for item in result['items']] return jsonify({ 'code': 200, 'msg': 'success', @@ -56,8 +111,7 @@ def get_service_list(): @inbound_bp.route('/service', methods=['POST']) -@jwt_required() -@role_required('admin,manager') +@permission_required('inbound_service:operation') def create_service(): """创建服务权益""" data = request.get_json() @@ -72,10 +126,12 @@ def create_service(): try: service = ServiceService.create_service(data) + user_permissions = get_current_user_permissions() + filtered_data = filter_item_by_permissions(service.to_dict(), user_permissions) return jsonify({ 'code': 201, 'msg': '创建成功', - 'data': service.to_dict() + 'data': filtered_data }), 201 except ValueError as e: return jsonify({'code': 400, 'msg': str(e)}), 400 @@ -86,15 +142,17 @@ def create_service(): @inbound_bp.route('/service/', methods=['GET']) -@jwt_required() +@permission_required('inbound_service') def get_service(service_id): """获取单个服务权益详情""" try: service = ServiceService.get_service(service_id) + user_permissions = get_current_user_permissions() + filtered_data = filter_item_by_permissions(service.to_dict(), user_permissions) return jsonify({ 'code': 200, 'msg': 'success', - 'data': service.to_dict() + 'data': filtered_data }) except ValueError as e: return jsonify({'code': 404, 'msg': str(e)}), 404 @@ -104,8 +162,7 @@ def get_service(service_id): @inbound_bp.route('/service/', methods=['PUT']) -@jwt_required() -@role_required('admin,manager') +@permission_required('inbound_service:operation') def update_service(service_id): """更新服务权益""" data = request.get_json() @@ -124,10 +181,12 @@ def update_service(service_id): try: service = ServiceService.update_service(service_id, filtered_data) + user_permissions = get_current_user_permissions() + filtered_service = filter_item_by_permissions(service.to_dict(), user_permissions) return jsonify({ 'code': 200, 'msg': '更新成功', - 'data': service.to_dict() + 'data': filtered_service }) except ValueError as e: return jsonify({'code': 404, 'msg': str(e)}), 404 @@ -137,8 +196,7 @@ def update_service(service_id): @inbound_bp.route('/service/', methods=['DELETE']) -@jwt_required() -@role_required('admin,manager') +@permission_required('inbound_service:operation') def delete_service(service_id): """删除服务权益""" try: @@ -155,7 +213,7 @@ def delete_service(service_id): @inbound_bp.route('/service/suggestions/providers', methods=['GET']) -@jwt_required() +@permission_required('inbound_service') def get_provider_suggestions(): base_id = request.args.get('base_id', type=int) if not base_id: @@ -165,7 +223,7 @@ def get_provider_suggestions(): @inbound_bp.route('/service/suggestions/users', methods=['GET']) -@jwt_required() +@permission_required('inbound_service') def get_user_suggestions(): keyword = request.args.get('keyword', '') data = ServiceService.search_system_users(keyword) @@ -173,10 +231,10 @@ def get_user_suggestions(): @inbound_bp.route('/service/options', methods=['GET']) -@jwt_required() +@permission_required('inbound_service') def get_options(): try: data = ServiceService.get_filter_options() return jsonify({'code': 200, 'msg': 'success', 'data': data}) except Exception as e: - return jsonify({'code': 500, 'msg': str(e)}), 500 \ No newline at end of file + return jsonify({'code': 500, 'msg': str(e)}), 500 diff --git a/inventory-web/src/views/stock/inbound/service.vue b/inventory-web/src/views/stock/inbound/service.vue index c04a867..5ceec2d 100644 --- a/inventory-web/src/views/stock/inbound/service.vue +++ b/inventory-web/src/views/stock/inbound/service.vue @@ -45,21 +45,21 @@ 搜索 重置 - 新增服务 + 新增服务 - - - - + + + + - - - + + +