Skip to content

Commit 15cba69

Browse files
committed
[add] Forum model & controller
[optimize] any Activity Organizer can edit Place data
1 parent 76ed86f commit 15cba69

File tree

13 files changed

+194
-38
lines changed

13 files changed

+194
-38
lines changed

ReadMe.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353

5454
- [x] [Session](source/model/Activity/Session.ts) 活动环节(演讲、实训等)
5555

56+
- [x] [Forum](source/model/Activity/Forum.ts) 论坛(多 Agenda)
57+
5658
- [x] [Agenda](source/model/Activity/Agenda.ts) 环节申报(Activity 与 Session)
5759

5860
- [ ] Ticket 门票类别(单 Activity、多 Session)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import {
2+
Authorized,
3+
Body,
4+
CurrentUser,
5+
Get,
6+
HttpCode,
7+
JsonController,
8+
NotFoundError,
9+
OnNull,
10+
Param,
11+
Post,
12+
Put,
13+
QueryParams
14+
} from 'routing-controllers';
15+
import { ResponseSchema } from 'routing-controllers-openapi';
16+
17+
import { BaseFilter, dataSource, Forum, ForumListChunk, User } from '../../model';
18+
import { searchConditionOf } from '../../utility';
19+
import { ActivityLogController } from '../User';
20+
import { ActivityController } from './Activity';
21+
22+
const forumStore = dataSource.getRepository(Forum);
23+
24+
@JsonController('/activity/:aid/forum')
25+
export class ForumController {
26+
@Post()
27+
@Authorized()
28+
@HttpCode(201)
29+
@ResponseSchema(Forum)
30+
async createOne(
31+
@CurrentUser() createdBy: User,
32+
@Param('aid') aid: number,
33+
@Body() forum: Forum
34+
) {
35+
const activity = await ActivityController.assertAdmin(aid, createdBy);
36+
37+
const created = await forumStore.save({ ...forum, activity, createdBy });
38+
39+
await ActivityLogController.logCreate(createdBy, 'Forum', created.id);
40+
41+
return created;
42+
}
43+
44+
@Get('/:id')
45+
@OnNull(404)
46+
@ResponseSchema(Forum)
47+
getOne(@Param('id') id: number) {
48+
return forumStore.findOne({ where: { id }, relations: ['activity', 'producers', 'place'] });
49+
}
50+
51+
@Put('/:id')
52+
@Authorized()
53+
@ResponseSchema(Forum)
54+
async updateOne(
55+
@CurrentUser() updatedBy: User,
56+
@Param('aid') aid: number,
57+
@Param('id') id: number,
58+
@Body() forum: Forum
59+
) {
60+
const existing = await forumStore.findOne({ where: { id }, relations: ['activity'] });
61+
62+
if (!existing || existing.activity.id !== aid) throw new NotFoundError();
63+
64+
await ActivityController.assertAdmin(aid, updatedBy);
65+
66+
const updated = await forumStore.save({ ...existing, ...forum, updatedBy });
67+
68+
await ActivityLogController.logUpdate(updatedBy, 'Forum', updated.id);
69+
70+
return updated;
71+
}
72+
73+
@Get()
74+
@ResponseSchema(ForumListChunk)
75+
async getList(@QueryParams() { keywords, pageSize = 10, pageIndex = 1 }: BaseFilter) {
76+
const where = searchConditionOf<Forum>(['title', 'summary'], keywords);
77+
const [list, count] = await forumStore.findAndCount({
78+
where,
79+
skip: pageSize * (pageIndex - 1),
80+
take: pageSize
81+
});
82+
return { list, count };
83+
}
84+
}

source/controller/Activity/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export * from './Activity';
22
export * from './Agenda';
33
export * from './CheckEvent';
44
export * from './Cooperation';
5+
export * from './Forum';
56
export * from './Session';

source/controller/Organization/Place.ts

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
Authorized,
33
Body,
44
CurrentUser,
5+
ForbiddenError,
56
Get,
67
JsonController,
78
OnNull,
@@ -12,45 +13,53 @@ import {
1213
} from 'routing-controllers';
1314
import { ResponseSchema } from 'routing-controllers-openapi';
1415

