forked from wangziqi/gongxue-base
feat: add student wallet utility billing
This commit is contained in:
@@ -11,6 +11,7 @@ export class DatabaseMigrationsService implements OnApplicationBootstrap {
|
||||
async onApplicationBootstrap(): Promise<void> {
|
||||
await this.ensureAiConfigTable();
|
||||
await this.ensureCourseAttendanceSchema();
|
||||
await this.ensureStudentWalletSchema();
|
||||
await this.backfillOrganizations();
|
||||
await this.normalizeClassDates();
|
||||
await this.protectAttendanceHistory();
|
||||
@@ -21,6 +22,49 @@ export class DatabaseMigrationsService implements OnApplicationBootstrap {
|
||||
await this.normalizeClassroomStatuses();
|
||||
}
|
||||
|
||||
private async ensureStudentWalletSchema(): Promise<void> {
|
||||
const runner = this.dataSource.createQueryRunner();
|
||||
await runner.connect();
|
||||
try {
|
||||
const isMySQL = this.dataSource.options.type === 'mysql';
|
||||
const pk = isMySQL ? 'INTEGER PRIMARY KEY AUTO_INCREMENT' : 'INTEGER PRIMARY KEY AUTOINCREMENT';
|
||||
await runner.query(`CREATE TABLE IF NOT EXISTS student_wallets (
|
||||
id ${pk}, student_id INTEGER NOT NULL UNIQUE, balance DECIMAL(12,2) NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
await runner.query(`CREATE TABLE IF NOT EXISTS wallet_transactions (
|
||||
id ${pk}, student_id INTEGER NOT NULL, bill_id INTEGER, type VARCHAR(30) NOT NULL,
|
||||
amount DECIMAL(12,2) NOT NULL, balance_after DECIMAL(12,2) NOT NULL,
|
||||
description VARCHAR(300), recorded_by INTEGER,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
const bills = await runner.getTable('bills');
|
||||
if (bills) {
|
||||
const columns = new Set(bills.columns.map((column) => column.name));
|
||||
const additions = [
|
||||
['source', "VARCHAR(30) NOT NULL DEFAULT 'batch'"],
|
||||
['paid_amount', 'DECIMAL(10,2) NOT NULL DEFAULT 0'],
|
||||
['outstanding_amount', 'DECIMAL(10,2) NOT NULL DEFAULT 0'],
|
||||
['cancelled_at', 'DATETIME'],
|
||||
['cancel_reason', 'VARCHAR(300)'],
|
||||
];
|
||||
for (const [name, definition] of additions) {
|
||||
if (!columns.has(name)) await runner.query(`ALTER TABLE bills ADD COLUMN ${name} ${definition}`);
|
||||
}
|
||||
await runner.query("UPDATE bills SET outstanding_amount = total_amount WHERE outstanding_amount = 0 AND status <> 'paid'");
|
||||
await runner.query("UPDATE bills SET paid_amount = total_amount, outstanding_amount = 0 WHERE status = 'paid'");
|
||||
await runner.query("UPDATE bills SET status = 'unpaid' WHERE status IN ('draft', 'confirmed')");
|
||||
}
|
||||
const personalExpenses = await runner.getTable('personal_expenses');
|
||||
if (personalExpenses && !personalExpenses.columns.some((column) => column.name === 'bill_id')) {
|
||||
await runner.query('ALTER TABLE personal_expenses ADD COLUMN bill_id INTEGER');
|
||||
}
|
||||
} finally {
|
||||
await runner.release();
|
||||
}
|
||||
}
|
||||
|
||||
private async removeUnusedClassroomColumns(): Promise<void> {
|
||||
const runner = this.dataSource.createQueryRunner();
|
||||
await runner.connect();
|
||||
|
||||
Reference in New Issue
Block a user