Skip to content

Commit b6023db

Browse files
committed
feat:学生Tag
1 parent fa75b88 commit b6023db

File tree

19 files changed

+647
-20
lines changed

19 files changed

+647
-20
lines changed

src/main/db/DbManager.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { Context, Service } from '../../shared/kernel'
22
import { DataSource } from 'typeorm'
33
import path from 'path'
44
import fs from 'fs'
5-
import {
6-
ReasonEntity,
7-
ScoreEventEntity,
8-
SettlementEntity,
9-
SettingEntity,
10-
StudentEntity
11-
} from './entities'
5+
import { StudentEntity } from './entities/StudentEntity'
6+
import { ReasonEntity } from './entities/ReasonEntity'
7+
import { ScoreEventEntity } from './entities/ScoreEventEntity'
8+
import { SettlementEntity } from './entities/SettlementEntity'
9+
import { SettingEntity } from './entities/SettingEntity'
10+
import { TagEntity } from './entities/TagEntity'
11+
import { StudentTagEntity } from './entities/StudentTagEntity'
1212
import { migrations } from './migrations'
1313

1414
declare module '../../shared/kernel' {
@@ -29,7 +29,7 @@ export class DbManager extends Service {
2929
this.dataSource = new DataSource({
3030
type: 'better-sqlite3',
3131
database: dbPath,
32-
entities: [StudentEntity, ReasonEntity, ScoreEventEntity, SettlementEntity, SettingEntity],
32+
entities: [StudentEntity, ReasonEntity, ScoreEventEntity, SettlementEntity, SettingEntity, TagEntity, StudentTagEntity],
3333
migrations,
3434
synchronize: false,
3535
logging: false

src/main/db/entities/StudentEntity.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
1+
import { Column, Entity, PrimaryGeneratedColumn, ManyToMany, JoinTable } from 'typeorm'
2+
import { TagEntity } from './TagEntity'
23

34
@Entity({ name: 'students' })
45
export class StudentEntity {
@@ -8,6 +9,9 @@ export class StudentEntity {
89
@Column({ type: 'text' })
910
name!: string
1011

12+
@Column({ type: 'text', default: '[]' })
13+
tags!: string
14+
1115
@Column({ type: 'integer', default: 0 })
1216
score!: number
1317

@@ -19,4 +23,12 @@ export class StudentEntity {
1923

2024
@Column({ type: 'text', default: () => 'CURRENT_TIMESTAMP' })
2125
updated_at!: string
26+
27+
@ManyToMany(() => TagEntity, { cascade: true })
28+
@JoinTable({
29+
name: 'student_tags',
30+
joinColumn: { name: 'student_id', referencedColumnName: 'id' },
31+
inverseJoinColumn: { name: 'tag_id', referencedColumnName: 'id' }
32+
})
33+
tagEntities!: TagEntity[]
2234
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Column, Entity, PrimaryGeneratedColumn, ManyToOne, JoinColumn, Index } from 'typeorm'
2+
import { StudentEntity } from './StudentEntity'
3+
import { TagEntity } from './TagEntity'
4+
5+
@Entity({ name: 'student_tags' })
6+
@Index(['student_id', 'tag_id'], { unique: true })
7+
export class StudentTagEntity {
8+
@PrimaryGeneratedColumn()
9+
id!: number
10+
11+
@Column({ name: 'student_id', type: 'integer' })
12+
student_id!: number
13+
14+
@Column({ name: 'tag_id', type: 'integer' })
15+
tag_id!: number
16+
17+
@ManyToOne(() => StudentEntity, { onDelete: 'CASCADE' })
18+
@JoinColumn({ name: 'student_id' })
19+
student!: StudentEntity
20+
21+
@ManyToOne(() => TagEntity, { onDelete: 'CASCADE' })
22+
@JoinColumn({ name: 'tag_id' })
23+
tag!: TagEntity
24+
25+
@Column({ type: 'text', default: () => 'CURRENT_TIMESTAMP' })
26+
created_at!: string
27+
}

src/main/db/entities/TagEntity.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
2+
3+
@Entity({ name: 'tags' })
4+
export class TagEntity {
5+
@PrimaryGeneratedColumn()
6+
id!: number
7+
8+
@Column({ type: 'text', unique: true })
9+
name!: string
10+
11+
@Column({ type: 'text', default: () => 'CURRENT_TIMESTAMP' })
12+
created_at!: string
13+
14+
@Column({ type: 'text', default: () => 'CURRENT_TIMESTAMP' })
15+
updated_at!: string
16+
}

src/main/db/entities/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ export { ReasonEntity } from './ReasonEntity'
33
export { ScoreEventEntity } from './ScoreEventEntity'
44
export { SettlementEntity } from './SettlementEntity'
55
export { SettingEntity } from './SettingEntity'
6+
export { TagEntity } from './TagEntity'
7+
export { StudentTagEntity } from './StudentTagEntity'
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { MigrationInterface, QueryRunner } from 'typeorm'
2+
3+
export class AddTagsSchema2026020600000 implements MigrationInterface {
4+
name = 'AddTagsSchema2026020600000'
5+
6+
async up(queryRunner: QueryRunner): Promise<void> {
7+
if (!(await queryRunner.hasTable('tags'))) {
8+
await queryRunner.query(`
9+
CREATE TABLE "tags" (
10+
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
11+
"name" text NOT NULL UNIQUE,
12+
"created_at" text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
13+
"updated_at" text NOT NULL DEFAULT (CURRENT_TIMESTAMP)
14+
)
15+
`)
16+
}
17+
18+
if (!(await queryRunner.hasTable('student_tags'))) {
19+
await queryRunner.query(`
20+
CREATE TABLE "student_tags" (
21+
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
22+
"student_id" integer NOT NULL,
23+
"tag_id" integer NOT NULL,
24+
"created_at" text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
25+
FOREIGN KEY ("student_id") REFERENCES "students"("id") ON DELETE CASCADE,
26+
FOREIGN KEY ("tag_id") REFERENCES "tags"("id") ON DELETE CASCADE,
27+
UNIQUE("student_id", "tag_id")
28+
)
29+
`)
30+
}
31+
32+
await queryRunner.query(
33+
`CREATE INDEX IF NOT EXISTS "IDX_student_tags_student_id" ON "student_tags" ("student_id")`
34+
)
35+
await queryRunner.query(
36+
`CREATE INDEX IF NOT EXISTS "IDX_student_tags_tag_id" ON "student_tags" ("tag_id")`
37+
)
38+
39+
await queryRunner.query(`
40+
INSERT OR IGNORE INTO "tags" ("name") VALUES
41+
('优秀生'),
42+
('班干部'),
43+
('勤奋'),
44+
('活跃'),
45+
('进步快')
46+
`)
47+
}
48+
49+
async down(queryRunner: QueryRunner): Promise<void> {
50+
await queryRunner.query(`DROP TABLE IF EXISTS "student_tags"`)
51+
await queryRunner.query(`DROP TABLE IF EXISTS "tags"`)
52+
}
53+
}

src/main/db/migrations/InitSchema2026011800000.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export class InitSchema2026011800000 implements MigrationInterface {
1111
"name" text NOT NULL,
1212
"score" integer NOT NULL DEFAULT (0),
1313
"extra_json" text,
14+
"tags" text NOT NULL DEFAULT '[]',
1415
"created_at" text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
1516
"updated_at" text NOT NULL DEFAULT (CURRENT_TIMESTAMP)
1617
)

src/main/db/migrations/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import { InitSchema2026011800000 } from './InitSchema2026011800000'
2+
import { AddTagsSchema2026020600000 } from './AddTagsSchema2026020600000'
23

3-
export const migrations = [InitSchema2026011800000]
4+
export const migrations = [InitSchema2026011800000, AddTagsSchema2026020600000]

src/main/hosting/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export {
1212
ReasonRepositoryToken,
1313
EventRepositoryToken,
1414
SettlementRepositoryToken,
15+
TagRepositoryToken,
1516
ThemeServiceToken,
1617
WindowManagerToken,
1718
TrayServiceToken,

src/main/hosting/tokens.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const StudentRepositoryToken = Symbol.for('secscore.studentRepository')
1010
export const ReasonRepositoryToken = Symbol.for('secscore.reasonRepository')
1111
export const EventRepositoryToken = Symbol.for('secscore.eventRepository')
1212
export const SettlementRepositoryToken = Symbol.for('secscore.settlementRepository')
13+
export const TagRepositoryToken = Symbol.for('secscore.tagRepository')
1314
export const ThemeServiceToken = Symbol.for('secscore.themeService')
1415
export const WindowManagerToken = Symbol.for('secscore.windowManager')
1516
export const TrayServiceToken = Symbol.for('secscore.trayService')

0 commit comments

Comments
 (0)