Files
opc-manager/backend/migrations/seed_data.py
mac f9fe32beed feat: 费用状态字段 + 表格排序 + 首页回款提醒 + 回款率着色
- 平台费用新增状态字段(计入费用/暂不计入),默认计入费用
- 费用表格月份列置首,所有表头点击正/倒序排序
- 首页新增回款提醒条(金色渐变,含确收/回款/未回款/回款率)
- 业务表格回款率着色:<30%红,30-80%黄,>=100%绿
- 版本号v1.0.0.5
2026-07-07 18:34:47 +08:00

286 lines
15 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.
# seed_data.py — 丰富示例数据填充(仅在空库时执行一次)
# 为每个工作台生成 > 1 亿的总规模,覆盖万/十万/百万/千万量级
import json
import random
random.seed(42)
from datetime import date
from db import db, _exec, one
TENANTS = ["科普·无界", "科研·无界", "医患·无界", "MCN·无界", "学会·无界"]
# 每个工作台的差异化客户和项目模板
TENANT_CUSTOMERS = {
"科普·无界": [
("信达生物", ["肿瘤免疫科普年度框架", "信迪利单抗医生教育", "CSCO卫星会项目", "患者随访管理系统"]),
("百利天恒", ["BL-B01D1上市前教育", "双抗药物科普专区", "数字人患教视频"]),
("齐鲁制药", ["多产品线医生教育", "安可达学术推广", "基层医疗科普巡讲"]),
("天广实生物", ["血液肿瘤科普项目", "CD20单抗学术推广"]),
("复星医药", ["复宏汉霖学术年会", "CAR-T患者教育"]),
],
"科研·无界": [
("北京协和医院", ["临床研究数据采集系统", "罕见病队列研究", "AI辅助诊断验证"]),
("中国人民解放军总医院", ["战场创伤数据平台", "远程会诊系统"]),
("复旦大学附属华山医院", ["脑卒中AI预警系统", "神经内科临床研究"]),
("中山大学肿瘤防治中心", ["鼻咽癌早筛项目", "真实世界研究平台"]),
("四川大学华西医院", ["精准医学大数据平台", "基层科研能力提升"]),
],
"医患·无界": [
("平安好医生", ["在线问诊科普内容", "慢病管理专区", "随访SOP标准化"]),
("微医集团", ["互联网医院患教系统", "云药房科普推送"]),
("丁香园", ["医生社区运营升级", "用药助手科普专区"]),
("圆心科技", ["药房患者教育", "DTP药房随访管理", "罕见病用药指导"]),
("国药控股", ["国药患者管理平台", "肿瘤药房随访系统"]),
],
"MCN·无界": [
("抖音健康", ["医学IP孵化矩阵", "短视频科普内容制作", "健康直播运营"]),
("快手健康", ["医生个人IP打造", "快手健康科普专区"]),
("小红书医美", ["皮肤健康科普矩阵", "医美科普合规审核"]),
("B站知识区", ["深度科普纪录片", "医学动画制作"]),
("微信视频号", ["医生科普直播运营", "企业微信科普推送"]),
],
"学会·无界": [
("中华医学会", ["学术年会数字化平台", "继续教育学分系统", "基层医生培训"]),
("中国医师协会", ["医师定考管理系统", "专科医师培训平台"]),
("中国抗癌协会", ["肿瘤科普万里行", "CACA指南推广"]),
("中国药学会", ["合理用药科普平台", "药师继续教育"]),
("中华护理学会", ["护理科研能力提升", "专科护士培训系统"]),
],
}
# 签约金额模板:每个工作台的千万/百万/十万/万级别项目的签约金额
def generate_sign_amounts():
amounts = []
# 千万级2个4000-7000万
amounts.append(random.randint(4500, 7000) * 10000)
amounts.append(random.randint(4000, 6500) * 10000)
# 百万级4个400-980万
for _ in range(4):
amounts.append(random.randint(400, 980) * 10000)
# 十万级4个30-98万
for _ in range(4):
amounts.append(random.randint(30, 98) * 10000)
# 万级3个3-10万
for _ in range(3):
amounts.append(random.randint(3, 10) * 10000)
return sorted(amounts, reverse=True)
# 生成 budget_data JSON每月确收/毛利/回款)
def make_budget_data(sign_amount, signed_month, status):
"""生成12个月的预算数据"""
months = []
start_m = int(signed_month.split("-")[1]) if signed_month else 1
rev_total = sign_amount if status == "已签约" else int(sign_amount * random.uniform(0.3, 0.7))
gross_rate = random.uniform(0.25, 0.55)
payment_rate = random.uniform(0.7, 0.95)
for i in range(12):
m = (start_m + i - 1) % 12 + 1
yr = 2026 + (start_m + i - 1) // 12
month_key = f"{yr}-{m:02d}"
# 越往后确收越多
progress = min(1.0, (i + 1) / max(1, random.randint(3, 8)))
rev = round(rev_total * progress * random.uniform(0.85, 1.15))
gross = round(rev * gross_rate * random.uniform(0.9, 1.1))
payment = round(rev * payment_rate * random.uniform(0.85, 1.15))
cost = round(rev * random.uniform(0.3, 0.5))
paid = round(cost * random.uniform(0.8, 1.0))
months.append({
"month": month_key,
"rev": rev,
"gross": gross,
"payment": payment,
"cost": cost,
"paid": paid,
})
return json.dumps(months)
# 生成 expense_data平台费用
def make_expense_data():
"""生成若干条费用记录"""
items = []
expense_types = ["平台采购", "人力成本", "研发费用", "通用费用", "奖金"]
for q in range(1, 7):
for et in random.sample(expense_types, random.randint(2, 4)):
month_str = f"2026-{q:02d}"
amount = random.randint(5000, 150000)
incurred = int(amount * random.uniform(0.6, 1.0))
items.append({"month": month_str, "expense_type": et, "amount": amount, "incurred": incurred})
return json.dumps(items)
def seed_db():
"""填充丰富示例数据"""
conn = db()
try:
if one(conn, "SELECT id FROM sales_leads LIMIT 1"):
return
today = date.today().isoformat()
pf_ids = {} # tenant -> [project_finance_ids]
for tenant in TENANTS:
customers = TENANT_CUSTOMERS[tenant]
amounts = generate_sign_amounts()
cust_idx = 0
for i, sign_amount in enumerate(amounts):
cust_name, projects = customers[cust_idx % len(customers)]
proj_name = projects[i % len(projects)] if i < len(projects) else f"{cust_name} 项目{i+1}"
cust_idx += i % 3
# 70% 已签约, 30% 待签约
status = "已签约" if random.random() < 0.7 else "待签约"
sign_month = f"2026-{random.randint(1, 6):02d}" if status == "已签约" else ""
project_code = f"{tenant[:2]}-2026-{i+1:03d}"
business_type = random.choice(["学术推广", "医生教育", "患者管理", "科研合作", "科普内容"])
# 如果签约,总额 = 签约额否则为0
total_rev = int(sign_amount * random.uniform(0.8, 1.0)) if status == "已签约" else 0
total_gross = int(total_rev * random.uniform(0.25, 0.5)) if status == "已签约" else 0
total_payment = int(total_rev * random.uniform(0.7, 0.9)) if status == "已签约" else 0
total_cost = int(total_rev * random.uniform(0.3, 0.5)) if status == "已签约" else 0
total_paid = int(total_cost * random.uniform(0.7, 0.95)) if status == "已签约" else 0
budget_data = make_budget_data(sign_amount, sign_month, status)
expense_data = make_expense_data() if status == "已签约" else "[]"
task_data = json.dumps([
{"task": f"{proj_name} 项目启动", "due": f"2026-{min(12, 1+i//2):02d}-01", "status": "已完成" if i < 8 else "进行中"},
{"task": f"方案评审与确认", "due": f"2026-{min(12, 2+i//2):02d}-15", "status": "已完成" if i < 6 else "进行中"},
{"task": f"内容生产阶段", "due": f"2026-{min(12, 3+i//2):02d}-30", "status": "进行中"},
])
cur = _exec(conn, """
INSERT INTO project_finances
(tenant, project_id, business_type, customer_name, sign_amount, sign_month, status,
sales_person, total_rev, total_gross, total_payment, total_cost, total_paid,
budget_data, expense_data, task_data,
project_code, start_date, end_date, task_type, task_count,
service_fee_standard, project_manager, contact_name, contact_phone, other_info,
created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
""", (
tenant, f"PF-{tenant[:2]}-{i+1:04d}", business_type, cust_name,
sign_amount, sign_month, status,
f"销售{random.choice(['A','B','C','D'])}", total_rev, total_gross,
total_payment, total_cost, total_paid,
budget_data, expense_data, task_data,
project_code, f"2026-{random.randint(1,3):02d}-01", f"2026-{random.randint(9,12):02d}-30",
random.choice(["内容生产", "视频制作", "专家访谈", "学术会议", "患者教育"]),
random.randint(10, 100), random.choice([800, 1000, 1200, 1500, 2000]),
f"经理{random.choice(['A','B','C'])}", f"联系人{random.randint(1,20)}",
f"1380000{random.randint(1000,9999)}", f"备注信息 {i+1}",
today, today,
))
# Store for later use
pf_ids.setdefault(tenant, []).append((cur.lastrowid, proj_name, cust_name, status))
# operation_projects: 每个工作台 3-5 个
op_count = random.randint(3, 5)
for j in range(op_count):
pf_row = random.choice(pf_ids[tenant])
cur2 = _exec(conn, """
INSERT INTO operation_projects
(tenant, project_name, project_version, project_type, project_status,
current_stage, owner, start_date, end_date, target_customer, customer_need,
expected_contract_amount, expected_sign_date, sign_probability, next_action,
sop_stage, execution_progress, current_deliverable, notes)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
""", (
tenant, f"{pf_row[1]} 运营方案", "v1.0",
"execution" if random.random() < 0.6 else "opportunity",
"SOP 执行中" if random.random() < 0.6 else "方案已提交",
random.choice(["商务推进", "内容生产", "渠道分发"]),
f"OPC{random.randint(1,5)}", "2026-06-01", "2026-12-31",
pf_row[2], "科普内容项目执行与管理",
pf_row[0] if pf_row[3] == "已签约" else random.randint(50, 300) * 10000,
"2026-06", 70 + random.randint(0, 30),
"补齐版本要求文件并更新下一节点",
"SOP 执行", random.randint(30, 80), "当前交付物 v1.0",
"运营备注",
))
_exec(conn, """
INSERT INTO follow_up_records
(target_type, target_id, followed_at, content, next_action, follower)
VALUES (?,?,?,?,?,?)
""", (
"operation", cur2.lastrowid, today,
f"运营项目跟进:{pf_row[1]},当前进度正常", "继续推进下一里程碑",
f"销售{random.choice(['A','B'])}",
))
# expense_records: 每个工作台 15-20 条
expense_count = random.randint(15, 20)
expense_types = ["通用费用", "平台采购", "人力成本", "研发费用", "奖金", "其他"]
for j in range(expense_count):
et = random.choice(expense_types)
month_str = f"2026-{random.randint(1, 6):02d}"
amount = random.randint(5000, 500000)
incurred = int(amount * random.uniform(0.5, 1.0))
_exec(conn, """
INSERT INTO expense_records
(tenant, expense_type, expense_month, amount, incurred_amount, notes, status)
VALUES (?,?,?,?,?,?,?)
""", (
tenant, et, month_str, amount, incurred,
f"{et} - {random.choice(['季度预算', '紧急支出', '常规费用', '项目费用'])}",
"计入费用",
))
print(f"[seed] {tenant}: {len(amounts)} 项目, {op_count} 运营项目, {expense_count} 费用记录")
conn.commit() # 提交所有工作台的核心数据
# 全域数据sales_leads 对所有 tenant 可见
global_sales = [
("齐鲁制药", "P0", "跟进中", "多产品线科普年度框架"),
("百利天恒", "P0", "方案中", "BL-B01D1 上市前医生教育"),
("信达生物", "P0", "已签约", "现有科普项目升级/续约"),
("三生制药", "P1", "待跟进", "多科室医生教育+患者科普"),
("天广实生物", "P1", "待跟进", "血液肿瘤医生教育机会"),
]
for customer, priority, status, note in global_sales:
cur = _exec(conn,
"INSERT INTO sales_leads (target_customer, priority, status, created_at) VALUES (?,?,?,?)",
(customer, priority, status, today),
)
_exec(conn,
"INSERT INTO follow_up_records (target_type,target_id,followed_at,content,next_action,follower) VALUES (?,?,?,?,?,?)",
("sales", cur.lastrowid, today, note, "明确下一次沟通", "销售A"),
)
# 全域 business_proposals
cur = _exec(conn,
"INSERT INTO business_proposals (customer_or_project_name,version,description,status,created_date, tenant) VALUES (?,?,?,?,?,?)",
("信达生物", "v1.5", "信达科普项目续约与报价方案", "已提交客户", "2026-05-28", "科普·无界"),
)
proposal_id = cur.lastrowid
# 全域 product_versions
products = [
("妙手医生服务小程序", "v1.1", "视频任务增强 + 积分商城", "2026-Q3", "规划中", "P1"),
("数字化营销后台管理系统", "v1.2", "运营数据看板 + 智能审核", "2026-Q3", "设计中", "P0"),
("妙手患者服务", "v0.5", "科普浏览 + 医生主页 MVP", "2026-Q3", "规划中", "P2"),
("数字人内容平台", "v0.1", "基础数字人视频生成 MVP", "2026-Q3", "规划中", "P2"),
("渠道分发引擎", "v1.0", "六渠道统一分发", "2027-Q1", "规划中", "P1"),
]
for p in products:
cur = _exec(conn,
"INSERT INTO product_versions (product_name,version,version_goal,launch_date,status,priority,tenant) VALUES (?,?,?,?,?,?,?)",
(*p, "科普·无界"),
)
_exec(conn,
"INSERT INTO follow_up_records (target_type,target_id,followed_at,content,next_action,follower) VALUES (?,?,?,?,?,?)",
("product", cur.lastrowid, today, f"{p[0]} {p[1]}{p[2]}", "按路线图推进", "产品经理"),
)
conn.commit()
print("[seed] 丰富示例数据已填充")
finally:
conn.close()