签入版本

This commit is contained in:
2026-09-12 22:24:53 +08:00
commit 05cb22302c
863 changed files with 118186 additions and 0 deletions
+221
View File
@@ -0,0 +1,221 @@
"""
种子数据脚本(幂等,可重复执行)
=============================================================
在空库上初始化系统必需数据,供多数据库(mysql/pgsql/mssql/sqlite/oracle)就绪:
- admin 超管用户(id=1,密码见 DEFAULT_PASSWORD,默认 123456)
- 超级管理员角色 + 关联
- 系统管理目录 + 常用管理菜单 + 查询权限节点
- 基础字典(性别)、站点配置、系统参数
用法:由 scripts/setup_db.py 在 app 上下文内调用 seed_all()。
"""
import logging
import os
import sys
# 将项目根加入 sys.path(脚本以 `python scripts/xxx.py` 运行时 cwd 是 scripts/)
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if ROOT not in sys.path:
sys.path.insert(0, ROOT)
from src.core.database import db # noqa: E402
from src.core.password import encrypt_password, generate_salt # noqa: E402
from src.common.constants.common import DEFAULT_PASSWORD # noqa: E402
from src.modules.system.user.repository import UserRepository # noqa: E402
from src.modules.system.role.repository import RoleRepository # noqa: E402
from src.modules.association.user_role.repository import UserRoleRepository # noqa: E402
from src.modules.system.menu.repository import MenuRepository # noqa: E402
from src.modules.dictionary.dict.repository import DictRepository # noqa: E402
from src.modules.dictionary.dict_item.repository import DictItemRepository # noqa: E402
from src.modules.configuration.config.repository import ConfigRepository # noqa: E402
from src.modules.configuration.config_item.repository import ConfigItemRepository # noqa: E402
from src.modules.param.repository import ParamRepository # noqa: E402
logger = logging.getLogger(__name__)
def _get_or_create(repo, filters, defaults):
"""幂等:按等值条件查,不存在则创建"""
obj = repo.first_by(filters)
if obj is None:
obj = repo.create(**defaults)
db.session.commit()
logger.info(f"已创建 {repo.__name__}: {filters}")
return obj
def seed_admin_user():
"""创建 admin 超管用户(id=1,超管绕过 user_id==1)"""
if UserRepository.first_by({"username": "admin"}):
logger.info("admin 用户已存在,跳过")
return
salt = generate_salt()
admin = UserRepository.create(
username="admin",
realname="超级管理员",
gender=1,
avatar="",
mobile="",
email="",
dept_id=0,
level_id=0,
position_id=0,
province_code="",
city_code="",
district_code="",
street_code="",
address="",
status=1,
sort=1,
note="系统内置超管,请登录后修改密码",
password=encrypt_password(DEFAULT_PASSWORD, salt),
salt=salt,
create_user="system",
)
db.session.commit()
logger.info(f"已创建 admin 用户(id={admin.id},初始密码 {DEFAULT_PASSWORD})")
def seed_superadmin_role():
"""创建超级管理员角色并关联 admin 用户"""
role = _get_or_create(
RoleRepository,
{"code": "admin"},
{"name": "超级管理员", "code": "admin", "status": 1, "sort": 1, "create_user": "system"},
)
admin = UserRepository.first_by({"username": "admin"})
if admin and not UserRoleRepository.first_by({"user_id": admin.id, "role_id": role.id}):
UserRoleRepository.create(user_id=admin.id, role_id=role.id, create_user="system")
db.session.commit()
logger.info("已关联 admin → 超级管理员")
def seed_basic_menus():
"""创建系统管理目录 + 常用管理菜单 + 查询权限节点"""
if MenuRepository.first_by({"name": "系统管理", "parent_id": 0}):
logger.info("系统管理菜单已存在,跳过")
return
sys_dir = MenuRepository.create(
name="系统管理", parent_id=0, path="/system", component="",
permission="", type=0, status=1, target=0, sort=1, icon="setting",
hide=0, create_user="system",
)
db.session.commit()
child_menus = [
("用户管理", "/user", "system/user/index", "sys:user:page", 1),
("角色管理", "/role", "system/role/index", "sys:role:page", 2),
("菜单管理", "/menu", "system/menu/index", "sys:menu:page", 3),
("部门管理", "/dept", "system/dept/index", "sys:dept:page", 4),
("岗位管理", "/position", "system/position/index", "sys:position:page", 5),
("职级管理", "/level", "system/level/index", "sys:level:page", 6),
]
for name, path, component, permission, sort in child_menus:
menu = MenuRepository.create(
name=name, parent_id=sys_dir.id, path=path, component=component,
permission=permission, type=0, status=1, target=0, sort=sort,
hide=0, create_user="system",
)
db.session.commit()
# 查询权限节点(type=1,前端按钮/接口鉴权用)
MenuRepository.create(
name=f"查询{name}", parent_id=menu.id, path=f"{path}/page", component="",
permission=permission, type=1, status=1, target=0, sort=1,
create_user="system",
)
db.session.commit()
logger.info("已创建系统管理目录与常用菜单")
def seed_import_export_permissions():
"""为存量菜单幂等补齐「导入/导出」按钮权限节点
背景说明:
导入/导出 按钮在前端通过 v-perm 指令按权限码鉴权(如 sys:user:import),
权限码必须存在于菜单表中(type=1 按钮节点),否则即使后端接口已放行、
后端装饰器也已校验,前端按钮仍不会渲染。本函数为老库中已存在的菜单
补建对应按钮节点,与新建库流程(seed_basic_menus)保持一致。
设计要点:
1. 幂等:以「父菜单名 + type=0」定位父菜单,以「按钮名 + parent_id」判重,
已存在的节点直接跳过,脚本可安全重复执行。
2. 容错:父菜单缺失(如老库中未创建「职级管理」)时仅记录日志并跳过,
不中断其余按钮节点创建。
3. 按钮路径约定为 <父菜单路径>/import、<父菜单路径>/export,
与后端 router 的路由保持一致,便于前端跳转与鉴权对齐。
"""
# 待补建的按钮节点配置:父菜单名 / 按钮名 / 路径 / 权限码 / 排序
btn_specs = [
("用户管理", "导入用户", "/user/import", "sys:user:import", 2),
("用户管理", "导出用户", "/user/export", "sys:user:export", 3),
("职级管理", "导入职级", "/level/import", "sys:level:import", 2),
("职级管理", "导出职级", "/level/export", "sys:level:export", 3),
]
for menu_name, btn_name, btn_path, permission, sort in btn_specs:
parent = MenuRepository.first_by({"name": menu_name, "type": 0})
if not parent:
logger.warning(f"菜单「{menu_name}」不存在,跳过权限节点 {permission}")
continue
if MenuRepository.first_by({"name": btn_name, "parent_id": parent.id}):
logger.info(f"权限节点 {permission} 已存在,跳过")
continue
MenuRepository.create(
name=btn_name, parent_id=parent.id, path=btn_path, component="",
permission=permission, type=1, status=1, target=0, sort=sort,
create_user="system",
)
db.session.commit()
logger.info(f"已创建权限节点 {permission}({btn_name})")
def seed_basic_dict():
"""创建性别字典"""
if DictRepository.first_by({"code": "gender"}):
logger.info("性别字典已存在,跳过")
return
dict_obj = DictRepository.create(name="性别", code="gender", sort=1, create_user="system")
db.session.commit()
for name, value, sort in [("男", "1", 1), ("女", "2", 2), ("保密", "3", 3)]:
DictItemRepository.create(
dict_id=dict_obj.id, name=name, value=value, sort=sort, create_user="system"
)
db.session.commit()
def seed_basic_config():
"""创建站点配置"""
if ConfigRepository.first_by({"code": "site_config"}):
logger.info("站点配置已存在,跳过")
return
config = ConfigRepository.create(name="站点配置", code="site_config", sort=1, create_user="system")
db.session.commit()
ConfigItemRepository.create(
config_id=config.id, name="网站名称",
code="site_name", value="DjangoAdmin", type="text", sort=1, create_user="system",
)
db.session.commit()
def seed_basic_param():
"""创建系统参数"""
_get_or_create(
ParamRepository,
{"code": "system_param"},
{"name": "系统参数", "code": "system_param", "value": "", "type": 0, "status": 1, "sort": 1, "create_user": "system"},
)
def seed_all():
"""执行全部种子(幂等)"""
seed_admin_user()
seed_superadmin_role()
seed_basic_menus()
# 为存量库补建导入/导出按钮权限节点,必须在菜单创建之后执行
seed_import_export_permissions()
seed_basic_dict()
seed_basic_config()
seed_basic_param()
logger.info("种子数据初始化完成")