diff --git a/back/microsservicos/aluguel/lib/application/update_aluguel_usecase.dart b/back/microsservicos/aluguel/lib/application/update_aluguel_usecase.dart index 9747463..cece595 100644 --- a/back/microsservicos/aluguel/lib/application/update_aluguel_usecase.dart +++ b/back/microsservicos/aluguel/lib/application/update_aluguel_usecase.dart @@ -1,6 +1,7 @@ +import 'dart:math'; + import 'package:aluguel_dart/domain/entities/aluguel.dart'; import 'package:aluguel_dart/domain/repositories/aluguel_repository.dart'; -import 'package:aluguel_dart/infrastructure/clients/rabbitmq/rabbitmq.dart'; class UpdateAluguelUsecase { final AluguelRepository repository; @@ -14,7 +15,6 @@ class UpdateAluguelUsecase { int? people, double? finalPrice, String? status, - String? doorCode, }) async { if (startDate != null && endDate != null && endDate < startDate) { throw StateError('endDate não pode ser menor que startDate.'); @@ -26,35 +26,14 @@ class UpdateAluguelUsecase { throw StateError('finalPrice não pode ser negativo.'); } - String? desiredDoorCode = doorCode; + String? desiredDoorCode = Random().nextInt(100000).toString().padLeft(5, '0'); if (status != null && - status.toUpperCase() == 'CONFIRMED' && - doorCode == null) { + status.toUpperCase() == 'CONFIRMED') { final current = await repository.getAluguel(id); if (current == null) { throw StateError('aluguel_not_found'); } - - final fetchedDoorCode = await fetchDoorCodeFromCatalog( - current.workspaceId, - ); - if (fetchedDoorCode == null || fetchedDoorCode.isEmpty) { - throw StateError('door_code_not_found'); - } - desiredDoorCode = fetchedDoorCode; - } - - String? sanitizedDoorCode; - if (desiredDoorCode != null) { - final trimmedDoorCode = desiredDoorCode.trim(); - if (trimmedDoorCode.isEmpty) { - throw StateError('doorCode nǜo pode ser vazio.'); - } - if (!RegExp(r'^\d{5}$').hasMatch(trimmedDoorCode)) { - throw StateError('doorCode deve conter exatamente 5 d��gitos.'); - } - sanitizedDoorCode = trimmedDoorCode; } return repository.updateAluguel( @@ -64,7 +43,7 @@ class UpdateAluguelUsecase { people: people, finalPrice: finalPrice, status: status, - doorCode: sanitizedDoorCode, + doorCode: desiredDoorCode, ); } } diff --git a/back/microsservicos/aluguel/lib/application/verify_door_code_usecase.dart b/back/microsservicos/aluguel/lib/application/verify_door_code_usecase.dart index 3531990..214d099 100644 --- a/back/microsservicos/aluguel/lib/application/verify_door_code_usecase.dart +++ b/back/microsservicos/aluguel/lib/application/verify_door_code_usecase.dart @@ -1,3 +1,4 @@ +import 'package:aluguel_dart/domain/entities/aluguel.dart'; import 'package:aluguel_dart/domain/repositories/aluguel_repository.dart'; import 'package:aluguel_dart/infrastructure/clients/rabbitmq/rabbitmq.dart'; @@ -6,32 +7,55 @@ class VerifyDoorCodeUsecase { VerifyDoorCodeUsecase({required this.repository}); - Future call({ - required String aluguelId, - required String plainDoorCode, + Future call({ + required String doorCode, }) async { - if (!RegExp(r'^\d{5}$').hasMatch(plainDoorCode)) { - throw StateError('doorCode deve conter exatamente 5 digitos.'); + final normalizedDoorCode = doorCode.trim(); + + if (!RegExp(r'^\d{5}$').hasMatch(normalizedDoorCode)) { + throw StateError('door code invalid'); } - final aluguel = await repository.getAluguel(aluguelId); + final allAlugueis = await repository.getAllAluguel(); - if (aluguel == null) { - throw StateError('aluguel_not_found'); + if (allAlugueis == null || allAlugueis.isEmpty) { + throw StateError('door code not found'); } - if (aluguel.status.toUpperCase() != 'CONFIRMED') { - return false; + final nowEpoch = DateTime.now().millisecondsSinceEpoch ~/ 1000; + + Aluguel? matchingAluguel; + + for (final aluguel in allAlugueis.values) { + final storedDoor = aluguel.doorCode?.trim(); + if (storedDoor == null || storedDoor.isEmpty) { + continue; + } + if (storedDoor != normalizedDoorCode) { + continue; + } + if (aluguel.status.toUpperCase() != 'CONFIRMED') { + continue; + } + if (nowEpoch < aluguel.startDate || nowEpoch > aluguel.endDate) { + continue; + } + matchingAluguel = aluguel; + break; } - final nowEpoch = DateTime.now().millisecondsSinceEpoch ~/ 1000; - if (nowEpoch < aluguel.startDate || nowEpoch > aluguel.endDate) { - return false; + if (matchingAluguel == null) { + throw StateError('door code not found'); } - return await verifyDoorCodeWithCatalog( - workspaceId: aluguel.workspaceId, - doorCode: plainDoorCode, + final doorSerial = await fetchDoorCodeFromCatalog( + matchingAluguel.workspaceId, ); + + if (doorSerial == null || doorSerial.trim().isEmpty) { + throw StateError('door serial not found'); + } + + return doorSerial.trim(); } } diff --git a/back/microsservicos/aluguel/lib/domain/repositories/aluguel_repository.dart b/back/microsservicos/aluguel/lib/domain/repositories/aluguel_repository.dart index 653c297..2d74091 100644 --- a/back/microsservicos/aluguel/lib/domain/repositories/aluguel_repository.dart +++ b/back/microsservicos/aluguel/lib/domain/repositories/aluguel_repository.dart @@ -27,4 +27,6 @@ abstract class AluguelRepository { Future deleteAluguel(String id); + + Future getDoorAluguel(String doorCode); } diff --git a/back/microsservicos/aluguel/lib/infrastructure/clients/rabbitmq/rabbitmq.dart b/back/microsservicos/aluguel/lib/infrastructure/clients/rabbitmq/rabbitmq.dart index ddbc0b5..1437645 100644 --- a/back/microsservicos/aluguel/lib/infrastructure/clients/rabbitmq/rabbitmq.dart +++ b/back/microsservicos/aluguel/lib/infrastructure/clients/rabbitmq/rabbitmq.dart @@ -270,8 +270,10 @@ Future fetchDoorCodeFromCatalog( final decoded = jsonDecode(message.payloadAsString); String? doorCode; if (decoded is Map) { - final rawDoorCode = - decoded['doorCodeHash'] ?? decoded['doorCode'] ?? decoded['code']; + final rawDoorCode = decoded['doorSerial'] ?? + decoded['doorCodeHash'] ?? + decoded['doorCode'] ?? + decoded['code']; if (rawDoorCode is String && rawDoorCode.isNotEmpty) { doorCode = rawDoorCode; } @@ -338,104 +340,6 @@ Future fetchDoorCodeFromCatalog( } } -Future verifyDoorCodeWithCatalog({ - required String workspaceId, - required String doorCode, - Duration timeout = const Duration(seconds: 5), -}) async { - final channel = await connectRabbitMQ(); - final exchange = await channel.exchange( - EXCHANGE_NAME, - ExchangeType.TOPIC, - durable: true, - ); - final replyQueue = await channel.queue('', exclusive: true, autoDelete: true); - final correlationId = const Uuid().v4(); - final completer = Completer(); - final consumer = await replyQueue.consume(noAck: false); - late final StreamSubscription subscription; - - subscription = consumer.listen( - (AmqpMessage message) { - try { - if (message.properties?.corellationId != correlationId) { - message.reject(false); - return; - } - - final decoded = jsonDecode(message.payloadAsString); - bool result = false; - if (decoded is Map) { - final dynamic candidate = - decoded['doorVerifiedHash'] ?? decoded['valid']; - if (candidate is bool) { - result = candidate; - } - } else if (decoded is bool) { - result = decoded; - } - - message.ack(); - if (!completer.isCompleted) { - completer.complete(result); - } - subscription.cancel(); - } catch (error) { - message.reject(false); - if (!completer.isCompleted) { - completer.completeError(error); - } - } - }, - onError: (error) { - if (!completer.isCompleted) { - completer.completeError(error); - } - }, - onDone: () { - if (!completer.isCompleted) { - completer.completeError( - StateError('reply queue closed before receiving response'), - ); - } - }, - cancelOnError: false, - ); - - final payload = { - 'eventType': 'CatalogoVerifyDoorCodeRequest', - 'payload': {'workspaceId': workspaceId, 'doorCode': doorCode}, - }; - - final properties = MessageProperties() - ..replyTo = replyQueue.name - ..corellationId = correlationId - ..contentType = 'application/json' - ..deliveryMode = 1; - - exchange.publish( - utf8.encode(jsonEncode(payload)), - 'catalogo.verify-door-code.request', - properties: properties, - ); - - final timer = Timer(timeout, () { - if (!completer.isCompleted) { - completer.completeError( - TimeoutException('Door code verification timed out'), - ); - } - }); - - try { - return await completer.future; - } finally { - timer.cancel(); - await subscription.cancel(); - await replyQueue.delete(); - } -} - const String _DELAY_EXCHANGE = 'aluguel.delay.ex'; // exchange de delay (direct) const String _DELAY_QUEUE = 'aluguel.delay.q'; // fila de delay const String _DELAY_RK = 'aluguel.expire'; // routing key para entrar no delay diff --git a/back/microsservicos/aluguel/lib/infrastructure/repositories/aluguel_repository_mock.dart b/back/microsservicos/aluguel/lib/infrastructure/repositories/aluguel_repository_mock.dart index 37151f5..8ff6b3b 100644 --- a/back/microsservicos/aluguel/lib/infrastructure/repositories/aluguel_repository_mock.dart +++ b/back/microsservicos/aluguel/lib/infrastructure/repositories/aluguel_repository_mock.dart @@ -96,4 +96,17 @@ class AluguelRepositoryMock implements AluguelRepository { throw StateError('Aluguel com id "$id" não encontrado para exclusão.'); } } + + @override + Future getDoorAluguel(String doorCode) async { + for (var aluguel in store.values) { + + if (aluguel.doorCode == doorCode) { + return aluguel; + } + + } + return null; + } + } diff --git a/back/microsservicos/aluguel/lib/presentation/http/controllers/get_door_hash_controller.dart b/back/microsservicos/aluguel/lib/presentation/http/controllers/get_door_hash_controller.dart index 79cf164..bdf2447 100644 --- a/back/microsservicos/aluguel/lib/presentation/http/controllers/get_door_hash_controller.dart +++ b/back/microsservicos/aluguel/lib/presentation/http/controllers/get_door_hash_controller.dart @@ -20,29 +20,30 @@ class GetDoorHashController { final data = jsonDecode(body); - final aluguelId = (data['aluguelId'] ?? data['bookingId'])?.toString(); final doorCode = data['doorCode']?.toString(); - - if (aluguelId == null || aluguelId.isEmpty) { - throw AppFailure('aluguelId_required'); - } if (doorCode == null || doorCode.isEmpty) { throw AppFailure('doorCode_required'); } - final isValid = await verifyDoorCodeUsecase.call( - aluguelId: aluguelId, - plainDoorCode: doorCode, + final doorSerial = await verifyDoorCodeUsecase.call( + doorCode: doorCode, ); - return jsonOk({'valid': isValid}); + return jsonOk({'doorSerial': doorSerial}); } on AppFailure catch (e) { return jsonBadRequest({'error': e.message}); } on StateError catch (e) { - if (e.message == 'aluguel_not_found') { - return jsonNotFound({'error': e.message}); + final message = e.message; + if (message == 'door code not found') { + return jsonNotFound({'error': message}); } - return jsonBadRequest({'error': e.message}); + if (message == 'door serial not found') { + return jsonBadRequest({'error': message}); + } + if (message == 'door code invalid') { + return jsonBadRequest({'error': message}); + } + return jsonBadRequest({'error': message}); } on TimeoutException { return Response( 504, diff --git a/back/microsservicos/aluguel/lib/presentation/http/controllers/update_aluguel_controller.dart b/back/microsservicos/aluguel/lib/presentation/http/controllers/update_aluguel_controller.dart index 1e6e667..1285182 100644 --- a/back/microsservicos/aluguel/lib/presentation/http/controllers/update_aluguel_controller.dart +++ b/back/microsservicos/aluguel/lib/presentation/http/controllers/update_aluguel_controller.dart @@ -29,7 +29,6 @@ class UpdateAluguelController { final int? people = data['people']; final num? finalPrice = data['finalPrice']; final String? status = data['status']; - final String? doorCode = data['doorCode']; if (startDate != null && endDate != null && endDate <= startDate) { throw AppFailure('endDate_must_be_greater_than_startDate'); @@ -40,9 +39,6 @@ class UpdateAluguelController { if (finalPrice != null && finalPrice <= 0) { throw AppFailure('finalPrice_must_be_positive'); } - if (doorCode != null && doorCode.isEmpty) { - throw AppFailure('doorCode_cannot_be_empty'); - } final aluguelAtualizado = await updateAluguelUsecase.call( id.toString(), @@ -51,7 +47,6 @@ class UpdateAluguelController { people: people, finalPrice: finalPrice?.toDouble(), status: status, - doorCode: doorCode, ); return jsonOk(aluguelAtualizado.toJson()); diff --git a/back/microsservicos/catalogo/app/create_catalogo/create_catalogo_controller.ts b/back/microsservicos/catalogo/app/create_catalogo/create_catalogo_controller.ts index 82bfa63..6b9a86a 100644 --- a/back/microsservicos/catalogo/app/create_catalogo/create_catalogo_controller.ts +++ b/back/microsservicos/catalogo/app/create_catalogo/create_catalogo_controller.ts @@ -30,12 +30,8 @@ export class CreateCatalogoController { throw new Error("Missing catalogo price"); if (body.capacity === undefined) throw new Error("Missing catalogo capacity"); - - // const { - // doorCode, - // doorCodeHash: providedDoorCodeHash, - // ...restBody - // } = body; + if (body.doorSerial === undefined) + throw new Error("Missing door serial") const price = typeof body.price === "string" ? Number(body.price) : body.price; @@ -48,29 +44,7 @@ export class CreateCatalogoController { ? [body.comodities] : []; - // let doorCodeHash: string | undefined = providedDoorCodeHash; - // const rawDoorInput: unknown = doorCode ?? providedDoorCodeHash; - - // if (rawDoorInput !== undefined) { - // if (typeof rawDoorInput !== "string") { - // throw new Error("doorCode must be a string"); - // } - // const trimmed = rawDoorInput.trim(); - - // if (trimmed.length === 0) { - // throw new Error("doorCode cannot be empty"); - // } - // if (trimmed.length !== 5) { - // throw new Error("doorCode must be exactly 5 characters"); - // } - // if (!/^[0-9]+$/.test(trimmed)) { - // throw new Error("doorCode must be numeric"); - // } - - // doorCodeHash = trimmed.toLowerCase(); - // } - - const doorCodeHash = randomInt(0, 100000).toString().padStart(5, '0'); + const doorSerial = body.doorSerial const roomProps = { ...body, @@ -78,7 +52,7 @@ export class CreateCatalogoController { pictures: [], price, capacity, - doorCodeHash, + doorSerial, } as Catalogo; const createdRoom = this.usecase.execute(roomProps); diff --git a/back/microsservicos/catalogo/shared/domain/interfaces.ts b/back/microsservicos/catalogo/shared/domain/interfaces.ts index 01c5658..0251fd3 100644 --- a/back/microsservicos/catalogo/shared/domain/interfaces.ts +++ b/back/microsservicos/catalogo/shared/domain/interfaces.ts @@ -7,5 +7,5 @@ export interface Catalogo { pictures: string[] price: number capacity: number - doorCodeHash: string + doorSerial: string } diff --git a/back/microsservicos/catalogo/shared/infra/clients/rabbitmq/doorCodeVerificationConsumer.ts b/back/microsservicos/catalogo/shared/infra/clients/rabbitmq/doorCodeVerificationConsumer.ts index b324633..92c4dd5 100644 --- a/back/microsservicos/catalogo/shared/infra/clients/rabbitmq/doorCodeVerificationConsumer.ts +++ b/back/microsservicos/catalogo/shared/infra/clients/rabbitmq/doorCodeVerificationConsumer.ts @@ -51,16 +51,20 @@ export const startDoorCodeVerificationConsumer = async (): Promise => { const workspaceId = payload?.workspaceId; const doorCode: string | undefined = payload?.doorCode; - let valid = false; + let responseBody: any = { valid: false }; if (workspaceId && typeof doorCode === 'string') { try { const repo = Environments.getCatalogoRepo(); const catalogo = repo.getCatalogo(workspaceId); - const storedHash = catalogo?.doorCodeHash; - - if (storedHash) { - valid = storedHash.trim() === doorCode.trim(); + const storedSerial = catalogo?.doorSerial; + + if (storedSerial) { + const isValid = storedSerial.trim() === doorCode.trim(); + responseBody = { + valid: isValid, + doorSerial: storedSerial.trim(), + }; } } catch (error) { console.error( @@ -72,7 +76,7 @@ export const startDoorCodeVerificationConsumer = async (): Promise => { channel.sendToQueue( replyTo, - Buffer.from(JSON.stringify({ valid })), + Buffer.from(JSON.stringify(responseBody)), { correlationId, contentType: 'application/json', @@ -143,13 +147,13 @@ export const startDoorCodeFetchConsumer = async (): Promise => { const payload = parsedPayload?.payload ?? parsedPayload; const workspaceId = payload?.workspaceId; - let doorCodeHash: string | null = null; + let doorSerial: string | null = null; if (workspaceId) { try { const repo = Environments.getCatalogoRepo(); const catalogo = repo.getCatalogo(workspaceId); - doorCodeHash = catalogo?.doorCodeHash ?? null; + doorSerial = catalogo?.doorSerial ?? null; } catch (error) { console.error( '[DoorCodeFetch] Error while retrieving door code:', @@ -160,7 +164,7 @@ export const startDoorCodeFetchConsumer = async (): Promise => { channel.sendToQueue( replyTo, - Buffer.from(JSON.stringify({ doorCodeHash })), + Buffer.from(JSON.stringify({ doorSerial })), { correlationId, contentType: 'application/json', @@ -176,7 +180,7 @@ export const startDoorCodeFetchConsumer = async (): Promise => { channel.sendToQueue( replyTo, Buffer.from( - JSON.stringify({ doorCodeHash: null, error: 'internal_error' }), + JSON.stringify({ doorSerial: null, error: 'internal_error' }), ), { correlationId, diff --git a/back/microsservicos/catalogo/shared/infra/repo/catalogoRepositoryMock.ts b/back/microsservicos/catalogo/shared/infra/repo/catalogoRepositoryMock.ts index cb9b60d..951031d 100644 --- a/back/microsservicos/catalogo/shared/infra/repo/catalogoRepositoryMock.ts +++ b/back/microsservicos/catalogo/shared/infra/repo/catalogoRepositoryMock.ts @@ -33,7 +33,7 @@ export class CatalogoRepositoryMock implements CatalogoRepository { pictures: props.pictures, price: props.price, capacity: props.capacity, - doorCodeHash: props.doorCodeHash + doorSerial: props.doorSerial } this.baseCatalogo[id] = room @@ -57,7 +57,7 @@ export class CatalogoRepositoryMock implements CatalogoRepository { if(props.pictures && props.pictures.length > 0) room_to_update.pictures = props.pictures if(props.price) room_to_update.price = props.price if(props.capacity) room_to_update.capacity = props.capacity - if(props.doorCodeHash) room_to_update.doorCodeHash = props.doorCodeHash + if(props.doorSerial) room_to_update.doorSerial = props.doorSerial return room_to_update