fix permissions and teacher attendance workflows

This commit is contained in:
2026-07-10 20:40:52 +08:00
parent 247879f276
commit 8ed1682b90
95 changed files with 4745 additions and 1237 deletions

View File

@@ -0,0 +1,5 @@
import { Module } from '@nestjs/common';
import { DatabaseMigrationsService } from './database-migrations.service';
@Module({ providers: [DatabaseMigrationsService] })
export class DatabaseMigrationsModule {}

View File

@@ -0,0 +1,41 @@
import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common';
import { DataSource } from 'typeorm';
@Injectable()
export class DatabaseMigrationsService implements OnApplicationBootstrap {
private readonly logger = new Logger(DatabaseMigrationsService.name);
constructor(private readonly dataSource: DataSource) {}
async onApplicationBootstrap(): Promise<void> {
await this.normalizeClassDates();
}
private async normalizeClassDates(): Promise<void> {
const driver = this.dataSource.options.type;
const dateExpression = (column: string) =>
driver === 'mysql' ? `DATE(${column})` : `substr(${column}, 1, 10)`;
const lengthFunction = driver === 'mysql' ? 'CHAR_LENGTH' : 'length';
const result = await this.dataSource.transaction((manager) =>
manager.query(`
UPDATE classes
SET
start_date = CASE
WHEN start_date IS NULL OR start_date = '' THEN start_date
ELSE ${dateExpression('start_date')}
END,
end_date = CASE
WHEN end_date IS NULL OR end_date = '' THEN end_date
ELSE ${dateExpression('end_date')}
END
WHERE
(start_date IS NOT NULL AND ${lengthFunction}(start_date) > 10)
OR (end_date IS NOT NULL AND ${lengthFunction}(end_date) > 10)
`),
);
const affected = typeof result?.changes === 'number' ? result.changes : result?.affectedRows;
if (affected) this.logger.log(`已规范化 ${affected} 条班级日期数据`);
}
}

View File

@@ -0,0 +1,15 @@
import { normalizeDateOnly } from './date-normalization';
describe('normalizeDateOnly', () => {
it('keeps date-only values unchanged', () => {
expect(normalizeDateOnly('2026-07-02')).toBe('2026-07-02');
});
it('converts legacy ISO timestamps to their UTC calendar date', () => {
expect(normalizeDateOnly('2026-07-01T16:00:00.000Z')).toBe('2026-07-01');
});
it('rejects unsupported date formats', () => {
expect(() => normalizeDateOnly('07/01/2026')).toThrow('无效日期格式');
});
});

View File

@@ -0,0 +1,15 @@
const DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
const ISO_DATE_PREFIX_PATTERN = /^(\d{4}-\d{2}-\d{2})T/;
export function normalizeDateOnly(value?: string | null): string | null | undefined {
if (value == null || value === '') return value;
if (DATE_ONLY_PATTERN.test(value)) return value;
const isoPrefix = ISO_DATE_PREFIX_PATTERN.exec(value)?.[1];
if (isoPrefix) {
const date = new Date(value);
if (!Number.isNaN(date.getTime())) return date.toISOString().slice(0, 10);
}
throw new Error(`无效日期格式: ${value}`);
}