Files
server-core/app/infrastructure/seed.py
T
Pine 4dedb43d7f 算力多级折扣与企业余额体系 - 服务端核心改造
数据模型:
- 新增 tenant_discounts(运营端载体折扣)
- 新增 tenant_user_discounts(载体端账号折扣)
- 新增 compute_recharges(算力充值记录)
- 新增 compute_balance_allocations(余额分配记录)
- 新增 compute_usage_records(算力使用记录)
- 扩展 park_tenants(default_company_discount, default_user_discount)
- 扩展 park_companies(compute_balance, compute_balance_used)
- 扩展 company_members(role, compute_balance, compute_balance_used, left_at, left_reason)

核心服务:
- compute_pricing_service.py:四级折扣价格计算、余额扣费、企业余额管理

API接口(/api/compute):
- 运营端:载体折扣管理、标准价管理
- 载体端:企业折扣设置、账号折扣设置、默认折扣设置
- 企业端:余额充值、成员余额分配/回收、充值/分配记录
- 个人端:我的余额、使用记录、退出企业
- 内部:价格查询、算力扣费

数据库迁移:0064_compute_discount_balance.py
2026-09-06 17:59:56 +08:00

977 lines
52 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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_desktop_updates", "桌面端版本管理", "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_desktop_updates",
"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", "短信 providerstub|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))