Skip to content

Commit 61fd38c

Browse files
authored
Merge pull request #30 from GeneralMagicio/add-user-service
Add user service
2 parents b3c738c + f83bcde commit 61fd38c

File tree

5 files changed

+211
-3
lines changed

5 files changed

+211
-3
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
"private": true,
77
"license": "UNLICENSED",
88
"scripts": {
9-
"prebuild": "npx prisma generate",
9+
"prebuild": "npx prisma generate && rm -rf dist",
1010
"build": "nest build",
1111
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
1212
"start": "nest start",
1313
"start:dev": "nest start --watch",
1414
"start:debug": "nest start --debug --watch",
1515
"start:prod": "node dist/main",
16+
"migration:dev": "npx prisma migrate dev",
17+
"migration:prod": "npx prisma migrate deploy",
1618
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
1719
"test": "jest",
1820
"test:watch": "jest --watch",
@@ -31,7 +33,6 @@
3133
"rxjs": "^7.8.1"
3234
},
3335
"devDependencies": {
34-
"prisma": "^6.5.0",
3536
"@eslint/eslintrc": "^3.2.0",
3637
"@eslint/js": "^9.18.0",
3738
"@nestjs/cli": "^11.0.0",
@@ -49,6 +50,7 @@
4950
"globals": "^15.14.0",
5051
"jest": "^29.7.0",
5152
"prettier": "^3.4.2",
53+
"prisma": "^6.5.0",
5254
"source-map-support": "^0.5.21",
5355
"supertest": "^7.0.0",
5456
"ts-jest": "^29.2.5",

src/poll/poll.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export class PollController {
2020

2121
@Post()
2222
create(@Body() createPollDto: CreatePollDto) {
23-
let userId = 1; // need to implement Auth
23+
const userId = 1; // need to implement Auth
2424
return this.pollService.createPoll(userId, createPollDto);
2525
}
2626

src/user/user.controller.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
2+
import { UserService } from './user.service';
3+
import {
4+
GetUserActivitiesDto,
5+
GetUserDataDto,
6+
GetUserVotesDto,
7+
UserActivitiesResponseDto,
8+
UserDataResponseDto,
9+
UserVotesResponseDto,
10+
} from './user.dto';
11+
12+
@Controller('user')
13+
export class UserController {
14+
constructor(private readonly userService: UserService) {}
15+
16+
@Get('getUserData')
17+
async getUserData(
18+
@Query() query: GetUserDataDto,
19+
): Promise<UserDataResponseDto> {
20+
return this.userService.getUserData(query);
21+
}
22+
23+
@Get('getUserActivities')
24+
async getUserActivities(
25+
@Query() query: GetUserActivitiesDto,
26+
): Promise<UserActivitiesResponseDto> {
27+
return this.userService.getUserActivities(query);
28+
}
29+
30+
@Post('getUserVotes')
31+
async getUserVotes(
32+
@Body() body: GetUserVotesDto,
33+
): Promise<UserVotesResponseDto> {
34+
return this.userService.getUserVotes(body);
35+
}
36+
}

src/user/user.dto.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
export class GetUserDataDto {
2+
worldID: string;
3+
}
4+
5+
export class UserDataResponseDto {
6+
pollsCreated: number;
7+
pollsParticipated: number;
8+
worldID: string;
9+
}
10+
11+
export class GetUserActivitiesDto {
12+
worldID: string;
13+
filter?: 'active' | 'inactive' | 'created' | 'participated';
14+
}
15+
16+
export class UserActionDto {
17+
type: 'created' | 'voted';
18+
pollId: number;
19+
pollTitle: string;
20+
pollDescription: string;
21+
endDate: Date;
22+
votersParticipated: number;
23+
authorUserId: number;
24+
}
25+
26+
export class UserActivitiesResponseDto {
27+
userActions: UserActionDto[];
28+
}
29+
30+
export class GetUserVotesDto {
31+
pollID: number;
32+
worldID: string;
33+
}
34+
35+
export class UserVotesResponseDto {
36+
options: string[];
37+
votingPower: number;
38+
weightDistribution?: Record<string, number>;
39+
}

src/user/user.service.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// src/user/user.service.ts
2+
import { Injectable } from '@nestjs/common';
3+
import { DatabaseService } from '../database/database.service';
4+
import {
5+
GetUserActivitiesDto,
6+
GetUserDataDto,
7+
UserDataResponseDto,
8+
UserActivitiesResponseDto,
9+
UserActionDto,
10+
GetUserVotesDto,
11+
UserVotesResponseDto,
12+
} from './user.dto';
13+
14+
type UserActionFilters = {
15+
userId: number;
16+
type?: 'CREATED' | 'VOTED';
17+
poll?: {
18+
endDate?: {
19+
gte?: Date;
20+
lt?: Date;
21+
};
22+
};
23+
};
24+
25+
@Injectable()
26+
export class UserService {
27+
constructor(private readonly databaseService: DatabaseService) {}
28+
29+
async getUserData(dto: GetUserDataDto): Promise<UserDataResponseDto> {
30+
const user = await this.databaseService.user.findUnique({
31+
where: { worldID: dto.worldID },
32+
select: {
33+
pollsCreatedCount: true,
34+
pollsParticipatedCount: true,
35+
worldID: true,
36+
},
37+
});
38+
if (!user) {
39+
throw new Error('User not found');
40+
}
41+
return {
42+
pollsCreated: user.pollsCreatedCount,
43+
pollsParticipated: user.pollsParticipatedCount,
44+
worldID: user.worldID,
45+
};
46+
}
47+
48+
async getUserActivities(
49+
dto: GetUserActivitiesDto,
50+
): Promise<UserActivitiesResponseDto> {
51+
const user = await this.databaseService.user.findUnique({
52+
where: { worldID: dto.worldID },
53+
select: { id: true },
54+
});
55+
if (!user) {
56+
throw new Error('User not found');
57+
}
58+
const filters: UserActionFilters = { userId: user.id };
59+
const now = new Date();
60+
if (dto.filter === 'active') {
61+
filters.poll = { endDate: { gte: now } };
62+
} else if (dto.filter === 'inactive') {
63+
filters.poll = { endDate: { lt: now } };
64+
} else if (dto.filter === 'created') {
65+
filters.type = 'CREATED';
66+
} else if (dto.filter === 'participated') {
67+
filters.type = 'VOTED';
68+
}
69+
const userActions = await this.databaseService.userAction.findMany({
70+
where: filters,
71+
orderBy: { poll: { endDate: 'desc' } },
72+
select: {
73+
type: true,
74+
poll: {
75+
select: {
76+
pollId: true,
77+
title: true,
78+
description: true,
79+
endDate: true,
80+
participantCount: true,
81+
authorUserId: true,
82+
},
83+
},
84+
},
85+
});
86+
const actions: UserActionDto[] = userActions.map((action) => ({
87+
type: action.type.toLowerCase() as 'created' | 'voted',
88+
pollId: action.poll.pollId,
89+
pollTitle: action.poll.title,
90+
pollDescription: action.poll.description ?? '',
91+
endDate: action.poll.endDate,
92+
isActive: action.poll.endDate >= now,
93+
votersParticipated: action.poll.participantCount,
94+
authorUserId: action.poll.authorUserId,
95+
}));
96+
return { userActions: actions };
97+
}
98+
99+
async getUserVotes(dto: GetUserVotesDto): Promise<UserVotesResponseDto> {
100+
const user = await this.databaseService.user.findUnique({
101+
where: { worldID: dto.worldID },
102+
select: { id: true },
103+
});
104+
if (!user) {
105+
throw new Error('User not found');
106+
}
107+
const poll = await this.databaseService.poll.findUnique({
108+
where: { pollId: dto.pollID },
109+
select: { endDate: true, options: true },
110+
});
111+
if (!poll || poll.endDate < new Date()) {
112+
throw new Error('Poll is not active or does not exist');
113+
}
114+
const votes = await this.databaseService.vote.findMany({
115+
where: {
116+
pollId: dto.pollID,
117+
userId: user.id,
118+
},
119+
select: { votingPower: true, weightDistribution: true },
120+
});
121+
if (votes.length === 0) {
122+
throw new Error('Vote not found for the given poll and user');
123+
}
124+
const vote = votes[0];
125+
return {
126+
options: poll.options,
127+
votingPower: vote.votingPower,
128+
weightDistribution: vote.weightDistribution as Record<string, number>,
129+
};
130+
}
131+
}

0 commit comments

Comments
 (0)