| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- from dataclasses import dataclass
- from datetime import UTC, datetime, timedelta
- from hashlib import sha256
- from app.core.config import Settings
- from app.core.passwords import PasswordService
- from app.domains.attribution.models import PromotionCode, Salesperson
- from app.domains.catalog.models import Plan, Product, ProductChangeLog, ProductVersion
- from app.domains.identity.models import AdminUser
- from app.infrastructure.mysql.attribution_repositories import (
- SqlAlchemyAttributionRepository,
- )
- from app.infrastructure.mysql.repositories import (
- SqlAlchemyCatalogRepository,
- SqlAlchemyIdentityRepository,
- )
- from app.infrastructure.mysql.sessions import (
- DatabaseName,
- create_mysql_engine,
- create_session_factory,
- )
- INITIAL_ADMIN_PASSWORD = "zaq1XSW@"
- @dataclass(frozen=True, slots=True)
- class SeedManifest:
- admin_users: tuple[AdminUser, ...]
- products: tuple[Product, ...]
- versions: tuple[ProductVersion, ...]
- salespersons: tuple[Salesperson, ...]
- promotion_codes: tuple[PromotionCode, ...]
- @dataclass(frozen=True, slots=True)
- class PlanConfig:
- summary: str
- premium_cents: int
- coverage_amount_cents: int
- min_age: int
- max_age: int
- def stable_id(value: str) -> str:
- return sha256(value.encode("utf-8")).hexdigest()[:26].upper()
- def build_seed_manifest(password_hash: str = "HASHED_DURING_DATABASE_SEED") -> SeedManifest:
- products = (
- _product("MED-BASIC", "蓉惠基础医疗险", "MEDICAL"),
- _product("MED-UPGRADE", "蓉惠升级医疗险", "MEDICAL"),
- _product("MED-SENIOR", "银龄守护医疗险", "MEDICAL"),
- _product("ACC-FAMILY", "家庭意外保障险", "ACCIDENT"),
- )
- product_ids = {product.product_code: product.id for product in products}
- versions = (
- _version(product_ids, "MED-BASIC", "1.0.0", "EXPIRED", 2023, 2024, ("BASIC",)),
- _version(product_ids, "MED-BASIC", "2.0.0", "PUBLISHED", 2025, 2029, ("BASIC",)),
- _version(product_ids, "MED-BASIC", "3.0.0", "DRAFT", 2030, None, ("BASIC",)),
- _version(
- product_ids,
- "MED-UPGRADE",
- "1.0.0",
- "PUBLISHED",
- 2025,
- 2029,
- ("STANDARD", "ENHANCED"),
- ),
- _version(
- product_ids,
- "MED-UPGRADE",
- "1.1.0",
- "DRAFT",
- 2030,
- None,
- ("STANDARD", "ENHANCED"),
- ),
- _version(
- product_ids,
- "MED-SENIOR",
- "1.0.0",
- "PUBLISHED",
- 2025,
- 2029,
- ("SENIOR_STANDARD",),
- ),
- _version(
- product_ids,
- "ACC-FAMILY",
- "1.0.0",
- "PUBLISHED",
- 2025,
- 2029,
- ("INDIVIDUAL", "FAMILY"),
- ),
- )
- admin_users = (
- _admin("admin", "超级管理员", "SUPER_ADMIN", "ALL", ("*",), password_hash),
- _admin(
- "operator01",
- "运营人员",
- "OPERATOR",
- "MASKED_ALL",
- ("dashboard:read", "order:read", "policy:read", "user:read"),
- password_hash,
- ),
- _admin(
- "reviewer01",
- "审核人员",
- "REVIEWER",
- "READ_ONLY",
- ("order:read", "policy:read"),
- password_hash,
- ),
- _admin(
- "sales_a",
- "张伟",
- "SALESPERSON",
- "SELF",
- (
- "dashboard:read",
- "order:read",
- "policy:read",
- "product:read",
- "attribution:read",
- ),
- password_hash,
- ),
- _admin(
- "sales_b",
- "李娜",
- "SALESPERSON",
- "SELF",
- (
- "dashboard:read",
- "order:read",
- "policy:read",
- "product:read",
- "attribution:read",
- ),
- password_hash,
- ),
- _admin(
- "sales_c",
- "王磊",
- "SALESPERSON",
- "SELF",
- (
- "dashboard:read",
- "order:read",
- "policy:read",
- "product:read",
- "attribution:read",
- ),
- password_hash,
- ),
- _admin(
- "sales_d",
- "陈晨",
- "SALESPERSON",
- "SELF",
- (
- "dashboard:read",
- "order:read",
- "policy:read",
- "product:read",
- "attribution:read",
- ),
- password_hash,
- ),
- _admin(
- "sales_e",
- "刘洋",
- "SALESPERSON",
- "SELF",
- (
- "dashboard:read",
- "order:read",
- "policy:read",
- "product:read",
- "attribution:read",
- ),
- password_hash,
- ),
- _admin(
- "sales_f",
- "赵敏",
- "SALESPERSON",
- "SELF",
- (
- "dashboard:read",
- "order:read",
- "policy:read",
- "product:read",
- "attribution:read",
- ),
- password_hash,
- ),
- )
- created_at = datetime(2026, 1, 1, tzinfo=UTC)
- salespersons = (
- Salesperson(
- id=stable_id("salesperson:sales_a"),
- admin_user_id=stable_id("admin:sales_a"),
- code="SALES-A",
- name="张伟",
- status="ACTIVE",
- created_at=created_at,
- ),
- Salesperson(
- id=stable_id("salesperson:sales_b"),
- admin_user_id=stable_id("admin:sales_b"),
- code="SALES-B",
- name="李娜",
- status="ACTIVE",
- created_at=created_at,
- ),
- Salesperson(
- id=stable_id("salesperson:sales_c"),
- admin_user_id=stable_id("admin:sales_c"),
- code="SALES-C",
- name="王磊",
- status="ACTIVE",
- created_at=created_at,
- ),
- Salesperson(
- id=stable_id("salesperson:sales_d"),
- admin_user_id=stable_id("admin:sales_d"),
- code="SALES-D",
- name="陈晨",
- status="ACTIVE",
- created_at=created_at,
- ),
- Salesperson(
- id=stable_id("salesperson:sales_e"),
- admin_user_id=stable_id("admin:sales_e"),
- code="SALES-E",
- name="刘洋",
- status="ACTIVE",
- created_at=created_at,
- ),
- Salesperson(
- id=stable_id("salesperson:sales_f"),
- admin_user_id=stable_id("admin:sales_f"),
- code="SALES-F",
- name="赵敏",
- status="ACTIVE",
- created_at=created_at,
- ),
- )
- promotion_codes = (
- PromotionCode(
- id=stable_id("promotion:ZBT-SALES-A"),
- code="ZBT-SALES-A",
- salesperson_id=salespersons[0].id,
- channel="GROUND_PROMOTION",
- status="ACTIVE",
- created_at=created_at,
- ),
- PromotionCode(
- id=stable_id("promotion:ZBT-SALES-B"),
- code="ZBT-SALES-B",
- salesperson_id=salespersons[1].id,
- channel="COMMUNITY",
- status="ACTIVE",
- created_at=created_at,
- ),
- PromotionCode(
- id=stable_id("promotion:ZBT-SALES-C"),
- code="ZBT-SALES-C",
- salesperson_id=salespersons[2].id,
- channel="ENTERPRISE",
- status="ACTIVE",
- created_at=created_at,
- ),
- PromotionCode(
- id=stable_id("promotion:ZBT-SALES-D"),
- code="ZBT-SALES-D",
- salesperson_id=salespersons[3].id,
- channel="PARTNER",
- status="ACTIVE",
- created_at=created_at,
- ),
- PromotionCode(
- id=stable_id("promotion:ZBT-SALES-E"),
- code="ZBT-SALES-E",
- salesperson_id=salespersons[4].id,
- channel="ONLINE_REFERRAL",
- status="ACTIVE",
- created_at=created_at,
- ),
- PromotionCode(
- id=stable_id("promotion:ZBT-SALES-F"),
- code="ZBT-SALES-F",
- salesperson_id=salespersons[5].id,
- channel="STORE",
- status="ACTIVE",
- created_at=created_at,
- ),
- )
- return SeedManifest(
- admin_users=admin_users,
- products=products,
- versions=versions,
- salespersons=salespersons,
- promotion_codes=promotion_codes,
- )
- def run_seed(settings: Settings | None = None) -> SeedManifest:
- resolved_settings = settings or Settings()
- password_hash = PasswordService().hash(INITIAL_ADMIN_PASSWORD)
- manifest = build_seed_manifest(password_hash)
- engine = create_mysql_engine(resolved_settings, DatabaseName.CORE)
- factory = create_session_factory(engine)
- identities = SqlAlchemyIdentityRepository(factory)
- catalog = SqlAlchemyCatalogRepository(factory)
- attribution = SqlAlchemyAttributionRepository(factory)
- for admin_user in manifest.admin_users:
- identities.save_admin_user(admin_user)
- for product in manifest.products:
- catalog.save_product(product)
- for version in manifest.versions:
- catalog.save_version(version)
- audit_anchor = datetime(2026, 7, 1, 9, 0, tzinfo=UTC)
- for index, product in enumerate(manifest.products):
- catalog.save_change_log(
- ProductChangeLog(
- id=stable_id(f"product-log:created:{product.id}"),
- product_id=product.id,
- version_id=None,
- action="PRODUCT_CREATED",
- actor_id="SYSTEM",
- actor_name="系统初始化",
- detail={"product_code": product.product_code},
- created_at=audit_anchor - timedelta(days=120 - index),
- )
- )
- for index, version in enumerate(manifest.versions):
- catalog.save_change_log(
- ProductChangeLog(
- id=stable_id(f"product-log:version:{version.id}"),
- product_id=version.product_id,
- version_id=version.id,
- action=(
- "VERSION_DRAFT_CREATED"
- if version.status == "DRAFT"
- else "VERSION_IMPORTED"
- ),
- actor_id="SYSTEM",
- actor_name="系统初始化",
- detail={"version_no": version.version_no, "status": version.status},
- created_at=audit_anchor - timedelta(days=40 - index * 3),
- )
- )
- for salesperson in manifest.salespersons:
- attribution.save_salesperson(salesperson)
- for promotion_code in manifest.promotion_codes:
- attribution.save_promotion_code(promotion_code)
- engine.dispose()
- return manifest
- def _product(code: str, name: str, category: str) -> Product:
- summaries = {
- "MED-BASIC": "覆盖常见住院医疗费用,为年轻家庭提供简洁实用的基础保障。",
- "MED-UPGRADE": "提高医疗费用保障额度,并提供标准与升级两档方案灵活选择。",
- "MED-SENIOR": "面向中老年人群的住院医疗保障,覆盖50至75周岁的投保需求。",
- "ACC-FAMILY": "覆盖日常意外风险,可按个人或家庭方案为家人统一配置保障。",
- }
- return Product(
- id=stable_id(f"product:{code}"),
- product_code=code,
- name=name,
- category=category,
- summary=summaries[code],
- status="ACTIVE",
- )
- def _version(
- product_ids: dict[str, str],
- product_code: str,
- version_no: str,
- status: str,
- start_year: int,
- end_year: int | None,
- plan_codes: tuple[str, ...],
- ) -> ProductVersion:
- version_key = f"{product_code}:{version_no}"
- return ProductVersion(
- id=stable_id(f"version:{version_key}"),
- product_id=product_ids[product_code],
- version_no=version_no,
- status=status,
- effective_from=datetime(start_year, 1, 1, tzinfo=UTC),
- effective_to=(
- datetime(end_year, 12, 31, 23, 59, 59, tzinfo=UTC) if end_year is not None else None
- ),
- terms_summary=(
- "本产品为一年期保障,等待期、责任免除及赔付比例以电子保险条款为准;"
- "投保前请如实完成健康告知并确认被保人信息。"
- ),
- plans=tuple(
- _plan(version_key, code)
- for code in plan_codes
- ),
- )
- def _plan_name(code: str) -> str:
- return {
- "BASIC": "基础计划",
- "STANDARD": "标准计划",
- "ENHANCED": "升级计划",
- "SENIOR_STANDARD": "银龄标准计划",
- "INDIVIDUAL": "个人计划",
- "FAMILY": "家庭计划",
- }[code]
- def _plan(version_key: str, code: str) -> Plan:
- config = _plan_config(code)
- return Plan(
- id=stable_id(f"plan:{version_key}:{code}"),
- code=code,
- name=_plan_name(code),
- summary=config.summary,
- premium_cents=config.premium_cents,
- coverage_amount_cents=config.coverage_amount_cents,
- min_age=config.min_age,
- max_age=config.max_age,
- )
- def _plan_config(code: str) -> PlanConfig:
- return {
- "BASIC": PlanConfig(
- "覆盖住院医疗及约定门诊费用,适合首次配置医疗保障的人群。",
- 23900,
- 200_000_000,
- 0,
- 65,
- ),
- "STANDARD": PlanConfig(
- "提升医疗保障额度,兼顾常见疾病住院和重大疾病医疗支出。",
- 39900,
- 300_000_000,
- 0,
- 65,
- ),
- "ENHANCED": PlanConfig(
- "提供更高医疗额度和扩展责任,适合重视全面保障的家庭。",
- 59900,
- 600_000_000,
- 0,
- 65,
- ),
- "SENIOR_STANDARD": PlanConfig(
- "面向中老年人群的住院医疗计划,覆盖高发疾病住院费用。",
- 19900,
- 100_000_000,
- 50,
- 75,
- ),
- "INDIVIDUAL": PlanConfig(
- "覆盖个人日常意外身故、伤残及意外医疗责任。",
- 9900,
- 50_000_000,
- 0,
- 70,
- ),
- "FAMILY": PlanConfig(
- "一份计划覆盖家庭成员的常见意外风险,适合家庭统一配置。",
- 15900,
- 100_000_000,
- 0,
- 70,
- ),
- }[code]
- def _admin(
- username: str,
- display_name: str,
- role: str,
- data_scope: str,
- permissions: tuple[str, ...],
- password_hash: str,
- ) -> AdminUser:
- return AdminUser(
- id=stable_id(f"admin:{username}"),
- username=username,
- password_hash=password_hash,
- display_name=display_name,
- status="ACTIVE",
- roles=(role,),
- permissions=permissions,
- data_scope=data_scope,
- )
- if __name__ == "__main__":
- seeded = run_seed()
- print(
- f"已写入 {len(seeded.admin_users)} 个后台账号、"
- f"{len(seeded.products)} 个产品、{len(seeded.versions)} 个产品版本、"
- f"{len(seeded.promotion_codes)} 个推广码。"
- )
|