247 lines
9.4 KiB
TypeScript
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;
|