"""显式、幂等的本地最小开发数据初始化。 执行:python -m dms.seed_dev 必须通过环境变量提供管理员和普通用户密码;默认不会覆盖已有用户密码。 """ from __future__ import annotations import os from flask import Flask from sqlalchemy import select from dms import init_dms from dms.common.enums import EnabledStatus, RoleCode, SecurityLevel from dms.extensions import db from dms.models import Organization, User from dms.security.passwords import hash_password PASSWORD_ENVIRONMENTS = { "admin": "DMS_SEED_ADMIN_PASSWORD", "user": "DMS_SEED_USER_PASSWORD", } def _required_passwords() -> dict[str, str]: missing = [ environment for environment in PASSWORD_ENVIRONMENTS.values() if not os.environ.get(environment) ] if missing: raise RuntimeError("缺少必需的密码环境变量:" + "、".join(missing)) return { username: os.environ[environment] for username, environment in PASSWORD_ENVIRONMENTS.items() } def _overwrite_passwords_enabled() -> bool: raw_value = os.environ.get("DMS_SEED_OVERWRITE_PASSWORDS", "false").lower() if raw_value not in {"true", "false"}: raise RuntimeError("DMS_SEED_OVERWRITE_PASSWORDS必须是true或false") return raw_value == "true" def _organization( *, org_code: str, org_name: str, org_path: str, sort_no: int, parent_id: int | None, ) -> tuple[Organization, bool]: existing = db.session.scalar( select(Organization).where(Organization.org_code == org_code) ) if existing is not None: return existing, False organization = Organization( org_code=org_code, org_name=org_name, org_path=org_path, sort_no=sort_no, parent_id=parent_id, status=EnabledStatus.ENABLED.value, ) db.session.add(organization) db.session.flush() return organization, True def _user( *, username: str, real_name: str, password: str, role_code: RoleCode, security_level: SecurityLevel, organization: Organization, overwrite_password: bool, ) -> bool: existing = db.session.scalar(select(User).where(User.username == username)) if existing is not None: if overwrite_password: existing.password_hash = hash_password(password) existing.auth_version += 1 existing.row_version += 1 return False db.session.add( User( username=username, password_hash=hash_password(password), real_name=real_name, organization_id=organization.id, organization_name=organization.org_name, role_code=role_code.value, security_level=security_level.value, status=EnabledStatus.ENABLED.value, ) ) return True def seed() -> tuple[int, int]: passwords = _required_passwords() overwrite_passwords = _overwrite_passwords_enabled() created_organizations = 0 created_users = 0 root, created = _organization( org_code="ORG_ROOT", org_name="机关", org_path="/机关", sort_no=10, parent_id=None, ) created_organizations += int(created) ops, created = _organization( org_code="ORG_OPS", org_name="作战部", org_path="/机关/作战部", sort_no=20, parent_id=root.id, ) created_organizations += int(created) _, created = _organization( org_code="ORG_COMMS", org_name="通信部", org_path="/机关/通信部", sort_no=30, parent_id=root.id, ) created_organizations += int(created) created_users += int( _user( username="admin", real_name="系统管理员", password=passwords["admin"], role_code=RoleCode.ADMIN, security_level=SecurityLevel.TOP_SECRET, organization=root, overwrite_password=overwrite_passwords, ) ) created_users += int( _user( username="user", real_name="普通用户", password=passwords["user"], role_code=RoleCode.USER, security_level=SecurityLevel.SECRET, organization=ops, overwrite_password=overwrite_passwords, ) ) db.session.commit() return created_organizations, created_users def main() -> None: app = Flask("dms-seed-dev") init_dms(app) if _overwrite_passwords_enabled(): print("警告:已显式启用已有开发用户密码覆盖,并将使其旧Token失效。") with app.app_context(): try: organization_count, user_count = seed() except Exception: db.session.rollback() raise print(f"初始化完成:新增组织{organization_count}个,新增用户{user_count}个。") if __name__ == "__main__": main()