15-
import { BaseFilter, dataSource, Organization, Place, PlaceListChunk, User } from '../../model';
16+
import {
17+
BaseFilter,
18+
dataSource,
19+
Forum,
20+
Organization,
21+
Place,
22+
PlaceListChunk,
23+
User
24+
} from '../../model';
1625
import { searchConditionOf } from '../../utility';
1726
import { ActivityLogController } from '../User';
18-
import { OrganizationController } from './Organization';
1927

20-
const placeStore = dataSource.getRepository(Place);
28+
const placeStore = dataSource.getRepository(Place),
29+
forumStore = dataSource.getRepository(Forum);
2130

22-
@JsonController('/organization')
31+
@JsonController('/place')
2332
export class PlaceController {
24-
@Post('/:oid/place')
33+
static async assertAdmin(user: User, id: number) {
34+
const isPlaceUser = await forumStore.exists({
35+
where: { place: { id }, activity: { organization: { members: { id: user.id } } } }
36+
});
37+
if (!isPlaceUser) throw new ForbiddenError('You never used this place');
38+
}
39+
40+
@Post()
2541
@Authorized()
2642
@ResponseSchema(Place)
27-
async create(@CurrentUser() createdBy: User, @Param('oid') oid: number, @Body() body: Place) {
28-
await OrganizationController.assertAdmin(createdBy, oid);
29-
30-
const place = await placeStore.save({ ...body, organization: { id: oid }, createdBy });
43+
async create(@CurrentUser() createdBy: User, @Body() body: Place) {
44+
const place = await placeStore.save({ ...body, createdBy });
3145

3246
await ActivityLogController.logCreate(createdBy, 'Place', place.id);
3347

3448
return place;
3549
}
3650

37-
@Get('/:oid/place/:id')
51+
@Get('/:id')
3852
@OnNull(404)
3953
@ResponseSchema(Place)
4054
getOne(@Param('id') id: number) {
41-
return placeStore.findOneBy({ id });
55+
return placeStore.findOne({ where: { id }, relations: ['organization', 'createdBy'] });
4256
}
4357

44-
@Put('/:oid/place/:id')
58+
@Put('/:id')
4559
@Authorized()
4660
@ResponseSchema(Place)
47-
async edit(
48-
@CurrentUser() updatedBy: User,
49-
@Param('oid') oid: number,
50-
@Param('id') id: number,
51-
@Body() body: Place
52-
) {
53-
await OrganizationController.assertAdmin(updatedBy, oid);
61+
async edit(@CurrentUser() updatedBy: User, @Param('id') id: number, @Body() body: Place) {
62+
await PlaceController.assertAdmin(updatedBy, id);
5463

5564
const place = await placeStore.save({ ...body, id, updatedBy });
5665

@@ -59,15 +68,11 @@ export class PlaceController {
5968
return place;
6069
}
6170

62-
@Get('/:oid/place')
71+
@Get()
6372
@ResponseSchema(PlaceListChunk)
64-
async getList(
65-
@Param('oid') oid: number,
66-
@QueryParams() { keywords, pageSize = 10, pageIndex = 1 }: BaseFilter
67-
) {
68-
const where = searchConditionOf<Place>(['name', 'address'], keywords, {
69-
organization: { id: oid }
70-
});
73+
async getList(@QueryParams() { keywords, pageSize = 10, pageIndex = 1 }: BaseFilter) {
74+
const where = searchConditionOf<Place>(['name', 'address'], keywords);
75+
7176
const [list, count] = await placeStore.findAndCount({
7277
where,
7378
skip: pageSize * (pageIndex - 1),

source/controller/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
ActivitySessionController,
88
AgendaController,
99
CheckEventController,
10-
CooperationController
10+
CooperationController,
11+
ForumController
1112
} from './Activity';
1213
import { BaseController } from './Base';
1314
import { FileController } from './File';
@@ -24,6 +25,7 @@ export * from './User';
2425
export const controllers = [
2526
CheckEventController,
2627
AgendaController,
28+
ForumController,
2729
ActivitySessionController,
2830
CooperationController,
2931
ActivityController,

source/model/Activity/Activity.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ export abstract class ActivityBase extends OrganizationBase {
7474
@Type(() => Activity)
7575
@Transform(({ value }) => Activity.from(value))
7676
@ValidateNested()
77-
@IsOptional()
7877
@ManyToOne(() => Activity)
7978
activity: Activity;
8079
}

source/model/Activity/SessionSubmit.ts renamed to source/model/Activity/Agenda.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import { Column, Entity, JoinTable, ManyToMany, ManyToOne } from 'typeorm';
44

55
import { InputData, ListChunk } from '../Base';
66
import { User } from '../User';
7-
import { ActivityBase, ActivityBaseFilter } from './Activity';
7+
import { ActivityBaseFilter } from './Activity';
8+
import { ForumBase } from './Forum';
89

910
@Entity()
10-
export class Agenda extends ActivityBase {
11+
export class Agenda extends ForumBase {
1112
@Type(() => User)
1213
@Transform(({ value }) => (Array.isArray(value) ? value.map(user => User.from(user)) : value))
1314
@ValidateNested()
@@ -21,7 +22,7 @@ export class Agenda extends ActivityBase {
2122
adopted?: boolean;
2223
}
2324

24-
export abstract class AgendaBase extends ActivityBase {
25+
export abstract class AgendaBase extends ForumBase {
2526
@Type(() => Agenda)
2627
@Transform(({ value }) => Agenda.from(value))
2728
@ValidateNested()

source/model/Activity/CheckEvent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Column, Entity, ManyToOne, ViewColumn, ViewEntity } from 'typeorm';
44

55
import { ListChunk } from '../Base';
66
import { User } from '../User';
7-
import { AgendaBase, AgendaBaseFilter } from './SessionSubmit';
7+
import { AgendaBase, AgendaBaseFilter } from './Agenda';
88

99
@Entity()
1010
export class CheckEvent extends AgendaBase {

source/model/Activity/Forum.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Transform, Type } from 'class-transformer';
2+
import { IsDateString, IsInt, IsOptional, IsString, Min, ValidateNested } from 'class-validator';
3+
import { Column, Entity, JoinTable, ManyToMany, ManyToOne } from 'typeorm';
4+
5+
import { ListChunk } from '../Base';
6+
import { Place } from '../Organization';
7+
import { User } from '../User';
8+
import { Activity, ActivityBase } from './Activity';
9+
10+
@Entity()
11+
export class Forum extends ActivityBase {
12+
@IsString()
13+
@Column()
14+
title: string;
15+
16+
@IsString()
17+
@IsOptional()
18+
@Column({ nullable: true })
19+
summary?: string;
20+
21+
@Type(() => User)
22+
@Transform(({ value }) => (Array.isArray(value) ? value.map(user => User.from(user)) : []))
23+
@ValidateNested({ each: true })
24+
@ManyToMany(() => User)
25+
@JoinTable()
26+
producers: User[];
27+
28+
@IsDateString()
29+
@Column('date')
30+
startTime: string;
31+
32+
@IsDateString()
33+
@Column('date')
34+
endTime: string;
35+
36+
@Type(() => Place)
37+
@Transform(({ value }) => Place.from(value))
38+
@ValidateNested()
39+
@ManyToOne(() => Place)
40+
place: Place;
41+
}
42+
43+
export abstract class ForumBase extends ActivityBase {
44+
@Type(() => Forum)
45+
@Transform(({ value }) => Forum.from(value))
46+
@ValidateNested()
47+
@ManyToOne(() => Forum)
48+
forum: Forum;
49+
}
50+
51+
export class ForumListChunk implements ListChunk<Forum> {
52+
@IsInt()
53+
@Min(0)
54+
count: number;
55+
56+
@Type(() => Forum)
57+
@ValidateNested({ each: true })
58+
list: Forum[];
59+
}

source/model/Activity/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './Activity';
2+
export * from './Agenda';
23
export * from './CheckEvent';
34
export * from './Cooperation';
5+
export * from './Forum';
46
export * from './Session';
5-
export * from './SessionSubmit';

0 commit comments

Comments
 (0)