eva4j 1.0.13 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +51 -9
- package/DOMAIN_YAML_GUIDE.md +150 -0
- package/bin/eva4j.js +31 -1
- package/design-system.md +797 -0
- package/docs/commands/EVALUATE_SYSTEM.md +542 -0
- package/docs/commands/GENERATE_ENTITIES.md +196 -0
- package/docs/commands/INDEX.md +10 -1
- package/examples/domain-endpoints-relations.yaml +353 -0
- package/examples/domain-endpoints-versioned.yaml +144 -0
- package/examples/domain-endpoints.yaml +135 -0
- package/examples/system.yaml +289 -0
- package/package.json +1 -1
- package/src/commands/create.js +6 -3
- package/src/commands/evaluate-system.js +384 -0
- package/src/commands/generate-entities.js +677 -14
- package/src/commands/generate-kafka-event.js +59 -5
- package/src/commands/generate-system.js +243 -0
- package/src/generators/base-generator.js +9 -1
- package/src/utils/naming.js +3 -2
- package/src/utils/system-validator.js +314 -0
- package/src/utils/yaml-to-entity.js +31 -2
- package/templates/aggregate/AggregateRepository.java.ejs +5 -0
- package/templates/aggregate/AggregateRepositoryImpl.java.ejs +9 -0
- package/templates/aggregate/DomainEventHandler.java.ejs +24 -20
- package/templates/aggregate/JpaRepository.java.ejs +5 -0
- package/templates/base/root/skill-build-domain-yaml-references-generate-entities.md.ejs +1103 -0
- package/templates/base/root/skill-build-domain-yaml.ejs +292 -0
- package/templates/base/root/skill-build-system-yaml.ejs +252 -0
- package/templates/base/root/system.yaml.ejs +97 -0
- package/templates/crud/EndpointsController.java.ejs +178 -0
- package/templates/crud/FindByQuery.java.ejs +17 -0
- package/templates/crud/FindByQueryHandler.java.ejs +57 -0
- package/templates/crud/ScaffoldCommand.java.ejs +12 -0
- package/templates/crud/ScaffoldCommandHandler.java.ejs +43 -0
- package/templates/crud/ScaffoldQuery.java.ejs +12 -0
- package/templates/crud/ScaffoldQueryHandler.java.ejs +40 -0
- package/templates/crud/SubEntityAddCommand.java.ejs +17 -0
- package/templates/crud/SubEntityAddCommandHandler.java.ejs +43 -0
- package/templates/crud/SubEntityRemoveCommand.java.ejs +9 -0
- package/templates/crud/SubEntityRemoveCommandHandler.java.ejs +42 -0
- package/templates/crud/TransitionCommand.java.ejs +9 -0
- package/templates/crud/TransitionCommandHandler.java.ejs +39 -0
- package/templates/evaluate/report.html.ejs +971 -0
- package/templates/kafka-event/Event.java.ejs +7 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# EJEMPLO: ENDPOINTS CON VERSIONADO MÚLTIPLE (v1 + v2)
|
|
2
|
+
# Demuestra cómo v1 y v2 coexisten generando controladores independientes.
|
|
3
|
+
#
|
|
4
|
+
# REGLA ANTI-DUPLICADO:
|
|
5
|
+
# Si un useCase declarado en v2 ya fue generado en v1, el generador
|
|
6
|
+
# omite la creación del Command/Query + Handler y SOLO agrega la
|
|
7
|
+
# referencia en el controlador de v2.
|
|
8
|
+
#
|
|
9
|
+
# RESULTADO:
|
|
10
|
+
# - 1 controlador por versión (ProductV1Controller, ProductV2Controller)
|
|
11
|
+
# - Use cases propios de cada versión generados una sola vez
|
|
12
|
+
# - Use cases compartidos referenciados sin regenerar
|
|
13
|
+
|
|
14
|
+
aggregates:
|
|
15
|
+
- name: Product
|
|
16
|
+
entities:
|
|
17
|
+
- name: product
|
|
18
|
+
isRoot: true
|
|
19
|
+
tableName: products
|
|
20
|
+
audit:
|
|
21
|
+
enabled: true
|
|
22
|
+
fields:
|
|
23
|
+
- name: id
|
|
24
|
+
type: String
|
|
25
|
+
- name: name
|
|
26
|
+
type: String
|
|
27
|
+
validations:
|
|
28
|
+
- type: NotBlank
|
|
29
|
+
- name: description
|
|
30
|
+
type: String
|
|
31
|
+
- name: price
|
|
32
|
+
type: BigDecimal
|
|
33
|
+
validations:
|
|
34
|
+
- type: Positive
|
|
35
|
+
- name: stock
|
|
36
|
+
type: Integer
|
|
37
|
+
readOnly: true
|
|
38
|
+
defaultValue: 0
|
|
39
|
+
- name: active
|
|
40
|
+
type: Boolean
|
|
41
|
+
readOnly: true
|
|
42
|
+
defaultValue: true
|
|
43
|
+
|
|
44
|
+
endpoints:
|
|
45
|
+
basePath: /products
|
|
46
|
+
versions:
|
|
47
|
+
# ── v1: CRUD estándar ──────────────────────────────────────────────────
|
|
48
|
+
- version: v1
|
|
49
|
+
operations:
|
|
50
|
+
- method: GET
|
|
51
|
+
path: /{id}
|
|
52
|
+
description: "Obtener producto por ID"
|
|
53
|
+
useCase: FindProductById # completo
|
|
54
|
+
|
|
55
|
+
- method: GET
|
|
56
|
+
path: /
|
|
57
|
+
description: "Listar productos activos"
|
|
58
|
+
useCase: FindAllProducts # completo
|
|
59
|
+
|
|
60
|
+
- method: POST
|
|
61
|
+
path: /
|
|
62
|
+
description: "Crear producto"
|
|
63
|
+
useCase: CreateProduct # completo
|
|
64
|
+
|
|
65
|
+
- method: PUT
|
|
66
|
+
path: /{id}
|
|
67
|
+
description: "Actualizar datos del producto"
|
|
68
|
+
useCase: UpdateProduct # completo
|
|
69
|
+
|
|
70
|
+
- method: DELETE
|
|
71
|
+
path: /{id}
|
|
72
|
+
description: "Eliminar producto"
|
|
73
|
+
useCase: DeleteProduct # completo
|
|
74
|
+
|
|
75
|
+
# ── v2: nuevas operaciones + reuso de use cases de v1 ─────────────────
|
|
76
|
+
- version: v2
|
|
77
|
+
operations:
|
|
78
|
+
- method: GET
|
|
79
|
+
path: /{id}
|
|
80
|
+
description: "Producto con categorías y variantes embebidas"
|
|
81
|
+
useCase: FindProductByIdV2 # NUEVO handler — shape de respuesta distinto
|
|
82
|
+
|
|
83
|
+
- method: GET
|
|
84
|
+
path: /
|
|
85
|
+
description: "Listado con filtros avanzados y metadata extendida"
|
|
86
|
+
useCase: FindAllProductsV2 # NUEVO handler — parámetros de búsqueda distintos
|
|
87
|
+
|
|
88
|
+
- method: POST
|
|
89
|
+
path: /
|
|
90
|
+
description: "Crear producto (misma lógica que v1)"
|
|
91
|
+
useCase: CreateProduct # REUTILIZA v1 — no regenera handler
|
|
92
|
+
|
|
93
|
+
- method: PUT
|
|
94
|
+
path: /{id}
|
|
95
|
+
description: "Actualizar producto (misma lógica que v1)"
|
|
96
|
+
useCase: UpdateProduct # REUTILIZA v1 — no regenera handler
|
|
97
|
+
|
|
98
|
+
- method: DELETE
|
|
99
|
+
path: /{id}
|
|
100
|
+
description: "Eliminar producto (misma lógica que v1)"
|
|
101
|
+
useCase: DeleteProduct # REUTILIZA v1 — no regenera handler
|
|
102
|
+
|
|
103
|
+
- method: PUT
|
|
104
|
+
path: /{id}/activate
|
|
105
|
+
description: "Activar un producto desactivado"
|
|
106
|
+
useCase: ActivateProduct # NUEVO scaffold — TODO: product.activate()
|
|
107
|
+
|
|
108
|
+
- method: PUT
|
|
109
|
+
path: /{id}/deactivate
|
|
110
|
+
description: "Desactivar un producto activo"
|
|
111
|
+
useCase: DeactivateProduct # NUEVO scaffold — TODO: product.deactivate()
|
|
112
|
+
|
|
113
|
+
# Archivos generados:
|
|
114
|
+
#
|
|
115
|
+
# infrastructure/rest/controllers/product/
|
|
116
|
+
# ├── v1/ProductV1Controller.java ← 5 endpoints (v1)
|
|
117
|
+
# └── v2/ProductV2Controller.java ← 7 endpoints (v2)
|
|
118
|
+
#
|
|
119
|
+
# application/commands/ (solo use cases ÚNICOS — sin duplicados)
|
|
120
|
+
# ├── CreateProductCommand.java
|
|
121
|
+
# ├── UpdateProductCommand.java
|
|
122
|
+
# ├── DeleteProductCommand.java
|
|
123
|
+
# ├── ActivateProductCommand.java ← solo v2
|
|
124
|
+
# └── DeactivateProductCommand.java ← solo v2
|
|
125
|
+
#
|
|
126
|
+
# application/queries/ (solo use cases ÚNICOS)
|
|
127
|
+
# ├── FindProductByIdQuery.java
|
|
128
|
+
# ├── FindAllProductsQuery.java
|
|
129
|
+
# ├── FindProductByIdV2Query.java ← solo v2
|
|
130
|
+
# └── FindAllProductsV2Query.java ← solo v2
|
|
131
|
+
#
|
|
132
|
+
# application/usecases/
|
|
133
|
+
# ├── CreateProductCommandHandler.java ← completo
|
|
134
|
+
# ├── UpdateProductCommandHandler.java ← completo
|
|
135
|
+
# ├── DeleteProductCommandHandler.java ← completo
|
|
136
|
+
# ├── ActivateProductCommandHandler.java ← scaffold + TODO
|
|
137
|
+
# ├── DeactivateProductCommandHandler.java ← scaffold + TODO
|
|
138
|
+
# ├── FindProductByIdQueryHandler.java ← completo
|
|
139
|
+
# ├── FindAllProductsQueryHandler.java ← completo
|
|
140
|
+
# ├── FindProductByIdV2QueryHandler.java ← completo (shape distinto)
|
|
141
|
+
# └── FindAllProductsV2QueryHandler.java ← completo (filtros extendidos)
|
|
142
|
+
#
|
|
143
|
+
# CreateProduct, UpdateProduct, DeleteProduct aparecen en v2 pero ya
|
|
144
|
+
# existen de v1 → ProductV2Controller los inyecta sin regenerar handlers.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# EJEMPLO: ENDPOINTS DECLARATIVOS — CASO BÁSICO (una versión)
|
|
2
|
+
# Módulo de pedidos con operaciones específicas del negocio.
|
|
3
|
+
#
|
|
4
|
+
# COMPORTAMIENTO CONDICIONAL:
|
|
5
|
+
#
|
|
6
|
+
# SIN sección endpoints: → genera 5 CRUD fijos por defecto:
|
|
7
|
+
# CreateOrder, UpdateOrder, DeleteOrder,
|
|
8
|
+
# FindOrderById, FindAllOrders
|
|
9
|
+
#
|
|
10
|
+
# CON sección endpoints: → genera ÚNICAMENTE las operaciones
|
|
11
|
+
# declaradas en operations[] — ni más, ni menos.
|
|
12
|
+
#
|
|
13
|
+
# TYPE INFERIDO desde method:
|
|
14
|
+
# GET → query
|
|
15
|
+
# POST/PUT/DELETE/PATCH → command
|
|
16
|
+
#
|
|
17
|
+
# SCAFFOLD vs COMPLETO:
|
|
18
|
+
# Use cases estándar (Create*, Update*, Delete*, FindById*, FindAll*):
|
|
19
|
+
# → implementación completa con lógica de repositorio
|
|
20
|
+
# Use cases custom (ConfirmOrder, CancelOrder, etc.):
|
|
21
|
+
# → scaffold con // TODO apuntando al método de negocio sugerido
|
|
22
|
+
|
|
23
|
+
aggregates:
|
|
24
|
+
- name: Order
|
|
25
|
+
entities:
|
|
26
|
+
- name: order
|
|
27
|
+
isRoot: true
|
|
28
|
+
tableName: orders
|
|
29
|
+
audit:
|
|
30
|
+
enabled: true
|
|
31
|
+
trackUser: true
|
|
32
|
+
fields:
|
|
33
|
+
- name: id
|
|
34
|
+
type: String
|
|
35
|
+
- name: orderNumber
|
|
36
|
+
type: String
|
|
37
|
+
validations:
|
|
38
|
+
- type: NotBlank
|
|
39
|
+
message: "Order number is required"
|
|
40
|
+
- name: customerId
|
|
41
|
+
type: String
|
|
42
|
+
reference:
|
|
43
|
+
aggregate: Customer
|
|
44
|
+
module: customers
|
|
45
|
+
- name: status
|
|
46
|
+
type: OrderStatus
|
|
47
|
+
readOnly: true
|
|
48
|
+
- name: totalAmount
|
|
49
|
+
type: BigDecimal
|
|
50
|
+
readOnly: true
|
|
51
|
+
defaultValue: "0.00"
|
|
52
|
+
|
|
53
|
+
enums:
|
|
54
|
+
- name: OrderStatus
|
|
55
|
+
initialValue: PENDING
|
|
56
|
+
transitions:
|
|
57
|
+
- from: PENDING
|
|
58
|
+
to: CONFIRMED
|
|
59
|
+
method: confirm
|
|
60
|
+
- from: [PENDING, CONFIRMED]
|
|
61
|
+
to: CANCELLED
|
|
62
|
+
method: cancel
|
|
63
|
+
values: [PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED]
|
|
64
|
+
|
|
65
|
+
events:
|
|
66
|
+
- name: OrderPlacedEvent
|
|
67
|
+
fields:
|
|
68
|
+
- name: orderId
|
|
69
|
+
type: String
|
|
70
|
+
- name: customerId
|
|
71
|
+
type: String
|
|
72
|
+
kafka: true
|
|
73
|
+
|
|
74
|
+
# ─── Sección endpoints ─────────────────────────────────────────────────────
|
|
75
|
+
# Presente → genera solo los use cases declarados
|
|
76
|
+
# Ausente → comportamiento actual (5 CRUD por defecto)
|
|
77
|
+
# ──────────────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
endpoints:
|
|
80
|
+
basePath: /orders
|
|
81
|
+
versions:
|
|
82
|
+
- version: v1
|
|
83
|
+
operations:
|
|
84
|
+
- method: GET
|
|
85
|
+
path: /{id}
|
|
86
|
+
description: "Obtener detalle de un pedido"
|
|
87
|
+
useCase: FindOrderById # completo: FindOrderByIdQuery + Handler
|
|
88
|
+
|
|
89
|
+
- method: GET
|
|
90
|
+
path: /
|
|
91
|
+
description: "Listar pedidos con paginación"
|
|
92
|
+
useCase: FindAllOrders # completo: FindAllOrdersQuery + Handler
|
|
93
|
+
|
|
94
|
+
- method: POST
|
|
95
|
+
path: /
|
|
96
|
+
description: "Crear nuevo pedido"
|
|
97
|
+
useCase: CreateOrder # completo: CreateOrderCommand + Handler
|
|
98
|
+
|
|
99
|
+
- method: PUT
|
|
100
|
+
path: /{id}/confirm
|
|
101
|
+
description: "Confirmar pedido pendiente — PENDING → CONFIRMED"
|
|
102
|
+
useCase: ConfirmOrder # scaffold: TODO → order.confirm(); repo.save(order)
|
|
103
|
+
|
|
104
|
+
- method: PUT
|
|
105
|
+
path: /{id}/cancel
|
|
106
|
+
description: "Cancelar pedido — [PENDING, CONFIRMED] → CANCELLED"
|
|
107
|
+
useCase: CancelOrder # scaffold: TODO → order.cancel(); repo.save(order)
|
|
108
|
+
|
|
109
|
+
- method: DELETE
|
|
110
|
+
path: /{id}
|
|
111
|
+
description: "Eliminar pedido"
|
|
112
|
+
useCase: DeleteOrder # completo: DeleteOrderCommand + Handler
|
|
113
|
+
|
|
114
|
+
# Archivos generados con esta configuración:
|
|
115
|
+
#
|
|
116
|
+
# infrastructure/rest/controllers/order/v1/
|
|
117
|
+
# └── OrderV1Controller.java ← 6 métodos exactos
|
|
118
|
+
#
|
|
119
|
+
# application/commands/
|
|
120
|
+
# ├── CreateOrderCommand.java
|
|
121
|
+
# ├── ConfirmOrderCommand.java
|
|
122
|
+
# ├── CancelOrderCommand.java
|
|
123
|
+
# └── DeleteOrderCommand.java
|
|
124
|
+
#
|
|
125
|
+
# application/queries/
|
|
126
|
+
# ├── FindOrderByIdQuery.java
|
|
127
|
+
# └── FindAllOrdersQuery.java
|
|
128
|
+
#
|
|
129
|
+
# application/usecases/
|
|
130
|
+
# ├── CreateOrderCommandHandler.java ← implementación completa
|
|
131
|
+
# ├── ConfirmOrderCommandHandler.java ← scaffold + TODO
|
|
132
|
+
# ├── CancelOrderCommandHandler.java ← scaffold + TODO
|
|
133
|
+
# ├── DeleteOrderCommandHandler.java ← implementación completa
|
|
134
|
+
# ├── FindOrderByIdQueryHandler.java ← implementación completa
|
|
135
|
+
# └── FindAllOrdersQueryHandler.java ← implementación completa
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
system:
|
|
2
|
+
name: test-eva
|
|
3
|
+
groupId: com.example
|
|
4
|
+
javaVersion: 21
|
|
5
|
+
springBootVersion: 3.5.5
|
|
6
|
+
database: postgresql
|
|
7
|
+
|
|
8
|
+
messaging:
|
|
9
|
+
enabled: true
|
|
10
|
+
broker: kafka
|
|
11
|
+
kafka:
|
|
12
|
+
bootstrapServers: localhost:9092
|
|
13
|
+
defaultGroupId: test-eva
|
|
14
|
+
topicPrefix: cinema
|
|
15
|
+
|
|
16
|
+
modules:
|
|
17
|
+
|
|
18
|
+
# ─────────────────────────────────────────────
|
|
19
|
+
# CATÁLOGO
|
|
20
|
+
# ─────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
- name: movies
|
|
23
|
+
description: "Catálogo de películas: títulos, géneros, clasificaciones, sinopsis y duración"
|
|
24
|
+
exposes:
|
|
25
|
+
- method: GET
|
|
26
|
+
path: /movies
|
|
27
|
+
useCase: FindAllMovies
|
|
28
|
+
description: "Listar películas con filtros por género, clasificación y estado de estreno"
|
|
29
|
+
- method: GET
|
|
30
|
+
path: /movies/{id}
|
|
31
|
+
useCase: GetMovie
|
|
32
|
+
description: "Obtener detalle completo de una película"
|
|
33
|
+
- method: POST
|
|
34
|
+
path: /movies
|
|
35
|
+
useCase: CreateMovie
|
|
36
|
+
description: "Registrar nueva película en el catálogo"
|
|
37
|
+
- method: PUT
|
|
38
|
+
path: /movies/{id}
|
|
39
|
+
useCase: UpdateMovie
|
|
40
|
+
description: "Actualizar información de una película"
|
|
41
|
+
- method: DELETE
|
|
42
|
+
path: /movies/{id}
|
|
43
|
+
useCase: DeleteMovie
|
|
44
|
+
description: "Retirar una película del catálogo"
|
|
45
|
+
|
|
46
|
+
- name: theaters
|
|
47
|
+
description: "Salas de cine: configuración de capacidad, tipo de sala (2D, 3D, IMAX) y mapa de asientos"
|
|
48
|
+
exposes:
|
|
49
|
+
- method: GET
|
|
50
|
+
path: /theaters
|
|
51
|
+
useCase: FindAllTheaters
|
|
52
|
+
description: "Listar todas las salas disponibles"
|
|
53
|
+
- method: GET
|
|
54
|
+
path: /theaters/{id}
|
|
55
|
+
useCase: GetTheater
|
|
56
|
+
description: "Obtener configuración y capacidad de una sala"
|
|
57
|
+
- method: POST
|
|
58
|
+
path: /theaters
|
|
59
|
+
useCase: CreateTheater
|
|
60
|
+
description: "Registrar nueva sala de cine"
|
|
61
|
+
- method: PUT
|
|
62
|
+
path: /theaters/{id}
|
|
63
|
+
useCase: UpdateTheater
|
|
64
|
+
description: "Actualizar configuración de una sala"
|
|
65
|
+
|
|
66
|
+
# ─────────────────────────────────────────────
|
|
67
|
+
# PROGRAMACIÓN
|
|
68
|
+
# ─────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
- name: screenings
|
|
71
|
+
description: "Funciones programadas: asocia película + sala + horario, gestiona disponibilidad, precios y bloqueo exclusivo para eventos privados"
|
|
72
|
+
exposes:
|
|
73
|
+
- method: GET
|
|
74
|
+
path: /screenings
|
|
75
|
+
useCase: FindAllScreenings
|
|
76
|
+
description: "Listar funciones con filtros por película, sala y fecha"
|
|
77
|
+
- method: GET
|
|
78
|
+
path: /screenings/{id}
|
|
79
|
+
useCase: GetScreening
|
|
80
|
+
description: "Obtener detalle de una función (película, sala, horario, precio base)"
|
|
81
|
+
- method: GET
|
|
82
|
+
path: /screenings/{id}/seats
|
|
83
|
+
useCase: GetAvailableSeats
|
|
84
|
+
description: "Consultar mapa de asientos con disponibilidad en tiempo real"
|
|
85
|
+
- method: POST
|
|
86
|
+
path: /screenings
|
|
87
|
+
useCase: ScheduleScreening
|
|
88
|
+
description: "Programar una nueva función"
|
|
89
|
+
- method: PUT
|
|
90
|
+
path: /screenings/{id}/cancel
|
|
91
|
+
useCase: CancelScreening
|
|
92
|
+
description: "Cancelar una función programada y liberar todas las reservas"
|
|
93
|
+
- method: GET
|
|
94
|
+
path: /screenings/{id}/private-event-availability
|
|
95
|
+
useCase: CheckPrivateEventAvailability
|
|
96
|
+
description: "Verificar si la sala completa está libre de reservas individuales y puede ser bloqueada para un evento privado" # ★ NUEVO
|
|
97
|
+
- method: PUT
|
|
98
|
+
path: /screenings/{id}/lock
|
|
99
|
+
useCase: LockScreeningForPrivateEvent
|
|
100
|
+
description: "Bloquear toda la sala para un evento privado, impidiendo nuevas reservas individuales sobre esa función" # ★ NUEVO
|
|
101
|
+
|
|
102
|
+
# ─────────────────────────────────────────────
|
|
103
|
+
# CLIENTES
|
|
104
|
+
# ─────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
- name: customers
|
|
107
|
+
description: "Registro y perfil de clientes: datos personales, historial de compras e historial de puntos"
|
|
108
|
+
exposes:
|
|
109
|
+
- method: POST
|
|
110
|
+
path: /customers
|
|
111
|
+
useCase: CreateCustomer
|
|
112
|
+
description: "Registrar nuevo cliente"
|
|
113
|
+
- method: GET
|
|
114
|
+
path: /customers/{id}
|
|
115
|
+
useCase: GetCustomer
|
|
116
|
+
description: "Obtener perfil del cliente"
|
|
117
|
+
- method: GET
|
|
118
|
+
path: /customers
|
|
119
|
+
useCase: FindAllCustomers
|
|
120
|
+
description: "Listar clientes con filtros"
|
|
121
|
+
- method: PUT
|
|
122
|
+
path: /customers/{id}
|
|
123
|
+
useCase: UpdateCustomer
|
|
124
|
+
description: "Actualizar datos personales del cliente"
|
|
125
|
+
|
|
126
|
+
# ─────────────────────────────────────────────
|
|
127
|
+
# RESERVAS
|
|
128
|
+
# ─────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
- name: reservations
|
|
131
|
+
description: "Ciclo de vida de la reserva: selección y bloqueo de asientos, confirmación, cancelación y reserva exclusiva de sala completa para eventos privados"
|
|
132
|
+
exposes:
|
|
133
|
+
- method: POST
|
|
134
|
+
path: /reservations
|
|
135
|
+
useCase: CreateReservation
|
|
136
|
+
description: "Iniciar reserva: seleccionar función y asientos, bloqueo temporal (15 min)"
|
|
137
|
+
- method: GET
|
|
138
|
+
path: /reservations/{id}
|
|
139
|
+
useCase: GetReservation
|
|
140
|
+
description: "Consultar estado e información de una reserva"
|
|
141
|
+
- method: GET
|
|
142
|
+
path: /reservations
|
|
143
|
+
useCase: FindAllReservations
|
|
144
|
+
description: "Listar reservas con filtros por cliente, función y estado"
|
|
145
|
+
- method: PUT
|
|
146
|
+
path: /reservations/{id}/confirm
|
|
147
|
+
useCase: ConfirmReservation
|
|
148
|
+
description: "Confirmar la reserva después de recibir pago aprobado"
|
|
149
|
+
- method: PUT
|
|
150
|
+
path: /reservations/{id}/cancel
|
|
151
|
+
useCase: CancelReservation
|
|
152
|
+
description: "Cancelar la reserva y liberar los asientos seleccionados"
|
|
153
|
+
- method: PUT
|
|
154
|
+
path: /reservations/{id}/expire
|
|
155
|
+
useCase: ExpireReservation
|
|
156
|
+
description: "Expirar reserva no pagada dentro del tiempo límite de bloqueo"
|
|
157
|
+
- method: POST
|
|
158
|
+
path: /reservations/private-events
|
|
159
|
+
useCase: CreatePrivateEventReservation
|
|
160
|
+
description: "Reservar toda la sala para un evento privado: ocupa la capacidad total de la función de forma exclusiva" # ★ NUEVO
|
|
161
|
+
|
|
162
|
+
# ─────────────────────────────────────────────
|
|
163
|
+
# PAGOS
|
|
164
|
+
# ─────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
- name: payments
|
|
167
|
+
description: "Procesamiento de pagos: integración con pasarela externa, aprobaciones y reembolsos"
|
|
168
|
+
exposes:
|
|
169
|
+
- method: POST
|
|
170
|
+
path: /payments
|
|
171
|
+
useCase: InitiatePayment
|
|
172
|
+
description: "Iniciar proceso de pago vinculado a una reserva"
|
|
173
|
+
- method: GET
|
|
174
|
+
path: /payments/{id}
|
|
175
|
+
useCase: GetPayment
|
|
176
|
+
description: "Consultar estado de un pago"
|
|
177
|
+
- method: GET
|
|
178
|
+
path: /payments
|
|
179
|
+
useCase: FindAllPayments
|
|
180
|
+
description: "Listar pagos con filtros por estado y fecha"
|
|
181
|
+
- method: PUT
|
|
182
|
+
path: /payments/{id}/approve
|
|
183
|
+
useCase: ApprovePayment
|
|
184
|
+
description: "Registrar aprobación de pago recibida desde la pasarela"
|
|
185
|
+
- method: PUT
|
|
186
|
+
path: /payments/{id}/reject
|
|
187
|
+
useCase: RejectPayment
|
|
188
|
+
description: "Registrar rechazo de pago recibido desde la pasarela"
|
|
189
|
+
- method: PUT
|
|
190
|
+
path: /payments/{id}/refund
|
|
191
|
+
useCase: ProcessRefund
|
|
192
|
+
description: "Procesar reembolso por cancelación de reserva o función"
|
|
193
|
+
|
|
194
|
+
# ─────────────────────────────────────────────
|
|
195
|
+
# NOTIFICACIONES
|
|
196
|
+
# ─────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
- name: notifications
|
|
199
|
+
description: "Envío de notificaciones por email y SMS: confirmaciones, tickets QR, recordatorios y alertas de cancelación"
|
|
200
|
+
|
|
201
|
+
integrations:
|
|
202
|
+
async:
|
|
203
|
+
# Reserva creada → pago espera cobro; notificación informa el bloqueo de asientos
|
|
204
|
+
- event: ReservationCreatedEvent
|
|
205
|
+
producer: reservations
|
|
206
|
+
topic: RESERVATION_CREATED
|
|
207
|
+
consumers:
|
|
208
|
+
- module: payments
|
|
209
|
+
- module: notifications
|
|
210
|
+
|
|
211
|
+
# Pago aprobado → reserva debe confirmarse; cliente recibe confirmación
|
|
212
|
+
- event: PaymentApprovedEvent
|
|
213
|
+
producer: payments
|
|
214
|
+
topic: PAYMENT_APPROVED
|
|
215
|
+
consumers:
|
|
216
|
+
- module: reservations
|
|
217
|
+
- module: notifications
|
|
218
|
+
|
|
219
|
+
# Pago rechazado → reserva debe expirar y liberar asientos; cliente recibe aviso
|
|
220
|
+
- event: PaymentRejectedEvent
|
|
221
|
+
producer: payments
|
|
222
|
+
topic: PAYMENT_REJECTED
|
|
223
|
+
consumers:
|
|
224
|
+
- module: reservations
|
|
225
|
+
- module: notifications
|
|
226
|
+
|
|
227
|
+
# Reserva confirmada → se emiten los tickets QR y se envían al cliente
|
|
228
|
+
- event: ReservationConfirmedEvent
|
|
229
|
+
producer: reservations
|
|
230
|
+
topic: RESERVATION_CONFIRMED
|
|
231
|
+
consumers:
|
|
232
|
+
- module: notifications
|
|
233
|
+
|
|
234
|
+
# Reserva cancelada → pagos evalúa reembolso; cliente recibe notificación
|
|
235
|
+
- event: ReservationCancelledEvent
|
|
236
|
+
producer: reservations
|
|
237
|
+
topic: RESERVATION_CANCELLED
|
|
238
|
+
consumers:
|
|
239
|
+
- module: payments
|
|
240
|
+
- module: notifications
|
|
241
|
+
|
|
242
|
+
# Función cancelada → reservas libera asientos y expira reservas activas; clientes notificados
|
|
243
|
+
- event: ScreeningCancelledEvent
|
|
244
|
+
producer: screenings
|
|
245
|
+
topic: SCREENING_CANCELLED
|
|
246
|
+
consumers:
|
|
247
|
+
- module: reservations
|
|
248
|
+
- module: notifications
|
|
249
|
+
|
|
250
|
+
# ★ NUEVO — Reserva de evento privado creada → screenings bloquea la sala; payments inicia cobro total; notifications avisa
|
|
251
|
+
- event: PrivateEventReservationCreatedEvent
|
|
252
|
+
producer: reservations
|
|
253
|
+
topic: PRIVATE_EVENT_RESERVATION_CREATED
|
|
254
|
+
consumers:
|
|
255
|
+
- module: screenings
|
|
256
|
+
- module: payments
|
|
257
|
+
- module: notifications
|
|
258
|
+
|
|
259
|
+
# ★ NUEVO — Sala bloqueada exitosamente → notifications confirma al organizador del evento
|
|
260
|
+
- event: TheaterLockedForPrivateEventEvent
|
|
261
|
+
producer: screenings
|
|
262
|
+
topic: THEATER_LOCKED_FOR_PRIVATE_EVENT
|
|
263
|
+
consumers:
|
|
264
|
+
- module: notifications
|
|
265
|
+
|
|
266
|
+
sync:
|
|
267
|
+
# Al crear una reserva se consulta la función para validar disponibilidad y precio
|
|
268
|
+
# ★ MODIFICADO — se agrega consulta de disponibilidad para evento privado
|
|
269
|
+
- caller: reservations
|
|
270
|
+
calls: screenings
|
|
271
|
+
port: ScreeningService
|
|
272
|
+
using:
|
|
273
|
+
- GET /screenings/{id}
|
|
274
|
+
- GET /screenings/{id}/seats
|
|
275
|
+
- GET /screenings/{id}/private-event-availability # ★ NUEVO
|
|
276
|
+
|
|
277
|
+
# Al crear una reserva se verifica que el cliente exista y esté activo
|
|
278
|
+
- caller: reservations
|
|
279
|
+
calls: customers
|
|
280
|
+
port: CustomerService
|
|
281
|
+
using:
|
|
282
|
+
- GET /customers/{id}
|
|
283
|
+
|
|
284
|
+
# Al iniciar un pago se obtiene el detalle de la reserva para calcular el monto
|
|
285
|
+
- caller: payments
|
|
286
|
+
calls: reservations
|
|
287
|
+
port: ReservationService
|
|
288
|
+
using:
|
|
289
|
+
- GET /reservations/{id}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eva4j",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "A powerful Node.js CLI for generating Spring Boot projects with modular architecture that enables efficient monolith-first development with seamless transition to microservices",
|
|
5
5
|
"main": "bin/eva4j.js",
|
|
6
6
|
"bin": {
|
package/src/commands/create.js
CHANGED
|
@@ -55,7 +55,10 @@ async function createCommand(projectName, options) {
|
|
|
55
55
|
|
|
56
56
|
// Set all required dependencies
|
|
57
57
|
answers.dependencies = ['web', 'data-jpa', 'security', 'validation', 'actuator'];
|
|
58
|
-
|
|
58
|
+
|
|
59
|
+
// Preserve CLI name as project directory name (independent of artifactId)
|
|
60
|
+
answers.projectName = projectName || answers.artifactId;
|
|
61
|
+
|
|
59
62
|
// Build context
|
|
60
63
|
const context = buildBaseContext(answers);
|
|
61
64
|
|
|
@@ -69,7 +72,7 @@ async function createCommand(projectName, options) {
|
|
|
69
72
|
spinner.succeed(chalk.green('Project created successfully! ✨'));
|
|
70
73
|
|
|
71
74
|
console.log(chalk.blue('\n📦 Project structure:'));
|
|
72
|
-
console.log(chalk.gray(` ${context.
|
|
75
|
+
console.log(chalk.gray(` ${context.projectName}/`));
|
|
73
76
|
console.log(chalk.gray(` ├── src/main/java/${context.packagePath.replace(/\//g, '.')}`));
|
|
74
77
|
console.log(chalk.gray(` │ ├── ${context.applicationClassName}.java`));
|
|
75
78
|
console.log(chalk.gray(` │ └── common/`));
|
|
@@ -77,7 +80,7 @@ async function createCommand(projectName, options) {
|
|
|
77
80
|
console.log(chalk.gray(` └── README.md`));
|
|
78
81
|
|
|
79
82
|
console.log(chalk.blue('\n🚀 Next steps:'));
|
|
80
|
-
console.log(chalk.white(` cd ${context.
|
|
83
|
+
console.log(chalk.white(` cd ${context.projectName}`));
|
|
81
84
|
console.log(chalk.white(` eva4j add module user # Add your first module`));
|
|
82
85
|
console.log(chalk.white(` ./gradlew bootRun # Run the application`));
|
|
83
86
|
console.log();
|