feat: implement user authentication and permission-based data filtering

Add user login system with role-based access control:
- Admin users see all data and can filter by any manager
- Regular users only see records where ManagerName matches their username

New components:
- BIPUsersDAO: user authentication and management
- SessionManager: singleton session state management
- LoginDialog: modal login UI for app startup
- init_users.sql: initial user setup script

Permission enforcement:
- Material validation tab: hide manager filter for non-admin, force filter by current user
- Material type management: hide filter UI for non-admin, filter at database level
- Window title and status bar display current user info

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-02-09 18:17:31 +08:00
parent 57c23f4608
commit 04b99292ad
10 changed files with 607 additions and 22 deletions

View File

@@ -131,16 +131,18 @@ class CheckboxTreeview(ttk.Treeview):
class MaterialValidationTab(ttk.Frame):
"""物料校验标签页"""
def __init__(self, parent, config: ConfigManager):
def __init__(self, parent, config: ConfigManager, session_manager):
"""
初始化物料校验标签页
Args:
parent: 父容器
config: 配置管理器
session_manager: SessionManager 实例,用于权限控制
"""
super().__init__(parent)
self.config = config
self.session_manager = session_manager
self.validating = False
self.validation_results = None
self.material_records_cache = None # 缓存完整的物料记录
@@ -421,7 +423,17 @@ class MaterialValidationTab(ttk.Frame):
widget.destroy()
self.manager_checkboxes.clear()
# 从两个表获取负责人列表并合并去重
# PERMISSION CHECK: 非管理员用户只能看到自己的数据
if not self.session_manager.is_admin():
self.managers = [self.session_manager.get_username()]
# 不显示复选框,直接显示提示信息
ttk.Label(
self.filter_frame,
text=f"仅显示您的数据(负责人:{self.session_manager.get_username()}"
).pack(anchor="w")
return
# 管理员:从两个表获取负责人列表并合并去重
from db.materials_to_be_deleted_records_dao import MaterialsToBeDeletedDAO
record_dao = MaterialsToBeDeletedDAO()
@@ -501,7 +513,11 @@ class MaterialValidationTab(ttk.Frame):
if not self.material_records_cache:
return
selected_managers = self._get_selected_managers()
# PERMISSION CHECK: 非管理员用户强制筛选到当前用户
if not self.session_manager.is_admin():
selected_managers = [self.session_manager.get_username()]
else:
selected_managers = self._get_selected_managers()
if not selected_managers:
# 没有选中任何负责人,清空表格
@@ -541,7 +557,12 @@ class MaterialValidationTab(ttk.Frame):
# 从数据库获取已标记删除的记录
from db.materials_to_be_deleted_records_dao import MaterialsToBeDeletedDAO
record_dao = MaterialsToBeDeletedDAO()
marked_records = record_dao.get_all_records()
# PERMISSION CHECK: 非管理员用户只获取自己的记录
if not self.session_manager.is_admin():
marked_records = record_dao.get_materials_by_manager(self.session_manager.get_username())
else:
marked_records = record_dao.get_all_records()
# Build dictionary: MaterialCode -> ManagerName
marked_codes_dict = {
@@ -795,7 +816,12 @@ class MaterialValidationTab(ttk.Frame):
# 从数据库获取已标记删除的 MaterialCode -> ManagerName 映射
from db.materials_to_be_deleted_records_dao import MaterialsToBeDeletedDAO
record_dao = MaterialsToBeDeletedDAO()
marked_records = record_dao.get_all_records()
# PERMISSION CHECK: 非管理员用户只获取自己的记录
if not self.session_manager.is_admin():
marked_records = record_dao.get_materials_by_manager(self.session_manager.get_username())
else:
marked_records = record_dao.get_all_records()
# Build dictionary: MaterialCode -> ManagerName
marked_codes_dict = {
@@ -1067,4 +1093,4 @@ class MaterialValidationTab(ttk.Frame):
def open_type_management(self):
"""打开类型管理窗口"""
dialog = MaterialTypeManagementDialog(self, title="物料类型管理")
dialog = MaterialTypeManagementDialog(self, self.session_manager, title="物料类型管理")