Skip to content

Commit 76ed86f

Browse files
committed
[refactor] rewrite Cooperation with new Tag model & controller
1 parent aaae050 commit 76ed86f

File tree

12 files changed

+191
-36
lines changed

12 files changed

+191
-36
lines changed

ReadMe.md

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

4040
- [x] [User](source/model/User.ts) 组织者、讲师、合作方联系人、志愿者等
4141

42+
- [x] [Tag](source/model/Tag.ts) 标签、赞助级别等
43+
4244
- [x] [Organization](source/model/Organization/Organization.ts) 主办、协办、场地、赞助、媒体、展商等
4345

4446
- [x] [Membership](source/model/Organization/Membership.ts) 组织关系(User 与 Organization)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
"jest": "jest",
114114
"debug": "server-test start-test 8080 jest",
115115
"test": "npm run build && server-test start-test 8080 build-test && npm run debug",
116-
"pack-type": "tsc --emitDeclarationOnly --outDir type/ && cd type/ && rm -rf controller/ *.d.ts && mv -f model/* . && rm -rf model/",
116+
"pack-type": "tsc --emitDeclarationOnly --outDir type/ && cd type/ && rm -rf controller/ *.d.ts && cp -rf model/* . && rm -rf model/",
117117
"build": "rm -rf dist/ type/*.d.ts && tsc && npm run pack-type",
118118
"start": "cross-env NODE_ENV=production node dist/",
119119
"typeorm": "typeorm-ts-node-commonjs -d source/model/index.ts",

