2026-07-30 12:06:41 +08:00
|
|
|
import { sql } from "drizzle-orm";
|
|
|
|
|
import {
|
2026-08-11 23:07:26 +08:00
|
|
|
datetime,
|
2026-07-30 12:06:41 +08:00
|
|
|
index,
|
2026-08-11 23:07:26 +08:00
|
|
|
int,
|
|
|
|
|
mysqlTable,
|
2026-07-30 12:06:41 +08:00
|
|
|
text,
|
|
|
|
|
uniqueIndex,
|
2026-08-11 23:07:26 +08:00
|
|
|
varchar,
|
|
|
|
|
} from "drizzle-orm/mysql-core";
|
2026-07-30 12:06:41 +08:00
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
const timestamp = (name: string) =>
|
|
|
|
|
datetime(name, { mode: "string", fsp: 3 })
|
|
|
|
|
.notNull()
|
|
|
|
|
.default(sql`CURRENT_TIMESTAMP(3)`);
|
|
|
|
|
|
|
|
|
|
export const partners = mysqlTable("partners", {
|
|
|
|
|
id: varchar("id", { length: 64 }).primaryKey(),
|
|
|
|
|
name: varchar("name", { length: 255 }).notNull(),
|
|
|
|
|
wecomName: varchar("wecom_name", { length: 255 }).notNull(),
|
|
|
|
|
owner: varchar("owner", { length: 255 }).notNull().default("运营组"),
|
2026-08-18 16:39:38 +08:00
|
|
|
wecomExternalUserId: varchar("wecom_external_user_id", { length: 128 }),
|
2026-08-11 23:07:26 +08:00
|
|
|
claimedTotal: int("claimed_total").notNull().default(0),
|
|
|
|
|
completedTotal: int("completed_total").notNull().default(0),
|
|
|
|
|
createdAt: timestamp("created_at"),
|
2026-07-30 12:06:41 +08:00
|
|
|
});
|
|
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
export const tasks = mysqlTable(
|
2026-07-30 12:06:41 +08:00
|
|
|
"tasks",
|
|
|
|
|
{
|
2026-08-11 23:07:26 +08:00
|
|
|
id: varchar("id", { length: 64 }).primaryKey(),
|
|
|
|
|
name: varchar("name", { length: 255 }).notNull(),
|
|
|
|
|
brand: varchar("brand", { length: 255 }).notNull(),
|
|
|
|
|
quantity: int("quantity").notNull(),
|
|
|
|
|
claimedQuantity: int("claimed_quantity").notNull().default(0),
|
|
|
|
|
dueAt: varchar("due_at", { length: 32 }).notNull(),
|
|
|
|
|
status: varchar("status", { length: 32 }).notNull().default("active"),
|
|
|
|
|
taskType: varchar("task_type", { length: 32 })
|
|
|
|
|
.notNull()
|
|
|
|
|
.default("content_publish"),
|
|
|
|
|
sourceUrl: text("source_url").notNull(),
|
|
|
|
|
sourceSheetId: varchar("source_sheet_id", { length: 255 }).notNull().default(""),
|
|
|
|
|
sourceSheetName: varchar("source_sheet_name", { length: 255 }).notNull().default(""),
|
|
|
|
|
sourceSyncedAt: datetime("source_synced_at", { mode: "string", fsp: 3 }),
|
|
|
|
|
shareToken: varchar("share_token", { length: 128 }),
|
|
|
|
|
collectionStartDate: varchar("collection_start_date", { length: 32 }),
|
|
|
|
|
collectionDays: text("collection_days").notNull(),
|
|
|
|
|
collectionScheduleUpdatedAt: datetime("collection_schedule_updated_at", {
|
|
|
|
|
mode: "string",
|
|
|
|
|
fsp: 3,
|
|
|
|
|
}),
|
|
|
|
|
createdAt: timestamp("created_at"),
|
2026-07-30 12:06:41 +08:00
|
|
|
},
|
|
|
|
|
(table) => [uniqueIndex("tasks_share_token_idx").on(table.shareToken)],
|
|
|
|
|
);
|
|
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
export const contents = mysqlTable("contents", {
|
|
|
|
|
id: varchar("id", { length: 64 }).primaryKey(),
|
|
|
|
|
taskId: varchar("task_id", { length: 64 }).notNull(),
|
2026-07-30 12:06:41 +08:00
|
|
|
title: text("title").notNull(),
|
2026-08-11 23:07:26 +08:00
|
|
|
body: text("body").notNull(),
|
|
|
|
|
imageAssets: text("image_assets").notNull(),
|
|
|
|
|
status: varchar("status", { length: 32 }).notNull().default("available"),
|
|
|
|
|
source: varchar("source", { length: 255 }).notNull().default("飞书内容表"),
|
|
|
|
|
sourceRow: int("source_row"),
|
|
|
|
|
createdAt: timestamp("created_at"),
|
2026-07-30 12:06:41 +08:00
|
|
|
});
|
|
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
export const accounts = mysqlTable(
|
2026-07-30 12:06:41 +08:00
|
|
|
"accounts",
|
|
|
|
|
{
|
2026-08-11 23:07:26 +08:00
|
|
|
id: varchar("id", { length: 64 }).primaryKey(),
|
|
|
|
|
platform: varchar("platform", { length: 32 }).notNull().default("小红书"),
|
|
|
|
|
platformUid: varchar("platform_uid", { length: 255 }).notNull(),
|
|
|
|
|
publicAccountId: varchar("public_account_id", { length: 255 }).notNull().default(""),
|
|
|
|
|
nickname: varchar("nickname", { length: 255 }).notNull(),
|
|
|
|
|
profileUrl: text("profile_url").notNull(),
|
|
|
|
|
ipLocation: varchar("ip_location", { length: 255 }).notNull().default("待识别"),
|
|
|
|
|
followers: int("followers").notNull().default(0),
|
|
|
|
|
postCount: int("post_count").notNull().default(0),
|
|
|
|
|
avgViews: int("avg_views").notNull().default(0),
|
|
|
|
|
cooperationSource: varchar("cooperation_source", { length: 500 })
|
|
|
|
|
.notNull()
|
|
|
|
|
.default(""),
|
2026-08-18 16:39:38 +08:00
|
|
|
tags: varchar("tags", { length: 500 }).notNull().default(""),
|
2026-08-11 23:07:26 +08:00
|
|
|
firstSeenAt: timestamp("first_seen_at"),
|
|
|
|
|
lastSeenAt: timestamp("last_seen_at"),
|
2026-07-30 12:06:41 +08:00
|
|
|
},
|
|
|
|
|
(table) => [
|
2026-08-11 23:07:26 +08:00
|
|
|
uniqueIndex("accounts_platform_uid_idx").on(table.platform, table.platformUid),
|
2026-07-30 12:06:41 +08:00
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
export const claims = mysqlTable(
|
2026-07-30 12:06:41 +08:00
|
|
|
"claims",
|
|
|
|
|
{
|
2026-08-11 23:07:26 +08:00
|
|
|
id: varchar("id", { length: 64 }).primaryKey(),
|
|
|
|
|
taskId: varchar("task_id", { length: 64 }).notNull(),
|
|
|
|
|
partnerId: varchar("partner_id", { length: 64 }).notNull(),
|
|
|
|
|
claimantName: varchar("claimant_name", { length: 255 }).notNull(),
|
|
|
|
|
claimToken: varchar("claim_token", { length: 128 }).notNull(),
|
|
|
|
|
quantity: int("quantity").notNull(),
|
|
|
|
|
createdAt: timestamp("created_at"),
|
2026-07-30 12:06:41 +08:00
|
|
|
},
|
|
|
|
|
(table) => [
|
|
|
|
|
uniqueIndex("claims_claim_token_idx").on(table.claimToken),
|
|
|
|
|
index("claims_task_partner_created_idx").on(
|
|
|
|
|
table.taskId,
|
|
|
|
|
table.partnerId,
|
|
|
|
|
table.createdAt,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
export const delegationBundles = mysqlTable(
|
2026-07-30 12:06:41 +08:00
|
|
|
"delegation_bundles",
|
|
|
|
|
{
|
2026-08-11 23:07:26 +08:00
|
|
|
id: varchar("id", { length: 64 }).primaryKey(),
|
|
|
|
|
taskId: varchar("task_id", { length: 64 }).notNull(),
|
|
|
|
|
claimId: varchar("claim_id", { length: 64 }).notNull(),
|
|
|
|
|
partnerId: varchar("partner_id", { length: 64 }).notNull(),
|
|
|
|
|
label: varchar("label", { length: 255 }).notNull(),
|
|
|
|
|
shareToken: varchar("share_token", { length: 128 }).notNull(),
|
|
|
|
|
quantity: int("quantity").notNull(),
|
|
|
|
|
status: varchar("status", { length: 32 }).notNull().default("active"),
|
|
|
|
|
createdAt: timestamp("created_at"),
|
|
|
|
|
updatedAt: timestamp("updated_at"),
|
|
|
|
|
revokedAt: datetime("revoked_at", { mode: "string", fsp: 3 }),
|
2026-07-30 12:06:41 +08:00
|
|
|
},
|
|
|
|
|
(table) => [
|
|
|
|
|
uniqueIndex("delegation_bundles_share_token_idx").on(table.shareToken),
|
2026-08-11 23:07:26 +08:00
|
|
|
index("delegation_bundles_claim_created_idx").on(table.claimId, table.createdAt),
|
2026-07-30 12:06:41 +08:00
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
export const distributions = mysqlTable("distributions", {
|
|
|
|
|
id: varchar("id", { length: 64 }).primaryKey(),
|
|
|
|
|
taskId: varchar("task_id", { length: 64 }).notNull(),
|
|
|
|
|
contentId: varchar("content_id", { length: 64 }).notNull(),
|
|
|
|
|
partnerId: varchar("partner_id", { length: 64 }).notNull(),
|
|
|
|
|
claimId: varchar("claim_id", { length: 64 }),
|
|
|
|
|
delegationBundleId: varchar("delegation_bundle_id", { length: 64 }),
|
|
|
|
|
accountId: varchar("account_id", { length: 64 }),
|
2026-07-30 12:06:41 +08:00
|
|
|
publishUrl: text("publish_url"),
|
2026-08-11 23:07:26 +08:00
|
|
|
publishTime: datetime("publish_time", { mode: "string", fsp: 3 }),
|
|
|
|
|
publishScreenshotKey: varchar("publish_screenshot_key", { length: 512 }),
|
|
|
|
|
resultScreenshotKey: text("result_screenshot_key"),
|
|
|
|
|
resultSubmittedAt: datetime("result_submitted_at", { mode: "string", fsp: 3 }),
|
|
|
|
|
status: varchar("status", { length: 32 }).notNull().default("claimed"),
|
|
|
|
|
claimedAt: timestamp("claimed_at"),
|
|
|
|
|
screenshotKey: varchar("screenshot_key", { length: 512 }),
|
|
|
|
|
ocrStatus: varchar("ocr_status", { length: 32 }).notNull().default("none"),
|
|
|
|
|
exposure: int("exposure"),
|
|
|
|
|
views: int("views"),
|
|
|
|
|
d2Likes: int("d2_likes"),
|
|
|
|
|
d2Comments: int("d2_comments"),
|
|
|
|
|
d2Collects: int("d2_collects"),
|
|
|
|
|
d5Likes: int("d5_likes"),
|
|
|
|
|
d5Comments: int("d5_comments"),
|
|
|
|
|
d5Collects: int("d5_collects"),
|
|
|
|
|
d7Likes: int("d7_likes"),
|
|
|
|
|
d7Comments: int("d7_comments"),
|
|
|
|
|
d7Collects: int("d7_collects"),
|
|
|
|
|
latestLikes: int("latest_likes"),
|
|
|
|
|
latestComments: int("latest_comments"),
|
|
|
|
|
latestCollects: int("latest_collects"),
|
|
|
|
|
collectionStatus: varchar("collection_status", { length: 32 })
|
|
|
|
|
.notNull()
|
|
|
|
|
.default("pending"),
|
2026-07-30 12:06:41 +08:00
|
|
|
collectionStatusDescription: text("collection_status_description"),
|
2026-08-11 23:07:26 +08:00
|
|
|
collectionUpdatedAt: datetime("collection_updated_at", { mode: "string", fsp: 3 }),
|
|
|
|
|
lastCollectionDay: int("last_collection_day"),
|
|
|
|
|
updatedAt: timestamp("updated_at"),
|
2026-07-30 12:06:41 +08:00
|
|
|
});
|
|
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
export const collectionRuns = mysqlTable(
|
2026-07-30 12:06:41 +08:00
|
|
|
"collection_runs",
|
|
|
|
|
{
|
2026-08-11 23:07:26 +08:00
|
|
|
id: varchar("id", { length: 64 }).primaryKey(),
|
|
|
|
|
taskId: varchar("task_id", { length: 64 }).notNull(),
|
|
|
|
|
distributionId: varchar("distribution_id", { length: 64 }).notNull(),
|
|
|
|
|
scheduledDate: varchar("scheduled_date", { length: 32 }).notNull(),
|
|
|
|
|
scheduleDay: int("schedule_day"),
|
|
|
|
|
scheduledAt: datetime("scheduled_at", { mode: "string", fsp: 3 }).notNull(),
|
|
|
|
|
status: varchar("status", { length: 32 }).notNull().default("pending"),
|
|
|
|
|
likes: int("likes"),
|
|
|
|
|
comments: int("comments"),
|
|
|
|
|
collects: int("collects"),
|
2026-07-30 12:06:41 +08:00
|
|
|
statusDescription: text("status_description"),
|
2026-08-11 23:07:26 +08:00
|
|
|
startedAt: datetime("started_at", { mode: "string", fsp: 3 }),
|
|
|
|
|
completedAt: datetime("completed_at", { mode: "string", fsp: 3 }),
|
|
|
|
|
createdAt: timestamp("created_at"),
|
2026-07-30 12:06:41 +08:00
|
|
|
},
|
|
|
|
|
(table) => [
|
|
|
|
|
uniqueIndex("collection_runs_distribution_date_idx").on(
|
|
|
|
|
table.distributionId,
|
|
|
|
|
table.scheduledDate,
|
|
|
|
|
),
|
2026-08-11 23:07:26 +08:00
|
|
|
index("collection_runs_task_date_idx").on(table.taskId, table.scheduledDate),
|
2026-07-30 12:06:41 +08:00
|
|
|
],
|
|
|
|
|
);
|
2026-08-07 11:06:20 +08:00
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
export const users = mysqlTable(
|
2026-08-07 11:06:20 +08:00
|
|
|
"users",
|
|
|
|
|
{
|
2026-08-11 23:07:26 +08:00
|
|
|
id: varchar("id", { length: 64 }).primaryKey(),
|
|
|
|
|
username: varchar("username", { length: 255 }).notNull(),
|
|
|
|
|
passwordHash: varchar("password_hash", { length: 255 }).notNull(),
|
|
|
|
|
passwordSalt: varchar("password_salt", { length: 255 }).notNull(),
|
|
|
|
|
passwordIterations: int("password_iterations").notNull(),
|
|
|
|
|
role: varchar("role", { length: 32 }).notNull(),
|
|
|
|
|
createdAt: timestamp("created_at"),
|
|
|
|
|
updatedAt: timestamp("updated_at"),
|
2026-08-07 11:06:20 +08:00
|
|
|
},
|
2026-08-11 23:07:26 +08:00
|
|
|
(table) => [uniqueIndex("users_username_idx").on(table.username)],
|
2026-08-07 11:06:20 +08:00
|
|
|
);
|
|
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
export const authSessions = mysqlTable(
|
2026-08-07 11:06:20 +08:00
|
|
|
"auth_sessions",
|
|
|
|
|
{
|
2026-08-11 23:07:26 +08:00
|
|
|
tokenHash: varchar("token_hash", { length: 128 }).primaryKey(),
|
|
|
|
|
userId: varchar("user_id", { length: 64 }).notNull(),
|
|
|
|
|
expiresAt: datetime("expires_at", { mode: "string", fsp: 3 }).notNull(),
|
|
|
|
|
createdAt: timestamp("created_at"),
|
2026-08-07 11:06:20 +08:00
|
|
|
},
|
|
|
|
|
(table) => [
|
|
|
|
|
index("auth_sessions_user_id_idx").on(table.userId),
|
|
|
|
|
index("auth_sessions_expires_at_idx").on(table.expiresAt),
|
|
|
|
|
],
|
|
|
|
|
);
|
2026-08-07 14:12:52 +08:00
|
|
|
|
2026-08-11 23:07:26 +08:00
|
|
|
export const mcpExportTokens = mysqlTable(
|
2026-08-07 14:12:52 +08:00
|
|
|
"mcp_export_tokens",
|
|
|
|
|
{
|
2026-08-11 23:07:26 +08:00
|
|
|
tokenHash: varchar("token_hash", { length: 128 }).primaryKey(),
|
|
|
|
|
kind: varchar("kind", { length: 64 }).notNull(),
|
2026-08-07 14:12:52 +08:00
|
|
|
payload: text("payload").notNull(),
|
2026-08-11 23:07:26 +08:00
|
|
|
expiresAt: datetime("expires_at", { mode: "string", fsp: 3 }).notNull(),
|
|
|
|
|
createdAt: timestamp("created_at"),
|
2026-08-07 14:12:52 +08:00
|
|
|
},
|
|
|
|
|
(table) => [index("mcp_export_tokens_expires_at_idx").on(table.expiresAt)],
|
|
|
|
|
);
|
2026-08-11 23:07:26 +08:00
|
|
|
|
|
|
|
|
export const backgroundJobs = mysqlTable(
|
|
|
|
|
"background_jobs",
|
|
|
|
|
{
|
|
|
|
|
id: varchar("id", { length: 64 }).primaryKey(),
|
|
|
|
|
type: varchar("type", { length: 64 }).notNull(),
|
|
|
|
|
payload: text("payload").notNull(),
|
|
|
|
|
status: varchar("status", { length: 32 }).notNull().default("pending"),
|
|
|
|
|
attempts: int("attempts").notNull().default(0),
|
|
|
|
|
availableAt: datetime("available_at", { mode: "string", fsp: 3 }).notNull(),
|
|
|
|
|
lockedAt: datetime("locked_at", { mode: "string", fsp: 3 }),
|
|
|
|
|
completedAt: datetime("completed_at", { mode: "string", fsp: 3 }),
|
|
|
|
|
lastError: text("last_error"),
|
|
|
|
|
createdAt: timestamp("created_at"),
|
|
|
|
|
updatedAt: timestamp("updated_at"),
|
|
|
|
|
},
|
|
|
|
|
(table) => [index("background_jobs_pending_idx").on(table.status, table.availableAt)],
|
|
|
|
|
);
|