Files
ai_web/drizzle/schema.ts
2026-01-27 14:51:35 +08:00

247 lines
9.4 KiB
TypeScript

import { int, mysqlEnum, mysqlTable, text, timestamp, varchar, decimal, boolean } from "drizzle-orm/mysql-core";
/**
* Core user table backing auth flow.
*/
export const users = mysqlTable("users", {
id: int("id").autoincrement().primaryKey(),
openId: varchar("openId", { length: 64 }).notNull().unique(),
name: text("name"),
email: varchar("email", { length: 320 }),
avatar: text("avatar"),
loginMethod: varchar("loginMethod", { length: 64 }),
role: mysqlEnum("role", ["user", "admin"]).default("user").notNull(),
stripeCustomerId: varchar("stripeCustomerId", { length: 255 }),
stripeAccountId: varchar("stripeAccountId", { length: 255 }),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
lastSignedIn: timestamp("lastSignedIn").defaultNow().notNull(),
});
export type User = typeof users.$inferSelect;
export type InsertUser = typeof users.$inferInsert;
/**
* Product categories for navigation
*/
export const categories = mysqlTable("categories", {
id: int("id").autoincrement().primaryKey(),
name: varchar("name", { length: 100 }).notNull(),
slug: varchar("slug", { length: 100 }).notNull().unique(),
description: text("description"),
icon: varchar("icon", { length: 100 }),
parentId: int("parentId"),
sortOrder: int("sortOrder").default(0),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type Category = typeof categories.$inferSelect;
export type InsertCategory = typeof categories.$inferInsert;
/**
* External shopping/card websites
*/
export const websites = mysqlTable("websites", {
id: int("id").autoincrement().primaryKey(),
name: varchar("name", { length: 200 }).notNull(),
url: varchar("url", { length: 500 }).notNull(),
logo: text("logo"),
description: text("description"),
categoryId: int("categoryId").notNull(),
rating: decimal("rating", { precision: 2, scale: 1 }).default("0"),
isVerified: boolean("isVerified").default(false),
sortOrder: int("sortOrder").default(0),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type Website = typeof websites.$inferSelect;
export type InsertWebsite = typeof websites.$inferInsert;
/**
* Products for price comparison
*/
export const products = mysqlTable("products", {
id: int("id").autoincrement().primaryKey(),
name: varchar("name", { length: 300 }).notNull(),
description: text("description"),
image: text("image"),
categoryId: int("categoryId").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type Product = typeof products.$inferSelect;
export type InsertProduct = typeof products.$inferInsert;
/**
* Product prices from different websites
*/
export const productPrices = mysqlTable("productPrices", {
id: int("id").autoincrement().primaryKey(),
productId: int("productId").notNull(),
websiteId: int("websiteId").notNull(),
price: decimal("price", { precision: 10, scale: 2 }).notNull(),
originalPrice: decimal("originalPrice", { precision: 10, scale: 2 }),
currency: varchar("currency", { length: 10 }).default("CNY"),
url: varchar("url", { length: 500 }).notNull(),
inStock: boolean("inStock").default(true),
lastChecked: timestamp("lastChecked").defaultNow().notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type ProductPrice = typeof productPrices.$inferSelect;
export type InsertProductPrice = typeof productPrices.$inferInsert;
/**
* Bounty/Reward tasks
*/
export const bounties = mysqlTable("bounties", {
id: int("id").autoincrement().primaryKey(),
title: varchar("title", { length: 300 }).notNull(),
description: text("description").notNull(),
reward: decimal("reward", { precision: 10, scale: 2 }).notNull(),
currency: varchar("currency", { length: 10 }).default("CNY"),
publisherId: int("publisherId").notNull(),
acceptorId: int("acceptorId"),
status: mysqlEnum("status", ["open", "in_progress", "completed", "cancelled", "disputed"]).default("open").notNull(),
deadline: timestamp("deadline"),
completedAt: timestamp("completedAt"),
stripePaymentIntentId: varchar("stripePaymentIntentId", { length: 255 }),
stripeTransferId: varchar("stripeTransferId", { length: 255 }),
isPaid: boolean("isPaid").default(false),
isEscrowed: boolean("isEscrowed").default(false),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type Bounty = typeof bounties.$inferSelect;
export type InsertBounty = typeof bounties.$inferInsert;
/**
* Bounty applications/bids
*/
export const bountyApplications = mysqlTable("bountyApplications", {
id: int("id").autoincrement().primaryKey(),
bountyId: int("bountyId").notNull(),
applicantId: int("applicantId").notNull(),
message: text("message"),
status: mysqlEnum("status", ["pending", "accepted", "rejected"]).default("pending").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type BountyApplication = typeof bountyApplications.$inferSelect;
export type InsertBountyApplication = typeof bountyApplications.$inferInsert;
/**
* Bounty comments
*/
export const bountyComments = mysqlTable("bountyComments", {
id: int("id").autoincrement().primaryKey(),
bountyId: int("bountyId").notNull(),
userId: int("userId").notNull(),
content: text("content").notNull(),
parentId: int("parentId"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type BountyComment = typeof bountyComments.$inferSelect;
export type InsertBountyComment = typeof bountyComments.$inferInsert;
/**
* User notifications
*/
export const notifications = mysqlTable("notifications", {
id: int("id").autoincrement().primaryKey(),
userId: int("userId").notNull(),
type: mysqlEnum("type", ["bounty_accepted", "bounty_completed", "new_comment", "payment_received", "system"]).notNull(),
title: varchar("title", { length: 200 }).notNull(),
content: text("content"),
relatedId: int("relatedId"),
relatedType: varchar("relatedType", { length: 50 }),
isRead: boolean("isRead").default(false),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type Notification = typeof notifications.$inferSelect;
export type InsertNotification = typeof notifications.$inferInsert;
/**
* User product favorites/collections
*/
export const favorites = mysqlTable("favorites", {
id: int("id").autoincrement().primaryKey(),
userId: int("userId").notNull(),
productId: int("productId").notNull(),
websiteId: int("websiteId").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type Favorite = typeof favorites.$inferSelect;
export type InsertFavorite = typeof favorites.$inferInsert;
/**
* Favorite collection tags for organizing collections
*/
export const favoriteTags = mysqlTable("favoriteTags", {
id: int("id").autoincrement().primaryKey(),
userId: int("userId").notNull(),
name: varchar("name", { length: 100 }).notNull(),
color: varchar("color", { length: 20 }).default("#6366f1"),
description: text("description"),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type FavoriteTag = typeof favoriteTags.$inferSelect;
export type InsertFavoriteTag = typeof favoriteTags.$inferInsert;
/**
* Junction table for favorites and tags
*/
export const favoriteTagMappings = mysqlTable("favoriteTagMappings", {
id: int("id").autoincrement().primaryKey(),
favoriteId: int("favoriteId").notNull(),
tagId: int("tagId").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
});
export type FavoriteTagMapping = typeof favoriteTagMappings.$inferSelect;
export type InsertFavoriteTagMapping = typeof favoriteTagMappings.$inferInsert;
/**
* Price monitoring for favorites
*/
export const priceMonitors = mysqlTable("priceMonitors", {
id: int("id").autoincrement().primaryKey(),
favoriteId: int("favoriteId").notNull(),
userId: int("userId").notNull(),
currentPrice: decimal("currentPrice", { precision: 10, scale: 2 }),
targetPrice: decimal("targetPrice", { precision: 10, scale: 2 }),
lowestPrice: decimal("lowestPrice", { precision: 10, scale: 2 }),
highestPrice: decimal("highestPrice", { precision: 10, scale: 2 }),
isActive: boolean("isActive").default(true).notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type PriceMonitor = typeof priceMonitors.$inferSelect;
export type InsertPriceMonitor = typeof priceMonitors.$inferInsert;
/**
* Price history tracking
*/
export const priceHistory = mysqlTable("priceHistory", {
id: int("id").autoincrement().primaryKey(),
monitorId: int("monitorId").notNull(),
price: decimal("price", { precision: 10, scale: 2 }).notNull(),
priceChange: decimal("priceChange", { precision: 10, scale: 2 }),
percentChange: decimal("percentChange", { precision: 5, scale: 2 }),
recordedAt: timestamp("recordedAt").defaultNow().notNull(),
});
export type PriceHistory = typeof priceHistory.$inferSelect;
export type InsertPriceHistory = typeof priceHistory.$inferInsert;