/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

package com.yami.shop.admin.task;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.yami.shop.bean.enums.OrderStatus;
import com.yami.shop.bean.model.Order;
import com.yami.shop.bean.model.OrderItem;
import com.yami.shop.service.OrderService;
import com.yami.shop.service.ProductService;
import com.yami.shop.service.SkuService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;

/**
 * 订单相关定时任务类，包含了取消超时未支付订单以及系统自动确认收货订单的定时任务逻辑。
 * 定时任务的配置，请查看xxl-job的java配置文件。
 * @see com.yami.shop.admin.config.XxlJobConfig
 *
 * @author FrozenWatermelon
 */
@Component("orderTask")
public class OrderTask {

    // 创建一个日志记录器，用于记录定时任务执行过程中的相关信息，方便后续查看任务执行情况以及排查问题。
    private static final Logger logger = LoggerFactory.getLogger(OrderTask.class);

    // 注入OrderService，用于处理与订单相关的业务逻辑，例如查询订单、取消订单、确认订单等操作。
    @Autowired
    private OrderService orderService;

    // 注入ProductService，用于处理与商品相关的业务逻辑，在订单操作涉及影响商品缓存的场景下（如订单取消、确认收货等），会调用其方法来清除相关商品缓存，保证数据一致性。
    @Autowired
    private ProductService productService;

    // 注入SkuService，用于处理与商品规格（Sku）相关的业务逻辑，同样在订单操作涉及影响商品规格缓存的场景下，会调用其方法来清除相关规格缓存，保证数据一致性。
    @Autowired
    private SkuService skuService;

    /**
     * 取消超时未支付订单的定时任务方法，通过@XxlJob注解标记该方法为一个XXL-JOB定时任务，任务名为"cancelOrder"。
     * 任务逻辑如下：
     * 1. 首先获取当前时间，用于后续判断订单是否超时未支付。
     * 2. 从数据库中查询出状态为未支付（OrderStatus.UNPAY.value()表示未支付状态对应的代码值）且距离当前时间已经超过30分钟的订单列表。
     * 3. 如果查询到的订单列表为空，则直接返回，不进行后续操作。
     * 4. 若订单列表不为空，则调用OrderService的cancelOrders方法来取消这些超时未支付的订单。
     * 5. 接着遍历这些被取消的订单，对于每个订单中的商品项（OrderItem），分别调用ProductService和SkuService的方法来清除对应的商品缓存和商品规格缓存，
     *    以确保相关数据的一致性，避免缓存数据与数据库实际数据不一致的情况。
     */
    @XxlJob("cancelOrder")
    public void cancelOrder() {
        Date now = new Date();
        logger.info("取消超时未支付订单。。。");
        // 获取30分钟之前未支付的订单，通过OrderService的listOrderAndOrderItems方法，传入未支付状态值和30分钟之前的时间作为筛选条件进行查询。
        List<Order> orders = orderService.listOrderAndOrderItems(OrderStatus.UNPAY.value(), DateUtil.offsetMinute(now, -30));
        if (CollectionUtil.isEmpty(orders)) {
            return;
        }
        orderService.cancelOrders(orders);
        for (Order order : orders) {
            List<OrderItem> orderItems = order.getOrderItems();
            for (OrderItem orderItem : orderItems) {
                productService.removeProductCacheByProdId(orderItem.getProdId());
                skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
            }
        }
    }

    /**
     * 系统自动确认收货订单的定时任务方法，通过@XxlJob注解标记该方法为一个XXL-JOB定时任务，任务名为"confirmOrder"。
     * 任务逻辑如下：
     * 1. 首先获取当前时间，用于后续判断订单是否满足自动确认收货的时间条件。
     * 2. 从数据库中查询出状态为已发货（OrderStatus.CONSIGNMENT.value()表示已发货状态对应的代码值）且距离当前时间已经超过15天的订单列表。
     * 3. 如果查询到的订单列表为空，则直接返回，不进行后续操作。
     * 4. 若订单列表不为空，则调用OrderService的confirmOrder方法来自动确认这些符合条件的订单已收货。
     * 5. 接着遍历这些被确认收货的订单，对于每个订单中的商品项（OrderItem），分别调用ProductService和SkuService的方法来清除对应的商品缓存和商品规格缓存，
     *    以确保相关数据的一致性，避免缓存数据与数据库实际数据不一致的情况。
     */
    @XxlJob("confirmOrder")
    public void confirmOrder() {
        Date now = new Date();
        logger.info("系统自动确认收货订单。。。");
        // 获取15天之前已发货的订单，通过OrderService的listOrderAndOrderItems方法，传入已发货状态值和15天之前的时间作为筛选条件进行查询。
        List<Order> orders = orderService.listOrderAndOrderItems(OrderStatus.CONSIGNMENT.value(), DateUtil.offsetDay(now, -15));
        if (CollectionUtil.isEmpty(orders)) {
            return;
        }
        orderService.confirmOrder(orders);
        for (Order order : orders) {
            List<OrderItem> orderItems = order.getOrderItems();
            for (OrderItem orderItem : orderItems) {
                productService.removeProductCacheByProdId(orderItem.getProdId());
                skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
            }
        }
    }

}