Estructuras de Datos Implicadas
Se utilizan las siguientes tablas para gestionar la información de los cupones y las subastas:
tb_voucher: Almacena los detalles generales de los cupones, como el monto del descuento y las reglas de uso.tb_seckill_voucher: Contiene la información específica de las subastas de cupones, como el stock disponible, la fecha y hora de inicio y fin de la promoción. Esta tabla solo se completa para cupones promocionales.voucher_id: Clave primaria, enlazada al cupón correspondiente.stock: Cantidad de unidades disponibles.create_time: Fecha y hora de creación.begin_time: Fecha y hora de inicio de validez.end_time: Fecha y hora de fin de validez.update_time: Fecha y hora de última actualización.
tb_voucher_order: Registra los pedidos de cupones.
Lógica del Proceso
1. Consulta y Validación de Información del Cupón
Se recupera la información del cupón y se verificen las condiciones temporales y de stock utilizando los campos de la tabla tb_seckill_voucher.
- El objeto
SeckillVoucher, que representa la entidad de la tablatb_seckill_voucher, se obtiene mediante el métodogetById()proporcionado automáticamente por la implementación de la interfazIService. - Se utilizan los métodos 'getter' generados automáticamente por MyBatis para comparar las fechas de inicio y fin de la subasta con la fecha y hora actuales.
// 1. Obtener detalles del cupón
SeckillVoucher promocion = servicioPromocion.obtenerPorId(idCuponCreate.toString());
// 2. Verificar si la subasta ha comenzado
if (promocion.getHoraInicio().isAfter(LocalDateTime.now())) {
return Resultado.fallo("La subasta aún no ha comenzado.");
}
// 3. Verificar si la subasta ha finalizado
if (promocion.getHoraFin().isBefore(LocalDateTime.now())) {
return Resultado.fallo("La subasta ha finalizado.");
}
// 4. Verificar la disponibilidad de stock
if (promocion.getStock() < 1) {
return Resultado.fallo("No hay suficiente stock disponible.");
}
2. Actualización de Stock Mediante Sentencia Condicional
Se emplea el método de acutalización de MyBatis-Plus para modificar los registros que cumplen ciertas condiciones. Se utiliza un objeto Wrapper para construir las condiciones y especificar la actualización.
- Se invoca el método
update()de MyBatis-Plus, que opera sobre la tabla correspondiente. - Se utiliza un objeto
LambdaUpdateWrapperpara definir las condiciones de la consulta y la operación de actualización. - La condición
eq()se aplica para identificar el registro específico mediante su identificador. - El método
setSql()permite ejecutar una sentencia SQL personalizada para decrementar el stock.
// Decrementar el stock del cupón promocional
boolean exitoActualizacionStock = servicioPromocion.actualizar(
new LambdaUpdateWrapper<SeckillVoucher>()
.eq(SeckillVoucher::getIdCuponCreate, idCuponCreate)
.setSql("stock = stock - 1")
);
3. Creación y Persistencia del Pedido del Cupón
Una vez confirmada la participación en la subasta, se crea un nuevo registro de pedido de cupón y se guarda en la base de datos.
- Se instancia un nuevo objeto
VoucherOrderpara representar el pedido. - Se genera un identificador único para el pedido utilizando un servicio de generación de IDs (
redisIdWorker). - Se asigna el identificador del usuario actual al pedido.
- Se asocia el identificador del cupón comprado al pedido.
- Se persiste el objeto
VoucherOrderen la base de datos.
// 6. Si la subasta es exitosa, crear el pedido correspondiente y guardarlo
VoucherOrder pedidoCuponCreate = new VoucherOrder();
long idPedido = generadorIdRedis.obtenerSiguienteId("pedido_cuponCreate_subasta");
pedidoCuponCreate.setId(idPedido);
pedidoCuponCreate.setUserId(usuarioActual.obtenerId());
pedidoCuponCreate.setVoucherId(idCuponCreate);
boolean exitoGuardadoPedido = this.guardar(pedidoCuponCreate);
Ejemplo de Código Completo
/**
* Implementa la funcionalidad de pedido para la subasta de cupones (sin considerar concurrencia).
*/
public Resultado seckillVoucher1(Long idCuponCreate) {
// 1. Obtener detalles del cupón
SeckillVoucher promocion = servicioPromocion.obtenerPorId(idCuponCreate.toString());
// 2. Verificar si la subasta ha comenzado
if (promocion.getHoraInicio().isAfter(LocalDateTime.now())) {
return Resultado.fallo("La subasta aún no ha comenzado.");
}
// 3. Verificar si la subasta ha finalizado
if (promocion.getHoraFin().isBefore(LocalDateTime.now())) {
return Resultado.fallo("La subasta ha finalizado.");
}
// 4. Verificar la disponibilidad de stock
if (promocion.getStock() < 1) {
return Resultado.fallo("No hay suficiente stock disponible.");
}
// 5. Si el cupón es válido, la subasta es exitosa; decrementar el stock
boolean exitoActualizacionStock = servicioPromocion.actualizar(
new LambdaUpdateWrapper<SeckillVoucher>()
.eq(SeckillVoucher::getIdCuponCreate, idCuponCreate)
.setSql("stock = stock - 1")
);
if (!exitoActualizacionStock) {
throw new RuntimeException("Fallo al reducir el stock del cupón promocional.");
}
// 6. Si la subasta es exitosa, crear el pedido correspondiente y guardarlo
VoucherOrder pedidoCuponCreate = new VoucherOrder();
long idPedido = generadorIdRedis.obtenerSiguienteId("pedido_cuponCreate_subasta");
pedidoCuponCreate.setId(idPedido);
pedidoCuponCreate.setUserId(usuarioActual.obtenerId());
pedidoCuponCreate.setVoucherId(idCuponCreate);
boolean exitoGuardadoPedido = this.guardar(pedidoCuponCreate);
if (!exitoGuardadoPedido) {
throw new RuntimeException("Fallo al crear el pedido del cupón promocional.");
}
return Resultado.exito(idPedido);
}