feat: ship self-hosted KOC LOOP workflows
This commit is contained in:
106
lib/mvp-db.ts
106
lib/mvp-db.ts
@@ -1,26 +1,80 @@
|
||||
import { env } from "cloudflare:workers";
|
||||
import { type DatabaseClient, getDatabase } from "./database";
|
||||
import { getObjectStore } from "./object-store";
|
||||
import { getRuntimeEnv, isEnabled } from "./runtime-env";
|
||||
import feishuSnapshot from "./feishu-source-snapshot.json";
|
||||
|
||||
type D1ResultRow = Record<string, unknown>;
|
||||
|
||||
export function getRawDb(): D1Database {
|
||||
const database = (env as unknown as { DB?: D1Database }).DB;
|
||||
if (!database) {
|
||||
throw new Error("数据库尚未连接");
|
||||
}
|
||||
return database;
|
||||
export function getRawDb(): DatabaseClient {
|
||||
return getDatabase();
|
||||
}
|
||||
|
||||
export function getUploadBucket(): R2Bucket {
|
||||
const bucket = (env as unknown as { UPLOADS?: R2Bucket }).UPLOADS;
|
||||
if (!bucket) {
|
||||
throw new Error("文件存储尚未连接");
|
||||
}
|
||||
return bucket;
|
||||
export function getUploadBucket() {
|
||||
return getObjectStore();
|
||||
}
|
||||
|
||||
export async function ensureSchema(database?: D1Database) {
|
||||
export async function ensureSchema(database?: DatabaseClient) {
|
||||
const db = database ?? getRawDb();
|
||||
{
|
||||
await db.prepare("SELECT id FROM tasks LIMIT 1").all();
|
||||
|
||||
const tasksWithoutShare = await db
|
||||
.prepare("SELECT id FROM tasks WHERE share_token IS NULL OR share_token = ''")
|
||||
.all<{ id: string }>();
|
||||
for (const task of tasksWithoutShare.results) {
|
||||
await db
|
||||
.prepare("UPDATE tasks SET share_token = ? WHERE id = ?")
|
||||
.bind(crypto.randomUUID().replaceAll("-", ""), task.id)
|
||||
.run();
|
||||
}
|
||||
|
||||
await db
|
||||
.prepare(
|
||||
`UPDATE distributions
|
||||
SET latest_likes = COALESCE(d7_likes, d5_likes, d2_likes),
|
||||
latest_comments = COALESCE(d7_comments, d5_comments, d2_comments),
|
||||
latest_collects = COALESCE(d7_collects, d5_collects, d2_collects),
|
||||
collection_status = 'success',
|
||||
collection_status_description = '历史采集数据已迁移',
|
||||
collection_updated_at = updated_at,
|
||||
last_collection_day = CASE
|
||||
WHEN d7_likes IS NOT NULL THEN 7
|
||||
WHEN d5_likes IS NOT NULL THEN 5
|
||||
WHEN d2_likes IS NOT NULL THEN 2
|
||||
ELSE NULL
|
||||
END
|
||||
WHERE latest_likes IS NULL
|
||||
AND COALESCE(d7_likes, d5_likes, d2_likes) IS NOT NULL`,
|
||||
)
|
||||
.run();
|
||||
|
||||
const tasksNeedingImages = await db
|
||||
.prepare(
|
||||
`SELECT DISTINCT t.id
|
||||
FROM tasks t
|
||||
JOIN contents c ON c.task_id = t.id
|
||||
WHERE t.source_sheet_id = ?
|
||||
AND (c.image_assets IS NULL OR c.image_assets = '' OR c.image_assets = '[]')`,
|
||||
)
|
||||
.bind(feishuSnapshot.sheetId)
|
||||
.all<{ id: string }>();
|
||||
for (const task of tasksNeedingImages.results) {
|
||||
const updates = feishuSnapshot.rows
|
||||
.filter((row) => row.images.length > 0)
|
||||
.map((row) =>
|
||||
db
|
||||
.prepare(
|
||||
`UPDATE contents SET image_assets = ?
|
||||
WHERE task_id = ? AND source_row = ?
|
||||
AND (image_assets IS NULL OR image_assets = '' OR image_assets = '[]')`,
|
||||
)
|
||||
.bind(JSON.stringify(row.images), task.id, row.sourceRow),
|
||||
);
|
||||
if (updates.length > 0) await db.batch(updates);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
const statements = [
|
||||
`CREATE TABLE IF NOT EXISTS partners (
|
||||
id TEXT PRIMARY KEY,
|
||||
@@ -39,6 +93,7 @@ export async function ensureSchema(database?: D1Database) {
|
||||
claimed_quantity INTEGER NOT NULL DEFAULT 0,
|
||||
due_at TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
task_type TEXT NOT NULL DEFAULT 'content_publish',
|
||||
source_url TEXT NOT NULL DEFAULT '',
|
||||
source_sheet_id TEXT NOT NULL DEFAULT '',
|
||||
source_sheet_name TEXT NOT NULL DEFAULT '',
|
||||
@@ -71,6 +126,7 @@ export async function ensureSchema(database?: D1Database) {
|
||||
followers INTEGER NOT NULL DEFAULT 0,
|
||||
post_count INTEGER NOT NULL DEFAULT 0,
|
||||
avg_views INTEGER NOT NULL DEFAULT 0,
|
||||
cooperation_source TEXT NOT NULL DEFAULT '',
|
||||
first_seen_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
last_seen_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
@@ -109,6 +165,8 @@ export async function ensureSchema(database?: D1Database) {
|
||||
publish_url TEXT,
|
||||
publish_time TEXT,
|
||||
publish_screenshot_key TEXT,
|
||||
result_screenshot_key TEXT,
|
||||
result_submitted_at TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'claimed',
|
||||
claimed_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
screenshot_key TEXT,
|
||||
@@ -192,6 +250,11 @@ export async function ensureSchema(database?: D1Database) {
|
||||
};
|
||||
|
||||
await ensureColumn("tasks", "source_url", "source_url TEXT NOT NULL DEFAULT ''");
|
||||
await ensureColumn(
|
||||
"tasks",
|
||||
"task_type",
|
||||
"task_type TEXT NOT NULL DEFAULT 'content_publish'",
|
||||
);
|
||||
await ensureColumn(
|
||||
"tasks",
|
||||
"source_sheet_id",
|
||||
@@ -241,6 +304,16 @@ export async function ensureSchema(database?: D1Database) {
|
||||
"publish_screenshot_key",
|
||||
"publish_screenshot_key TEXT",
|
||||
);
|
||||
await ensureColumn(
|
||||
"distributions",
|
||||
"result_screenshot_key",
|
||||
"result_screenshot_key TEXT",
|
||||
);
|
||||
await ensureColumn(
|
||||
"distributions",
|
||||
"result_submitted_at",
|
||||
"result_submitted_at TEXT",
|
||||
);
|
||||
await ensureColumn(
|
||||
"distributions",
|
||||
"latest_likes",
|
||||
@@ -399,6 +472,7 @@ export async function ensureSchema(database?: D1Database) {
|
||||
}
|
||||
|
||||
export async function seedIfEmpty() {
|
||||
if (!isEnabled(getRuntimeEnv().SEED_DEMO_DATA)) return;
|
||||
const db = getRawDb();
|
||||
const row = await db.prepare("SELECT COUNT(*) AS count FROM tasks").first<{
|
||||
count: number;
|
||||
@@ -733,6 +807,7 @@ export async function getDashboardData() {
|
||||
a.platform AS account_platform,
|
||||
t.name AS task_name,
|
||||
t.brand AS task_brand,
|
||||
t.task_type AS task_type,
|
||||
t.due_at AS due_at
|
||||
FROM distributions d
|
||||
JOIN contents c ON c.id = d.content_id
|
||||
@@ -749,8 +824,7 @@ export async function getDashboardData() {
|
||||
tasks: tasksResult.results as D1ResultRow[],
|
||||
accounts: accountsResult.results as D1ResultRow[],
|
||||
distributions: distributionsResult.results as D1ResultRow[],
|
||||
portal_url:
|
||||
(env as unknown as { KOC_PORTAL_URL?: string }).KOC_PORTAL_URL ?? "",
|
||||
portal_url: getRuntimeEnv().KOC_PORTAL_URL ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user