feat(task1): restructure directories for turborepo monorepo
- Move backend/ to apps/server/ via git mv - Move frontend/ to apps/admin/ via git mv - Create packages/typescript-config/ with base, nestjs, and react-vite presets
This commit is contained in:
36
apps/server/src/entities/bill-item.entity.ts
Normal file
36
apps/server/src/entities/bill-item.entity.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { Bill } from './bill.entity';
|
||||
|
||||
@Entity('bill_items')
|
||||
export class BillItem {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'bill_id' })
|
||||
billId: number;
|
||||
|
||||
@Column({ name: 'room_id', nullable: true })
|
||||
roomId: number;
|
||||
|
||||
@Column({ name: 'expense_type', length: 20, nullable: true })
|
||||
expenseType: string;
|
||||
|
||||
@Column({ length: 200, nullable: true })
|
||||
description: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
days: number;
|
||||
|
||||
@Column({ name: 'total_room_days', nullable: true })
|
||||
totalRoomDays: number;
|
||||
|
||||
@Column({ name: 'room_total_amount', type: 'decimal', precision: 10, scale: 2, nullable: true })
|
||||
roomTotalAmount: number;
|
||||
|
||||
@Column({ name: 'student_amount', type: 'decimal', precision: 10, scale: 2, nullable: true })
|
||||
studentAmount: number;
|
||||
|
||||
@ManyToOne(() => Bill, (b) => b.items)
|
||||
@JoinColumn({ name: 'bill_id' })
|
||||
bill: Bill;
|
||||
}
|
||||
40
apps/server/src/entities/bill.entity.ts
Normal file
40
apps/server/src/entities/bill.entity.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne, JoinColumn, OneToMany } from 'typeorm';
|
||||
import { Student } from './student.entity';
|
||||
import { BillItem } from './bill-item.entity';
|
||||
|
||||
@Entity('bills')
|
||||
export class Bill {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'student_id' })
|
||||
studentId: number;
|
||||
|
||||
@Column({ name: 'period_start', type: 'date' })
|
||||
periodStart: string;
|
||||
|
||||
@Column({ name: 'period_end', type: 'date' })
|
||||
periodEnd: string;
|
||||
|
||||
@Column({ name: 'shared_amount', type: 'decimal', precision: 10, scale: 2, default: 0 })
|
||||
sharedAmount: number;
|
||||
|
||||
@Column({ name: 'personal_amount', type: 'decimal', precision: 10, scale: 2, default: 0 })
|
||||
personalAmount: number;
|
||||
|
||||
@Column({ name: 'total_amount', type: 'decimal', precision: 10, scale: 2, default: 0 })
|
||||
totalAmount: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 20, default: 'draft' })
|
||||
status: string;
|
||||
|
||||
@CreateDateColumn({ name: 'generated_at' })
|
||||
generatedAt: Date;
|
||||
|
||||
@ManyToOne(() => Student, (s) => s.bills)
|
||||
@JoinColumn({ name: 'student_id' })
|
||||
student: Student;
|
||||
|
||||
@OneToMany(() => BillItem, (bi) => bi.bill)
|
||||
items: BillItem[];
|
||||
}
|
||||
58
apps/server/src/entities/classroom-rental.entity.ts
Normal file
58
apps/server/src/entities/classroom-rental.entity.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn, Index } from 'typeorm';
|
||||
import { Classroom } from './classroom.entity';
|
||||
import { Tenant } from './tenant.entity';
|
||||
|
||||
@Entity('classroom_rentals')
|
||||
@Index(['classroomId', 'startDate', 'endDate'])
|
||||
export class ClassroomRental {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'classroom_id' })
|
||||
classroomId: number;
|
||||
|
||||
@ManyToOne(() => Classroom)
|
||||
@JoinColumn({ name: 'classroom_id' })
|
||||
classroom: Classroom;
|
||||
|
||||
@Column({ name: 'tenant_id' })
|
||||
tenantId: number;
|
||||
|
||||
@ManyToOne(() => Tenant)
|
||||
@JoinColumn({ name: 'tenant_id' })
|
||||
tenant: Tenant;
|
||||
|
||||
@Column({ name: 'start_date', type: 'date' })
|
||||
startDate: string;
|
||||
|
||||
@Column({ name: 'end_date', type: 'date' })
|
||||
endDate: string;
|
||||
|
||||
// 合同 PDF 相对路径(相对 UPLOAD_DIR),仅存文件名
|
||||
@Column({ name: 'contract_path', length: 255, nullable: true })
|
||||
contractPath: string;
|
||||
|
||||
@Column({ name: 'contract_original_name', length: 255, nullable: true })
|
||||
contractOriginalName: string;
|
||||
|
||||
@Column({ name: 'daily_rate', type: 'decimal', precision: 10, scale: 2, nullable: true })
|
||||
dailyRate: number;
|
||||
|
||||
@Column({ name: 'total_amount', type: 'decimal', precision: 10, scale: 2, nullable: true })
|
||||
totalAmount: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 20, default: 'active' })
|
||||
status: string; // active / ended / cancelled
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
notes: string;
|
||||
|
||||
@Column({ name: 'created_by', nullable: true })
|
||||
createdBy: number;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt: Date;
|
||||
}
|
||||
37
apps/server/src/entities/classroom.entity.ts
Normal file
37
apps/server/src/entities/classroom.entity.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
|
||||
|
||||
@Entity('classrooms')
|
||||
export class Classroom {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ length: 50 })
|
||||
name: string;
|
||||
|
||||
@Column({ length: 50, nullable: true })
|
||||
building: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
floor: number;
|
||||
|
||||
@Column({ default: 30 })
|
||||
capacity: number;
|
||||
|
||||
@Column({ name: 'room_type', type: 'varchar', length: 20, default: '大' })
|
||||
roomType: string; // 大 / 次大 / 小
|
||||
|
||||
@Column({ name: 'course_type', length: 50, nullable: true })
|
||||
courseType: string;
|
||||
|
||||
@Column({ length: 50, nullable: true })
|
||||
supervisor: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 20, default: 'available' })
|
||||
status: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
notes: string;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
}
|
||||
46
apps/server/src/entities/deposit.entity.ts
Normal file
46
apps/server/src/entities/deposit.entity.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { Student } from './student.entity';
|
||||
|
||||
@Entity('deposits')
|
||||
export class Deposit {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'student_id' })
|
||||
studentId: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 10, scale: 2, default: 500 })
|
||||
amount: number;
|
||||
|
||||
// paid: 已缴 | refunded: 已退 | deducted: 已扣除(部分或全部)
|
||||
@Column({ type: 'varchar', length: 20, default: 'paid' })
|
||||
status: string;
|
||||
|
||||
@Column({ name: 'paid_date', type: 'date' })
|
||||
paidDate: string;
|
||||
|
||||
@Column({ name: 'refund_date', type: 'date', nullable: true })
|
||||
refundDate: string;
|
||||
|
||||
@Column({ name: 'refund_amount', type: 'decimal', precision: 10, scale: 2, nullable: true })
|
||||
refundAmount: number;
|
||||
|
||||
@Column({ name: 'deduction_amount', type: 'decimal', precision: 10, scale: 2, default: 0 })
|
||||
deductionAmount: number;
|
||||
|
||||
@Column({ name: 'deduction_reason', type: 'text', nullable: true })
|
||||
deductionReason: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
notes: string;
|
||||
|
||||
@Column({ name: 'recorded_by', nullable: true })
|
||||
recordedBy: number;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@ManyToOne(() => Student, { eager: true })
|
||||
@JoinColumn({ name: 'student_id' })
|
||||
student: Student;
|
||||
}
|
||||
15
apps/server/src/entities/index.ts
Normal file
15
apps/server/src/entities/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export { Student } from './student.entity';
|
||||
export { Room } from './room.entity';
|
||||
export { Occupancy } from './occupancy.entity';
|
||||
export { RoomExpense } from './room-expense.entity';
|
||||
export { PersonalExpense } from './personal-expense.entity';
|
||||
export { Bill } from './bill.entity';
|
||||
export { BillItem } from './bill-item.entity';
|
||||
export { User } from './user.entity';
|
||||
export { OperationLog } from './operation-log.entity';
|
||||
export { Deposit } from './deposit.entity';
|
||||
export { Classroom } from './classroom.entity';
|
||||
export { Tenant } from './tenant.entity';
|
||||
export { ClassroomRental } from './classroom-rental.entity';
|
||||
export { Permission } from './permission.entity';
|
||||
export { Role } from './role.entity';
|
||||
44
apps/server/src/entities/occupancy.entity.ts
Normal file
44
apps/server/src/entities/occupancy.entity.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { Student } from './student.entity';
|
||||
import { Room } from './room.entity';
|
||||
|
||||
@Entity('occupancies')
|
||||
export class Occupancy {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'student_id' })
|
||||
studentId: number;
|
||||
|
||||
@Column({ name: 'room_id' })
|
||||
roomId: number;
|
||||
|
||||
@Column({ name: 'check_in_date', type: 'date' })
|
||||
checkInDate: string;
|
||||
|
||||
@Column({ name: 'check_out_date', type: 'date', nullable: true })
|
||||
checkOutDate: string;
|
||||
|
||||
@Column({ name: 'billing_start_date', type: 'date' })
|
||||
billingStartDate: string;
|
||||
|
||||
@Column({ name: 'billing_end_date', type: 'date', nullable: true })
|
||||
billingEndDate: string;
|
||||
|
||||
@Column({ name: 'check_out_reason', length: 100, nullable: true })
|
||||
checkOutReason: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
notes: string;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@ManyToOne(() => Student, (s) => s.occupancies)
|
||||
@JoinColumn({ name: 'student_id' })
|
||||
student: Student;
|
||||
|
||||
@ManyToOne(() => Room, (r) => r.occupancies)
|
||||
@JoinColumn({ name: 'room_id' })
|
||||
room: Room;
|
||||
}
|
||||
40
apps/server/src/entities/operation-log.entity.ts
Normal file
40
apps/server/src/entities/operation-log.entity.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
|
||||
|
||||
@Entity('operation_logs')
|
||||
export class OperationLog {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'user_id', nullable: true })
|
||||
userId: number;
|
||||
|
||||
@Column({ length: 50, nullable: true })
|
||||
username: string;
|
||||
|
||||
@Column({ length: 50 })
|
||||
module: string;
|
||||
|
||||
@Column({ length: 50 })
|
||||
action: string;
|
||||
|
||||
@Column({ name: 'target_id', nullable: true })
|
||||
targetId: number;
|
||||
|
||||
@Column({ name: 'target_type', length: 50, nullable: true })
|
||||
targetType: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
detail: string;
|
||||
|
||||
@Column({ name: 'ip_address', length: 50, nullable: true })
|
||||
ipAddress: string;
|
||||
|
||||
@Column({ name: 'user_agent', length: 500, nullable: true })
|
||||
userAgent: string;
|
||||
|
||||
@Column({ name: 'status', length: 20, nullable: true, default: 'success' })
|
||||
status: string;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
}
|
||||
19
apps/server/src/entities/permission.entity.ts
Normal file
19
apps/server/src/entities/permission.entity.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
|
||||
@Entity('permissions')
|
||||
export class Permission {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, unique: true })
|
||||
code: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50 })
|
||||
name: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 30 })
|
||||
group: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 200, nullable: true })
|
||||
description: string;
|
||||
}
|
||||
36
apps/server/src/entities/personal-expense.entity.ts
Normal file
36
apps/server/src/entities/personal-expense.entity.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { Student } from './student.entity';
|
||||
|
||||
@Entity('personal_expenses')
|
||||
export class PersonalExpense {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'student_id' })
|
||||
studentId: number;
|
||||
|
||||
@Column({ name: 'room_id', nullable: true })
|
||||
roomId: number;
|
||||
|
||||
@Column({ name: 'expense_type', type: 'varchar', length: 20 })
|
||||
expenseType: string;
|
||||
|
||||
@Column({ type: 'decimal', precision: 10, scale: 2 })
|
||||
amount: number;
|
||||
|
||||
@Column({ name: 'expense_date', type: 'date' })
|
||||
expenseDate: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description: string;
|
||||
|
||||
@Column({ name: 'recorded_by', nullable: true })
|
||||
recordedBy: number;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@ManyToOne(() => Student, (s) => s.personalExpenses)
|
||||
@JoinColumn({ name: 'student_id' })
|
||||
student: Student;
|
||||
}
|
||||
38
apps/server/src/entities/role.entity.ts
Normal file
38
apps/server/src/entities/role.entity.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToMany, JoinTable } from 'typeorm';
|
||||
import { Permission } from './permission.entity';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity('roles')
|
||||
export class Role {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 30, unique: true })
|
||||
name: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 200, nullable: true })
|
||||
description: string;
|
||||
|
||||
@Column({ name: 'is_system', type: 'boolean', default: false })
|
||||
isSystem: boolean;
|
||||
|
||||
@Column({ type: 'tinyint', default: 1 })
|
||||
status: number;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt: Date;
|
||||
|
||||
@ManyToMany(() => Permission)
|
||||
@JoinTable({
|
||||
name: 'role_permissions',
|
||||
joinColumn: { name: 'role_id', referencedColumnName: 'id' },
|
||||
inverseJoinColumn: { name: 'permission_id', referencedColumnName: 'id' },
|
||||
})
|
||||
permissions: Permission[];
|
||||
|
||||
@ManyToMany(() => User, (user) => user.roles)
|
||||
users: User[];
|
||||
}
|
||||
36
apps/server/src/entities/room-expense.entity.ts
Normal file
36
apps/server/src/entities/room-expense.entity.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { Room } from './room.entity';
|
||||
|
||||
@Entity('room_expenses')
|
||||
export class RoomExpense {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'room_id' })
|
||||
roomId: number;
|
||||
|
||||
@Column({ name: 'expense_type', type: 'varchar', length: 20 })
|
||||
expenseType: string;
|
||||
|
||||
@Column({ type: 'decimal', precision: 10, scale: 2 })
|
||||
amount: number;
|
||||
|
||||
@Column({ name: 'period_start', type: 'date' })
|
||||
periodStart: string;
|
||||
|
||||
@Column({ name: 'period_end', type: 'date' })
|
||||
periodEnd: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description: string;
|
||||
|
||||
@Column({ name: 'recorded_by', nullable: true })
|
||||
recordedBy: number;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@ManyToOne(() => Room, (r) => r.roomExpenses)
|
||||
@JoinColumn({ name: 'room_id' })
|
||||
room: Room;
|
||||
}
|
||||
39
apps/server/src/entities/room.entity.ts
Normal file
39
apps/server/src/entities/room.entity.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, OneToMany } from 'typeorm';
|
||||
import { Occupancy } from './occupancy.entity';
|
||||
import { RoomExpense } from './room-expense.entity';
|
||||
|
||||
@Entity('rooms')
|
||||
export class Room {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'room_number', length: 20, unique: true })
|
||||
roomNumber: string;
|
||||
|
||||
@Column({ length: 50, nullable: true })
|
||||
building: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
floor: number;
|
||||
|
||||
@Column()
|
||||
capacity: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 20, default: 'available' })
|
||||
status: string;
|
||||
|
||||
@Column({ name: 'room_type', length: 20, nullable: true })
|
||||
roomType: string;
|
||||
|
||||
@Column({ length: 10, nullable: true })
|
||||
gender: string;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@OneToMany(() => Occupancy, (o) => o.room)
|
||||
occupancies: Occupancy[];
|
||||
|
||||
@OneToMany(() => RoomExpense, (e) => e.room)
|
||||
roomExpenses: RoomExpense[];
|
||||
}
|
||||
55
apps/server/src/entities/student.entity.ts
Normal file
55
apps/server/src/entities/student.entity.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';
|
||||
import { Occupancy } from './occupancy.entity';
|
||||
import { PersonalExpense } from './personal-expense.entity';
|
||||
import { Bill } from './bill.entity';
|
||||
|
||||
@Entity('students')
|
||||
export class Student {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ length: 50 })
|
||||
name: string;
|
||||
|
||||
@Column({ length: 20, nullable: true })
|
||||
phone: string;
|
||||
|
||||
@Column({ name: 'id_number', length: 30, nullable: true })
|
||||
idNumber: string;
|
||||
|
||||
@Column({ length: 10, nullable: true })
|
||||
gender: string;
|
||||
|
||||
@Column({ length: 20, nullable: true })
|
||||
ethnicity: string;
|
||||
|
||||
@Column({ name: 'emergency_contact', length: 50, nullable: true })
|
||||
emergencyContact: string;
|
||||
|
||||
@Column({ name: 'emergency_phone', length: 20, nullable: true })
|
||||
emergencyPhone: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 20, default: 'active' })
|
||||
status: string;
|
||||
|
||||
@Column({ length: 100, nullable: true })
|
||||
organization: string;
|
||||
|
||||
@Column({ length: 50, nullable: true })
|
||||
supervisor: string;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt: Date;
|
||||
|
||||
@OneToMany(() => Occupancy, (o) => o.student)
|
||||
occupancies: Occupancy[];
|
||||
|
||||
@OneToMany(() => PersonalExpense, (e) => e.student)
|
||||
personalExpenses: PersonalExpense[];
|
||||
|
||||
@OneToMany(() => Bill, (b) => b.student)
|
||||
bills: Bill[];
|
||||
}
|
||||
32
apps/server/src/entities/tenant.entity.ts
Normal file
32
apps/server/src/entities/tenant.entity.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
||||
|
||||
@Entity('tenants')
|
||||
export class Tenant {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ length: 100 })
|
||||
name: string;
|
||||
|
||||
@Column({ length: 50, nullable: true })
|
||||
contact: string;
|
||||
|
||||
@Column({ length: 30, nullable: true })
|
||||
phone: string;
|
||||
|
||||
// 可视化颜色(hex),为空时由后端自动分配
|
||||
@Column({ length: 20, nullable: true })
|
||||
color: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
notes: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 20, default: 'active' })
|
||||
status: string; // active / archived
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt: Date;
|
||||
}
|
||||
43
apps/server/src/entities/user.entity.ts
Normal file
43
apps/server/src/entities/user.entity.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, ManyToMany, JoinTable } from 'typeorm';
|
||||
import { Role } from './role.entity';
|
||||
|
||||
@Entity('users')
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ length: 50, unique: true })
|
||||
username: string;
|
||||
|
||||
@Column({ name: 'password_hash', length: 255 })
|
||||
passwordHash: string;
|
||||
|
||||
// 移除: @Column({ type: 'varchar', length: 20, default: 'operator' })
|
||||
// role: string;
|
||||
|
||||
@Column({ length: 50, nullable: true })
|
||||
name: string;
|
||||
|
||||
// 移除: @Column({ name: 'allowed_menus', type: 'text', nullable: true })
|
||||
// allowedMenus: string;
|
||||
|
||||
@Column({ name: 'is_active', default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@Column({ name: 'last_login_at', type: 'datetime', nullable: true })
|
||||
lastLoginAt: Date;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt: Date;
|
||||
|
||||
@ManyToMany(() => Role, (role) => role.users)
|
||||
@JoinTable({
|
||||
name: 'user_roles',
|
||||
joinColumn: { name: 'user_id', referencedColumnName: 'id' },
|
||||
inverseJoinColumn: { name: 'role_id', referencedColumnName: 'id' },
|
||||
})
|
||||
roles: Role[];
|
||||
}
|
||||
Reference in New Issue
Block a user