Skip to content

Commit f249483

Browse files
authored
Refactor/room settlement service (#170)
* Feat: room-settlement service 기존 로직 복사 * Fix: Contain RoomUser update logic in the same transaction * Delete(Refactor): Duplicated room check logic * Refactor: Delegate settlement logic in room service
1 parent 24558d7 commit f249483

File tree

3 files changed

+372
-301
lines changed

3 files changed

+372
-301
lines changed

src/room/room.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { FcmModule } from 'src/fcm/fcm.module';
99

1010
import { RoomService } from './room.service';
1111
import { RoomController } from './room.controller';
12+
import { RoomSettlementService } from './services/room-settlement.service';
1213
@Module({
1314
imports: [
1415
TypeOrmModule.forFeature([Room, RoomUser]),
@@ -17,7 +18,7 @@ import { RoomController } from './room.controller';
1718
FcmModule,
1819
],
1920
controllers: [RoomController],
20-
providers: [RoomService],
21+
providers: [RoomService, RoomSettlementService],
2122
exports: [RoomService],
2223
})
2324
export class RoomModule {}

src/room/room.service.ts

Lines changed: 13 additions & 300 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ import { CreateRoomDto } from './dto/create-room.dto';
3030
import { UpdateRoomDto } from './dto/update-room.dto';
3131
import { CreateSettlementDto } from './dto/create-settlement.dto';
3232
import { UpdateSettlementDto } from './dto/update-settlement.dto';
33-
import { ResponseSettlementDto } from './dto/response-settlement.dto';
3433
import { RoomStatisticsDto } from './dto/room-statistics.dto';
34+
import { RoomSettlementService } from './services/room-settlement.service';
3535

3636
@Injectable()
3737
export class RoomService {
@@ -45,6 +45,7 @@ export class RoomService {
4545
private readonly chatService: ChatService,
4646
private readonly fcmService: FcmService,
4747
private readonly dataSource: DataSource,
48+
private readonly roomSettlementService: RoomSettlementService,
4849
) {}
4950
private readonly logger = new Logger(RoomService.name);
5051

@@ -525,322 +526,43 @@ export class RoomService {
525526
userUuid: string,
526527
dto: CreateSettlementDto,
527528
) {
528-
const room = await this.findOne(uuid);
529-
if (!room) {
530-
throw new NotFoundException('방이 존재하지 않습니다.');
531-
}
532-
533-
if (room.status == RoomStatus.IN_SETTLEMENT) {
534-
throw new BadRequestException('이미 정산이 진행되고 있습니다.');
535-
}
536-
537-
if (room.status == RoomStatus.DELETED) {
538-
throw new BadRequestException('삭제된 방입니다.');
539-
}
540-
541-
if (room.status == RoomStatus.COMPLETED) {
542-
throw new BadRequestException('정산이 종료된 방입니다.');
543-
}
544-
545-
const queryRunner = this.dataSource.createQueryRunner();
546-
await queryRunner.connect();
547-
await queryRunner.startTransaction();
548-
549-
try {
550-
if (dto.updateAccount) {
551-
await this.userService.createOrUpdateAccount(
552-
userUuid,
553-
dto.payerAccountNumber,
554-
dto.payerAccountHolderName,
555-
dto.payerBankName,
556-
queryRunner,
557-
);
558-
}
559-
560-
await queryRunner.manager.update(
561-
Room,
562-
{ uuid: uuid },
563-
{
564-
status: RoomStatus.IN_SETTLEMENT,
565-
payerUuid: userUuid,
566-
payAmount: dto.payAmount,
567-
payerEncryptedAccountNumber: this.userService.encryptAccountNumber(
568-
dto.payerAccountNumber,
569-
),
570-
payerAccountHolderName: dto.payerAccountHolderName,
571-
payerBankName: dto.payerBankName,
572-
},
573-
);
574-
575-
await queryRunner.manager.update(
576-
RoomUser,
577-
{ roomUuid: uuid, userUuid: userUuid },
578-
{ isPaid: true },
579-
);
580-
await queryRunner.commitTransaction();
581-
return await this.getSettlement(uuid);
582-
} catch (err) {
583-
await queryRunner.rollbackTransaction();
584-
throw err;
585-
} finally {
586-
await queryRunner.release();
587-
}
529+
return this.roomSettlementService.requestSettlement(uuid, userUuid, dto);
588530
}
589531

590532
async updateSettlement(
591533
uuid: string,
592534
userUuid: string,
593535
dto: UpdateSettlementDto,
594536
) {
595-
const room = await this.findOne(uuid);
596-
if (!room) {
597-
throw new NotFoundException('방이 존재하지 않습니다.');
598-
}
599-
600-
if (room.status == RoomStatus.COMPLETED) {
601-
throw new BadRequestException('정산이 종료된 방입니다.');
602-
}
603-
604-
if (room.status != RoomStatus.IN_SETTLEMENT) {
605-
throw new BadRequestException('정산이 진행되고 있지 않습니다.');
606-
}
607-
608-
if (room.payerUuid != userUuid) {
609-
throw new UnauthorizedException(
610-
'정산자가 아니므로 정산 정보를 수정할 수 없습니다.',
611-
);
612-
}
613-
614-
const queryRunner = this.dataSource.createQueryRunner();
615-
await queryRunner.connect();
616-
await queryRunner.startTransaction();
617-
618-
try {
619-
if (dto.updateAccount) {
620-
await this.userService.createOrUpdateAccount(
621-
userUuid,
622-
dto.payerAccountNumber,
623-
dto.payerAccountHolderName,
624-
dto.payerBankName,
625-
queryRunner,
626-
);
627-
}
628-
629-
await queryRunner.manager.update(
630-
Room,
631-
{ uuid: uuid },
632-
{
633-
status: RoomStatus.IN_SETTLEMENT,
634-
payAmount: dto.payAmount,
635-
payerEncryptedAccountNumber: dto.payerAccountNumber
636-
? this.userService.encryptAccountNumber(dto.payerAccountNumber)
637-
: room.payerEncryptedAccountNumber,
638-
payerAccountHolderName: dto.payerAccountHolderName,
639-
payerBankName: dto.payerBankName,
640-
},
641-
);
642-
643-
await this.roomUserRepo.update(
644-
{
645-
roomUuid: uuid,
646-
status: RoomUserStatus.JOINED,
647-
userUuid: Not(userUuid),
648-
},
649-
{ isPaid: false },
650-
);
651-
652-
await queryRunner.commitTransaction();
653-
654-
return await this.getSettlement(uuid);
655-
} catch (err) {
656-
await queryRunner.rollbackTransaction();
657-
throw err;
658-
} finally {
659-
await queryRunner.release();
660-
}
537+
return this.roomSettlementService.updateSettlement(uuid, userUuid, dto);
661538
}
662539

663540
async cancelSettlement(uuid: string, userUuid: string) {
664-
const room = await this.findOne(uuid);
665-
if (!room) {
666-
throw new NotFoundException('방이 존재하지 않습니다.');
667-
}
668-
669-
if (room.status == RoomStatus.COMPLETED) {
670-
throw new BadRequestException('정산이 종료된 방입니다.');
671-
}
672-
673-
if (room.status != RoomStatus.IN_SETTLEMENT) {
674-
throw new BadRequestException('정산이 진행되고 있지 않습니다.');
675-
}
676-
677-
if (room.payerUuid != userUuid) {
678-
throw new UnauthorizedException(
679-
'정산자가 아니므로 정산 요청을 취소할 수 없습니다.',
680-
);
681-
}
682-
683-
const queryRunner = this.dataSource.createQueryRunner();
684-
await queryRunner.connect();
685-
await queryRunner.startTransaction();
686-
687-
try {
688-
await queryRunner.manager.update(
689-
RoomUser,
690-
{ roomUuid: uuid, status: RoomUserStatus.JOINED },
691-
{ isPaid: false },
692-
);
693-
await queryRunner.manager.update(
694-
Room,
695-
{ uuid: uuid },
696-
{
697-
status: RoomStatus.ACTIVATED,
698-
// NOTE: update에 undefined를 넣으면 무시됨, 값을 초기화하고 싶으면 null을 넣어야 함
699-
payerUuid: null,
700-
payAmount: null,
701-
payerEncryptedAccountNumber: null,
702-
payerAccountHolderName: null,
703-
payerBankName: null,
704-
},
705-
);
706-
await queryRunner.commitTransaction();
707-
708-
return await this.roomRepo.findOne({
709-
where: { uuid: uuid },
710-
});
711-
} catch (err) {
712-
await queryRunner.rollbackTransaction();
713-
throw err;
714-
} finally {
715-
await queryRunner.release();
716-
}
541+
return this.roomSettlementService.cancelSettlement(uuid, userUuid);
717542
}
718543

719544
async getSettlement(roomUuid: string) {
720-
const room = await this.roomRepo.findOne({
721-
where: {
722-
uuid: roomUuid,
723-
},
724-
});
725-
726-
if (!room) {
727-
throw new NotFoundException('방이 존재하지 않습니다.');
728-
}
729-
730-
if (
731-
room.status != RoomStatus.IN_SETTLEMENT &&
732-
room.status != RoomStatus.COMPLETED
733-
) {
734-
throw new BadRequestException('정산이 진행되고 있지 않습니다.');
735-
}
736-
737-
if (!room.payAmount || !room.payerUuid) {
738-
throw new NotFoundException('정산 내역이 없습니다.');
739-
}
740-
741-
if (
742-
!room.payerEncryptedAccountNumber ||
743-
!room.payerAccountHolderName ||
744-
!room.payerBankName
745-
) {
746-
throw new NotFoundException('정산자의 계좌 정보가 없습니다.');
747-
}
748-
749-
const decryptedAccountNumber = this.userService.decryptAccountNumber(
750-
room.payerEncryptedAccountNumber,
751-
);
752-
753-
const payAmountPerPerson = this.calculatePayAmountPerPerson(
754-
room.payAmount,
755-
room.currentParticipant,
756-
);
757-
758-
const payerNickname = await this.userService.getNickname(room.payerUuid);
759-
if (!payerNickname) {
760-
throw new NotFoundException('정산자 닉네임을 찾을 수 없습니다.');
761-
}
762-
763-
// Settlement DTO의 내용을 리턴함
764-
return new ResponseSettlementDto(
765-
room,
766-
payerNickname.nickname,
767-
decryptedAccountNumber,
768-
room.payerAccountHolderName,
769-
room.payerBankName,
770-
payAmountPerPerson,
771-
);
545+
return this.roomSettlementService.getSettlement(roomUuid);
772546
}
773547

774548
async getUserPayStatus(roomUuid: string, userUuid: string) {
775-
const room = await this.findOne(roomUuid);
776-
if (!room) {
777-
throw new NotFoundException('방이 존재하지 않습니다.');
778-
}
779-
780-
const roomUser = await this.roomUserRepo.findOne({
781-
where: { roomUuid, userUuid },
782-
});
783-
784-
if (!roomUser) {
785-
throw new NotFoundException('유저가 방에 가입되어 있지 않습니다.');
786-
}
787-
788-
return roomUser.isPaid;
549+
return this.roomSettlementService.getUserPayStatus(roomUuid, userUuid);
789550
}
790551

791552
async updateRoomUserIsPaid(
792553
roomUuid: string,
793554
userUuid: string,
794555
isPaid: boolean,
795556
) {
796-
const room = await this.findOne(roomUuid);
797-
if (!room) {
798-
throw new NotFoundException('방이 존재하지 않습니다.');
799-
}
800-
801-
if (room.status != RoomStatus.IN_SETTLEMENT || room.payerUuid == null) {
802-
throw new BadRequestException('정산이 진행되고 있지 않습니다.');
803-
}
804-
805-
const roomUser = await this.roomUserRepo.findOne({
806-
where: { roomUuid, userUuid },
807-
});
808-
809-
if (!roomUser || roomUser.status !== RoomUserStatus.JOINED) {
810-
throw new BadRequestException('방에 가입되어 있지 않습니다.');
811-
}
812-
813-
await this.roomUserRepo.update({ roomUuid, userUuid }, { isPaid });
814-
815-
const result = await this.roomUserRepo.findOneOrFail({
816-
where: { roomUuid, userUuid },
817-
});
818-
819-
return { payerUuid: room.payerUuid, roomUser: result };
557+
return this.roomSettlementService.updateRoomUserIsPaid(
558+
roomUuid,
559+
userUuid,
560+
isPaid,
561+
);
820562
}
821563

822564
async completeRoom(uuid: string, userUuid: string, userType: UserType) {
823-
const room = await this.findOne(uuid);
824-
if (!room) {
825-
throw new NotFoundException('방이 존재하지 않습니다.');
826-
}
827-
828-
if (userUuid != room.payerUuid && userType != UserType.admin) {
829-
throw new UnauthorizedException('정산자 또는 관리자가 아닙니다.');
830-
}
831-
832-
if (room.status == RoomStatus.COMPLETED) {
833-
throw new BadRequestException('이미 종료된 방입니다.');
834-
}
835-
836-
await this.roomRepo.update(
837-
{ uuid: uuid },
838-
{ status: RoomStatus.COMPLETED },
839-
);
840-
841-
return await this.roomRepo.findOne({
842-
where: { uuid: uuid },
843-
});
565+
return this.roomSettlementService.completeRoom(uuid, userUuid, userType);
844566
}
845567

846568
async saveLastReadChat(roomUuid: string, userUuid: string) {
@@ -919,15 +641,6 @@ export class RoomService {
919641
}
920642
}
921643

922-
private calculatePayAmountPerPerson(
923-
payAmount: number,
924-
currentParticipant: number,
925-
) {
926-
// NOTE: 서비스 이용약관에 따라 소수점은 올려서 계산함
927-
// 정산자가 정산 금액보다 최대 (currentParticipant-1)원 더 받을 수 있음
928-
return Math.ceil(payAmount / currentParticipant);
929-
}
930-
931644
private getDepartureAlertTargetRooms() {
932645
return this.roomRepo.find({
933646
where: {

0 commit comments

Comments
 (0)