source/controller/Activity/Activity.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,10 @@ export class ActivityController {
8282
@Get()
8383
@ResponseSchema(ActivityListChunk)
8484
async getList(@QueryParams() { keywords, pageSize = 10, pageIndex = 1 }: BaseFilter) {
85-
const where = searchConditionOf<Activity>(['title', 'address'], keywords);
86-
85+
const where = searchConditionOf<Activity>(
86+
['title', 'slug', 'address', 'description'],
87+
keywords
88+
);
8789
const [list, count] = await activityStore.findAndCount({
8890
where,
8991
skip: pageSize * (pageIndex - 1),

source/controller/Activity/Cooperation.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
import {
22
Authorized,
3+
BadRequestError,
34
Body,
45
CurrentUser,
56
Get,
67
HttpCode,
78
JsonController,
89
Param,
9-
Patch,
1010
Post,
11+
Put,
1112
QueryParams
1213
} from 'routing-controllers';
1314
import { ResponseSchema } from 'routing-controllers-openapi';
1415

1516
import { BaseFilter, Cooperation, CooperationListChunk, dataSource, User } from '../../model';
16-
import { searchConditionOf } from '../../utility';
1717
import { ActivityLogController } from '../User';
1818
import { ActivityController } from './Activity';
1919

2020
const cooperationStore = dataSource.getRepository(Cooperation);
2121

22-
@JsonController('/activity')
22+
@JsonController('/activity/:aid/cooperation')
2323
export class CooperationController {
24-
@Post('/:aid/cooperation')
24+
@Post()
2525
@Authorized()
2626
@HttpCode(201)
2727
@ResponseSchema(Cooperation)
@@ -32,27 +32,36 @@ export class CooperationController {
3232
) {
3333
const activity = await ActivityController.assertAdmin(aid, createdBy);
3434

35-
const cooperation = await cooperationStore.save({ ...body, activity, createdBy });
35+
if (!activity.cooperationLevels?.find(({ id }) => id === body.level.id))
36+
throw new BadRequestError(
37+
`Cooperation Level with ID "${body.level.id}" isn't included in "${activity.title}" activity`
38+
);
39+
if (body.partner.id === activity.organization.id)
40+
throw new BadRequestError("Can't cooperate with yourself");
3641

42+
const cooperation = await cooperationStore.save({
43+
...body,
44+
activity,
45+
organization: activity.organization,
46+
createdBy
47+
});
3748
await ActivityLogController.logCreate(createdBy, 'Cooperation', cooperation.id);
3849

3950
return cooperation;
4051
}
4152

42-
@Get('/:aid/cooperation')
53+
@Get()
4354
@ResponseSchema(CooperationListChunk)
44-
async getList(@QueryParams() { keywords, pageSize = 10, pageIndex = 1 }: BaseFilter) {
45-
const where = searchConditionOf<Cooperation>(['title'], keywords);
46-
55+
async getList(@QueryParams() { pageSize = 10, pageIndex = 1 }: BaseFilter) {
4756
const [list, count] = await cooperationStore.findAndCount({
48-
where,
4957
skip: pageSize * (pageIndex - 1),
50-
take: pageSize
58+
take: pageSize,
59+
relations: ['level', 'partner']
5160
});
5261
return { list, count };
5362
}
5463

55-
@Patch(':aid/cooperation/:id')
64+
@Put('/:id')
5665
@Authorized()
5766
@ResponseSchema(Cooperation)
5867
async edit(

source/controller/Tag.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import {
2+
Authorized,
3+
Body,
4+
CurrentUser,
5+
Delete,
6+
Get,
7+
HttpCode,
8+
JsonController,
9+
OnNull,
10+
OnUndefined,
11+
Param,
12+
Post,
13+
QueryParams
14+
} from 'routing-controllers';
15+
import { ResponseSchema } from 'routing-controllers-openapi';
16+
17+
import { dataSource, Tag, TagFilter, TagListChunk, User } from '../model';
18+
import { searchConditionOf } from '../utility';
19+
import { ActivityLogController } from './User/ActivityLog';
20+
21+
const store = dataSource.getRepository(Tag);
22+
23+
@JsonController('/tag')
24+
export class TagController {
25+
@Post()
26+
@Authorized()
27+
@HttpCode(201)
28+
@ResponseSchema(Tag)
29+
async createOne(@CurrentUser() createdBy: User, @Body() data: Tag) {
30+
const saved = await store.save({ ...data, createdBy });
31+
32+
await ActivityLogController.logCreate(createdBy, 'Tag', saved.id);
33+
34+
return saved;
35+
}
36+
37+
@Get('/:id')
38+
@OnNull(404)
39+
@ResponseSchema(Tag)
40+
getOne(@Param('id') id: number) {
41+
return store.findOne({ where: { id }, relations: ['createdBy'] });
42+
}
43+
44+
@Delete('/:id')
45+
@Authorized()
46+
@OnUndefined(204)
47+
async deleteOne(@Param('id') id: number) {
48+
await store.delete(id);
49+
}
50+
51+
@Get()
52+
@ResponseSchema(TagListChunk)
53+
async getList(@QueryParams() { name, type, keywords, pageSize, pageIndex }: TagFilter) {
54+
const where = searchConditionOf<Tag>(['name'], name || keywords, type && { type });
55+
const [list, count] = await store.findAndCount({
56+
where,
57+
skip: pageSize * (pageIndex - 1),
58+
take: pageSize
59+
});
60+
return { list, count };
61+
}
62+
}

source/controller/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import {
77
ActivitySessionController,
88
AgendaController,
99
CheckEventController,
10-
CooperationController} from './Activity';
10+
CooperationController
11+
} from './Activity';
1112
import { BaseController } from './Base';
1213
import { FileController } from './File';
1314
import { OrganizationController, PlaceController } from './Organization';
14-
import { ActivityLogController, OauthController, UserController,WebAuthnController } from './User';
15+
import { TagController } from './Tag';
16+
import { ActivityLogController, OauthController, UserController, WebAuthnController } from './User';
1517

1618
export * from './Activity';
1719
export * from './Base';
@@ -27,6 +29,7 @@ export const controllers = [
2729
ActivityController,
2830
PlaceController,
2931
OrganizationController,
32+
TagController,
3033
WebAuthnController,
3134
OauthController,
3235
UserController,

source/model/Activity/Activity.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,29 @@ import {
33
IsDateString,
44
IsInt,
55
IsOptional,
6+
IsString,
67
IsUrl,
78
Length,
89
Min,
910
ValidateNested
1011
} from 'class-validator';
11-
import { Column, Entity, ManyToOne } from 'typeorm';
12+
import { Column, Entity, JoinTable, ManyToMany, ManyToOne } from 'typeorm';
1213

1314
import { BaseFilter, InputData, ListChunk } from '../Base';
1415
import { OrganizationBase } from '../Organization';
16+
import { Tag } from '../Tag';
1517

1618
@Entity()
1719
export class Activity extends OrganizationBase {
1820
@Length(3)
1921
@Column()
2022
title: string;
2123

24+
@IsString()
25+
@IsOptional()
26+
@Column({ nullable: true })
27+
slug?: string;
28+
2229
@IsDateString()
2330
@Column('date')
2431
startTime: string;
@@ -28,16 +35,39 @@ export class Activity extends OrganizationBase {
2835
endTime: string;
2936

3037
@Length(3)
38+
@IsOptional()
3139
@Column({ nullable: true })
3240
address?: string;
3341

3442
@IsUrl()
43+
@IsOptional()
3544
@Column({ nullable: true })
36-
url?: string;
45+
liveLink?: string;
3746

3847
@IsUrl()
48+
@IsOptional()
3949
@Column({ nullable: true })
4050
banner?: string;
51+
52+
@Type(() => Tag)
53+
@Transform(({ value }) => (Array.isArray(value) ? value.map(user => Tag.from(user)) : value))
54+
@ValidateNested({ each: true })
55+
@IsOptional()
56+
@ManyToMany(() => Tag)
57+
@JoinTable()
58+
tags?: Tag[];
59+
60+
@IsString()
61+
@Column()
62+
description: string;
63+
64+
@Type(() => Tag)
65+
@Transform(({ value }) => (Array.isArray(value) ? value.map(user => Tag.from(user)) : value))
66+
@ValidateNested({ each: true })
67+
@IsOptional()
68+
@ManyToMany(() => Tag)
69+
@JoinTable()
70+
cooperationLevels?: Tag[];
4171
}
4272

4373
export abstract class ActivityBase extends OrganizationBase {

source/model/Activity/Cooperation.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
import { Transform, Type } from 'class-transformer';
2-
import { IsInt, IsPositive, Length, Min, ValidateNested } from 'class-validator';
3-
import { Column, Entity, ManyToOne } from 'typeorm';
2+
import { IsInt, Min, ValidateNested } from 'class-validator';
3+
import { Entity, ManyToOne } from 'typeorm';
44

55
import { ListChunk } from '../Base';
6-
import { User } from '../User';
6+
import { Organization } from '../Organization';
7+
import { Tag } from '../Tag';
78
import { ActivityBase } from './Activity';
89

910
@Entity()
1011
export class Cooperation extends ActivityBase {
11-
@IsPositive()
12-
@Column('int')
13-
level: number;
14-
15-
@Length(3)
16-
@Column()
17-
title: string;
12+
@Type(() => Tag)
13+
@Transform(({ value }) => Tag.from(value))
14+
@ValidateNested()
15+
@ManyToOne(() => Tag)
16+
level: Tag;
1817

19-
@Type(() => User)
20-
@Transform(({ value }) => User.from(value))
18+
@Type(() => Organization)
19+
@Transform(({ value }) => Organization.from(value))
2120
@ValidateNested()
22-
@ManyToOne(() => User)
23-
contact: User;
21+
@ManyToOne(() => Organization)
22+
partner: Organization;
2423
}
2524

2625
export class CooperationListChunk implements ListChunk<Cooperation> {

source/model/Tag.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Type } from 'class-transformer';
2+
import { IsEnum, IsNumber, IsOptional, IsString, ValidateNested } from 'class-validator';
3+
import { Column, Entity } from 'typeorm';
4+
5+
import { ListChunk } from './Base';
6+
import { UserBase, UserBaseFilter, UserInputData } from './User/User';
7+
8+
export enum TagType {
9+
Tag = 'tag',
10+
Category = 'category',
11+
City = 'city',
12+
Cooperation = 'cooperation'
13+
}
14+
15+
@Entity()
16+
export class Tag extends UserBase {
17+
@Column()
18+
@IsString()
19+
name: string = '';
20+
21+
@Column({ type: 'simple-enum', enum: TagType })
22+
@IsEnum(TagType)
23+
type: TagType = TagType.Tag;
24+
}
25+
26+
export class TagFilter extends UserBaseFilter implements Partial<UserInputData<Tag>> {
27+
@IsString()
28+
@IsOptional()
29+
name?: string;
30+
31+
@IsEnum(TagType)
32+
@IsOptional()
33+
type?: TagType;
34+
}
35+
36+
export class TagListChunk implements ListChunk<Tag> {
37+
@Type(() => Tag)
38+
@ValidateNested({ each: true })
39+
list: Tag[];
40+
41+
@IsNumber()
42+
count: number;
43+
}

source/model/User/ActivityLog.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { Type } from 'class-transformer';
22
import { IsEnum, IsInt, IsObject, IsOptional, Min, ValidateNested } from 'class-validator';
33
import { Column, Entity, ViewColumn, ViewEntity } from 'typeorm';
44

5-
import { Activity, Agenda, CheckEvent,Cooperation, Session } from '../Activity';
5+
import { Activity, Agenda, CheckEvent, Cooperation, Session } from '../Activity';
66
import { Base, BaseFilter, InputData, ListChunk } from '../Base';
77
import { Membership, Organization, Place } from '../Organization';
8+
import { Tag } from '../Tag';
89
import { User, UserBase } from './User';
910
import { UserCredential } from './WebAuthn';
1011

@@ -17,6 +18,7 @@ export enum Operation {
1718
export const LogableTable = {
1819
User,
1920
UserCredential,
21+
Tag,
2022
Place,
2123
Organization,
2224
Membership,

0 commit comments

Comments
 (0)