Implementación Inicial de Subasta de Cupones en Monolito (Sin Bloqueo)

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.

  1. El objeto SeckillVoucher, que representa la entidad de la tabla tb_seckill_voucher, se obtiene mediante el método getById() proporcionado automáticamente por la implementación de la interfaz IService.
  2. 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.

  1. Se invoca el método update() de MyBatis-Plus, que opera sobre la tabla correspondiente.
  2. Se utiliza un objeto LambdaUpdateWrapper para definir las condiciones de la consulta y la operación de actualización.
  3. La condición eq() se aplica para identificar el registro específico mediante su identificador.
  4. 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.

  1. Se instancia un nuevo objeto VoucherOrder para representar el pedido.
  2. Se genera un identificador único para el pedido utilizando un servicio de generación de IDs (redisIdWorker).
  3. Se asigna el identificador del usuario actual al pedido.
  4. Se asocia el identificador del cupón comprado al pedido.
  5. Se persiste el objeto VoucherOrder en 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);
}
   

Etiquetas: java MyBatis-Plus Subastas Optimización de Base de Datos Gestión de Cupones

Publicado el 6-23 16:02