feat: JWT多租户数据权限隔离 & 主管系统管理权限 & 含税单价补齐

## 多租户公司数据隔离
- 新增 get_current_company_filter() 工具函数 (decorators.py)
  SUPER_ADMIN: 可传company_name参数过滤或传ALL看全量
  其他角色: 强制隔离到JWT中的company_name
- 重构 base_service.py / buy_service.py: 用集中式函数替换内联公司过滤
- SysRolePermission 表新增 company_name 字段,支持同角色不同公司权限
- get_user_permissions() 新增 company_name 参数,查公司定制+全局模板权限
- permission.py API 新增 @permission_required 拦截 + 公司过滤
- 19个API/service文件传递 company_name 到权限查询

## 主管系统管理权限
- delete_user() 允许SUPERVISOR删除同公司用户 (原仅SUPER_ADMIN)
- get_all_users() 新增 company_name 参数过滤
- 用户列表/权限分配 API 应用 get_current_company_filter()
- 前端 UserCreate.vue: 超管可见公司下拉框,主管隐藏部门字段

## 前端多租户适配
- material/list.vue / buy.vue: 公司下拉框仅超管可见,默认ALL
- UserCreate.vue: 新增搜索栏公司筛选,部门字段按角色显隐
- auth.ts: getUserList() 支持 params 参数

## Bug修复: 含税单价字段补齐
- buy.vue: 表格列/高级筛选/排序/权限映射新增 post_tax_unit_price
- buy_service.py: allowed_fields/sort_field_map 新增 post_tax_unit_price
This commit is contained in:
yueli
2026-07-13 15:12:22 +08:00
parent 4ce42309db
commit 4f5965db02
22 changed files with 289 additions and 172 deletions

View File

@ -149,9 +149,12 @@ def permission_required(permission_code):
if user_role and user_role.upper() == 'SUPER_ADMIN':
return fn(*args, **kwargs)
# 获取当前用户公司,用于权限隔离
user_company = claims.get('company_name', '')
try:
from app.services.auth_service import AuthService
perm_dict = AuthService.get_user_permissions(user_role)
perm_dict = AuthService.get_user_permissions(user_role, company_name=user_company)
except Exception as e:
logging.warning(f"Failed to fetch permissions for role {user_role}: {e}")
return jsonify(msg='权限查询失败'), 403
@ -164,6 +167,46 @@ def permission_required(permission_code):
return decorator
return wrapper
def get_current_company_filter():
"""
多租户数据权限隔离工具函数。
根据当前 JWT 中的 role 和 company_name 决定 SQL 查询应过滤到哪个公司。
返回值:
None → 不限制公司(超级管理员全局视角,或显式传了 'ALL'
str → 仅查询该公司数据
使用示例:
company_limit = get_current_company_filter()
if company_limit is not None:
query = query.filter(MaterialBase.company_name == company_limit)
"""
from flask import request
claims = get_jwt()
user_role = claims.get('role', '')
# 规范化角色为大写
if user_role:
user_role = user_role.upper()
# 从请求参数获取前端指定的公司(兼容 company_name 和 company 两个参数名)
req_company = request.args.get('company_name', '') or request.args.get('company', '')
if user_role == 'SUPER_ADMIN':
# 超级管理员:允许通过参数指定公司过滤
if req_company and req_company.strip().upper() != 'ALL':
return req_company.strip()
# 未指定或指定了 ALL → 返回 None全量不限制
return None
else:
# 其他角色SUPERVISOR / FINANCE / WAREHOUSE_MGR 等):
# 强制隔离到 JWT 中记录的公司,无视前端参数
user_company = claims.get('company_name', '')
return user_company if user_company else None
def audit_log(module: str = None, action: str = None, get_target_id_fn=None, get_target_name_fn=None, get_details_fn=None):
"""
已废弃!