From 0dbe00a8b42bf209215853408ae3c6e97f5957dc Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Thu, 12 Feb 2026 18:46:22 +0800 Subject: [PATCH] feat: add seed_demo.py with demo data using real team members Co-authored-by: Cursor --- backend/airlabs.db | Bin 110592 -> 114688 bytes backend/seed_demo.py | 362 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 backend/seed_demo.py diff --git a/backend/airlabs.db b/backend/airlabs.db index 2d566959f0a0e1ac7b185cefbc90b54ed1c6078c..7ac369e0400c96ef663706bc0017e0e0280fdcbd 100644 GIT binary patch literal 114688 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 delta 4492 zcmeI0Urbwd6vuzRwzRjUy|)EgXiH0P3zYw~y_A1GIKo&mT?WHx<_v@f1Br>kV)Vfn zTKC|<;R3tp&47%}%sCCR0CR=0j2V4#FG^yTEHQ2&wjDl*4?gWdJ-4Ozw%skG%S?PA zO}}lL`#I-#&ON_#ez(D(D)>M-Zq+deAuGHs@Rm(fG+}0;@A9B#OipoB6S+x_E$N5e zB5QoLUZ2C$(&}w$_4(0pg$`BMyHGyHG(t?+t9o;WM-0k!f~zxR|{;Y%zG6 zTm4L{uK~R%*CDUVh1ke&J=4|Q;q&+!YdvwBiEDafI^=e{5UmEAawg1v5FH(he7fQ| z`bs<7Ic#Cy4A7#*0jxtUa5Z~A;AejhloAPFi$R4xr0Bv5$6;b?7q2ggHq|}V36)xT zQQ4BSo^v9{pjc2Gk#EUQ$xCD*StpL-04_vVSz7;UUcZ|{SWVL<1jfkhroc=I8b0c!@YWSlo(aF`8M4+C=#5X>T+adRtZyQr5jB(x&E>rM^xasrQ z%>J;VwFp<2WprI2;kt?Y_9ixpbg85-DYB6rTucI8S-JJI>&dWl*IpYqd@FgqbVj$a z{IGQkMQ+9`l~u>CefrLZ#6bH>Vw_RDwVd}XF4X#bhdSE!cXabPX8&F=yM>#YbSr!9 z0OLemxM)Tp6AB|23fK`HJr$Y%rh%)!j*ZsDE$y{QQzgroNTL{S=v*>YEKSYMU_vAM z;?_ghB)YOb2Jyva-&YWiPUM7~O4qCf?C={PQL`Lg#`a?SaUn@Qeh4dwa!b zPK4ZVmf~6cGNns~;?*lsMcY(j+i?r4xSTKEbZ@cZ6}iH6j$bVYnaVY!ld;MbNt4t% zcZxG!z@h=8lmR}_=aK_u;uY+zm9FK2^z~+5BWCeCe97_dLQkv9}?l zyuzEd5(I&444CGQOyMejcz!H0GA;la(1VYo4(`qr)+pnAY#Rx^4WXRhB>+9Y?C2sF z9{p;Y;k|I;50Dvd6aGN>>0JiMXYuXfmBgtY0w@;V?K)$d`pD9xSA-nzZ&0*G3p z0ub%n6dSnj#H!dSNI1;0YxiC0zkPN=zxd|{#s3UFNx1Y99-rt^m0>V$B_EJM(yqI% zJFJaphqb2M`P>7VUp4P&OzM#O7_qfP>%SvR12z1V_)ofjPj%e%Q*{5HDk*e_?%z`> z2|S_u4^;3}q7!D-{r8>$lAd~`Mn`J*@85d{NIA{_|DFNbvh4nRW6L282>u~xChw8& Tkp^gB|LFD_N_GuJXx;w;xr90n diff --git a/backend/seed_demo.py b/backend/seed_demo.py new file mode 100644 index 0000000..b40c6fe --- /dev/null +++ b/backend/seed_demo.py @@ -0,0 +1,362 @@ +"""补充演示数据 - 只添加项目/提交/成本,不动用户和角色""" +from datetime import date, timedelta +from database import SessionLocal, engine +from models import * + +db = SessionLocal() + + +def get_user(username): + u = db.query(User).filter(User.username == username).first() + if not u: + print(f" [WARN] user '{username}' not found") + return u + + +def seed_demo(): + # 清除旧的项目相关数据(不动 users 和 roles) + db.query(SubmissionHistory).delete() + db.query(Submission).delete() + db.query(OutsourceCost).delete() + db.query(CostOverride).delete() + db.query(AIToolCost).delete() + db.query(OverheadCost).delete() + db.query(Project).delete() + db.commit() + print("[1] Cleared old project data") + + # 获取真实用户 + huhaonan = get_user("huhaonan") # 主管/总导演 + dengqingrui = get_user("dengqingrui") # 主管/AI导演 + qiushaohui = get_user("qiushaohui") # 主管/制片 + chenbaodan = get_user("chenbaodan") # 组长/动画制作 + maruoqing = get_user("maruoqing") # 组长/AI导演 + weichunli = get_user("weichunli") # 组长/AI导演 + panziyan = get_user("panziyan") # 组长/剪辑 + daixiaoqian = get_user("daixiaoqian") # 组员/动画制作 + tanruping = get_user("tanruping") # 组员/动画制作 + zhengyiqing = get_user("zhengyiqing") # 组员/动画制作 + huangxuewen = get_user("huangxuewen") # 组员/动画制作 + liushiqi = get_user("liushiqi") # 组员/动画制作 + daiwei = get_user("daiwei") # 组员/动画制作 + huangrongying = get_user("huangrongying") # 组员/编剧 + jiahaozheng = get_user("jiahaozheng") # 组员/剪辑 + wangyansen = get_user("wangyansen") # 组员/剪辑 + huangqiuxia = get_user("huangqiuxia") # 组员/动画制作 + lijing = get_user("lijing") # 组员/动画制作 + yemeilian = get_user("yemeilian") # 组员/动画制作 + chenxuanying = get_user("chenxuanying") # 组员/动画制作 + + # ── 项目 ── + proj_a = Project( + name="星际漫游 第一季", project_type=ProjectType.CLIENT_FORMAL, + leader_id=huhaonan.id, current_phase=PhaseGroup.PRODUCTION, + episode_duration_minutes=5, episode_count=13, + estimated_completion_date=date.today() + timedelta(days=60), + contract_amount=100000, + ) + proj_b = Project( + name="品牌方 TVC 宣传片", project_type=ProjectType.CLIENT_FORMAL, + leader_id=dengqingrui.id, current_phase=PhaseGroup.PRODUCTION, + episode_duration_minutes=1, episode_count=3, + estimated_completion_date=date.today() + timedelta(days=20), + contract_amount=50000, + ) + proj_c = Project( + name="甲方风格测试", project_type=ProjectType.CLIENT_TEST, + leader_id=maruoqing.id, current_phase=PhaseGroup.PRE, + episode_duration_minutes=1, episode_count=1, + ) + proj_d = Project( + name="AI 短剧原创 S1", project_type=ProjectType.INTERNAL_ORIGINAL, + leader_id=weichunli.id, current_phase=PhaseGroup.PRE, + episode_duration_minutes=8, episode_count=6, + estimated_completion_date=date.today() + timedelta(days=90), + ) + db.add_all([proj_a, proj_b, proj_c, proj_d]) + db.flush() + print("[2] Created 4 projects") + + # ── 内容提交(模拟近 20 天的数据) ── + base = date.today() - timedelta(days=20) + subs = [] + + # --- 项目A:星际漫游 --- + # 黄溶莹 - 编剧 - 前期方案 + for i in range(6): + d = base + timedelta(days=i) + subs.append(Submission( + user_id=huangrongying.id, project_id=proj_a.id, + project_phase=PhaseGroup.PRE, work_type=WorkType.PLAN, + content_type=ContentType.DESIGN, total_seconds=0, + submit_to=SubmitTo.INTERNAL, description=f"第{i+1}集剧本初稿", + submit_date=d, + )) + + # 陈保丹 - 组长 - 动画制作 + for i in range(12): + d = base + timedelta(days=i + 3) + secs = 55 + (i % 3) * 20 + wt = WorkType.TEST if i < 2 else WorkType.PRODUCTION + subs.append(Submission( + user_id=chenbaodan.id, project_id=proj_a.id, + project_phase=PhaseGroup.PRODUCTION, work_type=wt, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.INTERNAL, description=f"第1集场景{i+1}动画", + submit_date=d, + )) + + # 代晓倩 - 动画制作 + for i in range(10): + d = base + timedelta(days=i + 2) + secs = 40 + (i % 4) * 15 + subs.append(Submission( + user_id=daixiaoqian.id, project_id=proj_a.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.PRODUCTION, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.LEADER, description=f"第2集片段{i+1}", + submit_date=d, + )) + + # 谭如平 - 动画制作 + for i in range(8): + d = base + timedelta(days=i + 4) + secs = 35 + (i % 3) * 25 + wt = WorkType.TEST if i == 0 else WorkType.PRODUCTION + subs.append(Submission( + user_id=tanruping.id, project_id=proj_a.id, + project_phase=PhaseGroup.PRODUCTION, work_type=wt, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.LEADER, description=f"第3集镜头{i+1}", + submit_date=d, + )) + + # 郑奕晴 - 动画制作 + for i in range(9): + d = base + timedelta(days=i + 3) + secs = 45 + (i % 2) * 30 + subs.append(Submission( + user_id=zhengyiqing.id, project_id=proj_a.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.PRODUCTION, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.LEADER, description=f"第4集场景动画{i+1}", + submit_date=d, + )) + + # 黄雪雯 - 动画制作 + for i in range(7): + d = base + timedelta(days=i + 5) + secs = 30 + (i % 3) * 20 + subs.append(Submission( + user_id=huangxuewen.id, project_id=proj_a.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.PRODUCTION, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.LEADER, description=f"第5集片段{i+1}", + submit_date=d, + )) + + # 潘梓彦 - 剪辑 - 后期 + for i in range(4): + d = base + timedelta(days=i + 14) + subs.append(Submission( + user_id=panziyan.id, project_id=proj_a.id, + project_phase=PhaseGroup.POST, work_type=WorkType.PRODUCTION, + content_type=ContentType.EDITING, total_seconds=0, + submit_to=SubmitTo.PRODUCER, description=f"第{i+1}集粗剪", + submit_date=d, + )) + + # 贾浩正 - 剪辑 + for i in range(3): + d = base + timedelta(days=i + 15) + subs.append(Submission( + user_id=jiahaozheng.id, project_id=proj_a.id, + project_phase=PhaseGroup.POST, work_type=WorkType.PRODUCTION, + content_type=ContentType.EDITING, total_seconds=0, + submit_to=SubmitTo.PRODUCER, description=f"第{i+5}集粗剪", + submit_date=d, + )) + + # --- 项目B:品牌方 TVC --- + # 马若情 - AI导演 + for i in range(6): + d = base + timedelta(days=i + 5) + secs = 20 + i * 10 + subs.append(Submission( + user_id=maruoqing.id, project_id=proj_b.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.PRODUCTION, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.INTERNAL, description=f"TVC 片段{i+1}", + submit_date=d, + )) + + # 刘诗琪 - 动画制作 + for i in range(5): + d = base + timedelta(days=i + 7) + secs = 15 + (i % 3) * 10 + subs.append(Submission( + user_id=liushiqi.id, project_id=proj_b.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.PRODUCTION, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.LEADER, description=f"TVC 补充镜头{i+1}", + submit_date=d, + )) + + # 王炎森 - 剪辑 + for i in range(3): + d = base + timedelta(days=i + 13) + subs.append(Submission( + user_id=wangyansen.id, project_id=proj_b.id, + project_phase=PhaseGroup.POST, work_type=WorkType.PRODUCTION, + content_type=ContentType.EDITING, total_seconds=0, + submit_to=SubmitTo.PRODUCER, description=f"TVC 第{i+1}版剪辑", + submit_date=d, + )) + + # --- 项目C:甲方风格测试 --- + for i in range(3): + d = base + timedelta(days=i + 1) + subs.append(Submission( + user_id=huangrongying.id, project_id=proj_c.id, + project_phase=PhaseGroup.PRE, work_type=WorkType.PLAN, + content_type=ContentType.DESIGN, total_seconds=0, + submit_to=SubmitTo.INTERNAL, description=f"风格方案{i+1}", + submit_date=d, + )) + for i in range(4): + d = base + timedelta(days=i + 4) + secs = 10 + i * 5 + subs.append(Submission( + user_id=daiwei.id, project_id=proj_c.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.TEST, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.INTERNAL, description=f"风格测试片段{i+1}", + submit_date=d, + )) + + # --- 项目D:AI 短剧原创 --- + for i in range(5): + d = base + timedelta(days=i) + subs.append(Submission( + user_id=huangrongying.id, project_id=proj_d.id, + project_phase=PhaseGroup.PRE, work_type=WorkType.PLAN, + content_type=ContentType.DESIGN, total_seconds=0, + submit_to=SubmitTo.INTERNAL, description=f"原创剧本第{i+1}集大纲", + submit_date=d, + )) + for i in range(6): + d = base + timedelta(days=i + 8) + secs = 60 + (i % 3) * 25 + subs.append(Submission( + user_id=weichunli.id, project_id=proj_d.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.PRODUCTION, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.INTERNAL, description=f"原创第1集片段{i+1}", + submit_date=d, + )) + for i in range(5): + d = base + timedelta(days=i + 10) + secs = 50 + (i % 2) * 35 + subs.append(Submission( + user_id=huangqiuxia.id, project_id=proj_d.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.PRODUCTION, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.LEADER, description=f"原创第2集动画{i+1}", + submit_date=d, + )) + for i in range(4): + d = base + timedelta(days=i + 12) + secs = 45 + i * 15 + subs.append(Submission( + user_id=lijing.id, project_id=proj_d.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.PRODUCTION, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.LEADER, description=f"原创第3集片段{i+1}", + submit_date=d, + )) + for i in range(3): + d = base + timedelta(days=i + 14) + secs = 40 + i * 20 + subs.append(Submission( + user_id=yemeilian.id, project_id=proj_d.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.PRODUCTION, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.LEADER, description=f"原创第4集动画{i+1}", + submit_date=d, + )) + for i in range(3): + d = base + timedelta(days=i + 15) + secs = 55 + (i % 2) * 20 + subs.append(Submission( + user_id=chenxuanying.id, project_id=proj_d.id, + project_phase=PhaseGroup.PRODUCTION, work_type=WorkType.PRODUCTION, + content_type=ContentType.ANIMATION, total_seconds=secs, + duration_minutes=secs // 60, duration_seconds=secs % 60, + submit_to=SubmitTo.LEADER, description=f"原创第5集场景{i+1}", + submit_date=d, + )) + + db.add_all(subs) + print(f"[3] Created {len(subs)} submissions") + + # ── AI 工具成本 ── + db.add(AIToolCost( + tool_name="Midjourney", subscription_period=SubscriptionPeriod.MONTHLY, + amount=200, allocation_type=CostAllocationType.TEAM, + recorded_by=qiushaohui.id, record_date=date.today().replace(day=1), + )) + db.add(AIToolCost( + tool_name="Runway", subscription_period=SubscriptionPeriod.MONTHLY, + amount=600, allocation_type=CostAllocationType.PROJECT, + project_id=proj_a.id, + recorded_by=qiushaohui.id, record_date=date.today().replace(day=1), + )) + db.add(AIToolCost( + tool_name="ChatGPT Plus", subscription_period=SubscriptionPeriod.MONTHLY, + amount=150, allocation_type=CostAllocationType.TEAM, + recorded_by=qiushaohui.id, record_date=date.today().replace(day=1), + )) + print("[4] Created 3 AI tool costs") + + # ── 外包成本 ── + db.add(OutsourceCost( + project_id=proj_a.id, outsource_type=OutsourceType.ANIMATION, + episode_start=10, episode_end=13, amount=20000, + recorded_by=qiushaohui.id, record_date=date.today() - timedelta(days=5), + )) + print("[5] Created 1 outsource cost") + + # ── 固定开支 ── + db.add(OverheadCost( + cost_type=OverheadCostType.OFFICE_RENT, + amount=8000, record_month=date.today().strftime("%Y-%m"), + recorded_by=qiushaohui.id, note="办公室月租", + )) + db.add(OverheadCost( + cost_type=OverheadCostType.UTILITIES, + amount=500, record_month=date.today().strftime("%Y-%m"), + recorded_by=qiushaohui.id, note="水电费", + )) + print("[6] Created 2 overhead costs") + + db.commit() + print("\n[DONE] Demo data seeded successfully!") + print(f" Projects: 4") + print(f" Submissions: {len(subs)}") + print(f" AI tools: 3, Outsource: 1, Overhead: 2") + + +if __name__ == "__main__": + seed_demo()