feat: complete PRD verification fixes and deployment readiness

- Add CommonModule to provide/export CampusScope across business modules
- Replace DepartmentsModule imports with CommonModule in 11 modules
- Add missing Class/Tenant repository imports to StudentsModule and SchedulesModule
- Fix service specs with CampusScope, ClassRepository, StudentRepository mocks
- Move better-sqlite3 to optionalDependencies for production Docker builds
- Rewrite Dockerfiles for monorepo root build context
- Update docker-compose.yml service build contexts
This commit is contained in:
2026-07-06 01:55:35 +08:00
parent 5d102ca3c4
commit 7b298a615c
22 changed files with 109 additions and 46 deletions

View File

@@ -54,7 +54,7 @@ import { ClassroomRentalsModule } from './classroom-rentals/classroom-rentals.mo
import { SyncModule } from './sync/sync.module';
import { NotificationsModule } from './notifications/notifications.module';
import { DepartmentsModule } from './departments/departments.module';
import { CampusScope } from './common/campus-scope';
import { CommonModule } from './common/common.module';
import { CampusScopeMiddleware } from './common/campus-scope.middleware';
@Module({
@@ -139,9 +139,9 @@ import { CampusScopeMiddleware } from './common/campus-scope.middleware';
SyncModule,
NotificationsModule,
DepartmentsModule,
CommonModule,
],
providers: [
CampusScope,
{ provide: APP_GUARD, useClass: ThrottlerGuard },
{ provide: APP_GUARD, useClass: JwtAuthGuard },
{ provide: APP_GUARD, useClass: PermissionGuard },

View File

@@ -4,12 +4,12 @@ import { AttendanceRecord, DingAttendanceRaw, Student, Class } from '../entities
import { AttendanceService } from './attendance.service';
import { AttendanceController } from './attendance.controller';
import { OperationLogsModule } from '../operation-logs/operation-logs.module';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
@Module({
imports: [
TypeOrmModule.forFeature([AttendanceRecord, DingAttendanceRaw, Student, Class]),
OperationLogsModule,
DepartmentsModule,
CommonModule,
],
controllers: [AttendanceController],
providers: [AttendanceService],

View File

@@ -5,6 +5,9 @@ import { Repository } from 'typeorm';
import { AttendanceService } from './attendance.service';
import { AttendanceRecord } from '../entities/attendance-record.entity';
import { DingAttendanceRaw } from '../entities/ding-attendance-raw.entity';
import { Class } from '../entities/class.entity';
import { Student } from '../entities/student.entity';
import { CampusScope } from '../common/campus-scope';
import { BatchCreateAttendanceDto } from './dto/attendance.dto';
describe('AttendanceService — batchCreate', () => {
@@ -29,12 +32,18 @@ describe('AttendanceService — batchCreate', () => {
};
const mockDingRepo = {};
const mockClassRepo = { find: jest.fn().mockResolvedValue([]) };
const mockStudentRepo = { find: jest.fn().mockResolvedValue([]) };
const mockCampusScope = { getScopeDepartmentIds: jest.fn().mockResolvedValue(null), filter: jest.fn((w: any) => w) };
const module: TestingModule = await Test.createTestingModule({
providers: [
AttendanceService,
{ provide: getRepositoryToken(AttendanceRecord), useValue: mockRepo },
{ provide: getRepositoryToken(DingAttendanceRaw), useValue: mockDingRepo },
{ provide: getRepositoryToken(Class), useValue: mockClassRepo },
{ provide: getRepositoryToken(Student), useValue: mockStudentRepo },
{ provide: CampusScope, useValue: mockCampusScope },
],
}).compile();

View File

@@ -1,6 +1,6 @@
import { Module } from '@nestjs/common';
import { NotificationsModule } from '../notifications/notifications.module';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Bill } from '../entities/bill.entity';
import { BillItem } from '../entities/bill-item.entity';
@@ -27,7 +27,7 @@ import { BillsController } from './bills.controller';
Student,
]),
NotificationsModule,
DepartmentsModule,
CommonModule,
],
controllers: [BillsController],
providers: [BillsService, BillsExportService],

View File

@@ -9,6 +9,7 @@ import { PersonalExpense } from '../entities/personal-expense.entity';
import { Occupancy } from '../entities/occupancy.entity';
import { Room } from '../entities/room.entity';
import { Deposit } from '../entities/deposit.entity';
import { CampusScope } from '../common/campus-scope';
type MockRepository<T> = Partial<Record<keyof Repository<T>, jest.Mock>>;
@@ -56,7 +57,7 @@ describe('BillsService — generateBills', () => {
occRepo = mockRepo<Occupancy>();
roomRepo = mockRepo<Room>();
depositRepo = mockRepo<Deposit>();
dataSource = { transaction: jest.fn() };
dataSource = { transaction: jest.fn(), query: jest.fn().mockResolvedValue([]) };
const module: TestingModule = await Test.createTestingModule({
providers: [
@@ -69,6 +70,7 @@ describe('BillsService — generateBills', () => {
{ provide: getRepositoryToken(Room), useValue: roomRepo },
{ provide: getRepositoryToken(Deposit), useValue: depositRepo },
{ provide: DataSource, useValue: dataSource },
{ provide: CampusScope, useValue: { getScopeDepartmentIds: jest.fn().mockResolvedValue(null), filter: jest.fn((w: any) => w) } },
],
}).compile();

View File

@@ -1,6 +1,6 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
import { Class, ClassStudent, ClassTeacher } from '../entities';
import { ClassesService } from './classes.service';
import { ClassesController } from './classes.controller';
@@ -8,7 +8,7 @@ import { OperationLogsModule } from '../operation-logs/operation-logs.module';
import { NotificationsModule } from '../notifications/notifications.module';
@Module({
imports: [TypeOrmModule.forFeature([Class, ClassStudent, ClassTeacher]), OperationLogsModule, NotificationsModule, DepartmentsModule],
imports: [TypeOrmModule.forFeature([Class, ClassStudent, ClassTeacher]), OperationLogsModule, NotificationsModule, CommonModule],
controllers: [ClassesController],
providers: [ClassesService],
exports: [ClassesService],

View File

@@ -7,10 +7,10 @@ import { ClassSchedule } from '../entities/class-schedule.entity';
import { ClassroomRentalsService } from './classroom-rentals.service';
import { ClassroomRentalsController } from './classroom-rentals.controller';
import { OperationLogsModule } from '../operation-logs/operation-logs.module';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
@Module({
imports: [TypeOrmModule.forFeature([ClassroomRental, Classroom, Tenant, ClassSchedule]), OperationLogsModule, DepartmentsModule],
imports: [TypeOrmModule.forFeature([ClassroomRental, Classroom, Tenant, ClassSchedule]), OperationLogsModule, CommonModule],
controllers: [ClassroomRentalsController],
providers: [ClassroomRentalsService],
exports: [ClassroomRentalsService],

View File

@@ -6,10 +6,10 @@ import { ClassSchedule } from '../entities/class-schedule.entity';
import { ClassroomsService } from './classrooms.service';
import { ClassroomsController } from './classrooms.controller';
import { OperationLogsModule } from '../operation-logs/operation-logs.module';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
@Module({
imports: [TypeOrmModule.forFeature([Classroom, ClassroomRental, ClassSchedule]), OperationLogsModule, DepartmentsModule],
imports: [TypeOrmModule.forFeature([Classroom, ClassroomRental, ClassSchedule]), OperationLogsModule, CommonModule],
controllers: [ClassroomsController],
providers: [ClassroomsService],
exports: [ClassroomsService],

View File

@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { CampusScope } from './campus-scope';
import { CampusScopeMiddleware } from './campus-scope.middleware';
import { DepartmentsModule } from '../departments/departments.module';
@Module({
imports: [DepartmentsModule],
providers: [CampusScope, CampusScopeMiddleware],
exports: [CampusScope, CampusScopeMiddleware, DepartmentsModule],
})
export class CommonModule {}

View File

@@ -13,11 +13,11 @@ import { Deposit } from '../entities/deposit.entity';
import { ClassroomRental } from '../entities/classroom-rental.entity';
import { ClassTeacher } from '../entities/class-teacher.entity';
import { DashboardService } from './dashboard.service';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
import { DashboardController } from './dashboard.controller';
@Module({
imports: [TypeOrmModule.forFeature([Room, Student, Occupancy, Bill, RoomExpense, Classroom, ClassSchedule, AttendanceRecord, Class, Deposit, ClassroomRental, ClassTeacher]), DepartmentsModule],
imports: [TypeOrmModule.forFeature([Room, Student, Occupancy, Bill, RoomExpense, Classroom, ClassSchedule, AttendanceRecord, Class, Deposit, ClassroomRental, ClassTeacher]), CommonModule],
controllers: [DashboardController],
providers: [DashboardService],
})

View File

@@ -4,13 +4,13 @@ import { Student } from '../entities/student.entity';
import { Deposit } from '../entities/deposit.entity';
import { DepositInstallment } from '../entities/deposit-installment.entity';
import { DepositsService } from './deposits.service';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
import { DepositsController } from './deposits.controller';
import { OperationLogsModule } from '../operation-logs/operation-logs.module';
import { NotificationsModule } from '../notifications/notifications.module';
@Module({
imports: [TypeOrmModule.forFeature([Deposit, DepositInstallment, Student]), OperationLogsModule, NotificationsModule, DepartmentsModule],
imports: [TypeOrmModule.forFeature([Deposit, DepositInstallment, Student]), OperationLogsModule, NotificationsModule, CommonModule],
controllers: [DepositsController],
providers: [DepositsService],
exports: [DepositsService],

View File

@@ -1,6 +1,6 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
import { RoomExpense } from '../entities/room-expense.entity';
import { PersonalExpense } from '../entities/personal-expense.entity';
import { Room } from '../entities/room.entity';
@@ -13,7 +13,7 @@ import { OperationLogsModule } from '../operation-logs/operation-logs.module';
imports: [
TypeOrmModule.forFeature([RoomExpense, PersonalExpense, Room, Student]),
OperationLogsModule,
DepartmentsModule,
CommonModule,
],
controllers: [ExpensesController],
providers: [ExpensesService],

View File

@@ -8,10 +8,10 @@ import { OccupanciesService } from './occupancies.service';
import { OccupanciesController } from './occupancies.controller';
import { OperationLogsModule } from '../operation-logs/operation-logs.module';
import { NotificationsModule } from '../notifications/notifications.module';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
@Module({
imports: [TypeOrmModule.forFeature([Occupancy, Room, Student, Deposit]), OperationLogsModule, NotificationsModule, DepartmentsModule],
imports: [TypeOrmModule.forFeature([Occupancy, Room, Student, Deposit]), OperationLogsModule, NotificationsModule, CommonModule],
controllers: [OccupanciesController],
providers: [OccupanciesService],
exports: [OccupanciesService],

View File

@@ -6,10 +6,10 @@ import { RoomExpense } from '../entities/room-expense.entity';
import { RoomsService } from './rooms.service';
import { RoomsController } from './rooms.controller';
import { OperationLogsModule } from '../operation-logs/operation-logs.module';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
@Module({
imports: [TypeOrmModule.forFeature([Room, Occupancy, RoomExpense]), OperationLogsModule, DepartmentsModule],
imports: [TypeOrmModule.forFeature([Room, Occupancy, RoomExpense]), OperationLogsModule, CommonModule],
controllers: [RoomsController],
providers: [RoomsService],
exports: [RoomsService],

View File

@@ -1,14 +1,14 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ClassSchedule } from '../entities';
import { ClassSchedule, Class } from '../entities';
import { SchedulesService } from './schedules.service';
import { SchedulesController } from './schedules.controller';
import { OperationLogsModule } from '../operation-logs/operation-logs.module';
import { NotificationsModule } from '../notifications/notifications.module';
import { DepartmentsModule } from '../departments/departments.module';
import { CommonModule } from '../common/common.module';
@Module({
imports: [TypeOrmModule.forFeature([ClassSchedule]), OperationLogsModule, NotificationsModule, DepartmentsModule],
imports: [TypeOrmModule.forFeature([ClassSchedule, Class]), OperationLogsModule, NotificationsModule, CommonModule],
controllers: [SchedulesController],
providers: [SchedulesService],
exports: [SchedulesService],

View File

@@ -4,6 +4,8 @@ import { ConflictException } from '@nestjs/common';
import { Repository } from 'typeorm';
import { SchedulesService } from './schedules.service';
import { ClassSchedule } from '../entities/class-schedule.entity';
import { Class } from '../entities/class.entity';
import { CampusScope } from '../common/campus-scope';
/** Build a mock query-builder where each chain method returns `this`. */
function mockQueryBuilder<T>(results: T[] = []) {
@@ -28,6 +30,8 @@ describe('SchedulesService — checkConflict', () => {
providers: [
SchedulesService,
{ provide: getRepositoryToken(ClassSchedule), useValue: mockRepo },
{ provide: getRepositoryToken(Class), useValue: { find: jest.fn().mockResolvedValue([]) } },
{ provide: CampusScope, useValue: { getScopeDepartmentIds: jest.fn().mockResolvedValue(null), filter: jest.fn((w: any) => w) } },
],
}).compile();

View File

@@ -1,14 +1,16 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Student } from '../entities/student.entity';
import { DepartmentsModule } from '../departments/departments.module';
import { Class } from '../entities/class.entity';
import { Tenant } from '../entities/tenant.entity';
import { CommonModule } from '../common/common.module';
import { ClassStudent } from '../entities/class-student.entity';
import { AttendanceRecord } from '../entities/attendance-record.entity';
import { StudentsService } from './students.service';
import { StudentsController } from './students.controller';
@Module({
imports: [TypeOrmModule.forFeature([Student, ClassStudent, AttendanceRecord]), DepartmentsModule],
imports: [TypeOrmModule.forFeature([Student, Class, ClassStudent, AttendanceRecord, Tenant]), CommonModule],
controllers: [StudentsController],
providers: [StudentsService],
exports: [StudentsService],