seed.py 4.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. """幂等种子数据只补缺失项,重启不清空业务或恢复管理员已经修改的配置。"""
  2. from sqlalchemy import select
  3. from app.domain.models import AgentConfig, AuditRule, ApprovalChain, Department, Role, User
  4. from app.infrastructure.security import hash_password, validate_password
  5. from app.shared.config import settings
  6. RULE_DEFAULTS = {
  7. "invoice_date_valid": ("发票日期", "error", {"max_age_days": 365}),
  8. "invoice_amount_valid": ("金额与合计", "error", {"large_amount": "100000.00"}),
  9. "invoice_header_valid": ("买卖方抬头", "error", {"company_name": ""}),
  10. "invoice_no_duplicate": ("同内容重复", "critical", {}),
  11. "tax_calculation_valid": ("税额计算", "warning", {"tolerance": "0.01"}),
  12. "tax_number_valid": ("税号长度", "warning", {"lengths": [15, 18, 20]}),
  13. "policy_compliance": ("政策合规", "error", {"required": True}),
  14. }
  15. AGENT_DEFAULTS = {
  16. "extract": ("发票提取 Agent", {"max_attempts": 3, "timeout": 20, "concurrency": 3}),
  17. "rule_check": ("规则校验 Agent", {}),
  18. "risk_detect": ("双引擎风控 Agent", {"timeout": 20, "window_days": 30, "amount_high": "50000.00", "amount_medium": "20000.00", "frequency_limit": 10, "dept_ratio_limit": 3}),
  19. "make_decision": ("确定性路由 Agent", {}),
  20. "feedback": ("人工反馈 Agent", {}),
  21. }
  22. async def latest_configs(session, model, key):
  23. """历史版本保留,仅取每个标识的最新配置作为下一轮输入。"""
  24. rows = (await session.scalars(select(model).order_by(model.version))).all()
  25. result = {}
  26. for row in rows:
  27. result[getattr(row, key)] = row
  28. return result
  29. async def seed(session):
  30. """初始化角色和规则是必需步骤,演示账号单独受 SEED_DEMO 控制。"""
  31. for code, name in [("employee", "员工"), ("supervisor", "主管"), ("auditor", "审核员"), ("manager", "经理"), ("admin", "管理员")]:
  32. if not await session.scalar(select(Role).where(Role.code == code)):
  33. session.add(Role(code=code, display_name=name))
  34. for code, (name, severity, params) in RULE_DEFAULTS.items():
  35. if not await session.scalar(select(AuditRule).where(AuditRule.code == code)):
  36. session.add(AuditRule(code=code, name=name, severity=severity, params_json=params, scope_json={}))
  37. for node, (name, config) in AGENT_DEFAULTS.items():
  38. if not await session.scalar(select(AgentConfig).where(AgentConfig.node_name == node)):
  39. session.add(AgentConfig(node_name=node, name=name, config_json=config))
  40. await session.flush()
  41. if not settings.seed_demo:
  42. return
  43. validate_password(settings.demo_password)
  44. department = await session.scalar(select(Department).where(Department.name == "演示研发部"))
  45. if not department:
  46. department = Department(name="演示研发部")
  47. session.add(department)
  48. await session.flush()
  49. users = {}
  50. for username, name, role_code in [("alice", "演示员工", "employee"), ("bob", "演示主管", "supervisor"), ("carol", "演示财务", "auditor"), ("dave", "演示经理", "manager"), ("erin", "演示管理员", "admin")]:
  51. user = await session.scalar(select(User).where(User.username == username))
  52. if not user:
  53. role = await session.scalar(select(Role).where(Role.code == role_code))
  54. user = User(username=username, real_name=name, password_hash=hash_password(settings.demo_password),
  55. department_id=department.id, role_id=role.id)
  56. session.add(user)
  57. await session.flush()
  58. users[username] = user
  59. # 默认链提供人工闭环;用户自己创建的链不会被启动脚本重置。
  60. for route, node_user, role in [("to_supervisor", "bob", "supervisor"), ("to_finance", "carol", "auditor"), ("to_manual", "carol", "auditor")]:
  61. if not await session.scalar(select(ApprovalChain).where(ApprovalChain.route == route)):
  62. session.add(ApprovalChain(name="演示审批链 · " + route, route=route,
  63. nodes_json=[{"approver_id": users[node_user].id, "role": role}]))