package com.spot.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spot.dto.mq.OrderMessage;
import com.spot.entity.Account;
import com.spot.entity.AccountRecord;
import com.spot.entity.ConfigSymbol;
import com.spot.entity.ExOrder;
import com.spot.enums.AccountRecordIsChanges;
import com.spot.enums.AccountRecordOperateType;
import com.spot.enums.AccountRecordStatus;
import com.spot.enums.AccountRecordType;
import com.spot.enums.MessageCode;
import com.spot.enums.OrderSide;
import com.spot.enums.OrderStatus;
import com.spot.enums.OrderType;
import com.spot.exception.BizException;
import com.spot.mapper.ExOrderMapper;
import com.spot.req.AdminOrderQueryReq;
import com.spot.req.CreateOrderReq;
import com.spot.req.OrderListReq;
import com.spot.service.AccountRecordService;
import com.spot.service.AccountService;
import com.spot.service.ConfigSymbolService;
import com.spot.service.OrderService;
import com.spot.utils.Constant;
import com.spot.utils.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 订单服务实现类
 *
 * @author jean
 * @date 2026-05-06
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<ExOrderMapper, ExOrder> implements OrderService {

    @Autowired
    private ConfigSymbolService configSymbolService;

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    @Autowired
    private AccountService accountService;

    @Autowired
    private AccountRecordService accountRecordService;

    @Value("${kafka.topic.match:SPOT_MATCH_}")
    private String matchTopic;

    /**
     * 创建订单
     * 事务内完成：数据校验 → 交易对校验 → 冻结余额 → 保存订单 → 生成流水
     *
     * @param req    创建订单请求
     * @param userId 用户ID
     * @return OrderMessage，用于事务外发送 MQ
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderMessage createOrder(CreateOrderReq req, Long userId) {
        // 1. 基础数据校验
        validateCreateOrderReq(req);

        // 2. 获取并校验交易对配置
        String symbol = req.getSymbol().toUpperCase();
        ConfigSymbol configSymbol = configSymbolService.getConfigSymbol(symbol);
        if (configSymbol == null) {
            throw new BizException(MessageCode.SYMBOL_NOT_FOUND, symbol);
        }
        if (configSymbol.getIsOpen() == null || configSymbol.getIsOpen() != 1) {
            throw new BizException(MessageCode.SYMBOL_NOT_OPEN, symbol);
        }

        // 3. 校验用户委托单数量和单日交易额上限
        validateUserOrderLimits(userId, symbol, configSymbol);

        // 4. 价格/数量最小值校验
        validatePriceAndVolume(req, configSymbol);

        // 5. 确定需要冻结的币种
        String coinSymbol = OrderSide.BUY.getCode().equals(req.getSide())
                ? configSymbol.getQuote()  // 买单冻结计价币
                : configSymbol.getBase();  // 卖单冻结基础币

        // 6. 计算冻结金额
        BigDecimal orderBalance = calculateOrderBalance(req, configSymbol);

        // 7. 冻结余额（原子操作）
        Account account = freezeBalance(userId, coinSymbol, orderBalance);

        // 8. 构建并保存订单
        ExOrder exOrder = buildExOrder(req, userId, configSymbol);
        saveOrder(exOrder);

        // 9. 生成流水（非机器人账号才生成）
        if (exOrder.getUserId() != null && exOrder.getUserId() != 1) {
            createFreezeRecord(userId, account, coinSymbol, orderBalance, exOrder.getId());
        }

        // 10. 转换为 MQ 消息（事务外发送）
        return convertToOrderMessage(exOrder);
    }

    /**
     * 撤单
     * 事务内完成：查询订单 → 校验权限和状态 → 更新状态为撤单中
     *
     * @param orderId 订单ID
     * @param userId  用户ID
     * @return OrderMessage，用于事务外发送 MQ
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderMessage cancelOrder(Long orderId, Long userId) {
        if (orderId == null) {
            throw new BizException(MessageCode.ORDER_ID_EMPTY);
        }
        if (userId == null) {
            throw new BizException(MessageCode.USER_ID_EMPTY);
        }

        // 1. 查询订单
        ExOrder exOrder = this.getById(orderId);
        if (exOrder == null) {
            throw new BizException(MessageCode.ORDER_NOT_FOUND, orderId);
        }

        // 2. 校验订单所有权
        if (exOrder.getUserId() == null || !exOrder.getUserId().equals(userId.intValue())) {
            throw new BizException(MessageCode.ORDER_NO_PERMISSION, orderId);
        }

        // 3. 校验订单状态是否可撤
        OrderStatus currentStatus = OrderStatus.fromCode(exOrder.getStatus());
        if (currentStatus == null || !currentStatus.isCancelable()) {
            throw new BizException(MessageCode.ORDER_CANCEL_STATUS_INVALID,
                    currentStatus != null ? currentStatus.getDescription() : exOrder.getStatus());
        }

        // 4. 更新订单状态为撤单中
        exOrder.setStatus(OrderStatus.PENDING_CANCEL.getCode());
        exOrder.setMtime(LocalDateTime.now());
        this.updateById(exOrder);

        log.info("撤单成功 - OrderId: {}, UserId: {}, Symbol: {}", orderId, userId, exOrder.getSymbol());

        return convertToOrderMessage(exOrder);
    }

    /**
     * 一键撤销用户所有当前委托（INIT/NEW/PART_FILLED）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<OrderMessage> cancelAllCurrentOrders(Long userId) {
        if (userId == null) {
            throw new BizException(MessageCode.USER_ID_EMPTY);
        }

        List<ExOrder> activeOrders = this.list(new LambdaQueryWrapper<ExOrder>()
                .eq(ExOrder::getUserId, userId.intValue())
                .in(ExOrder::getStatus,
                        OrderStatus.INIT.getCode(),
                        OrderStatus.NEW.getCode(),
                        OrderStatus.PART_FILLED.getCode()));

        if (activeOrders == null || activeOrders.isEmpty()) {
            return new ArrayList<>();
        }

        LocalDateTime now = LocalDateTime.now();
        List<OrderMessage> orderMessages = new ArrayList<>();
        for (ExOrder order : activeOrders) {
            boolean updated = this.lambdaUpdate()
                    .eq(ExOrder::getId, order.getId())
                    .eq(ExOrder::getUserId, userId.intValue())
                    .in(ExOrder::getStatus,
                            OrderStatus.INIT.getCode(),
                            OrderStatus.NEW.getCode(),
                            OrderStatus.PART_FILLED.getCode())
                    .set(ExOrder::getStatus, OrderStatus.PENDING_CANCEL.getCode())
                    .set(ExOrder::getMtime, now)
                    .update();
            if (!updated) {
                continue;
            }
            order.setStatus(OrderStatus.PENDING_CANCEL.getCode());
            order.setMtime(now);
            orderMessages.add(convertToOrderMessage(order));
        }

        log.info("一键撤单完成 - UserId: {}, 已提交撤单数: {}", userId, orderMessages.size());
        return orderMessages;
    }

    /**
     * 发送 MQ 消息（事务外调用）
     *
     * @param orderMessage 订单消息
     */
    @Override
    public void sendMqMessage(OrderMessage orderMessage) {
        String topic = matchTopic + orderMessage.getSymbol();
        String messageBody = JSON.toJSONString(orderMessage);
        // 使用 messageId（UUID）作为 Kafka key，避免同一订单的 INIT 和 CANCEL 消息共享相同 key 被幂等过滤
        String kafkaKey = orderMessage.getMessageId() != null ? orderMessage.getMessageId() : String.valueOf(orderMessage.getId());
        kafkaTemplate.send(topic, kafkaKey, messageBody)
                .addCallback(
                        result -> log.info("订单消息发送成功 - OrderId: {}, Symbol: {}, Topic: {}",
                                orderMessage.getId(), orderMessage.getSymbol(), topic),
                        ex -> {
                            log.error("发送订单消息失败 - OrderId: {}, Symbol: {}",
                                    orderMessage.getId(), orderMessage.getSymbol(), ex);
                            throw new RuntimeException("发送订单消息失败: " + ex.getMessage(), ex);
                        }
                );
    }

    /**
     * 查询当前委托（状态：初始化、新建、部分成交）
     *
     * @param req 查询请求
     * @param uid 用户ID
     * @return 分页结果
     */
    @Override
    public Page<ExOrder> currentOrders(OrderListReq req, Long uid) {
        int page = req.getPage() != null ? req.getPage() : 1;
        int size = req.getSize() != null ? req.getSize() : 10;
        Page<ExOrder> pg = new Page<>(page, size);
        LambdaQueryWrapper<ExOrder> qw = buildOrderListQuery(req, uid);
        qw.in(ExOrder::getStatus,
                OrderStatus.INIT.getCode(),
                OrderStatus.NEW.getCode(),
                OrderStatus.PART_FILLED.getCode());
        qw.orderByDesc(ExOrder::getCtime);
        return this.page(pg, qw);
    }

    /**
     * 查询历史委托（状态：已成交、已撤单、撤单中）
     *
     * @param req 查询请求
     * @param uid 用户ID
     * @return 分页结果
     */
    @Override
    public Page<ExOrder> historyOrders(OrderListReq req, Long uid) {
        int page = req.getPage() != null ? req.getPage() : 1;
        int size = req.getSize() != null ? req.getSize() : 10;
        Page<ExOrder> pg = new Page<>(page, size);
        LambdaQueryWrapper<ExOrder> qw = buildOrderListQuery(req, uid);
        qw.in(ExOrder::getStatus,
                OrderStatus.FILLED.getCode(),
                OrderStatus.CANCELED.getCode(),
                OrderStatus.PENDING_CANCEL.getCode(),
                OrderStatus.EXPIRED.getCode());
        qw.orderByDesc(ExOrder::getCtime);
        return this.page(pg, qw);
    }

    /**
     * 查询成交记录（成交量大于0的订单）
     *
     * @param req 查询请求
     * @param uid 用户ID
     * @return 分页结果
     */
    @Override
    public Page<ExOrder> tradeRecords(OrderListReq req, Long uid) {
        int page = req.getPage() != null ? req.getPage() : 1;
        int size = req.getSize() != null ? req.getSize() : 10;
        Page<ExOrder> pg = new Page<>(page, size);
        LambdaQueryWrapper<ExOrder> qw = buildOrderListQuery(req, uid);
        qw.gt(ExOrder::getDealVolume, BigDecimal.ZERO);
        qw.orderByDesc(ExOrder::getCtime);
        return this.page(pg, qw);
    }

    // ==================== 私有方法 ====================

    /**
     * 校验用户委托单数量上限和单日交易额上限
     *
     * @param userId       用户ID
     * @param symbol       交易对（大写）
     * @param configSymbol 交易对配置
     */
    private void validateUserOrderLimits(Long userId, String symbol, ConfigSymbol configSymbol) {
        // 1. 校验单用户委托单数量上限
        if (configSymbol.getMaxOrdersPerUser() != null && configSymbol.getMaxOrdersPerUser() > 0) {
            long activeOrderCount = this.count(new LambdaQueryWrapper<ExOrder>()
                    .eq(ExOrder::getUserId, userId.intValue())
                    .eq(ExOrder::getSymbol, symbol)
                    .in(ExOrder::getStatus,
                            OrderStatus.INIT.getCode(),
                            OrderStatus.NEW.getCode(),
                            OrderStatus.PART_FILLED.getCode()));
            if (activeOrderCount >= configSymbol.getMaxOrdersPerUser()) {
                throw new BizException(MessageCode.MAX_ORDERS_PER_USER_EXCEEDED, configSymbol.getMaxOrdersPerUser());
            }
        }

        // 2. 校验单日交易额上限
        if (configSymbol.getDailyVolumeLimit() != null
                && configSymbol.getDailyVolumeLimit().compareTo(BigDecimal.ZERO) > 0) {
            LocalDateTime todayStart = TimeUtils.now().toLocalDate().atStartOfDay();
            LocalDateTime todayEnd = todayStart.plusDays(1);
            List<ExOrder> todayOrders = this.list(new LambdaQueryWrapper<ExOrder>()
                    .eq(ExOrder::getSymbol, symbol)
                    .ge(ExOrder::getCtime, todayStart)
                    .lt(ExOrder::getCtime, todayEnd));
            BigDecimal todayDealMoney = todayOrders.stream()
                    .map(o -> o.getDealMoney() != null ? o.getDealMoney() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            if (todayDealMoney.compareTo(configSymbol.getDailyVolumeLimit()) >= 0) {
                throw new BizException(MessageCode.DAILY_VOLUME_LIMIT_EXCEEDED,
                        configSymbol.getDailyVolumeLimit().stripTrailingZeros().toPlainString());
            }
        }
    }

    /**
     * 基础参数校验
     */
    private void validateCreateOrderReq(CreateOrderReq req) {
        if (StringUtils.isBlank(req.getSymbol())) {
            throw new BizException(MessageCode.SYMBOL_EMPTY);
        }
        if (StringUtils.isBlank(req.getSide())) {
            throw new BizException(MessageCode.ORDER_SIDE_EMPTY);
        }
        if (OrderSide.fromCode(req.getSide()) == null) {
            throw new BizException(MessageCode.ORDER_SIDE_UNKNOWN, req.getSide());
        }
        if (req.getType() == null) {
            throw new BizException(MessageCode.ORDER_TYPE_EMPTY);
        }
        if (OrderType.fromCode(req.getType()) == null) {
            throw new BizException(MessageCode.ORDER_TYPE_UNKNOWN, req.getType());
        }
        if (req.getVolume() == null || req.getVolume().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BizException(MessageCode.ORDER_VOLUME_INVALID);
        }
        if (OrderType.LIMIT.getCode().equals(req.getType())) {
            if (req.getPrice() == null || req.getPrice().compareTo(BigDecimal.ZERO) <= 0) {
                throw new BizException(MessageCode.LIMIT_ORDER_PRICE_INVALID);
            }
        }
    }

    /**
     * 价格和数量最小值校验
     */
    private void validatePriceAndVolume(CreateOrderReq req, ConfigSymbol configSymbol) {
        BigDecimal price = req.getPrice() != null ? req.getPrice() : BigDecimal.ZERO;
        BigDecimal volume = req.getVolume();

        if (OrderType.LIMIT.getCode().equals(req.getType())) {
            if (configSymbol.getLimitPriceMin() != null && price.compareTo(configSymbol.getLimitPriceMin()) < 0) {
                throw new BizException(MessageCode.LIMIT_PRICE_TOO_LOW,
                        configSymbol.getLimitPriceMin().stripTrailingZeros().toPlainString());
            }
            if (configSymbol.getLimitVolumeMin() != null && volume.compareTo(configSymbol.getLimitVolumeMin()) < 0) {
                throw new BizException(MessageCode.LIMIT_VOLUME_TOO_LOW,
                        configSymbol.getLimitVolumeMin().stripTrailingZeros().toPlainString());
            }
        } else if (OrderType.MARKET.getCode().equals(req.getType())) {
            if (OrderSide.BUY.getCode().equals(req.getSide())) {
                if (configSymbol.getMarketBuyMin() != null && volume.compareTo(configSymbol.getMarketBuyMin()) < 0) {
                    throw new BizException(MessageCode.MARKET_BUY_VOLUME_TOO_LOW,
                            configSymbol.getMarketBuyMin().stripTrailingZeros().toPlainString());
                }
            } else if (OrderSide.SELL.getCode().equals(req.getSide())) {
                if (configSymbol.getMarketSellMin() != null && volume.compareTo(configSymbol.getMarketSellMin()) < 0) {
                    throw new BizException(MessageCode.MARKET_SELL_VOLUME_TOO_LOW,
                            configSymbol.getMarketSellMin().stripTrailingZeros().toPlainString());
                }
            }
        }
    }

    /**
     * 计算需要冻结的金额
     */
    private BigDecimal calculateOrderBalance(CreateOrderReq req, ConfigSymbol configSymbol) {
        if (OrderSide.BUY.getCode().equals(req.getSide())) {
            if (OrderType.LIMIT.getCode().equals(req.getType())) {
                // 限价买单：价格 × 数量，向上取整
                return req.getPrice().multiply(req.getVolume())
                        .setScale(configSymbol.getPricePre(), RoundingMode.UP);
            } else {
                // 市价买单：直接使用数量（计价币种）
                return req.getVolume();
            }
        } else {
            // 卖单：冻结数量（基础币种）
            return req.getVolume();
        }
    }

    /**
     * 冻结余额（原子操作）
     */
    private Account freezeBalance(Long userId, String coinSymbol, BigDecimal orderBalance) {
        Account account = accountService.getOrCreateAccount(userId, coinSymbol);
        int result = accountService.freezeFromAvailableToFrozen(account.getId(), orderBalance);
        if (result <= 0) {
            throw new BizException(MessageCode.ACCOUNT_BALANCE_INSUFFICIENT);
        }
        return account;
    }

    /**
     * 构建 ExOrder 实体
     */
    private ExOrder buildExOrder(CreateOrderReq req, Long userId, ConfigSymbol configSymbol) {
        ExOrder exOrder = new ExOrder();
        exOrder.setUserId(userId != null ? userId.intValue() : null);
        exOrder.setSymbol(req.getSymbol().toUpperCase());
        exOrder.setSide(req.getSide());
        exOrder.setType(req.getType());
        exOrder.setStatus(OrderStatus.INIT.getCode());
        exOrder.setPrice(req.getPrice());
        exOrder.setVolume(req.getVolume());
        exOrder.setRemainingVolume(req.getVolume());
        exOrder.setDealVolume(BigDecimal.ZERO);
        exOrder.setDealMoney(BigDecimal.ZERO);
        exOrder.setAvgPrice(BigDecimal.ZERO);
        exOrder.setFee(BigDecimal.ZERO);
        exOrder.setRefundVolume(BigDecimal.ZERO);
        exOrder.setSource(req.getSource() != null ? req.getSource() : 1);

        if (configSymbol.getTakerFee() != null) {
            exOrder.setFeeRateTaker(configSymbol.getTakerFee().doubleValue());
        }
        if (configSymbol.getMakerFee() != null) {
            exOrder.setFeeRateMaker(configSymbol.getMakerFee().doubleValue());
        }

        // 买单手续费币种为基础币，卖单手续费币种为计价币
        if (OrderSide.BUY.getCode().equals(req.getSide())) {
            exOrder.setFeeCoin(configSymbol.getBase() != null ? configSymbol.getBase() : "");
        } else {
            exOrder.setFeeCoin(configSymbol.getQuote() != null ? configSymbol.getQuote() : "");
        }

        exOrder.setCtime(LocalDateTime.now());
        exOrder.setMtime(LocalDateTime.now());
        return exOrder;
    }

    /**
     * 保存订单（确保必要字段不为null）
     */
    private void saveOrder(ExOrder exOrder) {
        if (exOrder.getCtime() == null) {
            exOrder.setCtime(LocalDateTime.now());
        }
        if (exOrder.getMtime() == null) {
            exOrder.setMtime(LocalDateTime.now());
        }
        this.save(exOrder);
    }

    /**
     * 生成下单冻结流水（冻结操作，总金额不变）
     */
    private void createFreezeRecord(Long userId, Account account, String coinSymbol,
                                    BigDecimal orderBalance, Long orderId) {
        AccountRecord record = new AccountRecord();
        record.setUid(userId);
        record.setAccountId(account.getId());
        record.setSymbol(coinSymbol);
        record.setAmount(orderBalance);
        record.setSource(Constant.ACCOUNT_RECORD_SOURCE_EX_ORDER);
        record.setLinkId(orderId);
        record.setType(AccountRecordType.ORDER.getCode());
        record.setOperateType(AccountRecordOperateType.FREEZE.getCode());
        record.setStatus(AccountRecordStatus.COMPLETED.getCode());
        record.setIsChanges(AccountRecordIsChanges.NO.getCode());
        record.setRemark("下单冻结金额");
        record.setCtime(TimeUtils.now());
        record.setMtime(TimeUtils.now());
        accountRecordService.save(record);
    }

    /**
     * 将 ExOrder 转换为 OrderMessage
     */
    private OrderMessage convertToOrderMessage(ExOrder exOrder) {
        OrderMessage msg = new OrderMessage();
        msg.setId(exOrder.getId());
        msg.setUserId(exOrder.getUserId());
        msg.setSymbol(exOrder.getSymbol());
        msg.setSide(exOrder.getSide());
        msg.setType(exOrder.getType());
        msg.setStatus(exOrder.getStatus());
        msg.setPrice(exOrder.getPrice());
        msg.setVolume(exOrder.getVolume());
        msg.setFeeRateMaker(exOrder.getFeeRateMaker());
        msg.setFeeRateTaker(exOrder.getFeeRateTaker());
        msg.setFee(exOrder.getFee());
        msg.setDealVolume(exOrder.getDealVolume());
        msg.setDealMoney(exOrder.getDealMoney());
        msg.setAvgPrice(exOrder.getAvgPrice());
        msg.setSource(exOrder.getSource());
        msg.setRefundVolume(exOrder.getRefundVolume() != null ? exOrder.getRefundVolume() : BigDecimal.ZERO);
        msg.setCtime(exOrder.getCtime());
        msg.setMtime(exOrder.getMtime());
        msg.setTimestamp(System.currentTimeMillis());
        msg.setMessageId(UUID.randomUUID().toString());
        return msg;
    }

    /**
     * 处理超时初始化订单撤单
     * 更新状态为 PENDING_CANCEL，构建撤单消息；事务外再调用 sendCancelMqMessage 发送 Kafka
     *
     * @param order 超时订单
     * @return true-处理成功，false-状态已变更跳过
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelTimeoutInitOrder(ExOrder order) {
        try {
            // 1. 校验订单状态仍为 INIT 或 PENDING_CANCEL
            Integer currentStatus = order.getStatus();
            if (!OrderStatus.INIT.getCode().equals(currentStatus) &&
                    !OrderStatus.PENDING_CANCEL.getCode().equals(currentStatus)) {
                log.debug("订单状态已变更，跳过 - OrderId: {}, Status: {}", order.getId(), currentStatus);
                return false;
            }

            String symbol = order.getSymbol();
            if (symbol == null || symbol.isEmpty()) {
                log.warn("订单缺少交易对信息，跳过 - OrderId: {}", order.getId());
                return false;
            }

            // 2. 更新状态为撤单中
            order.setStatus(OrderStatus.PENDING_CANCEL.getCode());
            order.setMtime(TimeUtils.now());
            int updateResult = this.baseMapper.updateById(order);
            if (updateResult <= 0) {
                log.warn("更新订单状态失败 - OrderId: {}", order.getId());
                return false;
            }

            log.info("超时订单状态已更新为 PENDING_CANCEL - OrderId: {}, Symbol: {}", order.getId(), symbol);
            return true;

        } catch (Exception e) {
            log.error("处理超时订单撤单失败 - OrderId: {}", order.getId(), e);
            throw e;
        }
    }

    /**
     * 管理后台撤销指定交易对所有活跃订单
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int cancelAllOrdersBySymbol(String symbol) {
        if (!StringUtils.isNotBlank(symbol)) {
            throw new BizException(MessageCode.SYMBOL_EMPTY);
        }
        List<ExOrder> activeOrders = this.list(new LambdaQueryWrapper<ExOrder>()
                .eq(ExOrder::getSymbol, symbol)
                .in(ExOrder::getStatus,
                        OrderStatus.INIT.getCode(),
                        OrderStatus.NEW.getCode(),
                        OrderStatus.PART_FILLED.getCode()));

        if (activeOrders == null || activeOrders.isEmpty()) {
            return 0;
        }

        LocalDateTime now = LocalDateTime.now();
        int count = 0;
        List<OrderMessage> orderMessages = new ArrayList<>();
        for (ExOrder order : activeOrders) {
            boolean updated = this.lambdaUpdate()
                    .eq(ExOrder::getId, order.getId())
                    .eq(ExOrder::getSymbol, symbol)
                    .in(ExOrder::getStatus,
                            OrderStatus.INIT.getCode(),
                            OrderStatus.NEW.getCode(),
                            OrderStatus.PART_FILLED.getCode())
                    .set(ExOrder::getStatus, OrderStatus.PENDING_CANCEL.getCode())
                    .set(ExOrder::getMtime, now)
                    .update();
            if (updated) {
                count++;
                orderMessages.add(convertToOrderMessage(this.getById(order.getId())));
            }
        }

        for (OrderMessage orderMessage : orderMessages) {
            try {
                sendCancelMqMessage(orderMessage);
            } catch (Exception e) {
                log.error("发送撤单MQ失败 - OrderId: {}", orderMessage.getId(), e);
            }
        }
        return count;
    }

    /**
     * 发送撤单 Kafka 消息（事务外调用）
     *
     * @param orderMessage 撤单消息
     */
    @Override
    public void sendCancelMqMessage(OrderMessage orderMessage) {
        String topic = matchTopic + orderMessage.getSymbol();
        String messageBody = JSON.toJSONString(orderMessage);
        kafkaTemplate.send(topic, String.valueOf(orderMessage.getId()), messageBody)
                .addCallback(
                        result -> log.info("撤单消息发送成功 - OrderId: {}, Symbol: {}, Topic: {}",
                                orderMessage.getId(), orderMessage.getSymbol(), topic),
                        ex -> log.error("发送撤单消息失败 - OrderId: {}, Symbol: {}",
                                orderMessage.getId(), orderMessage.getSymbol(), ex)
                );
    }

    /**
     * 构建订单列表查询条件
     */
    private LambdaQueryWrapper<ExOrder> buildOrderListQuery(OrderListReq req, Long uid) {
        LambdaQueryWrapper<ExOrder> qw = new LambdaQueryWrapper<>();
        qw.eq(ExOrder::getUserId, uid.intValue());
        if (req.getSymbol() != null && !req.getSymbol().isEmpty()) {
            qw.eq(ExOrder::getSymbol, req.getSymbol());
        }
        if (req.getSide() != null && !req.getSide().isEmpty()) {
            qw.eq(ExOrder::getSide, req.getSide());
        }
        if (req.getCtimeBegin() != null) {
            LocalDateTime s = Instant.ofEpochMilli(req.getCtimeBegin())
                    .atZone(ZoneId.systemDefault()).toLocalDateTime();
            qw.ge(ExOrder::getCtime, s);
        }
        if (req.getCtimeEnd() != null) {
            LocalDateTime e = Instant.ofEpochMilli(req.getCtimeEnd())
                    .atZone(ZoneId.systemDefault()).toLocalDateTime();
            qw.le(ExOrder::getCtime, e);
        }
        if (req.getType() != null) {
            qw.eq(ExOrder::getType, req.getType());
        }
        if (req.getStatus() != null) {
            qw.eq(ExOrder::getStatus, req.getStatus());
        }
        return qw;
    }

    @Override
    public Page<ExOrder> adminPageQuery(AdminOrderQueryReq req) {
        Page<ExOrder> pg = new Page<>(req.getPage() != null ? req.getPage() : 1,
                req.getSize() != null ? req.getSize() : 15);
        LambdaQueryWrapper<ExOrder> qw = new LambdaQueryWrapper<>();
        if (req.getId() != null) {
            qw.eq(ExOrder::getId, req.getId());
        }
        if (req.getUserId() != null) {
            qw.eq(ExOrder::getUserId, req.getUserId());
        }
        if (req.getSymbol() != null && !req.getSymbol().isEmpty()) {
            qw.eq(ExOrder::getSymbol, req.getSymbol());
        }
        if (req.getType() != null) {
            qw.eq(ExOrder::getType, req.getType());
        }
        if (req.getSide() != null && !req.getSide().isEmpty()) {
            qw.eq(ExOrder::getSide, req.getSide());
        }
        if (req.getStatus() != null) {
            qw.eq(ExOrder::getStatus, req.getStatus());
        }
        if (req.getCtimeBegin() != null) {
            qw.ge(ExOrder::getCtime, LocalDateTime.ofInstant(
                    Instant.ofEpochMilli(req.getCtimeBegin()), ZoneId.systemDefault()));
        }
        if (req.getCtimeEnd() != null) {
            qw.le(ExOrder::getCtime, LocalDateTime.ofInstant(
                    Instant.ofEpochMilli(req.getCtimeEnd()), ZoneId.systemDefault()));
        }
        qw.orderByDesc(ExOrder::getCtime);
        return this.page(pg, qw);
    }
}
