chore: add credit related tables
This commit is contained in:
parent
d8904750d9
commit
12fb19e97b
25
src/db/migrations/0001_woozy_jigsaw.sql
Normal file
25
src/db/migrations/0001_woozy_jigsaw.sql
Normal file
@ -0,0 +1,25 @@
|
||||
CREATE TABLE "credit_transaction" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"type" text NOT NULL,
|
||||
"description" text,
|
||||
"amount" integer NOT NULL,
|
||||
"remaining_amount" integer,
|
||||
"payment_id" text,
|
||||
"expiration_date" timestamp,
|
||||
"expiration_date_processed_at" timestamp,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_credit" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" text NOT NULL,
|
||||
"current_credits" integer DEFAULT 0 NOT NULL,
|
||||
"last_refresh_at" timestamp,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "credit_transaction" ADD CONSTRAINT "credit_transaction_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_credit" ADD CONSTRAINT "user_credit_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
|
635
src/db/migrations/meta/0001_snapshot.json
Normal file
635
src/db/migrations/meta/0001_snapshot.json
Normal file
@ -0,0 +1,635 @@
|
||||
{
|
||||
"id": "6ed4f085-66bb-42c4-a708-2e5d86438ca2",
|
||||
"prevId": "7ecbd97a-94eb-4a46-996e-dbff727fc0c7",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.account": {
|
||||
"name": "account",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"provider_id": {
|
||||
"name": "provider_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"id_token": {
|
||||
"name": "id_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"access_token_expires_at": {
|
||||
"name": "access_token_expires_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"refresh_token_expires_at": {
|
||||
"name": "refresh_token_expires_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"scope": {
|
||||
"name": "scope",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"password": {
|
||||
"name": "password",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"account_user_id_user_id_fk": {
|
||||
"name": "account_user_id_user_id_fk",
|
||||
"tableFrom": "account",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.credit_transaction": {
|
||||
"name": "credit_transaction",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"amount": {
|
||||
"name": "amount",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"remaining_amount": {
|
||||
"name": "remaining_amount",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"payment_id": {
|
||||
"name": "payment_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expiration_date": {
|
||||
"name": "expiration_date",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expiration_date_processed_at": {
|
||||
"name": "expiration_date_processed_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"credit_transaction_user_id_user_id_fk": {
|
||||
"name": "credit_transaction_user_id_user_id_fk",
|
||||
"tableFrom": "credit_transaction",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.payment": {
|
||||
"name": "payment",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"price_id": {
|
||||
"name": "price_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"interval": {
|
||||
"name": "interval",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"subscription_id": {
|
||||
"name": "subscription_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"period_start": {
|
||||
"name": "period_start",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"period_end": {
|
||||
"name": "period_end",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"cancel_at_period_end": {
|
||||
"name": "cancel_at_period_end",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"trial_start": {
|
||||
"name": "trial_start",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"trial_end": {
|
||||
"name": "trial_end",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"payment_user_id_user_id_fk": {
|
||||
"name": "payment_user_id_user_id_fk",
|
||||
"tableFrom": "payment",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.session": {
|
||||
"name": "session",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"token": {
|
||||
"name": "token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"ip_address": {
|
||||
"name": "ip_address",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"user_agent": {
|
||||
"name": "user_agent",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"impersonated_by": {
|
||||
"name": "impersonated_by",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"session_user_id_user_id_fk": {
|
||||
"name": "session_user_id_user_id_fk",
|
||||
"tableFrom": "session",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"session_token_unique": {
|
||||
"name": "session_token_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"token"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.user": {
|
||||
"name": "user",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email_verified": {
|
||||
"name": "email_verified",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"banned": {
|
||||
"name": "banned",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"ban_reason": {
|
||||
"name": "ban_reason",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"ban_expires": {
|
||||
"name": "ban_expires",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"user_email_unique": {
|
||||
"name": "user_email_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"email"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.user_credit": {
|
||||
"name": "user_credit",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"current_credits": {
|
||||
"name": "current_credits",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": 0
|
||||
},
|
||||
"last_refresh_at": {
|
||||
"name": "last_refresh_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"user_credit_user_id_user_id_fk": {
|
||||
"name": "user_credit_user_id_user_id_fk",
|
||||
"tableFrom": "user_credit",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.verification": {
|
||||
"name": "verification",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"identifier": {
|
||||
"name": "identifier",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
@ -8,6 +8,13 @@
|
||||
"when": 1744304844165,
|
||||
"tag": "0000_fine_sir_ram",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1751214200582,
|
||||
"tag": "0001_woozy_jigsaw",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { boolean, integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
|
||||
export const user = pgTable("user", {
|
||||
id: text("id").primaryKey(),
|
||||
@ -70,27 +70,25 @@ export const payment = pgTable("payment", {
|
||||
updatedAt: timestamp('updated_at').notNull().defaultNow(),
|
||||
});
|
||||
|
||||
// Credits table: stores user's current credit balance and last refresh date
|
||||
export const userCredit = pgTable("user_credit", {
|
||||
id: text("id").primaryKey(),
|
||||
userId: text("user_id").notNull().references(() => user.id, { onDelete: 'cascade' }),
|
||||
balance: text("balance").notNull(), // store as string for bigints, or use integer if preferred
|
||||
lastRefresh: timestamp("last_refresh"), // last time free/monthly credits were refreshed
|
||||
currentCredits: integer("current_credits").notNull().default(0),
|
||||
lastRefreshAt: timestamp("last_refresh_at"),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
});
|
||||
|
||||
// Credit transaction table: records all credit changes (earn/spend/expire)
|
||||
export const creditTransaction = pgTable("credit_transaction", {
|
||||
id: text("id").primaryKey(),
|
||||
userId: text("user_id").notNull().references(() => user.id, { onDelete: 'cascade' }),
|
||||
type: text("type").notNull(), // main type, e.g. REGISTER_GIFT, MONTHLY_REFRESH, PURCHASE, USAGE, EXPIRE
|
||||
description: text("description"), // description, e.g. "Register gift credits: 100"
|
||||
amount: text("amount").notNull(), // positive for earn, negative for spend
|
||||
remainingAmount: text("remaining_amount"), // for FIFO consumption
|
||||
paymentId: text("payment_id"), // associated payment order, can be null, only has value when purchasing credits
|
||||
expirationDate: timestamp("expiration_date"), // when these credits expire, null for no expiration
|
||||
expirationDateProcessedAt: timestamp("expiration_date_processed_at"), // when expired credits were processed
|
||||
type: text("type").notNull(),
|
||||
description: text("description"),
|
||||
amount: integer("amount").notNull(),
|
||||
remainingAmount: integer("remaining_amount"),
|
||||
paymentId: text("payment_id"),
|
||||
expirationDate: timestamp("expiration_date"),
|
||||
expirationDateProcessedAt: timestamp("expiration_date_processed_at"),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
});
|
||||
|
@ -17,7 +17,7 @@ export async function getUserCredits(userId: string): Promise<number> {
|
||||
.from(userCredit)
|
||||
.where(eq(userCredit.userId, userId))
|
||||
.limit(1);
|
||||
return record[0]?.balance ? Number.parseInt(record[0].balance, 10) : 0;
|
||||
return record[0]?.currentCredits || 0;
|
||||
}
|
||||
|
||||
// Write a credit transaction record
|
||||
@ -40,8 +40,8 @@ async function logCreditTransaction(params: {
|
||||
id: crypto.randomUUID(),
|
||||
userId: params.userId,
|
||||
type: params.type,
|
||||
amount: params.amount.toString(),
|
||||
remainingAmount: params.amount > 0 ? params.amount.toString() : undefined,
|
||||
amount: params.amount,
|
||||
remainingAmount: params.amount > 0 ? params.amount : null,
|
||||
description: params.description,
|
||||
paymentId: params.paymentId,
|
||||
expirationDate: params.expirationDate,
|
||||
@ -84,15 +84,13 @@ export async function addCredits({
|
||||
.from(userCredit)
|
||||
.where(eq(userCredit.userId, userId))
|
||||
.limit(1);
|
||||
const newBalance = (
|
||||
Number.parseInt(current[0]?.balance || '0', 10) + amount
|
||||
).toString();
|
||||
const newBalance = (current[0]?.currentCredits || 0) + amount;
|
||||
if (current.length > 0) {
|
||||
await db
|
||||
.update(userCredit)
|
||||
.set({
|
||||
balance: newBalance,
|
||||
lastRefresh: new Date(),
|
||||
currentCredits: newBalance,
|
||||
lastRefreshAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(userCredit.userId, userId));
|
||||
@ -100,8 +98,8 @@ export async function addCredits({
|
||||
await db.insert(userCredit).values({
|
||||
id: crypto.randomUUID(),
|
||||
userId,
|
||||
balance: newBalance,
|
||||
lastRefresh: new Date(),
|
||||
currentCredits: newBalance,
|
||||
lastRefreshAt: new Date(),
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
@ -163,14 +161,14 @@ export async function consumeCredits({
|
||||
let left = amount;
|
||||
for (const tx of txs) {
|
||||
if (left <= 0) break;
|
||||
const remain = Number.parseInt(tx.remainingAmount || '0', 10);
|
||||
const remain = tx.remainingAmount || 0;
|
||||
if (remain <= 0) continue;
|
||||
// credits to consume at most in this transaction
|
||||
const consume = Math.min(remain, left);
|
||||
await db
|
||||
.update(creditTransaction)
|
||||
.set({
|
||||
remainingAmount: (remain - consume).toString(),
|
||||
remainingAmount: remain - consume,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(creditTransaction.id, tx.id));
|
||||
@ -182,12 +180,10 @@ export async function consumeCredits({
|
||||
.from(userCredit)
|
||||
.where(eq(userCredit.userId, userId))
|
||||
.limit(1);
|
||||
const newBalance = (
|
||||
Number.parseInt(current[0]?.balance || '0', 10) - amount
|
||||
).toString();
|
||||
const newBalance = (current[0]?.currentCredits || 0) - amount;
|
||||
await db
|
||||
.update(userCredit)
|
||||
.set({ balance: newBalance, updatedAt: new Date() })
|
||||
.set({ currentCredits: newBalance, updatedAt: new Date() })
|
||||
.where(eq(userCredit.userId, userId));
|
||||
// Write usage record
|
||||
await logCreditTransaction({
|
||||
@ -226,13 +222,13 @@ export async function processExpiredCredits(userId: string) {
|
||||
isAfter(now, tx.expirationDate) &&
|
||||
!tx.expirationDateProcessedAt
|
||||
) {
|
||||
const remain = Number.parseInt(tx.remainingAmount || '0', 10);
|
||||
const remain = tx.remainingAmount || 0;
|
||||
if (remain > 0) {
|
||||
expiredTotal += remain;
|
||||
await db
|
||||
.update(creditTransaction)
|
||||
.set({
|
||||
remainingAmount: '0',
|
||||
remainingAmount: 0,
|
||||
expirationDateProcessedAt: now,
|
||||
updatedAt: now,
|
||||
})
|
||||
@ -249,11 +245,11 @@ export async function processExpiredCredits(userId: string) {
|
||||
.limit(1);
|
||||
const newBalance = Math.max(
|
||||
0,
|
||||
Number.parseInt(current[0]?.balance || '0', 10) - expiredTotal
|
||||
).toString();
|
||||
(current[0]?.currentCredits || 0) - expiredTotal
|
||||
);
|
||||
await db
|
||||
.update(userCredit)
|
||||
.set({ balance: newBalance, updatedAt: now })
|
||||
.set({ currentCredits: newBalance, updatedAt: now })
|
||||
.where(eq(userCredit.userId, userId));
|
||||
// Write expire record
|
||||
await logCreditTransaction({
|
||||
@ -302,10 +298,10 @@ export async function addMonthlyFreeCredits(userId: string) {
|
||||
const now = new Date();
|
||||
let canAdd = false;
|
||||
// never added credits before
|
||||
if (!record[0]?.lastRefresh) {
|
||||
if (!record[0]?.lastRefreshAt) {
|
||||
canAdd = true;
|
||||
} else {
|
||||
const last = new Date(record[0].lastRefresh);
|
||||
const last = new Date(record[0].lastRefreshAt);
|
||||
canAdd =
|
||||
now.getMonth() !== last.getMonth() ||
|
||||
now.getFullYear() !== last.getFullYear();
|
||||
|
Loading…
Reference in New Issue
Block a user