| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636 |
- (() => {
- const scope = 'local-guest';
- function seedIpOperatorScenario(scenario = 'ready') {
- const now = new Date().toISOString();
- const user = {
- objectId: scope,
- username: 'ip-operator-e2e',
- displayName: 'IP Operator E2E',
- role: 'user',
- createdAt: now,
- };
- clearIpOperatorData();
- localStorage.setItem('videoWorkflow.authSession', JSON.stringify({ token: 'ip-operator-e2e-token', user }));
- localStorage.setItem('tiktok.currentTab', 'ip-operator');
- sessionStorage.clear();
- if (scenario === 'empty') return { scenario, profileId: '', planId: '' };
- const profileId = `ip_profile_e2e_${scenario}`;
- const planId = `ip_plan_e2e_${scenario}`;
- const profile = createProfile(profileId, now);
- const plan = createPlan({ scenario, profileId, planId, now });
- localStorage.setItem(`tiktok.ipOperator.profiles.${scope}`, JSON.stringify([profileId]));
- localStorage.setItem(`tiktok.ipOperator.profile.${profileId}.${scope}`, JSON.stringify(profile));
- localStorage.setItem(`tiktok.ipOperator.plans.${scope}`, JSON.stringify([planId]));
- localStorage.setItem(`tiktok.ipOperator.plan.${planId}.${scope}`, JSON.stringify(plan));
- localStorage.setItem(`tiktok.ipOperator.activeProfileId.${scope}`, profileId);
- localStorage.setItem(`tiktok.ipOperator.auditTrail.${scope}`, JSON.stringify([{
- id: 'ip_audit_e2e_switch',
- action: 'switch_profile',
- entityType: 'profile',
- entityId: profileId,
- userId: scope,
- createdAt: now,
- summary: `Switch current IP profile: ${profileId}`,
- }]));
- seedAccountWorkbench({ now, profileId, planId, scenario });
- return { scenario, profileId, planId };
- }
- function seedAccountWorkbench({ now, profileId, planId, scenario }) {
- const accountId = `ip_account_e2e_${scenario}`;
- const snapshotId = `ip_snapshot_e2e_${scenario}`;
- const proposalId = `ip_positioning_proposal_e2e_${scenario}`;
- const versionId = `ip_positioning_version_e2e_${scenario}`;
- const account = {
- id: accountId,
- userId: scope,
- platform: 'douyin',
- role: 'owned',
- displayName: '林川老板增长笔记',
- profileId,
- planId,
- homepageUrl: 'https://www.douyin.com/user/MS4wLjABAAAA_e2e',
- secUserId: 'MS4wLjABAAAA_e2e',
- accountId: 'MS4wLjABAAAA_e2e',
- intendedTrack: '传统行业老板 IP',
- intendedPersona: '增长顾问',
- intendedAudience: '传统行业中小企业老板',
- enabled: true,
- lastRefreshStatus: 'completed',
- lastRefreshedAt: now,
- createdAt: now,
- updatedAt: now,
- };
- const works = Array.from({ length: 10 }, (_, index) => {
- const awemeId = `738000000${index}`;
- const deep = index < 3;
- return {
- id: `work_${accountId}_${awemeId}`,
- accountId,
- awemeId,
- title: index === 0 ? '老板 IP 如何避免成为广告号' : `老板增长内容复盘 ${index + 1}`,
- desc: index === 0 ? '老板 IP 如何避免成为广告号,先讲信任再讲产品。' : `传统老板账号第 ${index + 1} 条内容复盘`,
- coverUrl: index === 0 ? 'http://127.0.0.1:9/expired-douyin-cover.jpg' : '',
- coverCandidates: index === 0
- ? [
- 'http://127.0.0.1:9/expired-douyin-cover.jpg',
- 'data:image/svg+xml;charset=UTF-8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2272%22 height=%2296%22%3E%3Crect width=%2272%22 height=%2296%22 fill=%22%230f766e%22/%3E%3C/svg%3E',
- ]
- : [],
- publishTime: now,
- url: `https://www.douyin.com/video/${awemeId}`,
- metrics: {
- playCount: 10000 - index * 600,
- likeCount: 320 - index * 12,
- commentCount: 46 - index * 3,
- collectCount: 80 - index * 4,
- shareCount: 20 - index,
- },
- interactionScore: 320 - index * 12 + (46 - index * 3) * 4 + (80 - index * 4) * 3 + (20 - index) * 5,
- isDeepSampled: deep,
- structure: {
- hook: '为什么你的老板号越发越像广告?',
- topic: '老板 IP 信任表达',
- style: '问题/避坑表达',
- cta: '评论区留下最卡的问题',
- },
- comments: deep ? Array.from({ length: 30 }, (_, commentIndex) => ({
- id: `comment_${index + 1}_${commentIndex + 1}`,
- workId: `work_${accountId}_${awemeId}`,
- text: commentIndex % 3 === 0
- ? '我们传统行业老板确实不知道怎么讲案例'
- : commentIndex % 3 === 1
- ? '想知道怎么把客户问题变成选题'
- : '不要讲太虚,最好给具体脚本结构',
- likeCount: 12 - (commentIndex % 5),
- replyCount: commentIndex % 4,
- authorName: `用户${commentIndex + 1}`,
- capturedAt: now,
- })) : [],
- capturedAt: now,
- };
- });
- const snapshot = {
- id: snapshotId,
- accountId,
- profile: {
- nickname: '林川老板增长笔记',
- signature: '传统行业老板 IP 增长与短视频内容转化',
- avatarUrl: '',
- followerCount: 2860,
- followingCount: 38,
- totalFavorited: 15800,
- awemeCount: 42,
- },
- works,
- dataMode: 'data_diagnosis',
- evidenceItemIds: [],
- capturedAt: now,
- warnings: [],
- };
- const proposedVersion = {
- accountId,
- targetAudience: '传统行业中小企业老板',
- persona: '传统行业老板增长顾问',
- followReason: '用真实案例和短视频结构帮助老板解决表达、信任和转化问题。',
- contentPillars: ['老板 IP 定位与认知纠偏', '老板表达案例与完整方法', '真实项目复盘与评论答疑'],
- expressionStyle: ['口语化', '案例优先', '前 3 秒直接给判断'],
- boundaries: ['不夸大收益', '不照搬对标账号话术', '不做无关泛流量内容'],
- suitableViralPatterns: ['反常识开头', '问题-方案结构', '评论问题反推选题'],
- evidenceItemIds: [],
- assumptionsToValidate: ['关注理由是否清晰', '评论痛点能否反推选题'],
- observeMetrics: ['播放量', '评论数', '收藏数', '涨粉'],
- };
- const proposal = {
- id: proposalId,
- accountId,
- snapshotId,
- status: 'proposed',
- reason: '基于最近作品表现、重点评论和对标证据生成的定位提案。',
- proposedVersion,
- evidenceItemIds: [],
- createdAt: now,
- updatedAt: now,
- };
- const diagnosis = {
- id: `ip_account_diagnosis_e2e_${scenario}`,
- accountId,
- snapshotId,
- mode: 'data_diagnosis',
- reportMarkdown: [
- '# 账号诊断报告',
- '',
- '## 当前账号像什么 IP',
- '当前更像传统行业老板增长顾问,内容需要继续收窄到老板表达、信任和转化。',
- '',
- '## 高互动作品共性',
- '问题开头、具体场景、明确判断。',
- '',
- '## 评论区用户关心的问题',
- '用户集中关心如何讲案例、如何把客户问题变成选题。',
- '',
- '## 当前最大增长障碍',
- '内容主题仍有分散,用户关注理由需要稳定。',
- '',
- '## 未来 7 天优先动作',
- '确认定位版本,分别测试涨粉、信任、互动三个方向。',
- ].join('\n'),
- scores: {
- positioningClarity: 82,
- contentStructure: 78,
- interactionConversion: 75,
- recognizability: 80,
- sustainability: 76,
- },
- evidenceItemIds: [],
- topWorkIds: works.slice(0, 3).map((item) => item.id),
- commentSampleCount: 90,
- createdAt: now,
- updatedAt: now,
- };
- const version = {
- id: versionId,
- ...proposedVersion,
- version: 1,
- status: 'active',
- createdAt: now,
- updatedAt: now,
- };
- const directions = [
- createDirection(accountId, versionId, 'growth', '老板 IP 定位与认知纠偏', '围绕老板做内容时的常见误区和判断标准,帮助新用户快速建立正确认知。', ['播放量', '分享数', '新粉'], now),
- createDirection(accountId, versionId, 'trust', '老板表达案例与完整方法', '通过真实案例、表达框架和执行过程建立专业信任。', ['收藏数', '咨询意向', '关注率'], now),
- createDirection(accountId, versionId, 'interaction', '真实项目复盘与评论答疑', '把高互动评论归纳成问题类型,用项目复盘和集中答疑形成持续栏目。', ['评论数', '评论率', '高频问题'], now),
- ];
- const tasks = [
- createTask(accountId, 'today', 'positioning', '确认账号当前定位提案', '定位版本会约束后续方向、选题、脚本和发布包。', 95, now),
- createTask(accountId, 'today', 'direction', '确认涨粉破圈方向', '先稳定最影响播放和涨粉的内容方向。', 90, now),
- createTask(accountId, 'this_week', 'pain', '复核重点作品评论痛点', '评论问题能反推互动型选题和脚本切入点。', 82, now),
- createTask(accountId, 'later', 'publish_pack', '绑定发布包与真实作品', '发布后绑定作品才能形成复盘闭环。', 72, now),
- ];
- const binding = {
- id: `publish_binding_${planId}_1`,
- publishPackageId: `${planId}_publish_1`,
- accountId,
- awemeId: works[0].awemeId,
- workUrl: works[0].url,
- status: 'auto_matched',
- matchScore: 86,
- matchReason: '根据标题/文案相似度自动匹配,请人工复核。',
- createdAt: now,
- updatedAt: now,
- };
- saveEntity('tiktok.ipOperator.accounts', 'tiktok.ipOperator.account.', account);
- saveEntity('tiktok.ipOperator.accountSnapshots', 'tiktok.ipOperator.accountSnapshot.', snapshot);
- saveEntity('tiktok.ipOperator.accountDiagnoses', 'tiktok.ipOperator.accountDiagnosis.', diagnosis);
- saveEntity('tiktok.ipOperator.positioningProposals', 'tiktok.ipOperator.positioningProposal.', proposal);
- saveEntity('tiktok.ipOperator.positioningVersions', 'tiktok.ipOperator.positioningVersion.', version);
- directions.forEach((item) => saveEntity('tiktok.ipOperator.contentDirections', 'tiktok.ipOperator.contentDirection.', item));
- tasks.forEach((item) => saveEntity('tiktok.ipOperator.operationTasks', 'tiktok.ipOperator.operationTask.', item));
- saveEntity('tiktok.ipOperator.publishBindings', 'tiktok.ipOperator.publishBinding.', binding);
- }
- function createDirection(accountId, positioningVersionId, role, title, purpose, observeMetrics, now) {
- return {
- id: `direction_${accountId}_${role}`,
- accountId,
- positioningVersionId,
- role,
- title,
- targetAudience: role === 'growth' ? '尚未关注账号的新用户' : role === 'trust' ? '正在判断账号是否可靠的用户' : '有具体问题并愿意评论追问的用户',
- purpose,
- sourceEvidenceIds: [],
- viralPatternRefs: ['反常识开头', '问题-方案结构'],
- commentPainRefs: [],
- topicIds: [],
- cadenceSuggestion: role === 'growth' ? '每周 2-3 条' : '每周 1-2 条',
- observeMetrics,
- createdAt: now,
- updatedAt: now,
- };
- }
- function createTask(accountId, column, type, title, reason, growthImpactScore, now) {
- return {
- id: `task_${accountId}_${type}_${column}`,
- accountId,
- column,
- type,
- title,
- reason,
- growthImpactScore,
- relatedEvidenceIds: [],
- createdAt: now,
- updatedAt: now,
- };
- }
- function saveEntity(indexKey, prefix, entity) {
- const scopedIndex = `${indexKey}.${scope}`;
- const ids = JSON.parse(localStorage.getItem(scopedIndex) || '[]');
- localStorage.setItem(scopedIndex, JSON.stringify(Array.from(new Set([entity.id, ...ids]))));
- localStorage.setItem(`${prefix}${entity.id}.${scope}`, JSON.stringify(entity));
- }
- function clearIpOperatorData() {
- for (const key of Object.keys(localStorage)) {
- if (
- key.startsWith('tiktok.ipOperator.')
- || key === 'videoWorkflow.authSession'
- || key === 'videoWorkflow.topicPool.items'
- ) {
- localStorage.removeItem(key);
- }
- }
- }
- function createProfile(id, now) {
- return {
- id,
- userId: scope,
- name: 'E2E IP Operator Profile',
- stage: 'started',
- identity: 'Traditional industry consultant for local business owners',
- industry: 'Local service business',
- targetAudience: 'Owners who need practical IP-driven content and trust conversion',
- audiencePainPoints: ['Content looks like advertising', 'No stable topic system'],
- productsOrServices: 'IP positioning and content operation consulting',
- personalStories: ['Helped a local service team turn cases into short video topics'],
- expertise: ['Positioning', 'Topic planning', 'Script diagnosis'],
- expressionStyle: ['Direct', 'Practical'],
- boundaries: ['No exaggerated income promise'],
- goals: ['Build trust', 'Collect consultation leads'],
- questionnaire: [],
- createdAt: now,
- updatedAt: now,
- };
- }
- function createPlan({ scenario, profileId, planId, now }) {
- const topics = createTopics();
- const scripts = createScripts(topics);
- const failed = scenario === 'failed';
- const needsInput = scenario === 'needs_input';
- return {
- id: planId,
- userId: scope,
- profileId,
- status: failed ? 'failed' : needsInput ? 'needs_input' : 'ready',
- generationProgress: [
- { stage: 'intake_summary', label: 'Intake summary', status: 'completed', durationMs: 120 },
- { stage: 'diagnosis', label: 'Diagnosis', status: failed ? 'failed' : 'completed', durationMs: 120, failureCode: failed ? 'llm_malformed_json' : undefined, errorMessage: failed ? 'Malformed JSON in diagnosis stage' : undefined },
- ],
- qualityCheck: {
- overallScore: needsInput ? 64 : failed ? 40 : 88,
- positioningClarityScore: needsInput ? 60 : 90,
- audienceClarityScore: needsInput ? 62 : 86,
- evidenceSupportScore: needsInput ? 50 : 84,
- benchmarkTransferScore: needsInput ? 58 : 88,
- topicUsabilityScore: needsInput ? 65 : 91,
- passed: !needsInput && !failed,
- blockingIssues: needsInput ? ['Missing real cases and proof material'] : failed ? ['Generation failed before quality gate'] : [],
- improvementSuggestions: needsInput ? ['Add two real client cases before syncing topics'] : ['Validate the first three full scripts with real material.'],
- },
- intakeSummary: {
- identitySummary: 'Local business IP operator',
- audienceSummary: 'Traditional business owners',
- advantageSummary: 'Can translate business cases into practical content',
- businessGoalSummary: 'Build trust and collect consultation leads',
- missingInputs: needsInput ? [{ type: 'case', description: 'Add real client before-after cases', priority: 'high' }] : [],
- },
- diagnosis: failed ? undefined : {
- positioningStatement: 'A practical operator who turns local business experience into trusted owner IP content.',
- targetAudience: {
- summary: 'Owners who need short video content but do not want to become ad accounts.',
- painPoints: ['No topic system', 'Content lacks trust', 'Hard to convert'],
- },
- marketOpportunity: 'Many local businesses have real cases but lack content packaging.',
- ipType: 'Operator partner',
- personaTags: ['Practical', 'Case-driven', 'Trust-first'],
- differentiation: 'Turns benchmark hot points into positioning-matched topics instead of copying formats.',
- contentTracks: ['Positioning diagnosis', 'Trust cases', 'Conversion path'],
- monetizationPath: 'Consultation and operation service leads.',
- risks: ['Avoid exaggerated conversion promises'],
- alternativeDirections: ['Focus on one local industry first'],
- missingInputs: needsInput ? [{ type: 'case', description: 'Add real proof material', priority: 'high' }] : [],
- },
- benchmarkAnalyses: failed ? [] : [{
- benchmarkId: 'benchmark_e2e_1',
- accountName: 'Benchmark Owner Account',
- positioning: 'Direct business advice for owners',
- contentColumns: ['Pain point diagnosis', 'Case breakdown'],
- viralPatterns: ['Question hook', 'Concrete contrast'],
- scriptStructure: 'Hook - pain - viewpoint - case - action',
- conversionPath: 'Content trust to consultation',
- borrowablePoints: ['Direct question hooks', 'Case contrast'],
- nonCopyablePoints: ['Personal background', 'Exact industry claims'],
- migrationDirection: 'Use the hook logic, replace the case and judgement with this IP assets.',
- fitScore: 86,
- }],
- opportunityMap: failed ? [] : [{
- id: 'ip_opp_e2e_1',
- type: 'Trust diagnosis',
- fitScore: 90,
- reason: 'Matches audience pain and available expertise.',
- audiencePainPoint: 'Users do not trust product-only content.',
- requiredMaterials: ['Before-after case'],
- extendableTopics: ['Ad account problem', 'Trust building'],
- riskBoundary: 'No guaranteed result claim.',
- }],
- topics: failed ? [] : topics,
- scripts: failed ? [] : scripts,
- sevenDayTestPlan: failed ? [] : topics.slice(0, 7).map((topic, index) => ({
- day: index + 1,
- topicId: topic.id,
- topicTitle: topic.title,
- testGoal: 'Validate hook clarity and trust response.',
- contentType: topic.trafficLayer,
- shootingFocus: 'Talk to camera with one real scenario.',
- observeMetrics: ['Completion', 'Comment quality', 'Consultation intent'],
- reviewQuestions: ['Did users understand the pain?', 'Was the CTA too hard?'],
- })),
- thirtyDayDirection: failed ? undefined : {
- columnStructure: [
- { column: 'Positioning diagnosis', ratio: 40, role: 'Trust entry' },
- { column: 'Trust content', ratio: 35, role: 'Relationship building' },
- { column: 'Conversion path', ratio: 25, role: 'Lead collection' },
- ],
- weeklyFocus: ['Validate hooks', 'Add case material', 'Test conversion CTA', 'Review topic columns'],
- materialDirections: ['Customer doubt list', 'Before-after cases', 'Service process screenshots'],
- iterationPath: ['Keep high-comment questions', 'Rewrite weak hooks', 'Promote proven topics'],
- },
- evidenceItems: [],
- commentPainInsights: [],
- accountStrategyReports: failed ? [] : createAccountStrategyReports({ scenario, planId, now }),
- calibrationRecords: failed ? [] : createCalibrationRecords({ scenario, planId, now }),
- publishRetrospectives: failed ? [] : createPublishRetrospectives({ scenario, planId, now }),
- commentLabelSystems: [],
- contentCalendar: failed ? [] : createContentCalendar({ planId, topics, now }),
- publishPackages: failed ? [] : createPublishPackages({ planId, topics, scripts, now }),
- retrospectiveFollowUpTopics: [],
- matrixAccounts: [],
- missingInputs: needsInput ? [{ type: 'case', description: 'Add real client before-after cases', priority: 'high' }] : [],
- errorMessage: failed ? 'Generation stopped because diagnosis JSON was malformed.' : undefined,
- createdAt: now,
- updatedAt: now,
- };
- }
- function createAccountStrategyReports({ scenario, planId, now }) {
- const accountId = `ip_account_e2e_${scenario}`;
- const snapshotId = `ip_snapshot_e2e_${scenario}`;
- const workId = `work_${accountId}_7380000000`;
- return [{
- id: `ip_strategy_report_e2e_${scenario}`,
- accountId,
- snapshotId,
- positioningProposalId: `ip_positioning_proposal_e2e_${scenario}`,
- sourceMode: 'llm',
- sourceLabel: 'LLM 证据诊断',
- diagnosisSummary: {
- accountSnapshot: '账号已读取最近作品和重点评论样本。',
- currentIpGuess: '传统行业老板增长顾问',
- actualAudience: '传统行业中小企业老板',
- positioningMismatch: '需要减少泛工具话题,强化经营问题和真实案例。',
- strongestFollowReason: '能把老板经营问题拆成短视频表达和执行步骤。',
- biggestGrowthBlocker: '定位校准和发布复盘还需要持续继承。',
- highInteractionPattern: '先指出老板号误区,再给可执行步骤。',
- commentPainSummary: '用户集中关心案例怎么讲、问题怎么变选题。',
- sevenDayPriority: '围绕经营问题继续测试 3 条内容。',
- },
- evidenceRefs: [{
- id: `strategy_ref_${scenario}_work_1`,
- sourceType: 'owned_work',
- sourceId: workId,
- label: '高互动经营问题作品',
- quote: '收藏和评论集中说明用户需要可执行的老板号内容顺序。',
- reason: '支持继续测试“经营问题拆解”方向,而不是复制原作品简介。',
- confidence: 'medium',
- gaps: ['仍需发布后转化表现'],
- analyzed: true,
- }],
- directions: [],
- taskSuggestions: [],
- appliedCalibrationIds: [`calibration_${planId}_1`],
- appliedCalibrationSummary: ['已采用用户校准:减少纯工具合集,强调老板经营问题。'],
- confidenceLevel: 'medium',
- evidenceGaps: ['仍缺发布后复盘数据的长期对比'],
- dataScopeSummary: {
- workCount: 10,
- commentCount: 90,
- hasPositioningVersion: true,
- calibrationCount: 1,
- publishBindingCount: 1,
- },
- createdAt: now,
- updatedAt: now,
- }];
- }
- function createCalibrationRecords({ scenario, planId, now }) {
- const accountId = `ip_account_e2e_${scenario}`;
- return [{
- id: `calibration_${planId}_1`,
- accountId,
- reportId: `ip_strategy_report_e2e_${scenario}`,
- source: 'manual_calibration',
- accuracy: 'partial',
- targetAudienceNotes: '更偏传统行业中小企业老板。',
- positioningNotes: '减少纯工具合集,强调老板经营问题。',
- personaNotes: '懂业务的增长顾问。',
- forbiddenTopics: ['纯工具合集'],
- forbiddenExpressions: ['保姆级万能'],
- confirmedDirectionIds: [`direction_${accountId}_trust`],
- rejectedDirectionIds: [],
- rejectedEvidenceIds: [],
- operatorExperienceNotes: '历史内容里案例拆解更容易带来收藏和有效评论。',
- createdAt: now,
- updatedAt: now,
- }];
- }
- function createPublishRetrospectives({ scenario, planId, now }) {
- const accountId = `ip_account_e2e_${scenario}`;
- return [{
- id: `publish_review_${planId}_1`,
- accountId,
- publishPackageId: `${planId}_publish_1`,
- bindingId: `publish_binding_${planId}_1`,
- workId: `work_${accountId}_7380000000`,
- outcome: 'met_expectation',
- expectedGoal: ['验证信任表达是否能带来评论和关注'],
- actualSignals: ['收藏和评论达到预期', '评论集中在案例怎么讲'],
- diagnosis: '信任表达方向可继续测试,但需要增加真实案例材料。',
- nextAction: 'continue',
- calibrationRecordId: `calibration_${planId}_1`,
- createdAt: now,
- updatedAt: now,
- }];
- }
- function createTopics() {
- return Array.from({ length: 12 }, (_, index) => ({
- id: `ip_topic_e2e_${index + 1}`,
- title: index === 0 ? 'Why owner IP should not look like an ad account' : `Owner IP trust topic ${index + 1}`,
- column: index % 3 === 0 ? 'Positioning diagnosis' : index % 3 === 1 ? 'Trust content' : 'Conversion path',
- trafficLayer: index < 4 ? 'vertical' : index < 8 ? 'conversion' : 'broad',
- contentGoal: 'Help the target user understand the trust problem and next action',
- priority: index < 4 ? 'high' : index < 9 ? 'medium' : 'low',
- qualityLevel: index < 4 ? 'make_now' : index < 8 ? 'polish_first' : 'needs_material',
- fitReason: 'Derived from benchmark hooks and adapted to this IP positioning',
- source: 'Benchmark migration',
- requiredMaterials: ['Real case', 'Service before-after comparison'],
- scriptStatus: index < 3 ? 'full' : 'outline',
- riskNote: 'Avoid hard selling and exaggerated claims',
- }));
- }
- function createScripts(topics) {
- return topics.map((topic, index) => ({
- id: `ip_script_e2e_${index + 1}`,
- topicId: topic.id,
- type: index < 3 ? 'full' : 'outline',
- title: topic.title,
- hook: 'If your account keeps introducing products, users will treat it as advertising.',
- painPoint: 'The content has information but no trust context.',
- viewpoint: 'Owner IP must first transmit judgement, not product inventory.',
- caseOrMethod: 'Use one benchmark hook, one real scenario, and one conversion boundary.',
- goldenSentence: 'Trust comes before transaction.',
- closingCta: 'Save this and check whether your next video is selling or building trust.',
- fullScript: index < 3
- ? 'Opening: many owner accounts look professional but still fail to convert. Middle: users do not reject business, they reject content that only pushes products. Example: when a local service owner changed from product introductions to decision-making stories, consultation quality improved. Ending: the next video should answer one real customer doubt before mentioning the product.'
- : '',
- shootingTips: ['Talk to camera', 'Use one service case'],
- subtitleHighlights: ['Do not sell first', 'Show judgement first'],
- replaceableSlots: ['Insert local case'],
- riskNote: 'Do not claim guaranteed conversion.',
- }));
- }
- function createContentCalendar({ planId, topics, now }) {
- return [{
- id: `${planId}_calendar_1`,
- planId,
- topicId: topics[0]?.id,
- scriptId: 'ip_script_e2e_1',
- date: now.slice(0, 10),
- timeSlot: '19:30',
- platform: 'douyin',
- accountName: '林川老板增长笔记',
- pillar: '信任建立内容',
- title: '老板 IP 如何避免成为广告号',
- contentGoal: '验证信任表达是否能带来评论和关注',
- materialNeeds: ['真实账号截图', '客户问题截图'],
- evidenceItemIds: [],
- painInsightIds: [],
- status: 'ready_for_review',
- publishPackageId: `${planId}_publish_1`,
- createdAt: now,
- updatedAt: now,
- }];
- }
- function createPublishPackages({ planId, topics, scripts, now }) {
- return [{
- id: `${planId}_publish_1`,
- calendarItemId: `${planId}_calendar_1`,
- topicId: topics[0]?.id || '',
- scriptId: scripts[0]?.id || '',
- platform: 'douyin',
- accountName: '林川老板增长笔记',
- titleOptions: ['老板 IP 如何避免成为广告号', '为什么你的老板号越发越不被信任'],
- coverSuggestions: ['左侧:广告号,右侧:信任号', '老板别再只讲产品'],
- caption: '老板 IP 如何避免成为广告号,先讲信任再讲产品。',
- scriptText: scripts[0]?.fullScript || scripts[0]?.hook || '',
- hashtags: ['老板IP', '短视频运营', '账号定位'],
- materialFiles: [
- { id: 'material_1', name: '账号主页截图', status: 'missing' },
- { id: 'material_2', name: '客户问题截图', status: 'missing' },
- ],
- scheduleSuggestion: '今天 19:30 人工发布,发布后回填作品链接。',
- isOriginal: true,
- isDraft: true,
- materialChecklist: [
- { id: 'material_check_1', label: '确认案例素材可公开', checked: false, required: true },
- { id: 'material_check_2', label: '检查封面文字不夸张', checked: true, required: true },
- ],
- riskChecklist: [
- { id: 'risk_check_1', label: '不承诺具体收益', checked: true, required: true },
- { id: 'risk_check_2', label: '不使用诱导互动话术', checked: true, required: true },
- ],
- platformVariants: {
- xiaohongshu: {
- title: '老板 IP 别再拍成广告号',
- coverText: '先讲信任,再讲产品',
- imageNoteOutline: ['问题', '原因', '改法'],
- hashtags: ['老板IP', '内容运营'],
- },
- douyin: {
- firstThreeSecondsHook: '为什么你的老板号越发越像广告?',
- shotList: ['正面口播', '账号截图', '标题对比'],
- spokenScript: scripts[0]?.fullScript || '',
- subtitleEmphasis: ['别急着卖产品', '先给关注理由'],
- },
- wechat: {
- calmerTitle: '老板内容如何建立信任',
- intro: '很多老板号的问题不是不专业,而是太像广告。',
- body: '先讲清楚用户问题,再放服务能力。',
- endingCta: '欢迎把你的账号问题发来一起看。',
- },
- },
- evidenceItemIds: [],
- painInsightIds: [],
- status: 'ready_for_review',
- createdAt: now,
- updatedAt: now,
- }];
- }
- window.__seedIpOperatorScenario = seedIpOperatorScenario;
- })();
|