Skip to content

Commit

Permalink
Feat/transaction (#20) (#21)
Browse files Browse the repository at this point in the history
* feat: transaction module

* feat: find transactions by period

* fix: npm test

* fix: transaction.interface.ts

* refactor: change function name

* refactor: change dto and func name

* fix: memberExcel > memberFile

---------

Co-authored-by: 박근형 <[email protected]>
  • Loading branch information
ssoxong and w8385 authored Apr 29, 2024
1 parent fef0867 commit 94ffe60
Show file tree
Hide file tree
Showing 15 changed files with 331 additions and 3 deletions.
49 changes: 49 additions & 0 deletions src/excel/excel.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as XLSX from 'exceljs';

import { Member } from '../member/entities/member.entity';
import { Transaction } from '../transaction/entities/transaction.entity';

export class ExcelService {
async convertExcelToJSON(groupId: string, excel: Express.Multer.File) {
Expand Down Expand Up @@ -95,4 +96,52 @@ export class ExcelService {

return members;
}

async convertTransactionFileToTransactionArr(
transactionGroupId: string,
excel: Express.Multer.File,
) {
const workbook = new XLSX.Workbook();
await workbook.xlsx.load(excel.buffer);

const worksheet = workbook.getWorksheet(1);

const transactions: Transaction[] = [];
const columns: string[] = ['거래일시', '구분', '거래금액', '', '', '내용'];

worksheet.eachRow((row, rowNumber) => {
if (rowNumber > 11) {
const transaction: Transaction = new Transaction();
transaction.metadata = {
groupId: transactionGroupId,
transactionType: '',
amount: 0,
name: '',
};
row.eachCell((cell, colNumber) => {
const col = String(columns[colNumber - 2]).trim();
if (col == '거래일시')
transaction.timestamp = this.parseDate(String(cell.value).trim());
if (col == '구분')
transaction.metadata.transactionType = String(cell.value).trim();
if (col == '거래금액')
transaction.metadata.amount = Number(cell.text.replace(/,/g, ''));
if (col == '내용')
transaction.metadata.name = String(cell.value).trim();
});
transactions.push(transaction);
}
});

return transactions;
}

parseDate(dateStr: string) {
const [datePart, timePart] = dateStr.split(' ');
const [year, month, day] = datePart.split('.').map(Number);
const [hours, minutes, seconds] = timePart.split(':').map(Number);
const date = new Date(year, month - 1, day, hours, minutes, seconds);
const UTFDiff = date.getTimezoneOffset() * 60000;
return new Date(date.getTime() - UTFDiff);
}
}
3 changes: 2 additions & 1 deletion src/group/group.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as process from 'process';
import { DatabaseModule } from '../database/database.module';
import { EventModule } from '../event/event.module';
import { MemberModule } from '../member/member.module';
import { TransactionModule } from '../transaction/transaction.module';
import { Group } from './entities/group.entity';
import { GroupController } from './group.controller';
import { groupProviders } from './group.providers';
Expand All @@ -22,7 +23,7 @@ describe('GroupController', () => {
process.env.MONGODB_URI = mongoServer.getUri();

const module: TestingModule = await Test.createTestingModule({
imports: [DatabaseModule, MemberModule, EventModule],
imports: [DatabaseModule, MemberModule, EventModule, TransactionModule],
controllers: [GroupController],
providers: [GroupService, ...groupProviders, GroupRepository],
}).compile();
Expand Down
45 changes: 44 additions & 1 deletion src/group/group.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { Event } from '../event/event.decorators';
import { CreateMemberDto } from '../member/dto/create-member.dto';
import { UpdateMemberDto } from '../member/dto/update-member.dto';
import { Member } from '../member/member.decorators';
import { GetTransactionsPeriodDto } from '../transaction/dto/get-transaction-period.dto';
import { Transaction } from '../transaction/transaction.decorators';
import { CreateGroupDto } from './dto/create-group.dto';
import { UpdateGroupDto } from './dto/update-group.dto';
import { UploadGroupDto } from './dto/upload-group.dto';
Expand All @@ -36,7 +38,7 @@ export class GroupController {
summary: '모임 생성',
description: '모임을 생성합니다.',
})
@ApiFile('memberExcel')
@ApiFile('memberFile')
@ApiBody({ type: UploadGroupDto })
create(
@Body() createGroupDto: CreateGroupDto,
Expand Down Expand Up @@ -78,13 +80,28 @@ export class GroupController {
summary: '모임 회원 명단 엑셀 업로드',
description: '모임 회원 명단 엑셀을 업로드하고, 회원을 재구성합니다.',
})
@ApiFile('memberFile')
uploadMemberFile(
@Param('groupId') groupId: string,
@UploadedFile() memberExcel: Express.Multer.File,
) {
return this.groupService.uploadMemberFile(groupId, memberExcel);
}

@Post(':groupId/transaction/excel')
@Transaction()
@ApiOperation({
summary: '모임 거래내역 업로드',
description: '모임의 거래내역을 업로드합니다.',
})
@ApiFile('transactionFile')
uploadTransactionFile(
@Param('groupId') groupId: string,
@UploadedFile() transactionExcel: Express.Multer.File,
) {
return this.groupService.uploadTransactionFile(groupId, transactionExcel);
}

@Get()
@Group()
@ApiOperation({
Expand Down Expand Up @@ -130,6 +147,32 @@ export class GroupController {
return this.groupService.getEvents(groupId);
}

@Get(':groupId/transaction')
@Transaction()
@ApiOperation({
summary: '모임 거래내역 조회',
description: '특정 모임의 거래내역을 조회합니다.',
})
getTransactions(@Param('groupId') groupId: string) {
return this.groupService.getTransactions(groupId);
}

@Post(':groupId/transaction/period')
@Transaction()
@ApiOperation({
summary: '모임 거래내역 조회',
description: '특정 모임의 거래내역을 기간을 설정하여 조회합니다.',
})
getTransactionsByPeriod(
@Param('groupId') groupId: string,
@Body() getTransactionsPeriodDto: GetTransactionsPeriodDto,
) {
return this.groupService.getTransactionsByPeriod(
groupId,
getTransactionsPeriodDto,
);
}

@Patch(':groupId')
@Group()
@ApiParam({
Expand Down
3 changes: 2 additions & 1 deletion src/group/group.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { Module } from '@nestjs/common';
import { DatabaseModule } from '../database/database.module';
import { EventModule } from '../event/event.module';
import { MemberModule } from '../member/member.module';
import { TransactionModule } from '../transaction/transaction.module';
import { GroupController } from './group.controller';
import { groupProviders } from './group.providers';
import { GroupRepository } from './group.repository';
import { GroupService } from './group.service';

@Module({
imports: [DatabaseModule, MemberModule, EventModule],
imports: [DatabaseModule, MemberModule, EventModule, TransactionModule],
controllers: [GroupController],
providers: [GroupService, ...groupProviders, GroupRepository],
exports: [GroupService],
Expand Down
24 changes: 24 additions & 0 deletions src/group/group.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { EventService } from '../event/event.service';
import { CreateMemberDto } from '../member/dto/create-member.dto';
import { UpdateMemberDto } from '../member/dto/update-member.dto';
import { MemberService } from '../member/member.service';
import { GetTransactionsPeriodDto } from '../transaction/dto/get-transaction-period.dto';
import { TransactionService } from '../transaction/transaction.service';
import { CreateGroupDto } from './dto/create-group.dto';
import { UpdateGroupDto } from './dto/update-group.dto';
import { Group } from './entities/group.entity';
Expand All @@ -18,6 +20,7 @@ export class GroupService {
private readonly groupRepository: GroupRepository,
private readonly memberService: MemberService,
private readonly eventService: EventService,
private readonly transactionService: TransactionService,
) {}

async create(
Expand Down Expand Up @@ -59,6 +62,16 @@ export class GroupService {
return await this.groupRepository.update(groupId, group);
}

async uploadTransactionFile(
groupId: string,
transactionExcel: Express.Multer.File,
): Promise<void> {
await this.transactionService.uploadTransactionFile(
groupId,
transactionExcel,
);
}

async addEvent(
groupId: string,
createEventDto: CreateEventDto,
Expand Down Expand Up @@ -99,6 +112,17 @@ export class GroupService {
});
}

async getTransactions(groupId: string) {
return this.transactionService.getTransactions(groupId);
}

async getTransactionsByPeriod(
groupId: string,
periodDto: GetTransactionsPeriodDto,
) {
return this.transactionService.getTransactionsByPeriod(groupId, periodDto);
}

async update(
groupId: string,
updateGroupDto: UpdateGroupDto,
Expand Down
40 changes: 40 additions & 0 deletions src/transaction/dto/create-transaction.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';

class Metadata {
@IsString()
@ApiProperty({ description: '모임 ID', example: '64125455d454s' })
groupId: string;

@IsString()
@ApiProperty({ description: '거래 유형', example: '입금' })
transactionType: string;

@IsNumber()
@ApiProperty({ description: '거래 금액', example: 25000 })
amount: number;

@IsString()
@ApiProperty({ description: '내용', example: '이소현' })
name: string;
}

export class CreateTransactionDto {
@IsNotEmpty()
@IsString()
@ApiProperty({
description: '거래 내역의 메타데이터',
example: {
groupId: '64125455d454s',
transactionType: '입금',
amount: 25000,
},
})
readonly metadata: Metadata;

@ApiProperty({
description: '거래 일시',
example: new Date(),
})
readonly timestamp: Date;
}
23 changes: 23 additions & 0 deletions src/transaction/dto/get-transaction-period.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import { IsDate, IsNotEmpty } from 'class-validator';

export class GetTransactionsPeriodDto {
@IsNotEmpty()
@IsDate()
@Transform(({ value }) => new Date(value))
@ApiProperty({
description: '2023-06-25T15:09:34.778Z',
example: new Date(),
})
readonly startDate: Date;

@IsNotEmpty()
@IsDate()
@Transform(({ value }) => new Date(value))
@ApiProperty({
description: '2023-08-25T15:09:34.778Z',
example: new Date(),
})
readonly endDate: Date;
}
9 changes: 9 additions & 0 deletions src/transaction/entities/transaction.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class Transaction {
metadata: {
groupId: string;
transactionType: string;
amount: number;
name: string;
};
timestamp: Date;
}
9 changes: 9 additions & 0 deletions src/transaction/interfaces/transaction.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface TransactionInterface extends Document {
metadata: {
groupId: string;
transactionType: string;
amount: number;
name: string;
};
timestamp: Date;
}
11 changes: 11 additions & 0 deletions src/transaction/schemas/transaction.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Schema } from 'mongoose';

export const TransactionSchema = new Schema({
metadata: {
groupId: String,
transactionType: String,
amount: Number,
name: String,
},
timestamp: Date,
});
6 changes: 6 additions & 0 deletions src/transaction/transaction.decorators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { applyDecorators } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';

export function Transaction() {
return applyDecorators(ApiTags('Transaction'));
}
19 changes: 19 additions & 0 deletions src/transaction/transaction.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Module } from '@nestjs/common';

import { DatabaseModule } from '../database/database.module';
import { ExcelService } from '../excel/excel.service';
import { transactionProviders } from './transaction.providers';
import { TransactionRepository } from './transaction.repository';
import { TransactionService } from './transaction.service';

@Module({
imports: [DatabaseModule],
providers: [
TransactionService,
...transactionProviders,
TransactionRepository,
ExcelService,
],
exports: [TransactionService],
})
export class TransactionModule {}
12 changes: 12 additions & 0 deletions src/transaction/transaction.providers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Connection } from 'mongoose';

import { TransactionSchema } from './schemas/transaction.schema';

export const transactionProviders = [
{
provide: 'TRANSACTION_MODEL',
useFactory: (connection: Connection) =>
connection.model('Transaction', TransactionSchema),
inject: ['MONGODB_CONNECTION'],
},
];
Loading

0 comments on commit 94ffe60

Please sign in to comment.