fix: 所有 init_ 方法增加字段级 Dirty Check,相同值不赋值,防止 SQLAlchemy 触发 UPDATE 事件产生冗余审计日志

This commit is contained in:
DXC
2026-05-19 11:10:41 +08:00
parent 2a6e3979e8
commit 7d02da2f5c

View File

@ -176,7 +176,28 @@ class PermissionService:
db.session.flush() # 获取新插入的 ID
print(f"✅ 审计日志菜单已创建 (code: {menu_code})")
else:
print(f" 审计日志菜单已存在 (code: {menu_code})")
# ★★★ Dirty Check仅当字段真正变化时才 add避免触发 UPDATE 事件
is_dirty = False
if existing_menu.parent_id != 0:
existing_menu.parent_id = 0
is_dirty = True
if existing_menu.name != '审计日志':
existing_menu.name = '审计日志'
is_dirty = True
if existing_menu.path != '/system/audit':
existing_menu.path = '/system/audit'
is_dirty = True
if existing_menu.sort_order != 110:
existing_menu.sort_order = 110
is_dirty = True
if existing_menu.is_visible != True:
existing_menu.is_visible = True
is_dirty = True
if is_dirty:
db.session.add(existing_menu)
print(f"🔄 审计日志菜单已更新 (code: {menu_code})")
else:
print(f" 审计日志菜单已存在,无需更新 (code: {menu_code})")
# 2. 为超级管理员赋予审计日志菜单权限
role_code = 'SUPER_ADMIN'
@ -230,7 +251,14 @@ class PermissionService:
db.session.flush()
print(f"✅ 盘点管理顶级菜单已创建")
else:
print(f" 盘点管理顶级菜单已存在")
is_dirty = False
if stocktake_menu.parent_id != 0: stocktake_menu.parent_id = 0; is_dirty = True
if stocktake_menu.name != '盘点管理': stocktake_menu.name = '盘点管理'; is_dirty = True
if stocktake_menu.path != '/stocktake': stocktake_menu.path = '/stocktake'; is_dirty = True
if stocktake_menu.sort_order != 30: stocktake_menu.sort_order = 30; is_dirty = True
if stocktake_menu.is_visible != True: stocktake_menu.is_visible = True; is_dirty = True
if is_dirty: db.session.add(stocktake_menu); print(f"🔄 盘点管理顶级菜单已更新")
else: print(f" 盘点管理顶级菜单已存在")
# 2. 创建子菜单:盲盘作业
stocktake_op_code = 'inventory_stocktake'
@ -248,7 +276,13 @@ class PermissionService:
db.session.flush()
print(f"✅ 盲盘作业菜单已创建")
else:
print(f" 盲盘作业菜单已存在")
is_dirty = False
if stocktake_op_menu.name != '盲盘作业': stocktake_op_menu.name = '盲盘作业'; is_dirty = True
if stocktake_op_menu.path != '/stocktake/operation': stocktake_op_menu.path = '/stocktake/operation'; is_dirty = True
if stocktake_op_menu.sort_order != 1: stocktake_op_menu.sort_order = 1; is_dirty = True
if stocktake_op_menu.is_visible != True: stocktake_op_menu.is_visible = True; is_dirty = True
if is_dirty: db.session.add(stocktake_op_menu); print(f"🔄 盲盘作业菜单已更新")
else: print(f" 盲盘作业菜单已存在")
# 3. 为盲盘作业添加操作权限元素
stocktake_op_element = SysElement.query.filter_by(
@ -265,7 +299,11 @@ class PermissionService:
db.session.add(stocktake_op_element)
print(f"✅ 盲盘作业操作权限已创建")
else:
print(f" 盲盘作业操作权限已存在")
is_dirty = False
if stocktake_op_element.name != '盲盘操作': stocktake_op_element.name = '盲盘操作'; is_dirty = True
if stocktake_op_element.element_type != 'operation': stocktake_op_element.element_type = 'operation'; is_dirty = True
if is_dirty: db.session.add(stocktake_op_element); print(f"🔄 盲盘作业操作权限已更新")
else: print(f" 盲盘作业操作权限已存在")
# 4. 创建子菜单:盈亏调整
adjustment_code = 'stock_adjustment'
@ -283,7 +321,13 @@ class PermissionService:
db.session.flush()
print(f"✅ 盈亏调整菜单已创建")
else:
print(f" 盈亏调整菜单已存在")
is_dirty = False
if adjustment_menu.name != '盈亏调整': adjustment_menu.name = '盈亏调整'; is_dirty = True
if adjustment_menu.path != '/stocktake/adjustment': adjustment_menu.path = '/stocktake/adjustment'; is_dirty = True
if adjustment_menu.sort_order != 2: adjustment_menu.sort_order = 2; is_dirty = True
if adjustment_menu.is_visible != True: adjustment_menu.is_visible = True; is_dirty = True
if is_dirty: db.session.add(adjustment_menu); print(f"🔄 盈亏调整菜单已更新")
else: print(f" 盈亏调整菜单已存在")
# 5. 为盈亏调整添加列表权限元素 (stock_adjustment:list)
adjustment_list_element = SysElement.query.filter_by(
@ -300,7 +344,11 @@ class PermissionService:
db.session.add(adjustment_list_element)
print(f"✅ 盈亏调整列表权限已创建")
else:
print(f" 盈亏调整列表权限已存在")
is_dirty = False
if adjustment_list_element.name != '盈亏列表': adjustment_list_element.name = '盈亏列表'; is_dirty = True
if adjustment_list_element.element_type != 'element': adjustment_list_element.element_type = 'element'; is_dirty = True
if is_dirty: db.session.add(adjustment_list_element); print(f"🔄 盈亏调整列表权限已更新")
else: print(f" 盈亏调整列表权限已存在")
# 6. 为盈亏调整添加操作权限元素 (stock_adjustment:operation)
adjustment_op_element = SysElement.query.filter_by(
@ -317,7 +365,11 @@ class PermissionService:
db.session.add(adjustment_op_element)
print(f"✅ 盈亏调整操作权限已创建")
else:
print(f" 盈亏调整操作权限已存在")
is_dirty = False
if adjustment_op_element.name != '盈亏操作': adjustment_op_element.name = '盈亏操作'; is_dirty = True
if adjustment_op_element.element_type != 'operation': adjustment_op_element.element_type = 'operation'; is_dirty = True
if is_dirty: db.session.add(adjustment_op_element); print(f"🔄 盈亏调整操作权限已更新")
else: print(f" 盈亏调整操作权限已存在")
# 7. 为超级管理员分配所有盘点相关权限
menu_codes = [stocktake_mgmt_code, stocktake_op_code, adjustment_code]
@ -491,13 +543,19 @@ class PermissionService:
db.session.delete(e)
db.session.delete(dup)
# 第三步:强制重新设置所有子菜单 parent_id,确保没有遗漏
# 改为对象级更新以触发审计事件
# 第三步:仅当子菜单 parent_id 有误时才更新Dirty Check
# 遍历所有子菜单,只在 parent_id 需要修正时才触碰对象
child_codes = [m[0] for m in menu_defs if m[3] is not None]
child_menus = SysMenu.query.filter(SysMenu.code.in_(child_codes)).all()
for m in child_menus:
m.parent_id = None
# 只有 parent_id 为 0 或 None即没有正确挂载父菜单时才更新
if m.parent_id == 0 or m.parent_id is None:
m.parent_id = None # SQLAlchemy 设为 None 表示挂载到根parent_id=None
db.session.add(m)
print(f"🔧 修正子菜单 parent_id: {m.code} (parent_id → None)")
# 创建或更新菜单
# 第四步:创建或更新菜单(带字段级 Dirty Check
# 只有至少有一个字段真正变化了才 add避免 SQLAlchemy 产生 UPDATE 事件
menu_map = {} # code -> menu obj
for code, name, path, parent_code, sort_order in menu_defs:
@ -508,21 +566,38 @@ class PermissionService:
db.session.flush()
print(f"✅ 菜单已创建: {name} ({code})")
else:
# 更新已有菜单的属性
menu.name = name
menu.path = path
menu.sort_order = sort_order
# ★★★ 字段级 Dirty Check逐字段比较仅在值真正变化时赋值
is_dirty = False
if menu.name != name:
menu.name = name
is_dirty = True
if menu.path != path:
menu.path = path
is_dirty = True
if menu.sort_order != sort_order:
menu.sort_order = sort_order
is_dirty = True
# 只有至少一个字段变化了才 add触发 UPDATE
if is_dirty:
db.session.add(menu)
print(f"🔄 菜单已更新: {name} ({code})")
menu_map[code] = menu
# 设置 parent_id
# 第五步:设置 parent_id(带 Dirty Check只在值真正变化时更新
for code, name, path, parent_code, sort_order in menu_defs:
if parent_code and parent_code in menu_map:
menu = menu_map[code]
parent = menu_map[parent_code]
menu.parent_id = parent.id
# 只有 parent_id 实际变化了才赋值,避免重复触发 UPDATE
if menu.parent_id != parent.id:
menu.parent_id = parent.id
db.session.add(menu)
print(f"🔗 菜单 {code} 已挂载到父菜单 {parent_code} (id={parent.id})")
# 为超级管理员分配所有菜单权限
# 第六步:为超级管理员分配顶级菜单权限(只做 INSERT不触碰已存在的记录
for code, name, path, parent_code, sort_order in menu_defs:
if parent_code is None: # 只分配顶级菜单
existing_perm = SysRolePermission.query.filter_by(