Files
----/后端源码/yifan.action-ai.cn/api/app/scripts/init_yifan_membership_menus.py

251 lines
9.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
一次性初始化:会员功能菜单(目录 + 页面菜单 + 按钮权限点)
推荐用法(在已配置好 env/.env.dev 或运行环境变量的情况下):
python -m app.scripts.init_yifan_membership_menus
该脚本是幂等的:重复执行会跳过已存在的菜单/按钮。
"""
from __future__ import annotations
import asyncio
import sqlalchemy as sa
from app.core.database import async_db_session
from app.api.v1.module_system.auth.schema import AuthSchema
from app.api.v1.module_system.menu.crud import MenuCRUD
from app.api.v1.module_system.menu.schema import MenuCreateSchema
from app.core.logger import log
# 关键:预先导入 RoleModel避免 MenuModel 关系解析失败
# MenuModel.roles -> "RoleModel"(字符串引用),若未导入会导致 mapper 初始化报错
from app.api.v1.module_system.role.model import RoleModel, RoleMenusModel # noqa: F401
from app.api.v1.module_system.position.model import PositionModel # noqa: F401
from app.api.v1.module_system.dept.model import DeptModel # noqa: F401
from app.api.v1.module_system.user.model import UserModel, UserRolesModel # noqa: F401
# 固定父节点(用户提供):壹梵后台管理
ROOT_PARENT_ID = 131
async def _create_if_not_exists(auth: AuthSchema, data: dict, *, unique_by: dict) -> int:
"""
幂等创建菜单:
- unique_by: 用于判重的字段(如 {"route_path": "/x", "type": 2} 或 {"permission": "...", "type": 3}
"""
crud = MenuCRUD(auth)
# 禁用默认预加载,避免在脚本上下文触发复杂关系初始化
exists = await crud.get(preload=[], **unique_by)
if exists:
return exists.id
created = await crud.create(data=MenuCreateSchema(**data))
return created.id
async def init_yifan_membership_menus(parent_id: int = ROOT_PARENT_ID) -> dict:
"""
初始化会员功能菜单(挂到 parent_id 下)。
返回创建结果的 id 汇总。
"""
async with async_db_session() as session:
async with session.begin():
# 0) 运行前检查sys_menu 是否存在(否则无法初始化菜单)
conn = await session.connection()
insp = sa.inspect(conn.sync_connection)
if not insp.has_table("sys_menu"):
raise RuntimeError(
"当前数据库缺少表 sys_menu无法初始化菜单。请先初始化数据库结构运行项目初始化/迁移脚本),"
"确保 sys_menu/sys_role/sys_user 等核心表已创建。"
)
auth = AuthSchema(db=session, check_data_scope=False, user=None)
# 1) 创建父目录会员功能type=1
membership_dir = {
"name": "yifan_membership",
"type": 1,
"icon": "menu",
"order": 10,
"permission": None,
"route_name": "YifanMembership",
"route_path": "/yifan/membership",
"component_path": None,
"redirect": "/yifan/membership/members",
"parent_id": parent_id,
"keep_alive": True,
"hidden": False,
"always_show": True,
"title": "会员功能",
"params": [],
"affix": False,
"status": "0",
"description": "脚本自动创建",
}
PARENT_ID = await _create_if_not_exists(
auth,
membership_dir,
unique_by={"route_path": "/yifan/membership", "type": 1},
)
# 2) 三个页面菜单type=2
discount_menu = {
"name": "yifan_membership_discount",
"type": 2,
"icon": "setting",
"order": 1,
"permission": "module_yifan:yifan_membership_discount:query",
"route_name": "YifanMembershipDiscount",
"route_path": "/yifan/membership/discount",
"component_path": "module_yifan/yifan_membership_discount/index",
"redirect": "",
"parent_id": PARENT_ID,
"keep_alive": True,
"hidden": False,
"always_show": False,
"title": "会员折扣设置",
"params": [],
"affix": False,
"status": "0",
"description": "脚本自动创建",
}
DISCOUNT_MENU_ID = await _create_if_not_exists(
auth,
discount_menu,
unique_by={"route_path": "/yifan/membership/discount", "type": 2},
)
member_menu = {
"name": "yifan_membership_member",
"type": 2,
"icon": "user",
"order": 2,
"permission": "module_yifan:yifan_membership_member:query",
"route_name": "YifanMembershipMember",
"route_path": "/yifan/membership/members",
"component_path": "module_yifan/yifan_membership_member/index",
"redirect": "",
"parent_id": PARENT_ID,
"keep_alive": True,
"hidden": False,
"always_show": False,
"title": "会员列表",
"params": [],
"affix": False,
"status": "0",
"description": "脚本自动创建",
}
MEMBER_MENU_ID = await _create_if_not_exists(
auth,
member_menu,
unique_by={"route_path": "/yifan/membership/members", "type": 2},
)
user_center_menu = {
"name": "yifan_membership_user_center",
"type": 2,
"icon": "user",
"order": 3,
"permission": "module_yifan:yifan_membership_user_center:query",
"route_name": "YifanMembershipUserCenter",
"route_path": "/yifan/membership/user-center",
"component_path": "module_yifan/yifan_membership_user_center/index",
"redirect": "",
"parent_id": PARENT_ID,
"keep_alive": True,
"hidden": False,
"always_show": False,
"title": "用户中心(会员权益)",
"params": [],
"affix": False,
"status": "0",
"description": "脚本自动创建",
}
USER_CENTER_MENU_ID = await _create_if_not_exists(
auth,
user_center_menu,
unique_by={"route_path": "/yifan/membership/user-center", "type": 2},
)
# 3) 按钮权限点type=3挂到对应页面菜单下
discount_btn = {
"name": "修改折扣",
"type": 3,
"icon": None,
"order": 1,
"permission": "module_yifan:yifan_membership_discount:update",
"route_name": None,
"route_path": None,
"component_path": None,
"redirect": "",
"parent_id": DISCOUNT_MENU_ID,
"keep_alive": False,
"hidden": True,
"always_show": False,
"title": "修改折扣",
"params": [],
"affix": False,
"status": "0",
"description": "脚本自动创建",
}
DISCOUNT_UPDATE_BTN_ID = await _create_if_not_exists(
auth,
discount_btn,
unique_by={"permission": "module_yifan:yifan_membership_discount:update", "type": 3},
)
quota_btn = {
"name": "名额调整",
"type": 3,
"icon": None,
"order": 1,
"permission": "module_yifan:yifan_membership_member:quota_adjust",
"route_name": None,
"route_path": None,
"component_path": None,
"redirect": "",
"parent_id": MEMBER_MENU_ID,
"keep_alive": False,
"hidden": True,
"always_show": False,
"title": "名额调整",
"params": [],
"affix": False,
"status": "0",
"description": "脚本自动创建",
}
MEMBER_QUOTA_BTN_ID = await _create_if_not_exists(
auth,
quota_btn,
unique_by={"permission": "module_yifan:yifan_membership_member:quota_adjust", "type": 3},
)
await session.commit()
return {
"parent_dir_id": PARENT_ID,
"discount_menu_id": DISCOUNT_MENU_ID,
"member_menu_id": MEMBER_MENU_ID,
"user_center_menu_id": USER_CENTER_MENU_ID,
"discount_update_button_id": DISCOUNT_UPDATE_BTN_ID,
"member_quota_adjust_button_id": MEMBER_QUOTA_BTN_ID,
}
async def _amain() -> None:
result = await init_yifan_membership_menus()
log.info(f"✅ 会员功能菜单初始化完成: {result}")
def main() -> None:
asyncio.run(_amain())
if __name__ == "__main__":
main()