rm initDB code
This commit is contained in:
@@ -2431,15 +2431,6 @@ export const openAPISpec = {
|
|||||||
type: 'integer',
|
type: 'integer',
|
||||||
default: 0
|
default: 0
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'useMockData',
|
|
||||||
in: 'query',
|
|
||||||
description: 'Force use of mock data (useful for testing and UI development)',
|
|
||||||
schema: {
|
|
||||||
type: 'boolean',
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
responses: {
|
responses: {
|
||||||
|
|||||||
@@ -3,497 +3,6 @@ import clickhouse from './clickhouse';
|
|||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化 Supabase (PostgreSQL) 数据库表
|
|
||||||
*/
|
|
||||||
export const initSupabaseTables = async () => {
|
|
||||||
try {
|
|
||||||
console.log('开始初始化 Supabase 数据表...');
|
|
||||||
|
|
||||||
// 创建用户扩展表
|
|
||||||
await supabase.rpc('create_user_profiles_if_not_exists');
|
|
||||||
|
|
||||||
// 创建项目表
|
|
||||||
await supabase.rpc('create_projects_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建网红(影响者)表
|
|
||||||
await supabase.rpc('create_influencers_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建项目-网红关联表
|
|
||||||
await supabase.rpc('create_project_influencers_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建帖子表
|
|
||||||
await supabase.rpc('create_posts_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建评论表
|
|
||||||
await supabase.rpc('create_comments_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建项目评论表
|
|
||||||
await supabase.rpc('create_project_comments_table_if_not_exists');
|
|
||||||
|
|
||||||
console.log('Supabase 数据表初始化完成');
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('初始化 Supabase 数据表失败:', error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化 ClickHouse 数据库表
|
|
||||||
*/
|
|
||||||
export const initClickHouseTables = async () => {
|
|
||||||
try {
|
|
||||||
console.log('开始初始化 ClickHouse 数据表...');
|
|
||||||
|
|
||||||
// Create events table for general analytics tracking
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
CREATE TABLE IF NOT EXISTS promote.events (
|
|
||||||
event_id UUID DEFAULT generateUUIDv4(),
|
|
||||||
event_type String,
|
|
||||||
influencer_id String DEFAULT '',
|
|
||||||
post_id String DEFAULT '',
|
|
||||||
project_id String DEFAULT '',
|
|
||||||
timestamp DateTime64(3) DEFAULT now64(3),
|
|
||||||
metric_name String DEFAULT '',
|
|
||||||
metric_value Int64 DEFAULT 0,
|
|
||||||
metric_total Int64 DEFAULT 0,
|
|
||||||
recorded_by String DEFAULT '',
|
|
||||||
extra_data String DEFAULT ''
|
|
||||||
)
|
|
||||||
ENGINE = MergeTree()
|
|
||||||
ORDER BY (event_type, influencer_id, post_id, timestamp)
|
|
||||||
`
|
|
||||||
});
|
|
||||||
console.log(' - Created events table');
|
|
||||||
|
|
||||||
// 创建事件表
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
CREATE TABLE IF NOT EXISTS events (
|
|
||||||
event_id UUID DEFAULT generateUUIDv4(),
|
|
||||||
project_id UUID,
|
|
||||||
influencer_id UUID,
|
|
||||||
post_id UUID NULL,
|
|
||||||
platform String,
|
|
||||||
event_type Enum(
|
|
||||||
'follower_change' = 1,
|
|
||||||
'post_like_change' = 2,
|
|
||||||
'post_view_change' = 3,
|
|
||||||
'click' = 4,
|
|
||||||
'comment' = 5,
|
|
||||||
'share' = 6,
|
|
||||||
'project_comment' = 7
|
|
||||||
),
|
|
||||||
metric_value Int64,
|
|
||||||
event_metadata String,
|
|
||||||
timestamp DateTime DEFAULT now()
|
|
||||||
) ENGINE = MergeTree()
|
|
||||||
PARTITION BY toYYYYMM(timestamp)
|
|
||||||
ORDER BY (platform, influencer_id, post_id, event_type, timestamp)
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
// 创建统计视图 - 按天统计
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
CREATE MATERIALIZED VIEW IF NOT EXISTS daily_stats
|
|
||||||
ENGINE = SummingMergeTree()
|
|
||||||
PARTITION BY toYYYYMM(date)
|
|
||||||
ORDER BY (date, platform, influencer_id, event_type)
|
|
||||||
AS SELECT
|
|
||||||
toDate(timestamp) AS date,
|
|
||||||
platform,
|
|
||||||
influencer_id,
|
|
||||||
event_type,
|
|
||||||
SUM(metric_value) AS total_value,
|
|
||||||
COUNT(*) AS event_count
|
|
||||||
FROM events
|
|
||||||
GROUP BY date, platform, influencer_id, event_type
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
// 创建统计视图 - 按月统计
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
CREATE MATERIALIZED VIEW IF NOT EXISTS monthly_stats
|
|
||||||
ENGINE = SummingMergeTree()
|
|
||||||
ORDER BY (month, platform, influencer_id, event_type)
|
|
||||||
AS SELECT
|
|
||||||
toStartOfMonth(timestamp) AS month,
|
|
||||||
platform,
|
|
||||||
influencer_id,
|
|
||||||
event_type,
|
|
||||||
SUM(metric_value) AS total_value,
|
|
||||||
COUNT(*) AS event_count
|
|
||||||
FROM events
|
|
||||||
GROUP BY month, platform, influencer_id, event_type
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
// 创建帖子互动统计视图
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
CREATE MATERIALIZED VIEW IF NOT EXISTS post_interaction_stats
|
|
||||||
ENGINE = SummingMergeTree()
|
|
||||||
ORDER BY (post_id, event_type, date)
|
|
||||||
AS SELECT
|
|
||||||
post_id,
|
|
||||||
event_type,
|
|
||||||
toDate(timestamp) AS date,
|
|
||||||
SUM(metric_value) AS value,
|
|
||||||
COUNT(*) AS count
|
|
||||||
FROM events
|
|
||||||
WHERE post_id IS NOT NULL
|
|
||||||
GROUP BY post_id, event_type, date
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
// 创建项目互动统计视图
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
CREATE MATERIALIZED VIEW IF NOT EXISTS project_interaction_stats
|
|
||||||
ENGINE = SummingMergeTree()
|
|
||||||
ORDER BY (project_id, event_type, date)
|
|
||||||
AS SELECT
|
|
||||||
project_id,
|
|
||||||
event_type,
|
|
||||||
toDate(timestamp) AS date,
|
|
||||||
SUM(metric_value) AS value,
|
|
||||||
COUNT(*) AS count
|
|
||||||
FROM events
|
|
||||||
WHERE project_id IS NOT NULL AND event_type = 'project_comment'
|
|
||||||
GROUP BY project_id, event_type, date
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('ClickHouse 数据表初始化完成');
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('初始化 ClickHouse 数据表失败:', error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化 Supabase 存储函数
|
|
||||||
*/
|
|
||||||
export const initSupabaseFunctions = async () => {
|
|
||||||
try {
|
|
||||||
console.log('开始初始化 Supabase 存储过程...');
|
|
||||||
|
|
||||||
// 创建用户简档表的存储过程
|
|
||||||
await supabase.rpc('create_function_create_user_profiles_if_not_exists');
|
|
||||||
|
|
||||||
// 创建项目表的存储过程
|
|
||||||
await supabase.rpc('create_function_create_projects_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建网红表的存储过程
|
|
||||||
await supabase.rpc('create_function_create_influencers_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建项目-网红关联表的存储过程
|
|
||||||
await supabase.rpc('create_function_create_project_influencers_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建帖子表的存储过程
|
|
||||||
await supabase.rpc('create_function_create_posts_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建评论表的存储过程
|
|
||||||
await supabase.rpc('create_function_create_comments_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建项目评论表的存储过程
|
|
||||||
await supabase.rpc('create_function_create_project_comments_table_if_not_exists');
|
|
||||||
|
|
||||||
// 创建评论相关的SQL函数
|
|
||||||
console.log('创建评论相关的SQL函数...');
|
|
||||||
const commentsSQL = await fs.readFile(
|
|
||||||
path.join(__dirname, 'supabase-comments-functions.sql'),
|
|
||||||
'utf8'
|
|
||||||
);
|
|
||||||
|
|
||||||
// 使用Supabase执行SQL
|
|
||||||
const { error: commentsFunctionsError } = await supabase.rpc(
|
|
||||||
'pgclient_execute',
|
|
||||||
{ query: commentsSQL }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (commentsFunctionsError) {
|
|
||||||
console.error('创建评论SQL函数失败:', commentsFunctionsError);
|
|
||||||
} else {
|
|
||||||
console.log('评论SQL函数创建成功');
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Supabase 存储过程初始化完成');
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('初始化 Supabase 存储过程失败:', error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建测试数据
|
|
||||||
*/
|
|
||||||
export const createSampleData = async () => {
|
|
||||||
try {
|
|
||||||
console.log('开始创建测试数据...');
|
|
||||||
|
|
||||||
// 创建测试用户
|
|
||||||
const { data: user, error: userError } = await supabase.auth.admin.createUser({
|
|
||||||
email: 'test@example.com',
|
|
||||||
password: 'password123',
|
|
||||||
user_metadata: {
|
|
||||||
full_name: '测试用户'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (userError) {
|
|
||||||
console.error('创建测试用户失败:', userError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建测试项目
|
|
||||||
const { data: project, error: projectError } = await supabase
|
|
||||||
.from('projects')
|
|
||||||
.insert({
|
|
||||||
name: '测试营销活动',
|
|
||||||
description: '这是一个测试营销活动',
|
|
||||||
created_by: user.user.id
|
|
||||||
})
|
|
||||||
.select()
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (projectError) {
|
|
||||||
console.error('创建测试项目失败:', projectError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建项目评论
|
|
||||||
await supabase
|
|
||||||
.from('project_comments')
|
|
||||||
.insert([
|
|
||||||
{
|
|
||||||
project_id: project.id,
|
|
||||||
user_id: user.user.id,
|
|
||||||
content: '这是对项目的一条测试评论',
|
|
||||||
sentiment_score: 0.8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
project_id: project.id,
|
|
||||||
user_id: user.user.id,
|
|
||||||
content: '这个项目很有前景',
|
|
||||||
sentiment_score: 0.9
|
|
||||||
},
|
|
||||||
{
|
|
||||||
project_id: project.id,
|
|
||||||
user_id: user.user.id,
|
|
||||||
content: '需要关注这个项目的进展',
|
|
||||||
sentiment_score: 0.7
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 创建测试网红
|
|
||||||
const platforms = ['youtube', 'instagram', 'tiktok'];
|
|
||||||
const influencers = [];
|
|
||||||
|
|
||||||
for (let i = 1; i <= 10; i++) {
|
|
||||||
const platform = platforms[Math.floor(Math.random() * platforms.length)];
|
|
||||||
|
|
||||||
const { data: influencer, error: influencerError } = await supabase
|
|
||||||
.from('influencers')
|
|
||||||
.insert({
|
|
||||||
name: `测试网红 ${i}`,
|
|
||||||
platform,
|
|
||||||
profile_url: `https://${platform}.com/user${i}`,
|
|
||||||
external_id: `user_${platform}_${i}`,
|
|
||||||
followers_count: Math.floor(Math.random() * 1000000) + 1000,
|
|
||||||
video_count: Math.floor(Math.random() * 500) + 10
|
|
||||||
})
|
|
||||||
.select()
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (influencerError) {
|
|
||||||
console.error(`创建测试网红 ${i} 失败:`, influencerError);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
influencers.push(influencer);
|
|
||||||
|
|
||||||
// 将网红添加到项目
|
|
||||||
await supabase
|
|
||||||
.from('project_influencers')
|
|
||||||
.insert({
|
|
||||||
project_id: project.id,
|
|
||||||
influencer_id: influencer.influencer_id
|
|
||||||
});
|
|
||||||
|
|
||||||
// 为每个网红创建 3-5 个帖子
|
|
||||||
const postCount = Math.floor(Math.random() * 3) + 3;
|
|
||||||
|
|
||||||
for (let j = 1; j <= postCount; j++) {
|
|
||||||
const { data: post, error: postError } = await supabase
|
|
||||||
.from('posts')
|
|
||||||
.insert({
|
|
||||||
influencer_id: influencer.influencer_id,
|
|
||||||
platform,
|
|
||||||
post_url: `https://${platform}.com/user${i}/post${j}`,
|
|
||||||
title: `测试帖子 ${j} - 由 ${influencer.name} 发布`,
|
|
||||||
description: `这是一个测试帖子的描述 ${j}`,
|
|
||||||
published_at: new Date(Date.now() - Math.floor(Math.random() * 30) * 24 * 60 * 60 * 1000).toISOString()
|
|
||||||
})
|
|
||||||
.select()
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (postError) {
|
|
||||||
console.error(`创建测试帖子 ${j} 失败:`, postError);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 为每个帖子创建 2-10 个评论
|
|
||||||
const commentCount = Math.floor(Math.random() * 9) + 2;
|
|
||||||
|
|
||||||
for (let k = 1; k <= commentCount; k++) {
|
|
||||||
await supabase
|
|
||||||
.from('comments')
|
|
||||||
.insert({
|
|
||||||
post_id: post.post_id,
|
|
||||||
user_id: user.user.id,
|
|
||||||
content: `这是对帖子 ${post.title} 的测试评论 ${k}`,
|
|
||||||
sentiment_score: (Math.random() * 2 - 1) // -1 到 1 之间的随机数
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建 ClickHouse 事件数据
|
|
||||||
// 粉丝变化事件
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
INSERT INTO events (
|
|
||||||
project_id,
|
|
||||||
influencer_id,
|
|
||||||
platform,
|
|
||||||
event_type,
|
|
||||||
metric_value,
|
|
||||||
event_metadata
|
|
||||||
) VALUES (?, ?, ?, 'follower_change', ?, ?)
|
|
||||||
`,
|
|
||||||
values: [
|
|
||||||
project.id,
|
|
||||||
influencer.influencer_id,
|
|
||||||
platform,
|
|
||||||
Math.floor(Math.random() * 1000) - 200, // -200 到 800 之间的随机数
|
|
||||||
JSON.stringify({ source: 'api_crawler' })
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// 帖子点赞变化事件
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
INSERT INTO events (
|
|
||||||
project_id,
|
|
||||||
influencer_id,
|
|
||||||
post_id,
|
|
||||||
platform,
|
|
||||||
event_type,
|
|
||||||
metric_value,
|
|
||||||
event_metadata
|
|
||||||
) VALUES (?, ?, ?, ?, 'post_like_change', ?, ?)
|
|
||||||
`,
|
|
||||||
values: [
|
|
||||||
project.id,
|
|
||||||
influencer.influencer_id,
|
|
||||||
post.post_id,
|
|
||||||
platform,
|
|
||||||
Math.floor(Math.random() * 500) + 10, // 10 到 510 之间的随机数
|
|
||||||
JSON.stringify({ source: 'api_crawler' })
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// 帖子观看数变化事件
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
INSERT INTO events (
|
|
||||||
project_id,
|
|
||||||
influencer_id,
|
|
||||||
post_id,
|
|
||||||
platform,
|
|
||||||
event_type,
|
|
||||||
metric_value,
|
|
||||||
event_metadata
|
|
||||||
) VALUES (?, ?, ?, ?, 'post_view_change', ?, ?)
|
|
||||||
`,
|
|
||||||
values: [
|
|
||||||
project.id,
|
|
||||||
influencer.influencer_id,
|
|
||||||
post.post_id,
|
|
||||||
platform,
|
|
||||||
Math.floor(Math.random() * 5000) + 100, // 100 到 5100 之间的随机数
|
|
||||||
JSON.stringify({ source: 'api_crawler' })
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// 互动事件
|
|
||||||
const interactionTypes = ['click', 'comment', 'share'];
|
|
||||||
const interactionType = interactionTypes[Math.floor(Math.random() * interactionTypes.length)];
|
|
||||||
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
INSERT INTO events (
|
|
||||||
project_id,
|
|
||||||
influencer_id,
|
|
||||||
post_id,
|
|
||||||
platform,
|
|
||||||
event_type,
|
|
||||||
metric_value,
|
|
||||||
event_metadata
|
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
||||||
`,
|
|
||||||
values: [
|
|
||||||
project.id,
|
|
||||||
influencer.influencer_id,
|
|
||||||
post.post_id,
|
|
||||||
platform,
|
|
||||||
interactionType,
|
|
||||||
1,
|
|
||||||
JSON.stringify({
|
|
||||||
ip: '192.168.1.' + Math.floor(Math.random() * 255),
|
|
||||||
user_agent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建项目评论事件
|
|
||||||
for (let i = 1; i <= 5; i++) {
|
|
||||||
await clickhouse.query({
|
|
||||||
query: `
|
|
||||||
INSERT INTO events (
|
|
||||||
project_id,
|
|
||||||
event_type,
|
|
||||||
metric_value,
|
|
||||||
event_metadata
|
|
||||||
) VALUES (?, 'project_comment', ?, ?)
|
|
||||||
`,
|
|
||||||
values: [
|
|
||||||
project.id,
|
|
||||||
1,
|
|
||||||
JSON.stringify({
|
|
||||||
user_id: user.user.id,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
comment: `项目评论事件 ${i}`
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('测试数据创建完成');
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('创建测试数据失败:', error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查数据库连接
|
* 检查数据库连接
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user