Files
dusuniot/src/db/schema.ts
T

872 lines
33 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* DusunIoT 重建版 PostgreSQL 数据模型(Drizzle ORM)
*
* 按业务域组织:content / products / commerce / inquiries / ops。
* 每张表保留 legacy_id 以便对照原 WordPress 数据并支持增量同步。
*/
import {
pgTable,
serial,
varchar,
text,
integer,
bigint,
boolean,
timestamp,
numeric,
jsonb,
index,
uniqueIndex,
primaryKey,
} from "drizzle-orm/pg-core";
/* ============================= 内容域 ============================= */
/** # 数据库表
* 内容主表(页面 page / 文章 post / 落地页 landing),默认语言(英文)的正本。
* Elementor 原始 JSON 存于 meta.elementor,供 1:1 还原渲染器使用。
*/
export const contents = pgTable(
"contents",
{
/** # 数据库字段 内容 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 内容类型:page | post | landing */
type: varchar("type", { length: 32 }).notNull(),
/** # 数据库字段 URL 别名(默认语言) */
slug: varchar("slug", { length: 255 }).notNull(),
/** # 数据库字段 标题 */
title: text("title").notNull(),
/** # 数据库字段 摘要 */
excerpt: text("excerpt").default("").notNull(),
/** # 数据库字段 正文 HTML */
html: text("html").default("").notNull(),
/** # 数据库字段 状态:draft | review | published */
status: varchar("status", { length: 16 }).default("published").notNull(),
/** # 数据库字段 作者 */
authorId: integer("author_id"),
/** # 数据库字段 特色图片 */
featuredMediaId: integer("featured_media_id"),
/** # 数据库字段 页面模板名(home / product / blog / blank …) */
template: varchar("template", { length: 64 }).default("default").notNull(),
/** # 数据库字段 菜单排序 */
menuOrder: integer("menu_order").default(0).notNull(),
/** # 数据库字段 浏览次数 */
views: integer("views").default(0).notNull(),
/** # 数据库字段 是否置顶 */
sticky: boolean("sticky").default(false).notNull(),
/** # 数据库字段 SEO 标题 */
seoTitle: text("seo_title"),
/** # 数据库字段 SEO 描述 */
seoDescription: text("seo_description"),
/** # 数据库字段 扩展数据(elementor JSON 等) */
meta: jsonb("meta").default({}).notNull(),
/** # 数据库字段 原 WordPress post ID */
legacyId: integer("legacy_id"),
/** # 数据库字段 发布时间 */
publishedAt: timestamp("published_at", { withTimezone: true }),
/** # 数据库字段 创建时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
/** # 数据库字段 更新时间 */
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [
uniqueIndex("contents_slug_type_idx").on(t.slug, t.type),
index("contents_type_status_idx").on(t.type, t.status),
index("contents_legacy_idx").on(t.legacyId),
],
);
/** # 数据库表
* 内容多语言翻译版本(每种语言独立 slug/标题/正文/SEO;缺翻译时不生成伪语言页面)。
*/
export const contentTranslations = pgTable(
"content_translations",
{
/** # 数据库字段 翻译 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 内容 ID */
contentId: integer("content_id").notNull(),
/** # 数据库字段 语言代码(BCP47,如 fr / de) */
lang: varchar("lang", { length: 16 }).notNull(),
/** # 数据库字段 该语言的 URL 别名 */
slug: varchar("slug", { length: 255 }).notNull(),
/** # 数据库字段 标题 */
title: text("title").notNull(),
/** # 数据库字段 摘要 */
excerpt: text("excerpt").default("").notNull(),
/** # 数据库字段 正文 HTML */
html: text("html").default("").notNull(),
/** # 数据库字段 状态:draft | review | published */
status: varchar("status", { length: 16 }).default("draft").notNull(),
/** # 数据库字段 SEO 标题 */
seoTitle: text("seo_title"),
/** # 数据库字段 SEO 描述 */
seoDescription: text("seo_description"),
/** # 数据库字段 翻译时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
/** # 数据库字段 更新时间 */
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [
uniqueIndex("content_translations_content_lang_idx").on(t.contentId, t.lang),
index("content_translations_lang_status_idx").on(t.lang, t.status),
],
);
/** # 数据库表
* 内容修订快照(发布前留档,支持回滚)。
*/
export const contentRevisions = pgTable("content_revisions", {
/** # 数据库字段 修订 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 内容 ID */
contentId: integer("content_id").notNull(),
/** # 数据库字段 语言(缺省为默认语言) */
lang: varchar("lang", { length: 16 }).default("en").notNull(),
/** # 数据库字段 标题快照 */
title: text("title").notNull(),
/** # 数据库字段 正文 HTML 快照 */
html: text("html").notNull(),
/** # 数据库字段 修订人 */
authorId: integer("author_id"),
/** # 数据库字段 修订时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
});
/** # 数据库表
* 分类/标签(taxonomy:category / post_tag / product_cat / product_tag / 属性 pa_*)。
*/
export const categories = pgTable(
"categories",
{
/** # 数据库字段 分类 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 taxonomy 名称 */
taxonomy: varchar("taxonomy", { length: 64 }).notNull(),
/** # 数据库字段 名称 */
name: varchar("name", { length: 255 }).notNull(),
/** # 数据库字段 URL 别名 */
slug: varchar("slug", { length: 255 }).notNull(),
/** # 数据库字段 父级分类 */
parentId: integer("parent_id"),
/** # 数据库字段 描述 */
description: text("description").default("").notNull(),
/** # 数据库字段 分类图片 */
imageMediaId: integer("image_media_id"),
/** # 数据库字段 排序 */
sort: integer("sort").default(0).notNull(),
/** # 数据库字段 原 WordPress term_id */
legacyId: integer("legacy_id"),
},
(t) => [
uniqueIndex("categories_taxonomy_slug_idx").on(t.taxonomy, t.slug),
index("categories_parent_idx").on(t.parentId),
],
);
/** # 数据库表
* 内容与分类/标签的关联。
*/
export const contentTerms = pgTable(
"content_terms",
{
/** # 数据库字段 内容 ID */
contentId: integer("content_id").notNull(),
/** # 数据库字段 分类 ID */
categoryId: integer("category_id").notNull(),
},
(t) => [
primaryKey({ columns: [t.contentId, t.categoryId] }),
index("content_terms_category_idx").on(t.categoryId),
],
);
/** # 数据库表
* 导航菜单定义(header / footer / account 等位置)。
*/
export const menus = pgTable("menus", {
/** # 数据库字段 菜单 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 菜单名称 */
name: varchar("name", { length: 128 }).notNull(),
/** # 数据库字段 位置别名(menu-1 / menu-2 / max_mega_menu_1 …) */
location: varchar("location", { length: 64 }).notNull(),
/** # 数据库字段 原菜单 term_id */
legacyId: integer("legacy_id"),
});
/** # 数据库表
* 菜单项(支持多级树)。
*/
export const menuItems = pgTable(
"menu_items",
{
/** # 数据库字段 菜单项 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 所属菜单 */
menuId: integer("menu_id").notNull(),
/** # 数据库字段 父菜单项 */
parentId: integer("parent_id"),
/** # 数据库字段 显示文字 */
title: varchar("title", { length: 255 }).notNull(),
/** # 数据库字段 链接地址 */
url: text("url").notNull(),
/** # 数据库字段 打开方式:_self | _blank */
target: varchar("target", { length: 16 }).default("_self").notNull(),
/** # 数据库字段 排序 */
sort: integer("sort").default(0).notNull(),
/** # 数据库字段 原 menu_item post_id */
legacyId: integer("legacy_id"),
},
(t) => [index("menu_items_menu_idx").on(t.menuId, t.sort)],
);
/* ============================= 产品域 ============================= */
/** # 数据库表
* 产品主表(WooCommerce 迁移)。
*/
export const products = pgTable(
"products",
{
/** # 数据库字段 产品 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 SKU */
sku: varchar("sku", { length: 128 }),
/** # 数据库字段 URL 别名 */
slug: varchar("slug", { length: 255 }).notNull(),
/** # 数据库字段 名称 */
title: text("title").notNull(),
/** # 数据库字段 短描述 */
shortDescription: text("short_description").default("").notNull(),
/** # 数据库字段 详细描述 HTML */
description: text("description").default("").notNull(),
/** # 数据库字段 现价 */
price: numeric("price", { precision: 12, scale: 2 }),
/** # 数据库字段 原价 */
regularPrice: numeric("regular_price", { precision: 12, scale: 2 }),
/** # 数据库字段 库存数量 */
stock: integer("stock"),
/** # 数据库字段 库存状态:instock | outofstock | onbackorder */
stockStatus: varchar("stock_status", { length: 32 }).default("instock").notNull(),
/** # 数据库字段 特色图片 */
featuredMediaId: integer("featured_media_id"),
/** # 数据库字段 图廊(媒体 ID 数组) */
gallery: jsonb("gallery").default([]).notNull(),
/** # 数据库字段 状态:draft | published */
status: varchar("status", { length: 16 }).default("published").notNull(),
/** # 数据库字段 是否主推产品 */
featured: boolean("featured").default(false).notNull(),
/** # 数据库字段 浏览次数 */
views: integer("views").default(0).notNull(),
/** # 数据库字段 SEO 标题 */
seoTitle: text("seo_title"),
/** # 数据库字段 SEO 描述 */
seoDescription: text("seo_description"),
/** # 数据库字段 规格书下载地址 */
specUrl: text("spec_url"),
/** # 数据库字段 Elementor 原始 JSON */
meta: jsonb("meta").default({}).notNull(),
/** # 数据库字段 原 WordPress post ID */
legacyId: integer("legacy_id"),
/** # 数据库字段 创建时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
/** # 数据库字段 更新时间 */
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [
uniqueIndex("products_slug_idx").on(t.slug),
index("products_status_idx").on(t.status),
index("products_sku_idx").on(t.sku),
index("products_legacy_idx").on(t.legacyId),
],
);
/** # 数据库表
* 产品多语言翻译。
*/
export const productTranslations = pgTable(
"product_translations",
{
/** # 数据库字段 翻译 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 产品 ID */
productId: integer("product_id").notNull(),
/** # 数据库字段 语言代码 */
lang: varchar("lang", { length: 16 }).notNull(),
/** # 数据库字段 该语言 slug */
slug: varchar("slug", { length: 255 }).notNull(),
/** # 数据库字段 名称 */
title: text("title").notNull(),
/** # 数据库字段 短描述 */
shortDescription: text("short_description").default("").notNull(),
/** # 数据库字段 详细描述 */
description: text("description").default("").notNull(),
/** # 数据库字段 状态:draft | review | published */
status: varchar("status", { length: 16 }).default("draft").notNull(),
/** # 数据库字段 SEO 标题 */
seoTitle: text("seo_title"),
/** # 数据库字段 SEO 描述 */
seoDescription: text("seo_description"),
/** # 数据库字段 更新时间 */
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [uniqueIndex("product_translations_product_lang_idx").on(t.productId, t.lang)],
);
/** # 数据库表
* 产品属性定义(RAM / Flash / Chip Platform / Downlink 等,原 WooCommerce 全局属性)。
*/
export const productAttributes = pgTable(
"product_attributes",
{
/** # 数据库字段 属性 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 属性名 */
name: varchar("name", { length: 128 }).notNull(),
/** # 数据库字段 属性 slug(attribute_id,如 ram / chip-platform) */
slug: varchar("slug", { length: 128 }).notNull(),
/** # 数据库字段 是否作为筛选条件 */
filterable: boolean("filterable").default(true).notNull(),
/** # 数据库字段 排序 */
sort: integer("sort").default(0).notNull(),
},
(t) => [uniqueIndex("product_attributes_slug_idx").on(t.slug)],
);
/** # 数据库表
* 产品属性可选值。
*/
export const productAttributeValues = pgTable(
"product_attribute_values",
{
/** # 数据库字段 值 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 所属属性 */
attributeId: integer("attribute_id").notNull(),
/** # 数据库字段 显示值 */
value: varchar("value", { length: 255 }).notNull(),
/** # 数据库字段 值 slug */
slug: varchar("slug", { length: 255 }).notNull(),
/** # 数据库字段 排序 */
sort: integer("sort").default(0).notNull(),
},
(t) => [
uniqueIndex("product_attr_values_attr_slug_idx").on(t.attributeId, t.slug),
index("product_attr_values_attr_idx").on(t.attributeId),
],
);
/** # 数据库表
* 产品 ↔ 属性值 关联(筛选查询主路径,建立复合索引)。
*/
export const productAttributeMap = pgTable(
"product_attribute_map",
{
/** # 数据库字段 产品 ID */
productId: integer("product_id").notNull(),
/** # 数据库字段 属性值 ID */
valueId: integer("value_id").notNull(),
},
(t) => [
primaryKey({ columns: [t.productId, t.valueId] }),
index("product_attr_map_value_idx").on(t.valueId, t.productId),
],
);
/** # 数据库表
* 产品 ↔ 产品分类 关联。
*/
export const productTerms = pgTable(
"product_terms",
{
/** # 数据库字段 产品 ID */
productId: integer("product_id").notNull(),
/** # 数据库字段 分类 ID(taxonomy=product_cat) */
categoryId: integer("category_id").notNull(),
},
(t) => [
primaryKey({ columns: [t.productId, t.categoryId] }),
index("product_terms_category_idx").on(t.categoryId),
],
);
/* ============================= 商城域 ============================= */
/** # 数据库表
* 用户(后台账号 + 商城注册客户)。保留 WordPress phpass 密码哈希用于存量客户登录兼容。
*/
export const users = pgTable(
"users",
{
/** # 数据库字段 用户 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 邮箱(登录名) */
email: varchar("email", { length: 255 }).notNull(),
/** # 数据库字段 用户名 */
username: varchar("username", { length: 128 }),
/** # 数据库字段 显示名称 */
displayName: varchar("display_name", { length: 255 }).default("").notNull(),
/** # 数据库字段 密码哈希(新账号 bcrypt) */
passwordHash: varchar("password_hash", { length: 255 }),
/** # 数据库字段 WordPress 旧密码哈希(phpass $P$) */
legacyPasswordHash: varchar("legacy_password_hash", { length: 255 }),
/** # 数据库字段 角色:admin | editor | manager | customer */
role: varchar("role", { length: 32 }).default("customer").notNull(),
/** # 数据库字段 头像 URL(媒体库选择,空 = 默认图标) */
avatar: varchar("avatar", { length: 512 }).default("").notNull(),
/** # 数据库字段 默认后台语言(BCP47,空 = 跟随浏览器/默认 en) */
locale: varchar("locale", { length: 16 }).default("").notNull(),
/** # 数据库字段 默认后台主题:light | dark(空 = 浅色) */
theme: varchar("theme", { length: 16 }).default("").notNull(),
/** # 数据库字段 MCP 访问令牌(Agent 经 MCP 网关调用时鉴权;空 = 未启用) */
mcpToken: varchar("mcp_token", { length: 64 }).default("").notNull(),
/** # 数据库字段 账单地址 */
billing: jsonb("billing").default({}).notNull(),
/** # 数据库字段 收货地址 */
shipping: jsonb("shipping").default({}).notNull(),
/** # 数据库字段 原 WP user ID */
legacyId: integer("legacy_id"),
/** # 数据库字段 创建时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [
uniqueIndex("users_email_idx").on(t.email),
index("users_legacy_idx").on(t.legacyId),
],
);
/** # 数据库表
* 服务端会话(HttpOnly Cookie → 会话记录)。
*/
export const sessions = pgTable(
"sessions",
{
/** # 数据库字段 会话令牌 */
token: varchar("token", { length: 64 }).primaryKey(),
/** # 数据库字段 用户 ID */
userId: integer("user_id").notNull(),
/** # 数据库字段 过期时间 */
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
/** # 数据库字段 创建时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [index("sessions_user_idx").on(t.userId)],
);
/** # 数据库表
* 购物车(登录用户或匿名 session_key 维度)。
*/
export const carts = pgTable("carts", {
/** # 数据库字段 购物车 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 用户 ID(登录用户) */
userId: integer("user_id"),
/** # 数据库字段 匿名会话键 */
sessionKey: varchar("session_key", { length: 64 }),
/** # 数据库字段 创建时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
/** # 数据库字段 更新时间 */
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
});
/** # 数据库表
* 购物车明细。
*/
export const cartItems = pgTable(
"cart_items",
{
/** # 数据库字段 明细 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 购物车 ID */
cartId: integer("cart_id").notNull(),
/** # 数据库字段 产品 ID */
productId: integer("product_id").notNull(),
/** # 数据库字段 数量 */
qty: integer("qty").default(1).notNull(),
},
(t) => [index("cart_items_cart_idx").on(t.cartId)],
);
/** # 数据库表
* 订单主表。
*/
export const orders = pgTable(
"orders",
{
/** # 数据库字段 订单 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 订单号(对外展示) */
orderNumber: varchar("order_number", { length: 32 }).notNull(),
/** # 数据库字段 客户 ID */
userId: integer("user_id"),
/** # 数据库字段 状态:pending | on-hold | processing | completed | cancelled | refunded */
status: varchar("status", { length: 24 }).default("pending").notNull(),
/** # 数据库字段 币种 */
currency: varchar("currency", { length: 8 }).default("USD").notNull(),
/** # 数据库字段 商品小计 */
subtotal: numeric("subtotal", { precision: 12, scale: 2 }).default("0").notNull(),
/** # 数据库字段 运费 */
shippingTotal: numeric("shipping_total", { precision: 12, scale: 2 }).default("0").notNull(),
/** # 数据库字段 税费 */
taxTotal: numeric("tax_total", { precision: 12, scale: 2 }).default("0").notNull(),
/** # 数据库字段 订单总额 */
total: numeric("total", { precision: 12, scale: 2 }).default("0").notNull(),
/** # 数据库字段 支付方式 */
paymentMethod: varchar("payment_method", { length: 64 }),
/** # 数据库字段 支付状态 */
paymentStatus: varchar("payment_status", { length: 24 }).default("unpaid").notNull(),
/** # 数据库字段 支付交易号 */
transactionId: varchar("transaction_id", { length: 128 }),
/** # 数据库字段 账单信息 */
billing: jsonb("billing").default({}).notNull(),
/** # 数据库字段 收货信息 */
shipping: jsonb("shipping").default({}).notNull(),
/** # 数据库字段 客户备注 */
customerNote: text("customer_note").default("").notNull(),
/** # 数据库字段 原 WP order ID */
legacyId: integer("legacy_id"),
/** # 数据库字段 下单时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
/** # 数据库字段 更新时间 */
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [
uniqueIndex("orders_number_idx").on(t.orderNumber),
index("orders_status_idx").on(t.status),
index("orders_user_idx").on(t.userId),
],
);
/** # 数据库表
* 订单明细(下单时快照商品名/价格)。
*/
export const orderItems = pgTable(
"order_items",
{
/** # 数据库字段 明细 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 订单 ID */
orderId: integer("order_id").notNull(),
/** # 数据库字段 产品 ID */
productId: integer("product_id"),
/** # 数据库字段 商品名快照 */
name: text("name").notNull(),
/** # 数据库字段 SKU 快照 */
sku: varchar("sku", { length: 128 }),
/** # 数据库字段 数量 */
qty: integer("qty").default(1).notNull(),
/** # 数据库字段 单价 */
price: numeric("price", { precision: 12, scale: 2 }).default("0").notNull(),
/** # 数据库字段 小计 */
total: numeric("total", { precision: 12, scale: 2 }).default("0").notNull(),
},
(t) => [index("order_items_order_idx").on(t.orderId)],
);
/** # 数据库表
* 支付流水(含第三方回调原始报文,幂等键 transaction_id)。
*/
export const payments = pgTable(
"payments",
{
/** # 数据库字段 支付 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 订单 ID */
orderId: integer("order_id").notNull(),
/** # 数据库字段 支付方式 */
method: varchar("method", { length: 64 }).notNull(),
/** # 数据库字段 状态:succeeded | failed | refunded | pending */
status: varchar("status", { length: 24 }).default("pending").notNull(),
/** # 数据库字段 交易号(唯一,回调幂等) */
transactionId: varchar("transaction_id", { length: 128 }),
/** # 数据库字段 金额 */
amount: numeric("amount", { precision: 12, scale: 2 }).default("0").notNull(),
/** # 数据库字段 回调原始数据 */
raw: jsonb("raw").default({}).notNull(),
/** # 数据库字段 创建时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [
index("payments_order_idx").on(t.orderId),
uniqueIndex("payments_txn_idx").on(t.transactionId),
],
);
/* ============================= 询盘域 ============================= */
/** # 数据库表
* 询盘表单定义(Contact Form 7 迁移,字段结构存 JSON)。
*/
export const inquiryForms = pgTable(
"inquiry_forms",
{
/** # 数据库字段 表单 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 表单名称 */
name: varchar("name", { length: 255 }).notNull(),
/** # 数据库字段 表单 key(slug) */
formKey: varchar("form_key", { length: 128 }).notNull(),
/** # 数据库字段 字段定义 JSON */
fields: jsonb("fields").default([]).notNull(),
/** # 数据库字段 收件邮箱 */
mailTo: varchar("mail_to", { length: 255 }),
/** # 数据库字段 是否启用 */
enabled: boolean("enabled").default(true).notNull(),
/** # 数据库字段 原 CF7 form_id */
legacyId: integer("legacy_id"),
},
(t) => [uniqueIndex("inquiry_forms_key_idx").on(t.formKey)],
);
/** # 数据库表
* 询盘记录(CF7/Flamingo 与 Elementor Form 提交合并归一)。
*/
export const inquiries = pgTable(
"inquiries",
{
/** # 数据库字段 询盘 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 来源表单名 */
formName: varchar("form_name", { length: 255 }).default("").notNull(),
/** # 数据库字段 表单 ID */
formId: integer("form_id"),
/** # 数据库字段 姓名 */
name: varchar("name", { length: 255 }).default("").notNull(),
/** # 数据库字段 邮箱 */
email: varchar("email", { length: 255 }).default("").notNull(),
/** # 数据库字段 电话 */
phone: varchar("phone", { length: 128 }).default("").notNull(),
/** # 数据库字段 公司 */
company: varchar("company", { length: 255 }).default("").notNull(),
/** # 数据库字段 国家/地区 */
country: varchar("country", { length: 128 }).default("").notNull(),
/** # 数据库字段 询盘内容 */
message: text("message").default("").notNull(),
/** # 数据库字段 感兴趣的产品 */
productInterest: varchar("product_interest", { length: 255 }).default("").notNull(),
/** # 数据库字段 完整原始字段(含 annualdemand、utm 等) */
fields: jsonb("fields").default({}).notNull(),
/** # 数据库字段 状态:new | read | replied | spam | archived */
status: varchar("status", { length: 16 }).default("new").notNull(),
/** # 数据库字段 来源渠道(channel / subject,可能很长用 text) */
channel: text("channel").default("").notNull(),
/** # 数据库字段 来源页面 */
sourcePage: text("source_page").default("").notNull(),
/** # 数据库字段 来源 IP */
ip: varchar("ip", { length: 64 }).default("").notNull(),
/** # 数据库字段 UTM 参数 */
utm: jsonb("utm").default({}).notNull(),
/** # 数据库字段 原 Flamingo/Elementor 记录 ID */
legacyId: integer("legacy_id"),
/** # 数据库字段 提交时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [
index("inquiries_status_idx").on(t.status, t.createdAt),
index("inquiries_email_idx").on(t.email),
index("inquiries_created_idx").on(t.createdAt),
],
);
/* ============================= 运营域 ============================= */
/** # 数据库表
* 媒体库(原 wp-content/uploads 附件)。
*/
export const media = pgTable(
"media",
{
/** # 数据库字段 媒体 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 相对路径(uploads 下) */
path: varchar("path", { length: 512 }).notNull(),
/** # 数据库字段 访问 URL */
url: text("url").notNull(),
/** # 数据库字段 MIME 类型 */
mimeType: varchar("mime_type", { length: 128 }).default("").notNull(),
/** # 数据库字段 宽 */
width: integer("width"),
/** # 数据库字段 高 */
height: integer("height"),
/** # 数据库字段 文件大小 */
size: bigint("size", { mode: "number" }),
/** # 数据库字段 替代文本 */
altText: text("alt_text").default("").notNull(),
/** # 数据库字段 标题 */
title: text("title").default("").notNull(),
/** # 数据库字段 引用次数 */
refCount: integer("ref_count").default(0).notNull(),
/** # 数据库字段 原 WP attachment ID */
legacyId: integer("legacy_id"),
/** # 数据库字段 上传时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [
uniqueIndex("media_path_idx").on(t.path),
index("media_legacy_idx").on(t.legacyId),
],
);
/** # 数据库表
* 301/302 重定向(Redirection 插件迁移)。
*/
export const redirects = pgTable(
"redirects",
{
/** # 数据库字段 重定向 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 源路径(支持正则) */
source: text("source").notNull(),
/** # 数据库字段 目标地址 */
target: text("target").notNull(),
/** # 数据库字段 状态码 */
code: integer("code").default(301).notNull(),
/** # 数据库字段 是否正则匹配 */
isRegex: boolean("is_regex").default(false).notNull(),
/** # 数据库字段 命中次数 */
hits: integer("hits").default(0).notNull(),
/** # 数据库字段 是否启用 */
enabled: boolean("enabled").default(true).notNull(),
/** # 数据库字段 原 redirection item id */
legacyId: integer("legacy_id"),
},
(t) => [index("redirects_source_idx").on(t.source)],
);
/** # 数据库表
* 筛选配置(Filter Everything 迁移,前台产品筛选器)。
*/
export const filters = pgTable(
"filters",
{
/** # 数据库字段 筛选 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 筛选名称 */
name: varchar("name", { length: 128 }).notNull(),
/** # 数据库字段 URL 参数名(如 chip-platform) */
slug: varchar("slug", { length: 128 }).notNull(),
/** # 数据库字段 类型:attribute | category | price */
type: varchar("type", { length: 24 }).default("attribute").notNull(),
/** # 数据库字段 关联属性 ID(type=attribute 时) */
attributeId: integer("attribute_id"),
/** # 数据库字段 关联 taxonomy */
taxonomy: varchar("taxonomy", { length: 64 }),
/** # 数据库字段 显示位置 */
position: integer("position").default(0).notNull(),
/** # 数据库字段 是否启用 */
enabled: boolean("enabled").default(true).notNull(),
/** # 数据库字段 原 filter-field post_id */
legacyId: integer("legacy_id"),
},
(t) => [uniqueIndex("filters_slug_idx").on(t.slug)],
);
/** # 数据库表
* 语言配置(GTranslate 迁移:启用的语言与默认语言)。
*/
export const languages = pgTable(
"languages",
{
/** # 数据库字段 语言代码(BCP47) */
code: varchar("code", { length: 16 }).primaryKey(),
/** # 数据库字段 显示名 */
name: varchar("name", { length: 64 }).notNull(),
/** # 数据库字段 是否默认语言 */
isDefault: boolean("is_default").default(false).notNull(),
/** # 数据库字段 是否启用(可发布真实页面) */
enabled: boolean("enabled").default(false).notNull(),
/** # 数据库字段 是否机器翻译(GTranslate 存量) */
machine: boolean("machine").default(false).notNull(),
/** # 数据库字段 排序 */
sort: integer("sort").default(0).notNull(),
},
);
/** # 数据库表
* 持久化任务队列(独立于 Web 请求的后台任务,带重试)。
*/
export const jobs = pgTable(
"jobs",
{
/** # 数据库字段 任务 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 任务类型(email.send / media.reindex …) */
type: varchar("type", { length: 64 }).notNull(),
/** # 数据库字段 任务参数 */
payload: jsonb("payload").default({}).notNull(),
/** # 数据库字段 状态:pending | running | done | failed */
status: varchar("status", { length: 16 }).default("pending").notNull(),
/** # 数据库字段 已重试次数 */
attempts: integer("attempts").default(0).notNull(),
/** # 数据库字段 最大重试次数 */
maxAttempts: integer("max_attempts").default(3).notNull(),
/** # 数据库字段 计划执行时间 */
runAt: timestamp("run_at", { withTimezone: true }).defaultNow().notNull(),
/** # 数据库字段 最近错误 */
lastError: text("last_error"),
/** # 数据库字段 创建时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
/** # 数据库字段 更新时间 */
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [index("jobs_status_runat_idx").on(t.status, t.runAt)],
);
/** # 数据库表
* 审计日志(后台敏感操作留痕)。
*/
export const auditLogs = pgTable(
"audit_logs",
{
/** # 数据库字段 日志 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 操作人 */
actorId: integer("actor_id"),
/** # 数据库字段 动作(如 product.update) */
action: varchar("action", { length: 128 }).notNull(),
/** # 数据库字段 实体类型 */
entity: varchar("entity", { length: 64 }).notNull(),
/** # 数据库字段 实体 ID */
entityId: varchar("entity_id", { length: 64 }),
/** # 数据库字段 附加数据 */
meta: jsonb("meta").default({}).notNull(),
/** # 数据库字段 操作 IP */
ip: varchar("ip", { length: 64 }).default("").notNull(),
/** # 数据库字段 操作时间 */
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
},
(t) => [index("audit_logs_time_idx").on(t.createdAt)],
);
/** # 数据库表
* 站点设置(键值 JSON:导航、邮件、支付、缓存开关等)。
*/
export const settings = pgTable("settings", {
/** # 数据库字段 设置键 */
key: varchar("key", { length: 128 }).primaryKey(),
/** # 数据库字段 设置值 */
value: jsonb("value").default({}).notNull(),
/** # 数据库字段 更新时间 */
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
});
/** # 数据库表
* 旧 ID 映射(WordPress ID → 新系统 ID,迁移校验与增量同步用)。
*/
export const legacyIdMappings = pgTable(
"legacy_id_mappings",
{
/** # 数据库字段 映射 ID */
id: serial("id").primaryKey(),
/** # 数据库字段 实体类型(post/product/user/order/media …) */
entity: varchar("entity", { length: 64 }).notNull(),
/** # 数据库字段 原 WP ID */
legacyId: integer("legacy_id").notNull(),
/** # 数据库字段 新系统 ID */
newId: integer("new_id").notNull(),
},
(t) => [uniqueIndex("legacy_map_entity_id_idx").on(t.entity, t.legacyId)],
);