# -*- coding: utf-8 -*- """幂等种子数据:角色 / 权限 / 角色权限矩阵 / 区域 / 组织 / 演示用户。 在 ``app.db.init_db()`` 之后调用;仅当 ``roles`` 表为空时才写入, 避免覆盖已有数据。 """ from __future__ import annotations import json from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from .models import ( AuditLog, ContentItem, FinanceRecord, InvestmentIntent, InvestorPreference, MarketCategory, Message, Notification, OpcProfile, Organization, OrganizationMember, ParkMember, ParkTenant, Permission, PortalDashboard, PortalPage, Region, Roadshow, RoadshowRegistration, Role, RolePermission, ServiceProvider, ServiceReferral, SessionToken, SubsidyApplication, SystemConfig, Task, TaskCategory, TrainingEnrollment, User, ) from .repositories import new_id, utcnow_iso from .security import hash_password # --------------------------------------------------------------------------- # 角色 # --------------------------------------------------------------------------- # 角色定义:仅保留 OPC基础(opc_member)、园区(carrier)、运营(operator) 三端 # 已移除:enterprise / provider / government / investor / developer # --------------------------------------------------------------------------- ROLES = [ # 业务角色 ("opc_member", "OPC超级个体", "business"), ("carrier", "创业载体(园区)", "business"), ("operator", "平台运营方", "business"), # 运营方内部子角色 ("operator|op_super_admin", "超级管理员", "operator-internal"), ("operator|op_admin", "运营管理员", "operator-internal"), ("operator|op_customer_service", "客服管理员", "operator-internal"), ("operator|op_finance", "财务管理员", "operator-internal"), ("operator|op_analyst", "数据分析师", "operator-internal"), ("operator|op_techops", "技术运维", "operator-internal"), # 园区 / OPC 端口内子角色 ("carrier|admin", "园区管理员", "business"), ("carrier|operator", "园区操作者", "business"), ("opc_member|certified", "认证OPC", "business"), ("opc_member|independent", "独立OPC", "business"), ] # --------------------------------------------------------------------------- # 权限 # --------------------------------------------------------------------------- PERMISSIONS = [ # 各门户菜单(仅保留 opc/carrier/operator) ("menu:opc_dashboard", "OPC 门户", "menu", "opc"), ("menu:carrier_dashboard", "载体门户", "menu", "carrier"), ("menu:operator_dashboard", "运营门户", "menu", "operator"), # 管理菜单 ("menu:admin_user_mgmt", "用户管理", "menu", "admin"), ("menu:admin_role_mgmt", "角色权限管理", "menu", "admin"), ("menu:audit", "审计日志", "menu", "audit"), ("menu:settlements", "结算台账", "menu", "operator"), # 操作 ("action:user.assign_role", "分配角色", "action", "admin"), ("action:user.disable", "禁用/启用用户", "action", "admin"), ("action:role.grant_perm", "配置角色权限", "action", "admin"), ("action:org.manage", "管理组织", "action", "org"), ("action:data.export", "数据导出", "action", "data"), ("action:audit.view", "查看审计", "action", "audit"), ("action:task.manage", "任务管理(建改)", "action", "operator"), ("action:provider.manage", "服务商管理(建改)", "action", "operator"), ("action:content.manage", "内容管理(建发)", "action", "operator"), ("action:config.manage", "系统配置(修改)", "action", "operator"), ("action:settlement.manage", "结算管理", "action", "operator"), # 平台运营(operator)功能页 ("menu:admin_courses", "课程管理", "menu", "admin"), ("menu:admin_activities", "活动管理", "menu", "admin"), ("menu:admin_bookings", "报名管理", "menu", "admin"), ("menu:admin_tests", "测评管理", "menu", "admin"), ("menu:admin_surveys", "调研结果", "menu", "admin"), ("menu:admin_policies", "政策结果", "menu", "admin"), ("menu:admin_plans", "流程启动", "menu", "admin"), ("menu:admin_compute", "算力管理", "menu", "admin"), ("menu:admin_content", "资讯管理", "menu", "admin"), ("menu:admin_content_review", "资讯审核", "menu", "admin"), ("menu:admin_tasks", "任务管理", "menu", "admin"), ("menu:admin_providers", "服务商管理", "menu", "admin"), ("menu:admin_market", "市场管理", "menu", "admin"), ("menu:admin_config", "系统配置", "menu", "admin"), ("menu:admin_agents", "智能体管理", "menu", "admin"), ("menu:admin_park", "平台园区", "menu", "admin"), ("menu:admin_park_screens", "屏幕绑定", "menu", "admin"), ("menu:opc_cert", "OPC认证", "menu", "admin"), ("menu:admission", "入驻审核", "menu", "admin"), ("menu:park_transfer", "转园审核", "menu", "admin"), # 园区(carrier)工作台 ("menu:park_screens", "屏幕管理", "menu", "carrier"), ("menu:park_screen", "大屏控制台", "menu", "carrier"), ("menu:park_companies", "企业管理", "menu", "carrier"), ("menu:park_users", "用户管理", "menu", "carrier"), ("menu:park_content", "资讯管理", "menu", "carrier"), ("menu:park_kb", "园区知识库", "menu", "carrier"), ("menu:park_agent", "园区智能体", "menu", "carrier"), ("menu:park_data", "大屏数据", "menu", "carrier"), ("menu:park_vision", "视频识别", "menu", "carrier"), ("menu:park_admissions", "入园申请", "menu", "carrier"), ("menu:park_transfers", "转园申请", "menu", "carrier"), # 企业管理能力(OPC 叠加,非身份):company_members.is_admin 即可调用 /admin/ent/*, # 不绑权限码角色(企业管理员不是身份)。 ] # role_id -> [permission_id] ROLE_PERMISSIONS: dict[str, list[str]] = { "opc_member": ["menu:opc_dashboard"], "carrier": [ "menu:carrier_dashboard", "action:org.manage", "menu:park_screens", "menu:park_screen", "menu:park_companies", "menu:park_users", "menu:park_content", "menu:park_kb", "menu:park_agent", "menu:park_data", "menu:park_vision", "menu:park_admissions", "menu:park_transfers", ], "operator": [ "menu:operator_dashboard", "menu:admin_user_mgmt", "menu:admin_role_mgmt", "menu:audit", "menu:settlements", "menu:admin_courses", "menu:admin_activities", "menu:admin_bookings", "menu:admin_tests", "menu:admin_surveys", "menu:admin_policies", "menu:admin_plans", "menu:admin_compute", "menu:admin_content", "menu:admin_content_review", "menu:admin_tasks", "menu:admin_providers", "menu:admin_config", "menu:admin_agents", "menu:admin_market", "menu:admin_park", "menu:admin_park_screens", "menu:opc_cert", "menu:admission", "menu:park_transfer", "action:user.assign_role", "action:user.disable", "action:role.grant_perm", "action:org.manage", "action:data.export", "action:audit.view", "action:task.manage", "action:provider.manage", "action:content.manage", "action:config.manage", "action:settlement.manage", ], "operator|op_super_admin": [ "menu:admin_user_mgmt", "menu:admin_role_mgmt", "menu:audit", "menu:settlements", "action:user.assign_role", "action:user.disable", "action:role.grant_perm", "action:org.manage", "action:data.export", "action:audit.view", "action:task.manage", "action:provider.manage", "action:content.manage", "action:config.manage", "action:settlement.manage", ], "operator|op_admin": [ "menu:admin_user_mgmt", "menu:admin_role_mgmt", "menu:audit", "menu:settlements", "action:user.assign_role", "action:user.disable", "action:org.manage", "action:data.export", "action:audit.view", "action:task.manage", "action:provider.manage", "action:content.manage", "action:config.manage", "action:settlement.manage", ], "operator|op_customer_service": [ "action:data.export", "action:task.manage", "action:provider.manage", "action:content.manage", ], "operator|op_finance": [ "menu:settlements", "action:data.export", "action:settlement.manage", ], "operator|op_analyst": ["menu:audit", "action:data.export", "action:audit.view"], "operator|op_techops": [ "menu:admin_user_mgmt", "action:org.manage", "action:data.export", "action:config.manage", ], "carrier|admin": ["menu:carrier_dashboard", "action:org.manage"], "carrier|operator": ["menu:carrier_dashboard"], "opc_member|certified": ["menu:opc_dashboard"], "opc_member|independent": ["menu:opc_dashboard"], } # --------------------------------------------------------------------------- # 区域(省/市/区县 示例树) # --------------------------------------------------------------------------- REGIONS = [ ("r_prov_yn", "云南省", "province", None), ("r_city_km", "昆明市", "city", "r_prov_yn"), ("r_city_qj", "曲靖市", "city", "r_prov_yn"), ("r_dist_pl", "盘龙区", "district", "r_city_km"), ("r_dist_wh", "五华区", "district", "r_city_km"), ("r_dist_ql", "麒麟区", "district", "r_city_qj"), ] # --------------------------------------------------------------------------- # 组织(甲方企业 / 创业载体 / 服务商) # --------------------------------------------------------------------------- ORGANIZATIONS = [ ("o_ent_001", "云南某某甲方企业", "enterprise", "r_dist_pl", None), ("o_car_001", "滇池创业园", "carrier", "r_dist_wh", None), ("o_pro_001", "云超财税服务所", "provider", "r_city_km", None), ("o_ent_prov", "省级示范企业", "enterprise", "r_prov_yn", None), ] # --------------------------------------------------------------------------- # 演示用户(role/sub_role/org/region 各异,用于 RBAC 演示与测试) # --------------------------------------------------------------------------- DEMO_USERS = [ # id, username, nickname, password, role, sub_role, org_id, region_id ("u_demo_01", "pine", "云超服小助手", "123456", "operator", "op_super_admin", None, "r_prov_yn"), ("u_car_01", "car01", "园区运营账号", "123456", "carrier", None, "o_car_001", "r_dist_wh"), ("u_op_an", "op_analyst", "运营数据分析", "123456", "operator", "op_analyst", None, "r_prov_yn"), ("u_op_admin", "op_admin", "运营管理员", "123456", "operator", "op_admin", None, "r_prov_yn"), ("u_op_cs", "op_cs", "运营客服", "123456", "operator", "op_customer_service", None, "r_prov_yn"), ("u_op_fin", "op_fin", "运营财务", "123456", "operator", "op_finance", None, "r_prov_yn"), ("u_op_tech", "op_tech", "运营运维", "123456", "operator", "op_techops", None, "r_prov_yn"), ("u_opc_01", "opc01", "OPC个人创业者", "123456", "opc_member", None, None, "r_dist_wh"), ("u_opc_02", "opc02", "OPC个体工商户", "123456", "opc_member", None, None, "r_dist_pl"), ] # 系统配置默认值(平台费率 + 短信生态)。幂等补种:仅插入缺失键,不覆盖已有值。 _DEFAULT_SYSTEM_CONFIGS: list[tuple[str, str, str]] = [ ("platform.fee.task", "0.05", "任务佣金费率(5%)"), ("platform.fee.provider", "0.15", "服务商分成比例(15%)"), # ── 短信生态配置(admin「系统配置 → 短信生态」在线维护) ────────────── ("sms.provider", "stub", "短信 provider(stub|aliyun)"), ("sms.aliyun", '{"access_key_id":"","access_key_secret":"","sign_name":"","region_id":"cn-hangzhou","endpoint":"dysmsapi.aliyuncs.com"}', "阿里云短信总配置(密钥/签名/区域)"), ("sms.template.login", '{"template_id":"","variables":["code","minute"]}', "登录验证码模板(变量:code 验证码, minute 有效分钟)"), ("sms.template.register", '{"template_id":"","variables":["code","minute"]}', "注册验证码模板(变量:code 验证码, minute 有效分钟)"), ("sms.template.notification", '{"template_id":"","variables":[]}', "通知提醒模板(变量按业务定义,如审核结果等)"), ("sms.template.order_status", '{"template_id":"","variables":["order_no","status"]}', "订单状态变动模板(变量:order_no 订单号, status 状态)"), ("sms.template.appointment", '{"template_id":"","variables":["service_name","schedule_at","contact"]}', "服务预约模板(变量:service_name 服务名, schedule_at 预约时间, contact 联系方式)"), ] async def _seed_system_configs(session: AsyncSession, now: str) -> None: """幂等补齐系统配置:已存在的键不覆盖,仅插入缺失项。""" for key, value, description in _DEFAULT_SYSTEM_CONFIGS: exists = await session.get(SystemConfig, key) if exists is None: session.add(SystemConfig(key=key, value=value, description=description, updated_at=now)) async def seed_data(session: AsyncSession) -> None: """幂等种子:仅当 roles 表为空时写入全部基础数据;身份与 OPC 业务始终补种。""" now = utcnow_iso() existing = await session.scalar(select(Role.id).limit(1)) if existing is not None: # 已初始化的库:仍补种 OPC 业务演示数据、账号身份与端口工作台(各自幂等)。 await _sync_permissions(session, now) await _ensure_extra_demo_users(session, now) await _seed_opc_business(session, now) await _seed_task_categories(session, now) await _seed_port_dashboards(session, now) await _seed_market(session, now) await _seed_market_categories(session, now) await _seed_port_pages(session, now) await _seed_org_members(session, now) await _seed_carrier_park_binding(session, now) await _seed_system_configs(session, now) await session.commit() return session.add_all( [Role(id=r[0], name=r[1], scope_type=r[2]) for r in ROLES] ) session.add_all( [ Permission(id=p[0], name=p[1], category=p[2], module=p[3]) for p in PERMISSIONS ] ) for role_id, perms in ROLE_PERMISSIONS.items(): session.add_all( [ RolePermission(role_id=role_id, permission_id=perm) for perm in perms ] ) session.add_all( [ Region(id=r[0], name=r[1], level=r[2], parent_id=r[3], created_at=now) for r in REGIONS ] ) session.add_all( [ Organization( id=o[0], name=o[1], type=o[2], region_id=o[3], parent_id=o[4], created_at=now, ) for o in ORGANIZATIONS ] ) for (uid, username, nickname, password, role, sub_role, org_id, region_id) in DEMO_USERS: digest, salt = hash_password(password) session.add( User( id=uid, username=username, nickname=nickname, password_hash=digest, password_salt=salt, role=role, sub_role=sub_role, org_id=org_id, region_id=region_id, status="active", token_version=0, created_at=now, updated_at=now, source="seed", auth_type="admin", ) ) await _seed_task_categories(session, now) await _seed_operator_business(session, now) await _seed_opc_business(session, now) await _seed_port_dashboards(session, now) await _seed_market(session, now) await _seed_market_categories(session, now) await _seed_port_pages(session, now) await _seed_org_members(session, now) await _seed_carrier_park_binding(session, now) await session.commit() # --------------------------------------------------------------------------- # 运营端业务演示数据(任务/服务商/内容/配置) # --------------------------------------------------------------------------- # 任务分类字典(顶层 35 类,取自 design/云超服平台/任务类别.json,幂等:表空才插) _TASK_CATEGORY_NAMES = [ "平面设计", "移动端开发", "网站建设服务", "软件开发", "硬件开发", "知识产权", "市场推广", "音视频", "动漫游戏设计", "UI设计", "工业设计与制造", "策划", "写作和内容", "工程设计", "空间设计服务", "人力资源", "科技服务", "技术服务", "AI人工智能", "VR/AR/MR", "翻译服务", "SaaS服务", "区块链", "企业后勤服务", "企业办公服务", "文印广告", "法律服务", "工商财税", "学习培训", "源码交易", "生产制造", "直播服务", "服装配饰设计", "摄影摄像服务", "数据服务", ] async def _seed_task_categories(session: AsyncSession, now: str) -> None: if await session.scalar(select(TaskCategory.id).limit(1)) is not None: return session.add_all([ TaskCategory(id=f"tc_{i:02d}", name=name, sort=i + 1, status="active") for i, name in enumerate(_TASK_CATEGORY_NAMES) ]) await session.commit() async def _seed_operator_business(session: AsyncSession, now: str) -> None: if await session.scalar(select(Task.id).limit(1)) is not None: return session.add_all([ Task(id="task_001", task_code="TK-001-UI", title="电商小程序首页 UI 设计", category="设计创意", category_id="tc_10", sub_category="UI设计", description="为电商小程序设计首页 UI,含 5 个页面", tags="UI,设计", mode="bid", budget_min=2000, budget_max=5000, deadline="2026-09-01", delivery_days=7, headcount=3, display_priority=10, published_at=now, status="published", publisher_org_id="o_ent_001", publisher_name="云南某某甲方企业", created_at=now, updated_at=now), Task(id="task_002", task_code="TK-002-CT", title="品牌文案写作(月度)", category="写作和内容", category_id="tc_13", sub_category="内容写作", description="每月 8 篇品牌公众号文案", tags="文案,内容", mode="assign", budget_min=3000, budget_max=6000, deadline="2026-12-31", delivery_days=14, headcount=1, display_priority=5, status="review", publisher_org_id="o_ent_001", publisher_name="云南某某甲方企业", created_at=now, updated_at=now), Task(id="task_003", task_code="TK-003-DEV", title="小程序前后端开发", category="移动端开发", category_id="tc_02", sub_category="小程序开发", description="点餐小程序全栈开发,3 期交付", tags="开发", mode="bid", budget_min=30000, budget_max=60000, deadline="2026-10-15", delivery_days=30, headcount=3, display_priority=20, status="pending", publisher_org_id="o_ent_001", publisher_name="云南某某甲方企业", created_at=now, updated_at=now), Task(id="task_004", task_code="TK-004-GRAB", title="短视频剪辑(扫码即领·独占)", category="音视频", category_id="tc_08", sub_category="视频剪辑", description="为园区企业制作 3 条产品短视频,扫码即可接单,独占一人,2 周内交付。", tags="短视频,剪辑", mode="grab", budget_min=1500, budget_max=3000, deadline="2026-11-30", delivery_days=14, exclusive=True, headcount=1, display_priority=30, published_at=now, status="published", publisher_org_id="o_ent_001", publisher_name="园区运营方", created_at=now, updated_at=now), Task(id="task_005", task_code="TK-005-REC", title="农产品品牌 Logo 设计(推荐)", category="平面设计", category_id="tc_01", sub_category="品牌设计", description="为农产品品牌设计 Logo,推荐合适合伙人接单。", tags="Logo,品牌设计", mode="recommend", budget_min=800, budget_max=2000, deadline="2026-12-20", delivery_days=10, headcount=1, display_priority=25, published_at=now, status="published", publisher_org_id="o_ent_001", publisher_name="园区运营方", created_at=now, updated_at=now), ]) session.add_all([ ServiceProvider(id="prov_001", name="云超财税服务所", category="财税服务", org_id="o_pro_001", level="gold", rating=4.8, order_count=320, status="active", contact="service@yuncao.example", created_at=now, updated_at=now), ServiceProvider(id="prov_002", name="滇峰法律咨询", category="法律服务", org_id=None, level="premium", rating=4.6, order_count=150, status="active", contact="law@dianfeng.example", created_at=now, updated_at=now), ServiceProvider(id="prov_003", name="云岭工商代办", category="工商服务", org_id=None, level="certified", rating=4.3, order_count=88, status="pending", contact="biz@yunling.example", created_at=now, updated_at=now), ]) session.add_all([ ContentItem(id="cont_001", type="policy", title="云南省一次性创业补贴政策", summary="对首次创业且正常经营 1 年以上的 OPC 发放一次性创业补贴", body="适用对象、申报条件、补贴标准、申报流程……", publisher_id="u_demo_01", status="published", created_at=now, updated_at=now), ContentItem(id="cont_002", type="news", title="平台上线公告", summary="云超服平台正式上线运营", body="欢迎入驻……", publisher_id="u_demo_01", status="published", created_at=now, updated_at=now), ]) await _seed_system_configs(session, now) # --------------------------------------------------------------------------- # OPC 超级个体业务演示数据(工作台聚合:档案/任务/财务/消息) # --------------------------------------------------------------------------- async def _seed_opc_business(session: AsyncSession, now: str) -> None: """OPC 个人工作台演示数据(按用户幂等)。""" if await session.scalar(select(OpcProfile.user_id).limit(1)) is not None: return # 档案(信用评分) session.add_all([ OpcProfile(user_id="u_opc_01", credit_score=88, created_at=now, updated_at=now), OpcProfile(user_id="u_opc_02", credit_score=82, created_at=now, updated_at=now), ]) # 财务流水(月度收入 + 少量支出) session.add_all([ FinanceRecord(id="fin_001", user_id="u_opc_01", category="income", amount=3200, date="2026-02-10", note="UI 设计项目款", created_at=now), FinanceRecord(id="fin_002", user_id="u_opc_01", category="income", amount=4500, date="2026-03-12", note="Logo 设计项目款", created_at=now), FinanceRecord(id="fin_003", user_id="u_opc_01", category="income", amount=2800, date="2026-04-08", note="文案代写项目款", created_at=now), FinanceRecord(id="fin_004", user_id="u_opc_01", category="income", amount=5100, date="2026-05-20", note="小程序 UI 项目款", created_at=now), FinanceRecord(id="fin_005", user_id="u_opc_01", category="income", amount=3900, date="2026-06-15", note="电商详情页设计", created_at=now), FinanceRecord(id="fin_006", user_id="u_opc_01", category="income", amount=4600, date="2026-07-18", note="H5 营销页设计", created_at=now), FinanceRecord(id="fin_007", user_id="u_opc_01", category="expense", amount=600, date="2026-07-02", note="素材版权购买", created_at=now), ]) # 站内消息 session.add_all([ Message(id="msg_001", user_id="u_opc_01", msg_type="policy", title="政策匹配提醒", content="2 项新政策与您高度匹配,请及时查看申报", read=False, created_at=now), Message(id="msg_002", user_id="u_opc_01", msg_type="task", title="任务状态更新", content="您参与的「电商小程序首页 UI 设计」进度已更新", read=False, created_at=now), Message(id="msg_003", user_id="u_opc_01", msg_type="service", title="服务商回复", content="云超财税服务所已回复您的询价", read=True, created_at=now), Message(id="msg_004", user_id="u_opc_01", msg_type="agent", title="智能体建议", content="您的月度收入较上月增长 18%", read=True, created_at=now), ]) # --------------------------------------------------------------------------- # 端口工作台聚合数据(企业/载体/服务商/政务) # --------------------------------------------------------------------------- _PORT_DASHBOARDS: dict[str, dict] = { "enterprise": { "stats": [ {"key": "postedTasks", "value": 56}, {"key": "activeTasks", "value": 12}, {"key": "completedTasks", "value": 38}, {"key": "totalSpent", "value": "¥32.0万"}, {"key": "matchedTalents", "value": 186}, {"key": "cooperationRate", "value": "92%"}, ], "list": [ {"title": "企业官网 UI 设计", "meta": "进行中 · ¥5000-8000 · 12 家投标", "status": "进行中"}, {"title": "小程序前端开发", "meta": "进行中 · ¥8000-15000 · 8 家投标", "status": "进行中"}, {"title": "农产品品牌 Logo 设计", "meta": "进行中 · ¥3000-5000 · 15 家投标", "status": "进行中"}, {"title": "商业计划书撰写", "meta": "进行中 · ¥2000-4000 · 6 家投标", "status": "进行中"}, ], }, "carrier": { "stats": [ {"key": "residentOPCs", "value": 186}, {"key": "totalSpace", "value": "5000㎡"}, {"key": "usedSpace", "value": "4200㎡"}, {"key": "occupancyRate", "value": "84%"}, {"key": "serviceOrders", "value": 45}, {"key": "thisMonthNewOPC", "value": 12}, ], "list": [ {"title": "云创设计工作室", "meta": "入驻 · 电子信息方向 · 工位 12", "status": "已入驻"}, {"title": "滇峰数据科技", "meta": "入驻 · 软件服务方向 · 工位 8", "status": "已入驻"}, {"title": "云南新媒体中心", "meta": "待审批 · 内容创作方向 · 工位 6", "status": "待审批"}, {"title": "绿野农业电商", "meta": "入驻 · 电商方向 · 工位 10", "status": "已入驻"}, ], }, "provider": { "stats": [ {"key": "activeServices", "value": 18}, {"key": "ongoingOrders", "value": 24}, {"key": "completedOrders", "value": 156}, {"key": "totalRevenue", "value": "¥52.6万"}, {"key": "thisMonthIncome", "value": "¥8.56万"}, {"key": "avgRating", "value": 4.7}, ], "list": [ {"title": "代理记账服务(月度)", "meta": "进行中 · 云南某某甲方企业 · ¥199/月", "status": "进行中"}, {"title": "公司注册代办", "meta": "待处理 · 个人创业者 · ¥599/次", "status": "待处理"}, {"title": "季度财务报表编制", "meta": "已完成 · 云创财税 · ¥2000-3000", "status": "已完成"}, {"title": "税务筹划咨询", "meta": "进行中 · 鼎信税务所 · ¥1999/次", "status": "进行中"}, ], }, "government": { "stats": [ {"key": "totalOPC", "value": 45600}, {"key": "registeredCarriers", "value": 168}, {"key": "policiesPublished", "value": 245}, {"key": "policiesMatched", "value": 18200}, {"key": "tasksInRegion", "value": 12500}, {"key": "completionRate", "value": "78%"}, ], "list": [ {"region": "昆明市", "opcCount": 12500, "taskCount": 3200, "completionRate": "82%"}, {"region": "大理州", "opcCount": 6800, "taskCount": 1800, "completionRate": "76%"}, {"region": "曲靖市", "opcCount": 5200, "taskCount": 1500, "completionRate": "79%"}, {"region": "红河州", "opcCount": 4800, "taskCount": 1200, "completionRate": "74%"}, {"region": "玉溪市", "opcCount": 3500, "taskCount": 900, "completionRate": "81%"}, ], "chart": [ {"label": "1月", "value": 2400}, {"label": "2月", "value": 2800}, {"label": "3月", "value": 3100}, {"label": "4月", "value": 2900}, {"label": "5月", "value": 3450}, {"label": "6月", "value": 3820}, ], }, } async def _seed_port_dashboards(session: AsyncSession, now: str) -> None: """按端口写入工作台 JSON 载荷(幂等:任一端口已存在则跳过)。""" if await session.scalar(select(PortalDashboard.port).limit(1)) is not None: return session.add_all( [ PortalDashboard( port=port, payload=json.dumps(payload, ensure_ascii=False), updated_at=now, ) for port, payload in _PORT_DASHBOARDS.items() ] ) async def _seed_market(session: AsyncSession, now: str) -> None: """补充平台市场数据(任务广场/政策/服务商),幂等(以 task_mk_001 为标记)。""" if await session.scalar(select(Task.id).where(Task.id == "task_mk_001").limit(1)) is not None: return session.add_all([ Task(id="task_mk_001", title="企业官网 UI 设计", category="设计创意", sub_category="UI设计", description="为企业官网设计首页与 3 个内页 UI", mode="bid", budget_min=5000, budget_max=8000, deadline="2026-09-10", status="published", publisher_org_id="o_ent_001", publisher_name="云南某某甲方企业", created_at=now, updated_at=now), Task(id="task_mk_002", title="小程序前后端开发", category="技术开发", sub_category="小程序开发", description="点餐小程序全栈开发,3 期交付", mode="bid", budget_min=30000, budget_max=60000, deadline="2026-10-15", status="published", publisher_org_id="o_ent_001", publisher_name="云南某某甲方企业", created_at=now, updated_at=now), Task(id="task_mk_003", title="农产品品牌 Logo 设计", category="设计创意", sub_category="Logo设计", description="为农业合作社设计品牌 Logo", mode="grab", budget_min=3000, budget_max=5000, deadline="2026-08-25", status="published", publisher_org_id=None, publisher_name="大理农业合作社", created_at=now, updated_at=now), Task(id="task_mk_004", title="短视频剪辑与制作", category="音视频", sub_category="短视频", description="文旅宣传短视频剪辑", mode="grab", budget_min=1500, budget_max=3000, deadline="2026-08-20", status="published", publisher_org_id=None, publisher_name="文旅传媒", created_at=now, updated_at=now), Task(id="task_mk_005", title="商业计划书撰写", category="策划/写作", sub_category="文案写作", description="创业项目商业计划书", mode="designated", budget_min=2000, budget_max=4000, deadline="2026-09-01", status="published", publisher_org_id=None, publisher_name="创业团队", created_at=now, updated_at=now), ]) session.add_all([ ContentItem(id="cont_pol_001", type="policy", title="昆明市创业担保贷款政策", summary="对符合条件的创业主体提供担保贷款支持", body="适用对象、额度、利率与申报流程……", publisher_id="u_demo_01", status="published", created_at=now, updated_at=now), ContentItem(id="cont_pol_002", type="policy", title="小微企业税收优惠减免", summary="符合条件的小微企业可享受税收减免", body="适用范围、减免标准与办理方式……", publisher_id="u_demo_01", status="published", created_at=now, updated_at=now), ContentItem(id="cont_pol_003", type="policy", title="云南省一次性创业补贴", summary="首次创业且正常经营 1 年以上发放一次性补贴", body="申报条件、补贴标准与流程……", publisher_id="u_demo_01", status="published", created_at=now, updated_at=now), ]) # --------------------------------------------------------------------------- # 市场分类(skill/plugin/app 各类型独立,admin 端弹窗在线维护) # --------------------------------------------------------------------------- async def _seed_market_categories(session: AsyncSession, now: str) -> None: """幂等补种市场分类字典(空表才写;此后由 admin 端维护)。""" from ..market.categories import ensure_default_categories await ensure_default_categories(session, now) # --------------------------------------------------------------------------- # 端口子页面演示数据(企业人才/载体OPC与服务/服务商订单与服务/政务OPC·政策·载体·数据) # --------------------------------------------------------------------------- _PORT_PAGES: dict[tuple[str, str], dict] = { ("enterprise", "talents"): { "items": [ {"title": "张伟 · UI设计师", "meta": "5年经验 · Figma/PS · 认证OPC", "tag": "认证"}, {"title": "李娜 · 前端开发", "meta": "3年经验 · Vue/React · 认证OPC", "tag": "认证"}, {"title": "王强 · 文案策划", "meta": "4年经验 · 公众号/短视频文案", "tag": "独立"}, {"title": "赵敏 · 数据分析", "meta": "2年经验 · Python/SQL", "tag": "独立"}, {"title": "陈晨 · 电商运营", "meta": "3年经验 · 抖音/淘宝", "tag": "认证"}, {"title": "刘洋 · 空间设计", "meta": "6年经验 · 民宿/商业空间", "tag": "认证"}, ], }, ("carrier", "opc"): { "items": [ {"title": "云创设计工作室", "meta": "电子信息方向 · 工位12 · 2026-03 入驻", "tag": "已入驻"}, {"title": "滇峰数据科技", "meta": "软件服务方向 · 工位8 · 2026-04 入驻", "tag": "已入驻"}, {"title": "云南新媒体中心", "meta": "内容创作方向 · 工位6 · 待审批", "tag": "待审批"}, {"title": "绿野农业电商", "meta": "电商方向 · 工位10 · 2026-05 入驻", "tag": "已入驻"}, {"title": "云南文创工坊", "meta": "文化创意方向 · 工位4 · 申请中", "tag": "申请中"}, ], }, ("carrier", "services"): { "items": [ {"title": "园区创业培训", "meta": "创业辅导 · 月度课程 · 已完成", "tag": "已完成"}, {"title": "工商注册代办", "meta": "云超财税 · 本期 12 单", "tag": "合作中"}, {"title": "政策申报指导", "meta": "省市区三级补贴申报", "tag": "进行中"}, {"title": "法务顾问服务", "meta": "滇峰法律 · 入驻企业专享", "tag": "合作中"}, ], }, ("provider", "orders"): { "items": [ {"title": "代理记账服务(月度)", "meta": "云南某某甲方企业 · ¥199/月 · 2026-07 开始", "tag": "进行中"}, {"title": "公司注册代办", "meta": "个人创业者 · ¥599/次 · 待处理", "tag": "待处理"}, {"title": "季度财务报表编制", "meta": "云创财税 · ¥2000-3000 · 已完成", "tag": "已完成"}, {"title": "税务筹划咨询", "meta": "鼎信税务所 · ¥1999/次 · 进行中", "tag": "进行中"}, {"title": "年度汇算清缴", "meta": "云南某某甲方企业 · ¥1500 · 待处理", "tag": "待处理"}, ], }, ("provider", "services"): { "items": [ {"title": "代理记账服务", "meta": "财税服务 · ¥199/月起 · 在售", "tag": "在售"}, {"title": "税务筹划咨询", "meta": "财税服务 · ¥1999/次 · 在售", "tag": "在售"}, {"title": "企业年报填报", "meta": "工商服务 · ¥99/次 · 在售", "tag": "在售"}, {"title": "审计报告出具", "meta": "财税服务 · ¥3000/份 · 草稿", "tag": "草稿"}, ], }, ("government", "opc"): { "items": [ {"title": "张伟 · UI设计师", "meta": "盘龙区 · 认证OPC · 信用88", "tag": "认证"}, {"title": "李娜 · 前端开发", "meta": "五华区 · 认证OPC · 信用85", "tag": "认证"}, {"title": "王强 · 文案策划", "meta": "盘龙区 · 独立OPC · 信用80", "tag": "独立"}, {"title": "赵敏 · 数据分析", "meta": "麒麟区 · 独立OPC · 信用82", "tag": "独立"}, ], }, ("government", "policies"): { "items": [ {"title": "云南省一次性创业补贴政策", "meta": "省级 · 2026-06 发布 · 申报中", "tag": "已发布"}, {"title": "昆明市创业担保贷款政策", "meta": "市级 · 2026-05 发布 · 申报中", "tag": "已发布"}, {"title": "小微企业税收优惠减免", "meta": "省级 · 2026-04 发布 · 申报中", "tag": "已发布"}, {"title": "青年人才创业支持计划", "meta": "市级 · 起草中", "tag": "草稿"}, ], }, ("government", "carriers"): { "items": [ {"title": "滇池创业园", "meta": "五华区 · 工位500 · 入驻率84%", "tag": "正常"}, {"title": "云创空间孵化器", "meta": "盘龙区 · 工位300 · 入驻率76%", "tag": "正常"}, {"title": "曲靖高新园区", "meta": "麒麟区 · 工位400 · 入驻率68%", "tag": "正常"}, ], }, ("government", "data"): { "stats": [ {"key": "opcTotal", "value": 45600}, {"key": "taskTotal", "value": 12500}, {"key": "carrierTotal", "value": 168}, {"key": "policyTotal", "value": 245}, ], "chart": [ {"label": "1月", "value": 3200}, {"label": "2月", "value": 3600}, {"label": "3月", "value": 4100}, {"label": "4月", "value": 3900}, {"label": "5月", "value": 4500}, {"label": "6月", "value": 5200}, ], }, ("investor", "projects"): { "items": [ {"title": "云上数仓 SaaS 平台", "meta": "人工智能 · 天使轮 · 融资 300万 · 估值 2000万", "tag": "天使轮"}, {"title": "滇南文旅数字营销", "meta": "文旅创意 · A轮 · 融资 800万 · 估值 6000万", "tag": "A轮"}, {"title": "生物医药检测服务", "meta": "生物医药 · 种子轮 · 融资 150万", "tag": "种子轮"}, {"title": "智慧农业物联", "meta": "农业科技 · 天使轮 · 融资 200万 · 估值 1500万", "tag": "天使轮"}, {"title": "本地生活 AI 助手", "meta": "人工智能 · 种子轮 · 融资 100万", "tag": "种子轮"}, ], }, ("investor", "trainings"): { "items": [ {"title": "商业计划书(BP)撰写实战", "meta": "投融资基础 · 视频课 · 免费", "tag": "免费"}, {"title": "创业公司估值与投资条款", "meta": "投融资进阶 · 视频课 · 付费", "tag": "付费"}, {"title": "尽职调查实务(投资人视角)", "meta": "投融资进阶 · 直播课 · 报名中", "tag": "报名中"}, {"title": "路演技巧与打动投资人", "meta": "融资准备 · 视频课 · 免费", "tag": "免费"}, {"title": "股权结构与法律合规", "meta": "融资准备 · 视频课 · 付费", "tag": "付费"}, ], }, ("investor", "portfolio"): { "stats": [ {"key": "totalInvested", "value": "¥500万"}, {"key": "portfolioCount", "value": 6}, {"key": "activeIntents", "value": 3}, {"key": "roadshowAttended", "value": 12}, ], "items": [ {"title": "云上数仓 SaaS", "meta": "已投 · 天使轮 · 当前估值 2000万", "tag": "已投"}, {"title": "滇南文旅", "meta": "已投 · A轮 · 当前估值 6000万", "tag": "已投"}, {"title": "本地生活 AI 助手", "meta": "跟踪中 · 种子轮", "tag": "跟踪中"}, ], }, ("opc", "tax"): { "items": [ {"title": "个人所得税 · 7月申报", "meta": "申报期至 2026-08-15 · 待申报", "tag": "待申报"}, {"title": "增值税 · 7月申报", "meta": "申报期至 2026-08-15 · 待申报", "tag": "待申报"}, {"title": "企业所得税 · 二季度", "meta": "已于 2026-07-20 申报", "tag": "已申报"}, ], }, ("opc", "affairs"): { "items": [ {"title": "个体工商户注册", "meta": "工商办理 · 材料已提交 · 办理中", "tag": "办理中"}, {"title": "年报公示", "meta": "工商办理 · 已完成", "tag": "已完成"}, {"title": "一次性创业补贴申报", "meta": "政务申报 · 区县初审中", "tag": "初审中"}, ], }, ("developer", "dashboard"): { "stats": [ {"key": "appCount", "value": 3}, {"key": "pluginCount", "value": 5}, {"key": "skillCount", "value": 8}, {"key": "apiCalls", "value": "12.6万"}, ], }, ("developer", "apps"): { "items": [ {"title": "云上订单助手", "meta": "应用 · 已上架 · 调用 3.2万次", "tag": "已上架"}, {"title": "创业政策查询", "meta": "应用 · 已上架 · 调用 5.1万次", "tag": "已上架"}, {"title": "财税自动记账", "meta": "应用 · 待审核", "tag": "待审核"}, ], }, ("developer", "plugins"): { "items": [ {"title": "task-helper", "meta": "插件 · 版本 v1.2 · 已上架", "tag": "已上架"}, {"title": "policy-mcp", "meta": "插件 · MCP · 已上架", "tag": "已上架"}, {"title": "invoice-extract", "meta": "插件 · 开发中", "tag": "开发中"}, ], }, ("developer", "skills"): { "items": [ {"title": "BP 撰写助手", "meta": "技能 · 已上架 · 使用 1200次", "tag": "已上架"}, {"title": "财税问答", "meta": "技能 · 已上架 · 使用 860次", "tag": "已上架"}, {"title": "路演 PPT 生成", "meta": "技能 · 待审核", "tag": "待审核"}, ], }, } async def _seed_port_pages(session: AsyncSession, now: str) -> None: """写入端口子页面演示数据(幂等:任一 (port,page) 已存在则跳过)。""" if await session.scalar(select(PortalPage.port).limit(1)) is not None: return session.add_all( [ PortalPage( port=port, page=page, payload=json.dumps(payload, ensure_ascii=False), updated_at=now, ) for (port, page), payload in _PORT_PAGES.items() ] ) async def _seed_investor(session: AsyncSession, now: str) -> None: """投资人端演示数据(偏好/路演/意向),幂等(以偏好为标记)。""" if await session.scalar(select(InvestorPreference.user_id).limit(1)) is not None: return session.add_all([ InvestorPreference( user_id="u_inv_01", industries='["人工智能","文旅","农业科技"]', stage="angel", amount_min=50, amount_max=300, region_id="r_prov_yn", updated_at=now, ), InvestorPreference( user_id="u_inv_02", industries='["人工智能","生物医药","智能制造"]', stage="series_a", amount_min=500, amount_max=3000, region_id="r_prov_yn", updated_at=now, ), ]) session.add_all([ Roadshow( id="rs_001", publisher_id="u_inv_02", publisher_role="investor", title="云南 AI 创业项目专场路演", summary="8 个 AI 方向创业项目线上路演", activity_type="online", scope_type="all", region_id=None, start_at="2026-08-20T14:00:00", end_at="2026-08-20T17:00:00", register_deadline="2026-08-18", quota=100, live_url="", status="published", need_review=True, created_at=now, updated_at=now, ), Roadshow( id="rs_002", publisher_id="u_demo_01", publisher_role="government", title="省级创业投融资对接会", summary="省级载体与投资人对接", activity_type="hybrid", scope_type="region", region_id="r_prov_yn", start_at="2026-08-28T09:00:00", end_at="2026-08-28T12:00:00", register_deadline="2026-08-25", quota=200, venue="昆明国际会展中心", status="published", need_review=False, created_at=now, updated_at=now, ), ]) session.add_all([ InvestmentIntent( id="intent_001", investor_id="u_inv_01", project_id="proj_001", project_name="云上数仓 SaaS 平台", status="contacted", message="对贵项目感兴趣,希望约谈", created_at=now, updated_at=now, ), ]) async def _sync_permissions(session: AsyncSession, now: str) -> None: """幂等补种权限与角色权限映射(供既有库使用)。 只插入缺失的 Permission 行与缺失的 RolePermission 链接,不删除已有 自定义授权,避免覆盖运营端后续经「角色权限配置」做的调整。 """ for pid, name, category, module in PERMISSIONS: if await session.get(Permission, pid) is None: session.add(Permission(id=pid, name=name, category=category, module=module)) for role_id, perms in ROLE_PERMISSIONS.items(): if await session.get(Role, role_id) is None: continue existing = { rp.permission_id for rp in await session.scalars( select(RolePermission).where(RolePermission.role_id == role_id) ) } for perm in perms: if perm not in existing: session.add(RolePermission(role_id=role_id, permission_id=perm)) async def _ensure_extra_demo_users(session: AsyncSession, now: str) -> None: """补建新增的演示用户(运营子角色等),供既有库使用(幂等)。""" EXTRA = [ # 运营端内部子角色演示账号 ("u_op_admin", "op_admin", "运营管理员", "operator", "op_admin", None, "r_prov_yn"), ("u_op_cs", "op_cs", "运营客服", "operator", "op_customer_service", None, "r_prov_yn"), ("u_op_fin", "op_fin", "运营财务", "operator", "op_finance", None, "r_prov_yn"), ("u_op_tech", "op_tech", "运营运维", "operator", "op_techops", None, "r_prov_yn"), ] for (uid, username, nickname, role, sub_role, org_id, region_id) in EXTRA: if await session.get(User, uid) is not None: continue digest, salt = hash_password("123456") session.add( User( id=uid, username=username, nickname=nickname, password_hash=digest, password_salt=salt, role=role, sub_role=sub_role, org_id=org_id, region_id=region_id, status="active", token_version=0, created_at=now, updated_at=now, source="seed", auth_type="admin", ) ) async def _seed_ecosystem(session: AsyncSession, now: str) -> None: """生态横切演示数据(通知/补贴/引荐/培训),幂等。""" if await session.scalar(select(Notification.id).limit(1)) is None: session.add_all([ Notification(id="ntf_001", user_id="u_opc_01", type="task", title="任务进度更新", content="您的任务「电商小程序首页 UI 设计」进度已更新", read=False, created_at=now), Notification(id="ntf_002", user_id="u_opc_01", type="policy", title="新政策匹配", content="2 项新政策与您高度匹配", read=False, created_at=now), Notification(id="ntf_003", user_id="u_ent_01", type="task", title="收到投标", content="您的任务收到新的竞标", read=False, created_at=now), ]) if await session.scalar(select(SubsidyApplication.id).limit(1)) is not None: return session.add_all([ SubsidyApplication(id="sub_001", opc_id="u_opc_01", opc_name="OPC个人创业者", title="云南省一次性创业补贴", amount=10000, region_id="r_dist_wh", status="applying", created_at=now, updated_at=now), SubsidyApplication(id="sub_002", opc_id="u_opc_02", opc_name="OPC个体工商户", title="昆明市创业担保贷款贴息", amount=5000, region_id="r_dist_pl", status="district_review", created_at=now, updated_at=now), SubsidyApplication(id="sub_003", opc_id="u_opc_01", opc_name="OPC个人创业者", title="青年人才创业支持计划", amount=20000, region_id="r_dist_wh", status="city_review", created_at=now, updated_at=now), ]) session.add_all([ ServiceReferral(id="ref_001", carrier_id="o_car_001", provider_id="o_pro_001", provider_name="云超财税服务所", opc_id="u_opc_01", opc_name="OPC个人创业者", status="referred", created_at=now, updated_at=now), ]) session.add_all([ TrainingEnrollment(id="treg_001", user_id="u_inv_01", training_id="商业计划书(BP)撰写实战", training_name="商业计划书(BP)撰写实战", status="enrolled", created_at=now), ]) async def _seed_carrier_park_binding(session: AsyncSession, now: str) -> None: """载体账号绑定默认园区(T001),保证载体端任务管理开箱可用(幂等)。 默认园区由园区子应用 ensure_default_tenant 创建;此处兜底确保存在并绑定 种子载体账号 u_car_01,载体端任务管理/大屏按园区归属解析依赖该绑定。 """ from sqlalchemy import select as _select tid = "T001" if await session.get(ParkTenant, tid) is None: session.add(ParkTenant( id=tid, name="昆明市大学生创业园", username="admin", status="active", intro_json="[]", data_json="{}", agent_json="{}", created_at=now, )) await session.flush() for uid in ("u_car_01",): row = await session.scalar( _select(ParkMember).where(ParkMember.user_id == uid, ParkMember.park_id == tid)) if row is None: session.add(ParkMember( id=new_id("pm"), user_id=uid, park_id=tid, member_type="admin", status="active", created_by="seed", created_at=now, )) async def _seed_org_members(session: AsyncSession, now: str) -> None: """机构成员(机构主账号 + 子账号),幂等(以 organization_members 为标记)。""" await session.flush() if await session.scalar(select(OrganizationMember.org_id).limit(1)) is not None: return # 每个机构默认一个管理员主账号(仅保留园区 carrier) members = [ ("o_car_001", "u_car_01", "admin", True), ] for org_id, user_id, role, is_admin in members: session.add(OrganizationMember(org_id=org_id, user_id=user_id, role=role, is_admin=is_admin, status="active", joined_at=now))