package com.ycwl.basic.service.printer.impl;

import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import com.ycwl.basic.pay.adapter.IPayAdapter;
import com.ycwl.basic.pay.adapter.WxMpPayAdapter;
import com.ycwl.basic.service.pc.ScenicService;
import com.ycwl.basic.biz.OrderBiz;
import com.ycwl.basic.constant.NumberConstant;
import com.ycwl.basic.enums.OrderStateEnum;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.image.enhancer.entity.BceEnhancerConfig;
import com.ycwl.basic.image.pipeline.core.PhotoProcessContext;
import com.ycwl.basic.image.pipeline.enums.ImageSource;
import com.ycwl.basic.image.pipeline.enums.ImageType;
import com.ycwl.basic.image.pipeline.enums.PipelineScene;
import com.ycwl.basic.image.pipeline.stages.CleanupStage;
import com.ycwl.basic.image.pipeline.stages.ConditionalRotateStage;
import com.ycwl.basic.image.pipeline.stages.DownloadStage;
import com.ycwl.basic.image.pipeline.stages.ImageEnhanceStage;
import com.ycwl.basic.image.pipeline.stages.ImageOrientationStage;
import com.ycwl.basic.image.pipeline.stages.ImageResizeStage;
import com.ycwl.basic.image.pipeline.stages.ImageSRStage;
import com.ycwl.basic.image.pipeline.stages.RestoreOrientationStage;
import com.ycwl.basic.image.pipeline.stages.UpdateMemberPrintStage;
import com.ycwl.basic.image.pipeline.stages.UploadStage;
import com.ycwl.basic.image.pipeline.stages.WatermarkConfig;
import com.ycwl.basic.image.pipeline.stages.WatermarkStage;
import com.ycwl.basic.image.watermark.edge.WatermarkEdgeService;
import com.ycwl.basic.image.watermark.enums.ImageWatermarkOperatorEnum;
import com.ycwl.basic.integration.common.manager.DeviceConfigManager;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.mapper.FaceMapper;
import com.ycwl.basic.mapper.FaceSampleMapper;
import com.ycwl.basic.mapper.MemberMapper;
import com.ycwl.basic.mapper.OrderMapper;
import com.ycwl.basic.mapper.PrintTaskMapper;
import com.ycwl.basic.mapper.PrinterMapper;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.model.Crop;
import com.ycwl.basic.model.PrinterOrderItem;
import com.ycwl.basic.model.mobile.face.FaceRecognizeResp;
import com.ycwl.basic.model.mobile.order.PriceObj;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.pc.member.resp.MemberRespVO;
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
import com.ycwl.basic.model.pc.order.entity.OrderEntity;
import com.ycwl.basic.model.pc.order.entity.OrderItemEntity;
import com.ycwl.basic.model.pc.printer.req.ReprintRequest;
import com.ycwl.basic.model.pc.source.entity.SourceEntity;
import com.ycwl.basic.pricing.dto.PriceCalculationRequest;
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
import com.ycwl.basic.pricing.dto.ProductItem;
import com.ycwl.basic.pricing.enums.ProductType;
import com.ycwl.basic.pricing.service.IAutoCouponService;
import com.ycwl.basic.pricing.service.IPriceCalculationService;
import com.ycwl.basic.model.pc.printer.entity.MemberPrintEntity;
import com.ycwl.basic.model.pc.printer.entity.PrintTaskEntity;
import com.ycwl.basic.model.pc.printer.entity.PrinterEntity;
import com.ycwl.basic.model.pc.printer.resp.MemberPrintResp;
import com.ycwl.basic.model.pc.printer.resp.PrinterResp;
import com.ycwl.basic.model.pc.source.resp.SourceRespVO;
import com.ycwl.basic.model.printer.req.FromSourceReq;
import com.ycwl.basic.model.printer.req.PrinterSyncReq;
import com.ycwl.basic.model.printer.req.WorkerAuthReqVo;
import com.ycwl.basic.model.printer.resp.PrintTaskResp;
import com.ycwl.basic.model.wx.WXPayOrderReqVO;
import com.ycwl.basic.repository.DeviceRepository;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.OrderRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.repository.SourceRepository;
import com.ycwl.basic.service.mobile.WxPayService;
import com.ycwl.basic.service.pc.FaceService;
import com.ycwl.basic.service.printer.PrinterService;
import com.ycwl.basic.storage.StorageFactory;
import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.pipeline.core.Pipeline;
import com.ycwl.basic.pipeline.core.PipelineBuilder;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.ImageUtils;
import com.ycwl.basic.utils.JacksonUtil;
import com.ycwl.basic.utils.SnowFlakeUtil;
import com.ycwl.basic.utils.WxMpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PrinterServiceImpl implements PrinterService {
    @Autowired
    private PrinterMapper printerMapper;
    @Autowired
    private SourceMapper sourceMapper;
    @Autowired
    private MemberMapper memberMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    @Lazy
    private OrderBiz orderBiz;
    @Autowired
    @Lazy
    private WxPayService wxPayService;
    @Autowired
    private PrintTaskMapper printTaskMapper;
    @Autowired
    @Lazy
    private IPriceCalculationService priceCalculationService;
    @Autowired
    @Lazy
    private IAutoCouponService autoCouponService;
    @Autowired
    @Lazy
    private ScenicRepository scenicRepository;
    @Autowired
    @Lazy
    private OrderRepository orderRepository;
    @Autowired
    private FaceSampleMapper faceSampleMapper;
    @Autowired
    private FaceMapper faceMapper;
    @Autowired
    @Lazy
    private FaceRepository faceRepository;
    @Lazy
    @Autowired
    private FaceService faceService;
    @Autowired
    @Lazy
    private DeviceRepository deviceRepository;

    // 用于优先打印的线程池,核心线程数根据实际情况调整
    private final ExecutorService preferPrintExecutor = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors() * 2
    );
    @Autowired
    private SourceRepository sourceRepository;
    @Autowired
    @Lazy
    private WatermarkEdgeService watermarkEdgeService;
    @Autowired
    @Lazy
    private ScenicService scenicService;

    @Override
    public List<PrinterResp> listByScenicId(Long scenicId) {
        return printerMapper.listByScenicId(scenicId);
    }

    @Override
    public ApiResponse<List<PrinterEntity>> list(PrinterEntity condition) {
        return ApiResponse.success(printerMapper.list(condition));
    }

    @Override
    public ApiResponse<PrinterEntity> get(Integer id) {
        return ApiResponse.success(printerMapper.getById(id));
    }

    @Override
    public ApiResponse<Integer> add(PrinterEntity entity) {
        ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(entity.getScenicId());
        Boolean printEnable = scenicConfig.getBoolean("print_enable");
        if (!Boolean.TRUE.equals(printEnable)) {
            return ApiResponse.fail("景区没有开启打印功能!");
        }
        return ApiResponse.success(printerMapper.add(entity));
    }

    @Override
    public ApiResponse<Integer> update(PrinterEntity payload) {
        PrinterEntity entity = printerMapper.getById(payload.getId());
        ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(entity.getScenicId());
        Boolean printEnable = scenicConfig.getBoolean("print_enable");
        if (!Boolean.TRUE.equals(printEnable)) {
            return ApiResponse.fail("景区没有开启打印功能!");
        }
        return ApiResponse.success(printerMapper.update(payload));
    }

    @Override
    public ApiResponse<Integer> delete(Integer id) {
        return ApiResponse.success(printerMapper.deleteById(id));
    }

    private PrinterEntity getPrinter(WorkerAuthReqVo req) {
        String accessKey = req.getAccessKey();
        if (accessKey == null) {
            return null;
        }
        PrinterEntity printer = printerMapper.findByAccessKey(accessKey);
        if (printer == null) {
            return null;
        }
        if (printer.getStatus() != 1) {
            return null;
        }
        return printer;
    }

    @Override
    public List<PrintTaskResp> sync(PrinterSyncReq req) {
        PrinterEntity printer = getPrinter(req);
        if (printer == null) {
            return Collections.emptyList();
        }
        List<String> printers = req.getPrinters();
        String printersStr = StringUtils.join(printers, "|");
        if (!Strings.CS.equals(printersStr, printer.getPrinters())) {
            printer.setPrinters(printersStr);
            printerMapper.update(printer);
        }
        syncTaskLock.lock();
        try {
            while (true) {
                PrintTaskResp task = printerMapper.findTaskByPrinterId(printer.getId());
                if (task == null) {
                    return Collections.emptyList();
                }
                int updatedRows = printerMapper.compareAndSetTaskStatus(
                        task.getId(),
                        TASK_STATUS_PENDING,
                        TASK_STATUS_PROCESSING
                );
                if (updatedRows == 1) {
                    task.setStatus(TASK_STATUS_PROCESSING);
                    return Collections.singletonList(task);
                }
            }
        } finally {
            syncTaskLock.unlock();
        }
    }

    @Override
    public void taskSuccess(Integer taskId, WorkerAuthReqVo req) {
        PrintTaskEntity task = printerMapper.getTaskById(taskId);
        if (task == null) {
            return;
        }
        PrinterEntity printer = getPrinter(req);
        if (printer == null) {
            return;
        }
        if (printer.getId().equals(task.getPrinterId())) {
            printerMapper.updateTaskStatus(taskId, 1);
        }
    }

    @Override
    public void taskFail(Integer taskId, WorkerAuthReqVo req) {
        PrintTaskEntity task = printerMapper.getTaskById(taskId);
        if (task == null) {
            return;
        }
        PrinterEntity printer = getPrinter(req);
        if (printer == null) {
            return;
        }
        if (printer.getId().equals(task.getPrinterId())) {
            printerMapper.updateTaskStatus(taskId, 2);
        }
    }

    @Override
    public List<MemberPrintResp> getUserPhotoList(Long userId, Long scenicId, Long faceId) {
        if (faceId == null) {
            List<MemberPrintResp> list = printerMapper.listRelation(userId, scenicId);
            return list.stream()
                    .filter(item -> Objects.isNull(item.getFaceId()))
                    .collect(Collectors.toList());
        }
        List<MemberPrintResp> list = printerMapper.listRelationByFaceId(userId, scenicId, faceId);
        return list;
    }

    @Override
    public List<MemberPrintResp> getUserPhotoListByOrderId(Long orderId) {
        return printerMapper.listRelationByOrderId(orderId);
    }

    @Override
    public boolean deleteUserPhoto(Long memberId, Long scenicId, Long relationId) {
        int record = printerMapper.deleteUserPhoto(memberId, scenicId, relationId);
        return record > 0;
    }

    @Override
    public Integer addUserPhoto(Long memberId, Long scenicId, String url, Long faceId, Long sourceId) {
        MemberPrintEntity entity = new MemberPrintEntity();
        entity.setMemberId(memberId);
        entity.setScenicId(scenicId);
        entity.setFaceId(faceId);
        entity.setSourceId(sourceId);
        // 根据 sourceId 判断图片类型
        entity.setImageType(sourceId == null ? ImageType.MOBILE_UPLOAD.getCode() : ImageType.NORMAL_PHOTO.getCode());
        entity.setOrigUrl(url);

        // 获取打印尺寸
        String cropUrl = url; // 默认使用原图
        try {
            // 从打印机表获取尺寸
            Integer printWidth = null;
            Integer printHeight = null;

            List<PrinterResp> printers = printerMapper.listByScenicId(scenicId);
            if (printers != null && !printers.isEmpty()) {
                PrinterResp firstPrinter = printers.get(0);
                printWidth = firstPrinter.getPreferW();
                printHeight = firstPrinter.getPreferH();
                log.debug("从打印机获取尺寸: scenicId={}, printerId={}, width={}, height={}",
                         scenicId, firstPrinter.getId(), printWidth, printHeight);
            }

            // 如果打印机没有配置或配置无效，使用默认值
            if (printWidth == null || printWidth <= 0) {
                printWidth = 1020;
                log.debug("打印机宽度未配置或无效,使用默认值: width={}", printWidth);
            }
            if (printHeight == null || printHeight <= 0) {
                printHeight = 1520;
                log.debug("打印机高度未配置或无效,使用默认值: height={}", printHeight);
            }

            // 检测原图方向
            boolean isLandscape = false;
            try {
                isLandscape = ImageUtils.isLandscape(url);
            } catch (Exception e) {
                log.warn("检测图片方向失败,默认为竖图: url={}", url, e);
            }

            // 使用smartCropAndFill裁剪图片
            File croppedFile = ImageUtils.smartCropAndFill(url, printWidth, printHeight);

            try {
                // 上传裁剪后的图片（使用File版本的uploadFile方法）
                String[] split = url.split("\\.");
                String ext = split.length > 0 ? split[split.length - 1] : "jpg";

                cropUrl = StorageFactory.use().uploadFile(null, croppedFile, "printer", UUID.randomUUID() + "." + ext);

                log.info("照片裁剪成功: memberId={}, scenicId={}, 原图={}, 裁剪后={}, 尺寸={}x{}",
                        memberId, scenicId, url, cropUrl, printWidth, printHeight);
                if (isLandscape) {
                    entity.setCrop(JacksonUtil.toJSONString(new Crop(270)));
                }
            } finally {
                // 清理临时文件
                if (croppedFile != null && croppedFile.exists()) {
                    croppedFile.delete();
                }
            }
        } catch (Exception e) {
            log.error("照片裁剪失败,使用原图: memberId={}, scenicId={}, url={}", memberId, scenicId, url, e);
            // 出现异常则使用原图
            cropUrl = url;
        }

        entity.setCropUrl(cropUrl);
        entity.setStatus(0);
        printerMapper.addUserPhoto(entity);
        return entity.getId();
    }

    @Override
    public MemberPrintResp getUserPhoto(Long userId, Long scenicId, Long id) {
        return printerMapper.getUserPhoto(userId, scenicId, id);
    }

    @Override
    public int setPhotoCropped(Long memberId, Long scenicId, Long id, String url, String crop) {
        return printerMapper.setPhotoCropped(memberId, scenicId, id, url, crop);
    }

    @Override
    public int setPhotoQuantity(Long memberId, Long scenicId, Long id, Integer quantity) {
        return printerMapper.setPhotoQuantity(memberId, scenicId, id, quantity);
    }

    @Override
    public PriceObj queryPrice(Long memberId, Long scenicId, Long faceId) {
        List<MemberPrintResp> userPhotoList = getUserPhotoList(memberId, scenicId, faceId);

        PriceObj obj = new PriceObj();
        obj.setScenicId(scenicId);
        obj.setGoodsId(faceId);
        obj.setFaceId(faceId);
        obj.setGoodsType(3);

        // 按照 sourceId 分类照片
        // sourceId > 0 且 source 表存在: 普通照片打印 (PHOTO_PRINT)
        // sourceId > 0 且 source 表不存在: 拼图打印 (PUZZLE)，归类为特效照片价格
        // sourceId == null: 手机照片打印 (PHOTO_PRINT_MU)
        // sourceId == 0: 特效照片打印 (PHOTO_PRINT_FX)
        long normalCount = userPhotoList.stream()
            .filter(item -> Objects.nonNull(item.getQuantity())
                && item.getSourceId() != null && item.getSourceId() > 0)
            .filter(item -> sourceMapper.getById(item.getSourceId()) != null)
            .mapToInt(MemberPrintResp::getQuantity)
            .sum();
        List<String> normalAttrs = userPhotoList.stream()
                .filter(item -> Objects.nonNull(item.getQuantity())
                        && item.getSourceId() != null && item.getSourceId() > 0)
                .filter(item -> sourceMapper.getById(item.getSourceId()) != null)
                .map(MemberPrintResp::getSourceId)
                .map(id -> {
                    SourceEntity source = sourceRepository.getSource(id);
                    if (source == null) {
                        return null;
                    }
                    return source.getDeviceId();
                })
                .filter(Objects::nonNull)
                .distinct()
                .map(String::valueOf)
                .toList();

        long mobileCount = userPhotoList.stream()
            .filter(item -> Objects.nonNull(item.getQuantity())
                && item.getSourceId() == null)
            .mapToInt(MemberPrintResp::getQuantity)
            .sum();

        long effectCount = userPhotoList.stream()
            .filter(item -> Objects.nonNull(item.getQuantity())
                && item.getSourceId() != null && item.getSourceId() == 0)
            .mapToInt(MemberPrintResp::getQuantity)
            .sum();

        // 拼图：sourceId > 0 但 source 表中不存在（即来自 puzzle_generation_record 表）
        long puzzleCount = userPhotoList.stream()
            .filter(item -> Objects.nonNull(item.getQuantity())
                && item.getSourceId() != null && item.getSourceId() > 0)
            .filter(item -> sourceMapper.getById(item.getSourceId()) == null)
            .mapToInt(MemberPrintResp::getQuantity)
            .sum();

        long totalCount = normalCount + mobileCount + effectCount + puzzleCount;

        if (totalCount == 0) {
            // 如果没有照片，返回零价格
            obj.setPrice(BigDecimal.ZERO);
            obj.setSlashPrice(BigDecimal.ZERO);
            obj.setFree(false);
            return obj;
        }

        // 构建价格计算请求
        PriceCalculationRequest request = new PriceCalculationRequest();
        request.setUserId(memberId);
        request.setScenicId(scenicId);

        // 创建商品项列表
        List<ProductItem> productItems = new ArrayList<>();

        // 添加普通照片打印商品项 (sourceId > 0)
        if (normalCount > 0) {
            ProductItem normalPhotoItem = new ProductItem();
            normalPhotoItem.setProductType(ProductType.PHOTO_PRINT);
            normalPhotoItem.setProductId(scenicId.toString());
            normalPhotoItem.setQuantity(Long.valueOf(normalCount).intValue());
            normalPhotoItem.setPurchaseCount(1);
            normalPhotoItem.setScenicId(scenicId.toString());
            normalPhotoItem.setAttributeKeys(normalAttrs);
            productItems.add(normalPhotoItem);
            log.debug("普通照片打印数量: {}", normalCount);
        }

        // 添加手机照片打印商品项 (sourceId == null)
        if (mobileCount > 0) {
            ProductItem mobilePhotoItem = new ProductItem();
            mobilePhotoItem.setProductType(ProductType.PHOTO_PRINT_MU);
            mobilePhotoItem.setProductId(scenicId.toString());
            mobilePhotoItem.setQuantity(Long.valueOf(mobileCount).intValue());
            mobilePhotoItem.setPurchaseCount(1);
            mobilePhotoItem.setScenicId(scenicId.toString());
            productItems.add(mobilePhotoItem);
            log.debug("手机照片打印数量: {}", mobileCount);
        }

        // 添加特效照片打印商品项 (sourceId == 0)
        if (effectCount > 0 || puzzleCount > 0) {
            ProductItem effectPhotoItem = new ProductItem();
            effectPhotoItem.setProductType(ProductType.PHOTO_PRINT_FX);
            effectPhotoItem.setProductId(scenicId.toString());
            effectPhotoItem.setQuantity(Long.valueOf(effectCount + puzzleCount).intValue());
            effectPhotoItem.setPurchaseCount(1);
            effectPhotoItem.setScenicId(scenicId.toString());
            productItems.add(effectPhotoItem);
            log.debug("特效照片打印数量: {}", effectCount + puzzleCount);
        }

        request.setProducts(productItems);

        if (normalCount > 0) {
            try {
                autoCouponService.autoGrantCoupon(
                        memberId,
                        faceId,
                        scenicId,
                        ProductType.PHOTO_PRINT
                );
            } catch (Exception e) {
                log.warn("自动发券失败，不影响下单流程: memberId={}, faceId={}, scenicId={}, error={}",
                        memberId, faceId, scenicId, e.getMessage());
            }
        }
        if (effectCount > 0) {
            try {
                autoCouponService.autoGrantCoupon(
                        memberId,
                        faceId,
                        scenicId,
                        ProductType.PHOTO_PRINT_FX
                );
            } catch (Exception e) {
                log.warn("自动发券失败，不影响下单流程: memberId={}, faceId={}, scenicId={}, error={}",
                        memberId, faceId, scenicId, e.getMessage());
            }
        }
        if (mobileCount > 0) {
            try {
                autoCouponService.autoGrantCoupon(
                        memberId,
                        faceId,
                        scenicId,
                        ProductType.PHOTO_PRINT_MU
                );
            } catch (Exception e) {
                log.warn("自动发券失败，不影响下单流程: memberId={}, faceId={}, scenicId={}, error={}",
                        memberId, faceId, scenicId, e.getMessage());
            }
        }
        request.setAutoUseCoupon(true);
        request.setPreviewOnly(true); // 仅查询价格，不实际使用优惠

        // 使用统一价格计算服务
        PriceCalculationResult result = priceCalculationService.calculatePrice(request);

        // 转换为原有的 PriceObj 格式
        obj.setPrice(result.getFinalAmount());
        obj.setSlashPrice(result.getOriginalAmount());
        obj.setFree(result.getFinalAmount().compareTo(BigDecimal.ZERO) == 0);

        return obj;
    }

    @Override
    public List<Integer> addUserPhotoFromSource(Long memberId, Long scenicId, FromSourceReq req, Long faceId) {
        List<Integer> resultIds = new ArrayList<>();

        // 预先查询用户在该景区的已有打印记录，用于去重
        List<MemberPrintResp> existingPhotos = printerMapper.listRelationByFaceId(memberId, scenicId, faceId);
        Map<Long, Integer> sourceIdToMemberPrintId = new HashMap<>();
        if (existingPhotos != null) {
            for (MemberPrintResp photo : existingPhotos) {
                if (photo.getSourceId() != null) {
                    sourceIdToMemberPrintId.put(photo.getSourceId(), photo.getId());
                }
            }
        }

        req.getIds().forEach(id -> {
            // 检查该sourceId是否已经添加过
            if (sourceIdToMemberPrintId.containsKey(id)) {
                Integer existingId = sourceIdToMemberPrintId.get(id);
                log.info("sourceId={}已存在于打印列表中，返回已有记录: memberPrintId={}", id, existingId);
                resultIds.add(existingId);
                return;
            }

            SourceRespVO byId = sourceMapper.getById(id);
            if (byId == null) {
                resultIds.add(null);
                return;
            }

            String url = byId.getUrl();
            // 特殊兼容处理
            if (Integer.valueOf(3).equals(byId.getType())) {
                url = byId.getThumbUrl().replace("_t.", "_o.");
            }
            MemberPrintEntity entity = new MemberPrintEntity();
            entity.setMemberId(memberId);
            entity.setScenicId(scenicId);
            entity.setFaceId(faceId);
            entity.setSourceId(id);
            entity.setImageType(ImageType.NORMAL_PHOTO.getCode());
            entity.setOrigUrl(url);

            // 获取打印尺寸并裁剪图片
            String cropUrl = url; // 默认使用原图
            try {
                // 从打印机表获取尺寸
                Integer printWidth = null;
                Integer printHeight = null;

                List<PrinterResp> printers = printerMapper.listByScenicId(scenicId);
                if (printers != null && !printers.isEmpty()) {
                    PrinterResp firstPrinter = printers.get(0);
                    printWidth = firstPrinter.getPreferW();
                    printHeight = firstPrinter.getPreferH();
                    log.debug("从打印机获取尺寸: scenicId={}, printerId={}, width={}, height={}",
                            scenicId, firstPrinter.getId(), printWidth, printHeight);
                }

                // 如果打印机没有配置或配置无效，使用默认值
                if (printWidth == null || printWidth <= 0) {
                    printWidth = 1020;
                    log.debug("打印机宽度未配置或无效,使用默认值: width={}", printWidth);
                }
                if (printHeight == null || printHeight <= 0) {
                    printHeight = 1520;
                    log.debug("打印机高度未配置或无效,使用默认值: height={}", printHeight);
                }

                // 检测原图方向
                boolean isLandscape = false;
                try {
                    isLandscape = ImageUtils.isLandscape(url);
                } catch (Exception e) {
                    log.warn("检测图片方向失败,默认为竖图: url={}", url, e);
                }

                // 使用smartCropAndFill裁剪图片
                File croppedFile = ImageUtils.smartCropAndFill(url, printWidth, printHeight);
                if (isLandscape) {
                    entity.setCrop(JacksonUtil.toJSONString(new Crop(270)));
                }

                try {
                    // 上传裁剪后的图片
                    String[] split = url.split("\\.");
                    String ext = split.length > 0 ? split[split.length - 1] : "jpg";

                    cropUrl = StorageFactory.use().uploadFile(null, croppedFile, "printer", UUID.randomUUID() + "." + ext);

                    log.info("照片裁剪成功: memberId={}, scenicId={}, sourceId={}, 原图={}, 裁剪后={}, 尺寸={}x{}",
                            memberId, scenicId, id, url, cropUrl, printWidth, printHeight);
                } finally {
                    // 清理临时文件
                    if (croppedFile != null && croppedFile.exists()) {
                        croppedFile.delete();
                    }
                }
            } catch (Exception e) {
                log.error("照片裁剪失败,使用原图: memberId={}, scenicId={}, sourceId={}, url={}", memberId, scenicId, id, url, e);
                // 出现异常则使用原图
                cropUrl = url;
            }

            entity.setCropUrl(cropUrl);
            entity.setStatus(0);

            try {
                int rows = printerMapper.addUserPhoto(entity);
                if (rows > 0 && entity.getId() != null) {
                    resultIds.add(entity.getId());
                } else {
                    resultIds.add(null);
                }
            } catch (Exception e) {
                log.error("添加用户照片失败, memberId={}, scenicId={}, sourceId={}", memberId, scenicId, id, e);
                resultIds.add(null);
            }
        });
        return resultIds;
    }

    @Override
    public Map<String, Object> createOrder(Long memberId, Long scenicId, Integer printerId, Long faceId) {
        PrinterEntity printer = null;
        if (printerId == null) {
            List<PrinterResp> printerList = printerMapper.listByScenicId(scenicId);
            if (printerList.size() != 1) {
                throw new BaseException("请选择打印机");
            }
            printerId = printerList.getFirst().getId();
        }
        printer = printerMapper.getById(printerId);
        if (printer == null) {
            throw new BaseException("打印机不存在");
        }
        if (printer.getStatus() != 1) {
            throw new BaseException("打印机已停用");
        }
        if (!printer.getScenicId().equals(scenicId)) {
            throw new BaseException("打印机不属于该景区");
        }
        // 验证照片数量
        List<MemberPrintResp> userPhotoList = getUserPhotoList(memberId, scenicId, faceId);

        // 按照 sourceId 分类照片
        // sourceId > 0 且 source 表存在: 普通照片打印 (PHOTO_PRINT)
        // sourceId > 0 且 source 表不存在: 拼图打印 (PUZZLE)，归类为特效照片价格
        // sourceId == null: 手机照片打印 (PHOTO_PRINT_MU)
        // sourceId == 0: 特效照片打印 (PHOTO_PRINT_FX)
        long normalCount = userPhotoList.stream()
            .filter(item -> Objects.nonNull(item.getQuantity())
                && item.getSourceId() != null && item.getSourceId() > 0)
            .filter(item -> sourceMapper.getById(item.getSourceId()) != null)
            .mapToInt(MemberPrintResp::getQuantity)
            .sum();

        long mobileCount = userPhotoList.stream()
            .filter(item -> Objects.nonNull(item.getQuantity())
                && item.getSourceId() == null)
            .mapToInt(MemberPrintResp::getQuantity)
            .sum();

        long effectCount = userPhotoList.stream()
            .filter(item -> Objects.nonNull(item.getQuantity())
                && item.getSourceId() != null && item.getSourceId() == 0)
            .mapToInt(MemberPrintResp::getQuantity)
            .sum();

        // 拼图：sourceId > 0 但 source 表中不存在（即来自 puzzle_generation_record 表）
        long puzzleCount = userPhotoList.stream()
            .filter(item -> Objects.nonNull(item.getQuantity())
                && item.getSourceId() != null && item.getSourceId() > 0)
            .filter(item -> sourceMapper.getById(item.getSourceId()) == null)
            .mapToInt(MemberPrintResp::getQuantity)
            .sum();
        long totalCount = normalCount + mobileCount + effectCount + puzzleCount;

        if (totalCount == 0) {
            throw new BaseException("没有可打印的照片");
        }

        OrderEntity order = new OrderEntity();
        Long orderId = SnowFlakeUtil.getLongId();
        redisTemplate.opsForValue().set("printer_size:"+orderId, printer.getPreferPaper(), 60, TimeUnit.SECONDS);
        order.setId(orderId);
        order.setMemberId(memberId);
        order.setFaceId(faceId);
        MemberRespVO member = memberMapper.getById(memberId);
        order.setOpenId(member.getOpenId());
        order.setScenicId(scenicId);
        order.setType(3); // 照片打印类型
        batchSetUserPhotoListToPrinter(memberId, scenicId, printerId);
        List<OrderItemEntity> orderItems = userPhotoList.stream().map(goods -> {
            OrderItemEntity orderItem = new OrderItemEntity();
            orderItem.setOrderId(orderId);
            // member_print
            orderItem.setGoodsId(Long.valueOf(goods.getId()));
            orderItem.setGoodsType(3);
            return orderItem;
        }).collect(Collectors.toList());

        // 使用统一价格计算服务计算最终价格

        PriceCalculationRequest request = new PriceCalculationRequest();
        request.setUserId(memberId);
        request.setScenicId(scenicId);

        // 创建商品项列表
        List<ProductItem> productItems = new ArrayList<>();

        // 添加普通照片打印商品项 (sourceId > 0)
        if (normalCount > 0) {
            List<String> normalAttrs = userPhotoList.stream()
                    .filter(item -> Objects.nonNull(item.getQuantity())
                            && item.getSourceId() != null && item.getSourceId() > 0)
                    .filter(item -> sourceMapper.getById(item.getSourceId()) != null)
                    .map(MemberPrintResp::getSourceId)
                    .map(id -> {
                        SourceEntity source = sourceRepository.getSource(id);
                        if (source == null) {
                            return null;
                        }
                        return source.getDeviceId();
                    })
                    .filter(Objects::nonNull)
                    .distinct()
                    .map(String::valueOf)
                    .toList();

            ProductItem normalPhotoItem = new ProductItem();
            normalPhotoItem.setProductType(ProductType.PHOTO_PRINT);
            normalPhotoItem.setProductId(scenicId.toString());
            normalPhotoItem.setQuantity(Long.valueOf(normalCount).intValue());
            normalPhotoItem.setPurchaseCount(1);
            normalPhotoItem.setScenicId(scenicId.toString());
            normalPhotoItem.setAttributeKeys(normalAttrs);
            productItems.add(normalPhotoItem);
            log.debug("创建订单-普通照片打印数量: {}", normalCount);
        }

        // 添加手机照片打印商品项 (sourceId == null)
        if (mobileCount > 0) {
            ProductItem mobilePhotoItem = new ProductItem();
            mobilePhotoItem.setProductType(ProductType.PHOTO_PRINT_MU);
            mobilePhotoItem.setProductId(scenicId.toString());
            mobilePhotoItem.setQuantity(Long.valueOf(mobileCount).intValue());
            mobilePhotoItem.setPurchaseCount(1);
            mobilePhotoItem.setScenicId(scenicId.toString());
            productItems.add(mobilePhotoItem);
            log.debug("创建订单-手机照片打印数量: {}", mobileCount);
        }

        // 添加特效照片打印商品项 (sourceId == 0)
        if (effectCount > 0 || puzzleCount > 0) {
            ProductItem effectPhotoItem = new ProductItem();
            effectPhotoItem.setProductType(ProductType.PHOTO_PRINT_FX);
            effectPhotoItem.setProductId(scenicId.toString());
            effectPhotoItem.setQuantity(Long.valueOf(effectCount + puzzleCount).intValue());
            effectPhotoItem.setPurchaseCount(1);
            effectPhotoItem.setScenicId(scenicId.toString());
            productItems.add(effectPhotoItem);
            log.debug("创建订单-特效照片打印数量: {}", effectCount + puzzleCount);
        }

        request.setProducts(productItems);
        request.setAutoUseCoupon(true);
        request.setPreviewOnly(false);
        request.setOrderId(String.valueOf(orderId)); // 设置订单ID

        PriceCalculationResult priceResult = priceCalculationService.calculatePrice(request);

        order.setPrice(priceResult.getFinalAmount());
        order.setSlashPrice(priceResult.getOriginalAmount());
        order.setPayPrice(priceResult.getFinalAmount());
        order.setFaceId(faceId);
        if (order.getPayPrice().equals(BigDecimal.ZERO)) {
            order.setStatus(OrderStateEnum.PAID.getState());
            order.setPayAt(new Date());
        } else {
            order.setStatus(OrderStateEnum.UNPAID.getState());
        }
        orderMapper.add(order);
        int addOrderItems = orderMapper.addOrderItems(orderItems);
        if (addOrderItems == NumberConstant.ZERO) {
            log.error("订单明细添加失败");
            throw new BaseException("订单添加失败");
        }
        Map<String, Object> data = new HashMap<>();
        if (order.getPayPrice().compareTo(BigDecimal.ZERO) == 0) {
            orderBiz.paidOrder(order.getId());
            data.put("needPay", false);
        } else {
            WXPayOrderReqVO wxPayOrderReqVO = new WXPayOrderReqVO();
            wxPayOrderReqVO.setOpenId(order.getOpenId())
                    .setMemberId(order.getMemberId())
                    .setOrderSn(order.getId())
                    .setTotalPrice(order.getPayPrice().setScale(2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).intValueExact())
                    .setGoodsName("照片打印")
                    .setDescription("照片打印");
            try {
                data = wxPayService.createOrder(order.getScenicId(), wxPayOrderReqVO);
            } catch (Exception e) {
                throw new BaseException(e);
            }
        }
        data.put("orderId", orderId);
        return data;
    }

    @Override
    public void batchSetUserPhotoListToPrinter(Long memberId, Long scenicId, Integer printerId) {
        printerMapper.updateUserPhotoListToPrinter(memberId, scenicId, printerId);
    }

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    private static final String USER_PHOTO_LIST_TO_PRINTER = "USER_PHOTO_LIST_TO_PRINTER:";
    private static final String PRINTER_INDEX_KEY_PREFIX = "PRINTER_INDEX:";
    private static final int PRINTER_INDEX_EXPIRE_MINUTES = 5;
    private static final int TASK_STATUS_PENDING = 0;
    private static final int TASK_STATUS_PROCESSING = 3;
    private static final int TASK_STATUS_PENDING_REVIEW = 4; // 待审核
    private final Lock syncTaskLock = new ReentrantLock();

    /**
     * 创建普通照片处理管线
     *
     * @param watermarkConfig 水印配置
     */
    private Pipeline<PhotoProcessContext> createNormalPhotoPipeline(WatermarkConfig watermarkConfig) {
        BceEnhancerConfig config = new BceEnhancerConfig();
        config.setQps(1);
        config.setAppId("119554288");
        config.setApiKey("OX6QoijgKio3eVtA0PiUVf7f");
        config.setSecretKey("dYatXReVriPeiktTjUblhfubpcmYfuMk");

        return new PipelineBuilder<PhotoProcessContext>("NormalPhotoPipeline")
                .addStage(new DownloadStage())
                .addStage(new ImageOrientationStage())
                .addStage(new ConditionalRotateStage())
                .addStage(new ImageEnhanceStage(config)) // 通过setStageState方法设置是否启用
                .addStage(new WatermarkStage(watermarkConfig))
                .addStage(new RestoreOrientationStage())
                .addStage(new UploadStage())
                .addStage(new CleanupStage())
                .build();
    }

    /**
     * 创建拼图打印处理管线（带专用水印）
     *
     * @param watermarkConfig 水印配置
     */
    private Pipeline<PhotoProcessContext> createPuzzlePrintPipeline(WatermarkConfig watermarkConfig) {
        return new PipelineBuilder<PhotoProcessContext>("PuzzlePrintPipeline")
                .addStage(new DownloadStage())
                .addStage(new WatermarkStage(watermarkConfig))
                .addStage(new UploadStage())
                .addStage(new CleanupStage())
                .build();
    }

    /**
     * 使用管线处理照片
     * @param item 打印项
     * @param scenicId 景区ID
     * @param qrCodeFile 二维码文件
     * @return 处理后的URL,失败返回原URL
     */
    private String processPhotoWithPipeline(MemberPrintResp item, Long scenicId, File qrCodeFile) {
        return processPhotoWithPipeline(item, scenicId, qrCodeFile, null);
    }

    /**
     * 使用管线处理照片（支持增强选项）
     * @param item 打印项
     * @param scenicId 景区ID
     * @param qrCodeFile 二维码文件
     * @param needEnhance 是否需要图像增强（null 或 false 表示不增强）
     * @return 处理后的URL,失败返回原URL
     */
    private String processPhotoWithPipeline(MemberPrintResp item, Long scenicId, File qrCodeFile, Boolean needEnhance) {
        PrinterOrderItem orderItem = PrinterOrderItem.fromMemberPrintResp(item);

        PhotoProcessContext context = PhotoProcessContext.fromPrinterOrderItem(orderItem, scenicId);

        try {
            // 设置景区配置管理器到context
            ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId);
            context.setScenicConfigManager(scenicConfig);

            // 设置管线场景为图片打印
            context.setScene(PipelineScene.IMAGE_PRINT);

            // 处理图像增强选项
            if (needEnhance != null && needEnhance) {
                context.setStageState("image_enhance", true);
            }

            // 根据 imageType 字段判断图片类型
            SourceEntity source = null;
            if (ImageType.PUZZLE.getCode().equals(item.getImageType())) {
                context.setImageType(ImageType.PUZZLE);
            } else if (ImageType.MOBILE_UPLOAD.getCode().equals(item.getImageType())) {
                context.setSource(ImageSource.PHONE);
            } else {
                // 普通照片，查询 source 获取设备类型信息
                context.setSource(ImageSource.IPC);
                if (item.getSourceId() != null && item.getSourceId() > 0) {
                    source = sourceMapper.getEntity(item.getSourceId());
                }
            }

            Pipeline<PhotoProcessContext> pipeline;

            // 特殊处理: sourceId > 0 且 source.type == 3
            if (source != null && source.getType() != null && source.getType() == 3) {
                // Type=3的特殊处理流程
                log.info("检测到source.type=3, 使用特殊处理管线: sourceId={}", item.getSourceId());

                // 准备百度云配置
                BceEnhancerConfig bceConfig = new BceEnhancerConfig();
                bceConfig.setQps(1);
                bceConfig.setAppId("119554288");
                bceConfig.setApiKey("OX6QoijgKio3eVtA0PiUVf7f");
                bceConfig.setSecretKey("dYatXReVriPeiktTjUblhfubpcmYfuMk");

                // 准备水印配置
                WatermarkConfig watermarkConfig = prepareWatermarkConfig(context, qrCodeFile, 3.0);
                // 准备存储适配器
                prepareStorageAdapter(context);
                context.enableStage("image_sr");
                context.enableStage("image_enhance");

                // 构建特殊管线: 超分(2倍) -> 增强 -> 更新MemberPrint -> 缩小2倍 -> 水印 -> 上传
                pipeline = new PipelineBuilder<PhotoProcessContext>("Type3Pipeline")
                        .addStage(new DownloadStage())                              // 1. 下载图片
                        .addStage(new ImageOrientationStage())                      // 2. 检测方向
                        .addStage(new ConditionalRotateStage())                     // 3. 条件性旋转
                        .addStage(new ImageSRStage(bceConfig))                      // 4. 超分辨率(2倍放大)
                        .addStage(new ImageEnhanceStage(bceConfig))                 // 5. 图像增强
                        .addStage(new UploadStage())                                // 6. 上传(用于更新MemberPrint)
                        .addStage(new UpdateMemberPrintStage(printerMapper,         // 7. 更新MemberPrint的cropUrl
                                item.getId(), item.getMemberId(), scenicId))
                        .addStage(new WatermarkStage(watermarkConfig))              // 9. 添加水印
                        .addStage(new RestoreOrientationStage())                    // 10. 恢复方向
                        .addStage(new UploadStage())                                // 11. 最终上传
                        .addStage(new CleanupStage())                               // 12. 清理
                        .build();

            } else if (context.getImageType() == ImageType.NORMAL_PHOTO) {
                // 普通照片处理流程
                WatermarkConfig watermarkConfig = prepareWatermarkConfig(context, qrCodeFile, 1.5);
                prepareStorageAdapter(context);
                pipeline = createNormalPhotoPipeline(watermarkConfig);
            } else {
                // 拼图处理流程（使用专用水印）
                WatermarkConfig watermarkConfig = preparePuzzleWatermarkConfig(context, qrCodeFile);
                prepareStorageAdapter(context);
                pipeline = createPuzzlePrintPipeline(watermarkConfig);
            }

            boolean success = pipeline.execute(context);

            if (success && context.getResultUrl() != null) {
                log.info("照片处理成功: photoId={}, type={}, url={}",
                        item.getId(),
                        context.getImageType() == ImageType.PUZZLE ? "拼图" : "普通照片",
                        context.getResultUrl());
                return context.getResultUrl();
            } else {
                log.warn("照片处理失败,使用原图: photoId={}", item.getId());
                return item.getCropUrl();
            }

        } catch (Exception e) {
            log.error("照片处理异常,使用原图: photoId={}", item.getId(), e);
            return item.getCropUrl();
        } finally {
            context.cleanup();
        }
    }

    /**
     * 准备普通照片的水印配置
     * 从 scenicConfigManager 获取配置并构建 WatermarkConfig
     *
     * @param context 照片处理上下文
     * @param qrCodeFile 二维码文件
     * @return WatermarkConfig
     */
    private WatermarkConfig prepareWatermarkConfig(PhotoProcessContext context, File qrCodeFile, Double scale) {
        ScenicConfigManager scenicConfig = context.getScenicConfigManager();
        if (scenicConfig == null) {
            log.warn("scenicConfigManager未设置,返回空水印配置");
            return WatermarkConfig.builder().build();
        }

        ImageWatermarkOperatorEnum watermarkType = null;
        String printWatermarkType = scenicConfig.getString("print_watermark_type");
        if (StringUtils.isNotBlank(printWatermarkType)) {
            watermarkType = ImageWatermarkOperatorEnum.getByCode(printWatermarkType);
        }

        String scenicText = scenicConfig.getString("print_watermark_scenic_text", "");
        String dateFormat = scenicConfig.getString("print_watermark_dt_format", "yyyy.MM.dd");
        String printWatermarkPUrl = scenicConfig.getString("print_watermark_p_url", null);
        String printWatermarkLUrl = scenicConfig.getString("print_watermark_l_url", null);

        WatermarkConfig.WatermarkConfigBuilder builder = WatermarkConfig.builder()
                .watermarkType(watermarkType)
                .scenicText(scenicText)
                .dateFormat(dateFormat)
                .edgeService(watermarkEdgeService)
                .storageAdapter(StorageFactory.use())
                .edgeEnabled(true)
                .qrcodeFile(qrCodeFile)
                .scale(scale);
        if (context.getSource() == ImageSource.IPC) {
            return builder
                    .printWatermarkPUrlList(Collections.singletonList(printWatermarkPUrl))
                    .printWatermarkLUrlList(Collections.singletonList(printWatermarkLUrl))
                    .build();
        }
        return builder
                .build();
    }

    /**
     * 准备拼图打印的水印配置
     * 使用拼图专用水印模板
     *
     * @param context 照片处理上下文
     * @param qrCodeFile 二维码文件
     * @return WatermarkConfig
     */
    private WatermarkConfig preparePuzzleWatermarkConfig(PhotoProcessContext context, File qrCodeFile) {
        ScenicConfigManager scenicConfig = context.getScenicConfigManager();

        String scenicText = scenicConfig != null
                ? scenicConfig.getString("print_watermark_scenic_text", "")
                : "";
        String dateFormat = scenicConfig != null
                ? scenicConfig.getString("print_watermark_dt_format", "yyyy.MM.dd")
                : "yyyy.MM.dd";

        return WatermarkConfig.builder()
                .watermarkType(ImageWatermarkOperatorEnum.PUZZLE_PRINT)
                .scenicText(scenicText)
                .dateFormat(dateFormat)
                .edgeService(watermarkEdgeService)
                .storageAdapter(StorageFactory.use())
                .edgeEnabled(true)
                .qrcodeFile(qrCodeFile)
                .scale(1.0)
                .build();
    }

    /**
     * 准备存储适配器
     * 从context中的scenicConfigManager获取配置
     */
    private void prepareStorageAdapter(PhotoProcessContext context) {
        ScenicConfigManager scenicConfig = context.getScenicConfigManager();
        if (scenicConfig == null) {
            log.warn("scenicConfigManager未设置,将使用默认存储");
            return;
        }

        try {
            String storeType = scenicConfig.getString("store_type");
            if (storeType != null) {
                IStorageAdapter adapter = StorageFactory.get(storeType);
                String storeConfigJson = scenicConfig.getString("store_config_json");
                if (StringUtils.isNotBlank(storeConfigJson)) {
                    adapter.loadConfig(JacksonUtil.parseObject(storeConfigJson, Map.class));
                }
                context.setStorageAdapter(adapter);
            }
        } catch (Exception e) {
            log.warn("准备存储适配器失败,将使用默认存储: {}", e.getMessage());
        }
    }

    @Override
    public void setUserIsBuyItem(Long memberId, Long id, Long orderId) {
        // 尝试从 Redis 读取虚拟订单存储的 needEnhance 配置
        Boolean needEnhance = null;
        String enhanceFlag = redisTemplate.opsForValue().get("virtual_order_enhance:" + orderId);
        if (enhanceFlag != null) {
            needEnhance = Boolean.parseBoolean(enhanceFlag);
            redisTemplate.delete("virtual_order_enhance:" + orderId);
        }
        setUserIsBuyItem(memberId, id, orderId, needEnhance);
    }

    @Override
    public void setUserIsBuyItem(Long memberId, Long id, Long orderId, Boolean needEnhance) {
        if (redisTemplate.opsForValue().get(USER_PHOTO_LIST_TO_PRINTER + memberId + ":" + orderId) != null) {
            return;
        }
        redisTemplate.opsForValue().set(USER_PHOTO_LIST_TO_PRINTER + memberId + ":" + orderId, "1", 24, TimeUnit.HOURS);
        OrderEntity order = orderRepository.getOrder(orderId);
        List<OrderItemEntity> orderItems = orderMapper.getOrderItems(orderId);
        orderItems.forEach(item -> {
            printerMapper.setUserIsBuyItem(memberId, item.getGoodsId(), orderId);
        });
        // 创建打印任务
        List<MemberPrintResp> userPhotoListByOrderId = getUserPhotoListByOrderId(orderId);
        File qrCodeFile = new File("qrCodeFile" + orderId + ".png");
        MpConfigEntity scenicMpConfig = scenicRepository.getScenicMpConfig(order.getScenicId());
        MemberRespVO member = memberMapper.getById(memberId);
        try {
            if (member == null) {
                // 虚拟订单
                WxMpUtil.generateUnlimitedWXAQRCode(scenicMpConfig.getAppId(), scenicMpConfig.getAppSecret(), "pages/videoSynthesis/bind_face", order.getFaceId().toString(), qrCodeFile);
            } else {
                WxMpUtil.generateWXAQRCode(scenicMpConfig.getAppId(), scenicMpConfig.getAppSecret(), scenicMpConfig.getState(), "pages/my/orderDetailNew?force=1&id="+orderId, "qrCodeFile" + orderId + ".png");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        final Boolean finalNeedEnhance = needEnhance;
        Thread.ofVirtual().start(() -> {
            userPhotoListByOrderId.forEach(item -> {
                PrinterEntity printer = printerMapper.getById(item.getPrinterId());

                // 使用管线处理照片
                String printUrl = processPhotoWithPipeline(item, item.getScenicId(), qrCodeFile, finalNeedEnhance);

                // 根据数量创建多个打印任务
                Integer quantity = item.getQuantity();
                if (quantity == null || quantity <= 0) {
                    quantity = 1; // 默认至少打印1张
                }

                for (int i = 0; i < quantity; i++) {
                    // 获取打印机名称（支持轮询）
                    String selectedPrinter = getNextPrinter(printer);

                    // 根据景区配置决定任务初始状态
                    ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(order.getScenicId());
                    Boolean purchaseNeedReview = scenicConfig.getBoolean("printer_manual_approve");
                    int initialStatus = (purchaseNeedReview != null && purchaseNeedReview)
                            ? TASK_STATUS_PENDING_REVIEW
                            : TASK_STATUS_PENDING;

                    PrintTaskEntity task = new PrintTaskEntity();
                    task.setPrinterId(printer.getId());
                    task.setPrinterName(selectedPrinter);
                    task.setMpId(item.getId());
                    task.setPaper(printer.getPreferPaper());
                    task.setStatus(initialStatus);
                    task.setUrl(printUrl);
                    task.setHeight(printer.getPreferH());
                    task.setWidth(printer.getPreferW());
                    task.setCreateTime(new Date());
                    task.setUpdateTime(new Date());
                    printTaskMapper.insertTask(task);
                }
            });
            redisTemplate.delete("order_content_not_downloadable_" + orderId);
        });
    }

    /**
     * 查询待审核的打印任务
     * @param printerId 打印机ID(可选)
     * @return 待审核任务列表
     */
    @Override
    public List<PrintTaskEntity> getPendingReviewTasks(Integer printerId) {
        return printTaskMapper.queryPendingReviewTasks(printerId);
    }

    /**
     * 更新待审核任务的URL（可用于重新添加水印等操作）
     * @param taskId 任务ID
     * @param url 新的打印URL
     * @return 是否成功
     */
    @Override
    public boolean updatePendingReviewTaskUrl(Integer taskId, String url) {
        if (taskId == null || url == null || url.trim().isEmpty()) {
            return false;
        }
        int rows = printTaskMapper.updateTaskUrl(taskId, url);
        return rows > 0;
    }

    /**
     * 批准待审核任务，下发到打印队列
     * @param taskIds 任务ID列表
     * @return 成功数量
     */
    @Override
    public int approvePrintTasks(List<Integer> taskIds) {
        if (taskIds == null || taskIds.isEmpty()) {
            return 0;
        }
        // 将状态从4（待审核）改为0（待处理）
        int count = printTaskMapper.batchUpdateStatus(taskIds, TASK_STATUS_PENDING);

        return count;
    }

    /**
     * 拒绝待审核任务（取消打印）
     * @param taskIds 任务ID列表
     * @return 成功数量
     */
    @Override
    public int rejectPrintTasks(List<Integer> taskIds) {
        if (taskIds == null || taskIds.isEmpty()) {
            return 0;
        }
        // 将状态从4（待审核）改为5（已取消）
        return printTaskMapper.batchUpdateStatus(taskIds, 5);
    }

    @Override
    public FaceRecognizeResp useSample(Long userId, Long sampleId) {
        // 1. 查询 faceSample 获取其 URL
        FaceSampleEntity faceSample = faceSampleMapper.getEntity(sampleId);
        SourceEntity sourceEntity = sourceMapper.getBySampleIdAndType(sampleId, 2);
        if (faceSample == null) {
            throw new BaseException("人脸样本不存在");
        }
        
        String faceUrl = faceSample.getFaceUrl();
        if (StringUtils.isBlank(faceUrl)) {
            throw new BaseException("人脸样本URL为空");
        }
        
        Long scenicId = faceSample.getScenicId();
        
        // 2. 检查face数据库中有没有同用户、同URL的face记录
        FaceEntity existingFace = null;
        Long faceId = null;
        
        // 查询该用户在该景区的所有人脸记录
        List<FaceRespVO> userFaces = faceMapper.listByScenicAndUserId(scenicId, userId);
        
        // 查找是否存在相同URL的记录
        for (FaceRespVO faceResp : userFaces) {
            if (faceUrl.equals(faceResp.getFaceUrl())) {
                existingFace = faceMapper.get(faceResp.getId());
                faceId = existingFace.getId();
                break;
            }
        }
        
        // 3. 如果不存在,则新建一个face记录
        if (existingFace == null) {
            faceId = SnowFlakeUtil.getLongId();
            FaceEntity newFace = new FaceEntity();
            newFace.setId(faceId);
            newFace.setCreateAt(new Date());
            newFace.setScenicId(scenicId);
            newFace.setMemberId(userId);
            newFace.setFaceUrl(faceUrl);
            faceMapper.add(newFace);
            
            log.info("创建新的face记录, userId: {}, sampleId: {}, faceId: {}, faceUrl: {}", 
                     userId, sampleId, faceId, faceUrl);
        } else {
            log.info("使用已存在的face记录, userId: {}, sampleId: {}, faceId: {}, faceUrl: {}", 
                     userId, sampleId, faceId, faceUrl);
        }
        
        // 4. 返回结果
        FaceRecognizeResp resp = new FaceRecognizeResp();
        resp.setUrl(faceUrl);
        resp.setFaceId(faceId);
        resp.setScenicId(scenicId);
        try {
            faceService.matchFaceId(faceId, true, "printer");
            if (existingFace == null) {
                autoAddPhotosToPreferPrint(faceId);
            }
        } catch (Exception e) {
            // 人脸匹配失败不可以阻止正常流程
            log.error("人脸匹配失败", e);
        }
        if (sourceEntity != null && existingFace == null) {
            List<MemberPrintResp> userPhotoList = getUserPhotoList(userId, scenicId, faceId);
            boolean noneMatch = userPhotoList.stream()
                    .noneMatch(item -> Strings.CI.equals(item.getOrigUrl(), sourceEntity.getUrl()));
            if (noneMatch) {
                addUserPhoto(userId, scenicId, sourceEntity.getUrl(), faceId, sourceEntity.getId());
            }
        }
        return resp;
    }

    /**
     * 自动将人脸关联的照片添加到优先打印列表
     * 根据景区和设备配置自动添加type=2的照片到用户打印列表
     *
     * @param faceId 人脸ID
     * @return 成功添加的照片列表
     */
    @Override
    public List<SourceEntity> autoAddPhotosToPreferPrint(Long faceId) {
        // 使用线程安全的List收集成功添加的SourceEntity
        List<SourceEntity> addedSources = Collections.synchronizedList(new ArrayList<>());

        try {
            // 1. 获取人脸信息
            FaceEntity face = faceRepository.getFace(faceId);
            if (face == null) {
                log.warn("人脸不存在,无法自动添加打印: faceId={}", faceId);
                return Collections.emptyList();
            }

            Long scenicId = face.getScenicId();
            Long memberId = face.getMemberId();

            // 2. 获取景区配置
            ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId);
            if (scenicConfig == null) {
                log.warn("景区配置不存在,跳过自动添加打印: scenicId={}", scenicId);
                return Collections.emptyList();
            }

            // 3. 检查景区是否启用打印功能
            Boolean printEnable = scenicConfig.getBoolean("print_enable");
            if (printEnable == null || !printEnable) {
                log.debug("景区未启用打印功能,跳过自动添加: scenicId={}", scenicId);
                return Collections.emptyList();
            }

            // 4. 查询该faceId关联的所有type=2的照片
            List<SourceEntity> imageSources = sourceMapper.listImageSourcesByFaceId(faceId);
            if (imageSources == null || imageSources.isEmpty()) {
                log.debug("该人脸没有关联的照片,跳过自动添加: faceId={}", faceId);
                return Collections.emptyList();
            }

            // 5. 按照deviceId分组处理
            Map<Long, List<SourceEntity>> sourcesByDevice = imageSources.stream()
                    .filter(source -> source.getDeviceId() != null)
                    .collect(Collectors.groupingBy(SourceEntity::getDeviceId));

            // 使用原子计数器统计成功添加的数量
            AtomicInteger totalAdded = new AtomicInteger(0);

            // 创建异步任务列表
            List<CompletableFuture<Void>> futures = new ArrayList<>();

            for (Map.Entry<Long, List<SourceEntity>> entry : sourcesByDevice.entrySet()) {
                Long deviceId = entry.getKey();
                List<SourceEntity> deviceSources = entry.getValue();

                // 为每个设备创建一个异步任务
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    try {
                        // 6. 获取设备配置
                        DeviceConfigManager deviceConfig = deviceRepository.getDeviceConfigManager(deviceId);
                        if (deviceConfig == null) {
                            log.debug("设备配置不存在,跳过该设备: deviceId={}", deviceId);
                            return;
                        }

                        // 7. 检查是否启用优先打印
                        Boolean preferPrintEnable = deviceConfig.getBoolean("prefer_print_enable");
                        if (preferPrintEnable == null || !preferPrintEnable) {
                            log.debug("设备未启用优先打印,跳过: deviceId={}", deviceId);
                            return;
                        }

                        // 8. 获取优先打印数量配置
                        Integer preferPrintCount = deviceConfig.getInteger("prefer_print_count");
                        if (preferPrintCount == null) {
                            log.debug("设备未配置优先打印数量,跳过: deviceId={}", deviceId);
                            return;
                        }

                        // 9. 根据配置添加照片到打印列表
                        List<SourceEntity> sourcesToAdd;
                        if (preferPrintCount > 0) {
                            // 如果大于0,按照数量限制添加
                            sourcesToAdd = deviceSources.stream()
                                    .limit(preferPrintCount)
                                    .collect(Collectors.toList());
                            log.info("设备{}配置优先打印{}张,实际添加{}张",
                                    deviceId, preferPrintCount, sourcesToAdd.size());
                        } else {
                            // 如果小于等于0,添加该设备的所有照片
                            sourcesToAdd = deviceSources;
                            log.info("设备{}配置优先打印所有照片,实际添加{}张",
                                    deviceId, sourcesToAdd.size());
                        }

                        // 10. 批量添加到打印列表
                        int deviceAdded = 0;
                        for (SourceEntity source : sourcesToAdd) {
                            try {
                                addUserPhoto(memberId, scenicId, source.getUrl(), faceId, source.getId());
                                deviceAdded++;
                                addedSources.add(source); // 添加成功的source
                            } catch (Exception e) {
                                log.warn("添加照片到打印列表失败: sourceId={}, url={}, error={}",
                                        source.getId(), source.getUrl(), e.getMessage());
                            }
                        }

                        // 累加成功添加的数量
                        totalAdded.addAndGet(deviceAdded);

                    } catch (Exception e) {
                        log.error("处理设备{}的照片添加任务失败: deviceId={}", deviceId, deviceId, e);
                    }
                }, preferPrintExecutor);

                futures.add(future);
            }

            // 等待所有任务完成
            try {
                CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
            } catch (Exception e) {
                log.error("等待照片添加任务完成时发生异常: faceId={}", faceId, e);
            }

            int added = totalAdded.get();
            if (added > 0) {
                log.info("自动添加打印完成: faceId={}, 成功添加{}张照片", faceId, added);
            } else {
                log.debug("自动添加打印完成: faceId={}, 无符合条件的照片", faceId);
            }

            return addedSources;

        } catch (Exception e) {
            // 出现异常则放弃,不影响主流程
            log.error("自动添加打印失败,已忽略: faceId={}", faceId, e);
            return Collections.emptyList();
        }
    }
    /**
     * 获取下一个要使用的打印机名称（轮询逻辑）
     * 
     * @param printer 打印机实体
     * @return 选中的打印机名称
     */
    private String getNextPrinter(PrinterEntity printer) {
        String usePrinter = printer.getUsePrinter();
        if (StringUtils.isBlank(usePrinter)) {
            log.warn("打印机 {} 没有配置 usePrinter", printer.getId());
            return "";
        }

        // 分割打印机列表
        String[] printers = usePrinter.split("\\|");
        if (printers.length == 0) {
            log.warn("打印机 {} 的 usePrinter 配置为空", printer.getId());
            return "";
        }

        // 如果只有一个打印机，直接返回
        if (printers.length == 1) {
            return printers[0].trim();
        }

        // 从 Redis 原子递增获取索引
        String redisKey = PRINTER_INDEX_KEY_PREFIX + printer.getId();
        Long incrementedValue = redisTemplate.opsForValue().increment(redisKey);
        
        if (incrementedValue == null) {
            log.warn("Redis increment 操作失败，打印机ID: {}", printer.getId());
            incrementedValue = 1L;
        }
        
        // 每次递增后都重新设置过期时间为 5 分钟
        redisTemplate.expire(redisKey, PRINTER_INDEX_EXPIRE_MINUTES, TimeUnit.MINUTES);
        
        // 对打印机数量取模得到当前索引
        int currentIndex = (int) ((incrementedValue - 1) % printers.length);
        
        // 获取当前打印机
        String selectedPrinter = printers[currentIndex].trim();

        log.debug("打印机 {} 选择了第 {} 个打印机: {} (递增值: {})", printer.getId(), currentIndex, selectedPrinter, incrementedValue);

        return selectedPrinter;
    }

    @Override
    public Integer addUserPhotoFromPuzzle(Long memberId, Long scenicId, Long faceId, String resultImageUrl, Long puzzleRecordId) {
        if (resultImageUrl == null || resultImageUrl.isEmpty()) {
            log.error("拼图图片URL为空: memberId={}, scenicId={}, puzzleRecordId={}", memberId, scenicId, puzzleRecordId);
            return null;
        }

        // 检查是否已经存在未打印的记录(status=0),避免重复导入
        List<MemberPrintResp> existingPhotos = printerMapper.listRelationByFaceId(memberId, scenicId, faceId);
        if (existingPhotos != null && !existingPhotos.isEmpty()) {
            for (MemberPrintResp photo : existingPhotos) {
                // 检查是否是同一个拼图记录且状态为0(未打印)
                if (photo.getOrigUrl() != null && photo.getOrigUrl().equals(resultImageUrl)
                    && photo.getStatus() != null && photo.getStatus() == 0) {
                    log.info("拼图照片已存在于打印列表中,直接返回: memberId={}, scenicId={}, puzzleRecordId={}, memberPrintId={}",
                            memberId, scenicId, puzzleRecordId, photo.getId());
                    return photo.getId();
                }
            }
        }

        MemberPrintEntity entity = new MemberPrintEntity();
        entity.setMemberId(memberId);
        entity.setScenicId(scenicId);
        entity.setFaceId(faceId);
        entity.setSourceId(puzzleRecordId);  // 使用拼图记录ID作为sourceId
        entity.setImageType(ImageType.PUZZLE.getCode());  // 明确标记为拼图类型
        entity.setOrigUrl(resultImageUrl);

        // 获取打印尺寸并裁剪图片
        String cropUrl = resultImageUrl; // 默认使用原图

        entity.setCropUrl(cropUrl);
        entity.setStatus(0);

        try {
            int rows = printerMapper.addUserPhoto(entity);
            if (rows > 0 && entity.getId() != null) {
                log.info("拼图照片添加到打印列表成功: memberId={}, scenicId={}, puzzleRecordId={}, memberPrintId={}",
                        memberId, scenicId, puzzleRecordId, entity.getId());
                return entity.getId();
            } else {
                log.error("拼图照片添加到打印列表失败: memberId={}, scenicId={}, puzzleRecordId={}",
                        memberId, scenicId, puzzleRecordId);
                return null;
            }
        } catch (Exception e) {
            log.error("拼图照片添加到打印列表异常: memberId={}, scenicId={}, puzzleRecordId={}",
                    memberId, scenicId, puzzleRecordId, e);
            return null;
        }
    }

    @Override
    public int handleReprint(Integer id, ReprintRequest request) {
        // 1. 参数校验和任务获取
        if (id == null) {
            log.error("handleReprint: 任务ID为空");
            return 0;
        }

        PrintTaskEntity task = printTaskMapper.selectById(id);
        if (task == null) {
            log.error("handleReprint: 打印任务不存在, taskId={}", id);
            return 0;
        }

        // 2. 获取 MemberPrint 记录
        Integer mpId = task.getMpId();
        if (mpId == null) {
            log.warn("handleReprint: 任务的mpId为空, taskId={}, 直接更新状态和打印机名称", id);
            return updateTaskStatusAndPrinter(task, request);
        }

        List<Long> ids = Arrays.asList(mpId.longValue());
        List<MemberPrintResp> memberPrints = printerMapper.getUserPhotoByIds(ids);
        if (memberPrints == null || memberPrints.isEmpty()) {
            log.warn("handleReprint: MemberPrint记录不存在, mpId={}, taskId={}, 直接更新状态和打印机名称", mpId, id);
            return updateTaskStatusAndPrinter(task, request);
        }

        MemberPrintResp memberPrint = memberPrints.get(0);

        File qrCodeFile = new File("qrCodeFile" + memberPrint.getOrderId() + ".png");
        MpConfigEntity scenicMpConfig = scenicRepository.getScenicMpConfig(memberPrint.getScenicId());
        try {
            WxMpUtil.generateWXAQRCode(scenicMpConfig.getAppId(), scenicMpConfig.getAppSecret(), scenicMpConfig.getState(), "pages/my/orderDetailNew?force=1&id="+memberPrint.getOrderId(), qrCodeFile.getAbsolutePath());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 3. 重新处理照片（带增强选项）
        Boolean needEnhance = request.getNeedEnhance();
        if (needEnhance == null) {
            needEnhance = false; // 默认不增强
        }

        // 3.1 使用管线处理照片（复用 processPhotoWithPipeline）
        String newPrintUrl;
        try {
            newPrintUrl = processPhotoWithPipeline(memberPrint, memberPrint.getScenicId(), qrCodeFile, needEnhance);
            log.info("handleReprint: 照片重新处理成功, taskId={}, mpId={}, enhance={}, newUrl={}",
                    id, mpId, needEnhance, newPrintUrl);
        } catch (Exception e) {
            log.error("handleReprint: 照片重新处理异常, taskId={}, 使用原图", id, e);
            newPrintUrl = memberPrint.getCropUrl();
        }

        // 4. 更新打印任务
        String printerName = request.getPrinterName();
        if (StringUtils.isBlank(printerName)) {
            // 如果请求中未指定，使用轮询逻辑获取
            PrinterEntity printer = printerMapper.getById(task.getPrinterId());
            if (printer != null) {
                printerName = getNextPrinter(printer);
            } else {
                printerName = task.getPrinterName(); // 保持原有打印机
            }
        }

        task.setStatus(TASK_STATUS_PENDING); // 状态设置为待处理(0)
        task.setPrinterName(printerName);
        task.setUrl(newPrintUrl);
        task.setUpdateTime(new Date());

        int rows = printTaskMapper.updateById(task);

        log.info("handleReprint: 任务更新完成, taskId={}, printerName={}, status=0, rows={}",
                id, printerName, rows);

        return rows;
    }

    /**
     * 当无法获取 MemberPrint 时，直接更新任务状态和打印机名称
     */
    private int updateTaskStatusAndPrinter(PrintTaskEntity task, ReprintRequest request) {
        String printerName = request.getPrinterName();
        if (StringUtils.isBlank(printerName)) {
            // 如果请求中未指定，使用轮询逻辑获取
            PrinterEntity printer = printerMapper.getById(task.getPrinterId());
            if (printer != null) {
                printerName = getNextPrinter(printer);
            } else {
                printerName = task.getPrinterName(); // 保持原有打印机
            }
        }

        // 使用现有的 updateStatusAndPrinter 方法
        int rows = printTaskMapper.updateStatusAndPrinter(task.getId(), TASK_STATUS_PENDING, printerName);

        log.info("handleReprint: 无MemberPrint记录，直接更新任务, taskId={}, printerName={}, status=0, rows={}",
                task.getId(), printerName, rows);

        return rows;
    }

    @Override
    public Map<String, Object> createVirtualOrder(Long sourceId, Long scenicId, Integer printerId) {
        return createVirtualOrder(sourceId, scenicId, printerId, null, null, null);
    }

    @Override
    public Map<String, Object> createVirtualOrder(Long sourceId, Long scenicId, Integer printerId, Boolean needEnhance) {
        return createVirtualOrder(sourceId, scenicId, printerId, needEnhance, null, null);
    }

    @Override
    public Map<String, Object> createVirtualOrder(Long sourceId, Long scenicId, Integer printerId, Boolean needEnhance, String printImgUrl) {
        return createVirtualOrder(sourceId, scenicId, printerId, needEnhance, printImgUrl, null);
    }

    @Override
    public Map<String, Object> createVirtualOrder(Long sourceId, Long scenicId, Integer printerId, Boolean needEnhance, String printImgUrl, Boolean needActualPayment) {
        // 1. 查询source记录
        SourceEntity source = sourceMapper.getEntity(sourceId);
        FaceSampleEntity faceSample = faceSampleMapper.getEntity(source.getFaceSampleId());
        if (source == null) {
            throw new BaseException("Source记录不存在");
        }
        if (!scenicId.equals(source.getScenicId())) {
            throw new BaseException("Source记录不属于该景区");
        }
        if (faceSample == null) {
            throw new BaseException("人脸样本不存在");
        }


        // 2. 生成虚拟用户ID（使用雪花算法生成一个不在用户表中的ID）
        Long virtualMemberId = SnowFlakeUtil.getLongId();
        log.info("创建虚拟用户0元订单: virtualMemberId={}, sourceId={}, scenicId={}", virtualMemberId, sourceId, scenicId);

        // 3. 创建人脸记录
        Long faceId = SnowFlakeUtil.getLongId();
        FaceEntity face = new FaceEntity();
        face.setId(faceId);
        face.setScenicId(scenicId);
        face.setMemberId(virtualMemberId);
        // 从source获取人脸URL
        face.setFaceUrl(faceSample.getFaceUrl());
        face.setCreateAt(new Date());
        faceMapper.add(face);
        log.info("创建人脸记录成功: faceId={}, virtualMemberId={}, origUrl={}", faceId, virtualMemberId, source.getUrl());

        // 4. 创建member_print记录（参考addUserPhotoFromSource方法）
        Integer memberPrintId = addUserPhoto(virtualMemberId, scenicId, (printImgUrl != null && !printImgUrl.isEmpty()) ? printImgUrl : source.getUrl(), faceId, sourceId);
        if (memberPrintId == null) {
            throw new BaseException("创建member_print记录失败");
        }
        log.info("创建member_print记录成功: memberPrintId={}, faceId={}", memberPrintId, faceId);

        // 设置打印数量为1
        setPhotoQuantity(virtualMemberId, scenicId, memberPrintId.longValue(), 1);

        // 5. 验证打印机
        if (printerId == null) {
            List<PrinterResp> printerList = printerMapper.listByScenicId(scenicId);
            if (printerList.isEmpty()) {
                throw new BaseException("该景区没有可用的打印机");
            }
            if (printerList.size() != 1) {
                throw new BaseException("请选择打印机");
            }
            printerId = printerList.getFirst().getId();
        }
        PrinterEntity printer = printerMapper.getById(printerId);
        if (printer == null) {
            throw new BaseException("打印机不存在");
        }
        if (printer.getStatus() != 1) {
            throw new BaseException("打印机已停用");
        }
        if (!printer.getScenicId().equals(scenicId)) {
            throw new BaseException("打印机不属于该景区");
        }

        // 6. 创建订单
        OrderEntity order = new OrderEntity();
        Long orderId = SnowFlakeUtil.getLongId();
        redisTemplate.opsForValue().set("printer_size:" + orderId, printer.getPreferPaper(), 60, TimeUnit.SECONDS);
        order.setId(orderId);
        order.setMemberId(virtualMemberId);
        order.setFaceId(faceId);
        order.setOpenId(""); // 虚拟用户没有openId
        order.setScenicId(scenicId);
        order.setType(3); // 照片打印类型

        // 设置打印机
        batchSetUserPhotoListToPrinter(virtualMemberId, scenicId, printerId);

        // 获取用户照片列表
        List<MemberPrintResp> userPhotoList = getUserPhotoList(virtualMemberId, scenicId, faceId);
        List<OrderItemEntity> orderItems = userPhotoList.stream().map(goods -> {
            OrderItemEntity orderItem = new OrderItemEntity();
            orderItem.setOrderId(orderId);
            orderItem.setGoodsId(Long.valueOf(goods.getId()));
            orderItem.setGoodsType(3);
            return orderItem;
        }).collect(Collectors.toList());

        boolean actualPayment = Boolean.TRUE.equals(needActualPayment);

        if (actualPayment) {
            // 需要实际支付：通过价格计算服务获取真实价格
            PriceCalculationRequest priceRequest = new PriceCalculationRequest();
            priceRequest.setUserId(virtualMemberId);
            priceRequest.setScenicId(scenicId);

            List<ProductItem> productItems = new ArrayList<>();
            ProductItem photoItem = new ProductItem();
            photoItem.setProductType(ProductType.PHOTO_PRINT);
            photoItem.setProductId(scenicId.toString());
            photoItem.setQuantity(1);
            photoItem.setPurchaseCount(1);
            photoItem.setScenicId(scenicId.toString());
            // 通过 source 的 deviceId 设置 attributeKeys
            SourceEntity priceSource = sourceRepository.getSource(sourceId);
            if (priceSource != null && priceSource.getDeviceId() != null) {
                photoItem.setAttributeKeys(List.of(String.valueOf(priceSource.getDeviceId())));
            }
            productItems.add(photoItem);

            priceRequest.setProducts(productItems);
            priceRequest.setAutoUseCoupon(false);
            priceRequest.setPreviewOnly(false);

            PriceCalculationResult priceResult = priceCalculationService.calculatePrice(priceRequest);

            order.setPrice(priceResult.getFinalAmount());
            order.setSlashPrice(priceResult.getOriginalAmount());
            order.setPayPrice(priceResult.getFinalAmount());
            order.setStatus(OrderStateEnum.UNPAID.getState());
            log.info("创建待支付虚拟订单: orderId={}, price={}", orderId, priceResult.getFinalAmount());

            // 将 needEnhance 存入 Redis，支付完成后 setUserIsBuyItem 可读取
            if (needEnhance != null) {
                redisTemplate.opsForValue().set("virtual_order_enhance:" + orderId, needEnhance.toString(), 24, TimeUnit.HOURS);
            }
        } else {
            // 虚拟0元购买：价格为0，直接标记已支付
            order.setPrice(BigDecimal.ZERO);
            order.setSlashPrice(BigDecimal.ZERO);
            order.setPayPrice(BigDecimal.ZERO);
            order.setStatus(OrderStateEnum.PAID.getState());
            order.setPayAt(new Date());
        }

        // 保存订单
        orderMapper.add(order);
        int addOrderItems = orderMapper.addOrderItems(orderItems);
        if (addOrderItems == NumberConstant.ZERO) {
            log.error("订单明细添加失败");
            throw new BaseException("订单添加失败");
        }

        log.info("创建虚拟订单成功: orderId={}, virtualMemberId={}, faceId={}, actualPayment={}", orderId, virtualMemberId, faceId, actualPayment);

        Map<String, Object> result = new HashMap<>();
        result.put("orderId", orderId);
        result.put("faceId", faceId);
        result.put("virtualMemberId", virtualMemberId);
        result.put("memberPrintId", memberPrintId);

        if (actualPayment) {
            if (order.getPayPrice().compareTo(BigDecimal.ZERO) <= 0) {
                // 计算后价格为0，直接走免费逻辑
                order.setStatus(OrderStateEnum.PAID.getState());
                order.setPayAt(new Date());
                orderMapper.updateOrder(order);
                log.info("待支付订单计算后价格为0，直接完成购买: orderId={}", orderId);
                result.put("needPay", false);
            } else {
                // 通过 Native 支付生成二维码
                IPayAdapter payAdapter = scenicService.getScenicPayAdapter(scenicId);
                if (payAdapter instanceof WxMpPayAdapter adapter) {
                    NativePayService nativePayService = new NativePayService.Builder().config(adapter.getConfig()).build();
                    PrepayRequest prepayRequest = new PrepayRequest();
                    prepayRequest.setAppid(adapter._config().getAppId());
                    prepayRequest.setMchid(adapter._config().getMerchantId());
                    prepayRequest.setDescription("照片打印");
                    prepayRequest.setOutTradeNo(String.valueOf(orderId));
                    prepayRequest.setNotifyUrl("https://zhentuai.com/api/mobile/wx/pay/v1/" + scenicId + "/payNotify");
                    Amount amount = new Amount();
                    amount.setTotal(order.getPayPrice().multiply(new BigDecimal(100)).intValue());
                    prepayRequest.setAmount(amount);
                    PrepayResponse prepayResponse = nativePayService.prepay(prepayRequest);
                    result.put("payCode", prepayResponse.getCodeUrl());
                } else {
                    throw new BaseException("该景区不支持 Native 支付");
                }
                result.put("needPay", true);
                result.put("price", order.getPayPrice());
            }
        } else {
            result.put("needPay", false);
        }

        // 无论是否需要支付，都立即触发购买后动作（打印等）
        // setUserIsBuyItem 内部通过 Redis 去重，支付回调到达时不会重复触发
        setUserIsBuyItem(virtualMemberId, memberPrintId.longValue(), orderId, needEnhance);
        log.info("触发购买后逻辑完成: orderId={}", orderId);

        return result;
    }

    @Override
    public Map<String, Object> createBatchVirtualOrder(List<Long> sourceIds, Long scenicId, Integer printerId, Boolean needEnhance, String printImgUrl, Boolean needActualPayment) {
        if (sourceIds == null || sourceIds.isEmpty()) {
            throw new BaseException("sourceIds不能为空");
        }

        // 1. 校验所有source并收集faceSample
        List<SourceEntity> sources = new ArrayList<>();
        FaceSampleEntity firstFaceSample = null;
        for (Long sourceId : sourceIds) {
            SourceEntity source = sourceMapper.getEntity(sourceId);
            if (source == null) {
                throw new BaseException("Source记录不存在: " + sourceId);
            }
            if (!scenicId.equals(source.getScenicId())) {
                throw new BaseException("Source记录不属于该景区: " + sourceId);
            }
            FaceSampleEntity faceSample = faceSampleMapper.getEntity(source.getFaceSampleId());
            if (faceSample == null) {
                throw new BaseException("人脸样本不存在, sourceId=" + sourceId);
            }
            if (firstFaceSample == null) {
                firstFaceSample = faceSample;
            }
            sources.add(source);
        }

        // 2. 生成一个虚拟用户 + 一条人脸记录
        Long virtualMemberId = SnowFlakeUtil.getLongId();
        Long faceId = SnowFlakeUtil.getLongId();
        FaceEntity face = new FaceEntity();
        face.setId(faceId);
        face.setScenicId(scenicId);
        face.setMemberId(virtualMemberId);
        face.setFaceUrl(firstFaceSample.getFaceUrl());
        face.setCreateAt(new Date());
        faceMapper.add(face);
        log.info("批量下单 - 创建虚拟用户: virtualMemberId={}, faceId={}, sourceCount={}", virtualMemberId, faceId, sourceIds.size());

        // 3. 为每个source创建member_print记录
        List<Integer> memberPrintIds = new ArrayList<>();
        for (SourceEntity source : sources) {
            String photoUrl = (printImgUrl != null && !printImgUrl.isEmpty()) ? printImgUrl : source.getUrl();
            Integer memberPrintId = addUserPhoto(virtualMemberId, scenicId, photoUrl, faceId, source.getId());
            if (memberPrintId == null) {
                throw new BaseException("创建member_print记录失败, sourceId=" + source.getId());
            }
            setPhotoQuantity(virtualMemberId, scenicId, memberPrintId.longValue(), 1);
            memberPrintIds.add(memberPrintId);
        }

        // 4. 验证打印机
        if (printerId == null) {
            List<PrinterResp> printerList = printerMapper.listByScenicId(scenicId);
            if (printerList.isEmpty()) {
                throw new BaseException("该景区没有可用的打印机");
            }
            if (printerList.size() != 1) {
                throw new BaseException("请选择打印机");
            }
            printerId = printerList.getFirst().getId();
        }
        PrinterEntity printer = printerMapper.getById(printerId);
        if (printer == null) {
            throw new BaseException("打印机不存在");
        }
        if (printer.getStatus() != 1) {
            throw new BaseException("打印机已停用");
        }
        if (!printer.getScenicId().equals(scenicId)) {
            throw new BaseException("打印机不属于该景区");
        }

        // 5. 创建订单
        OrderEntity order = new OrderEntity();
        Long orderId = SnowFlakeUtil.getLongId();
        redisTemplate.opsForValue().set("printer_size:" + orderId, printer.getPreferPaper(), 60, TimeUnit.SECONDS);
        order.setId(orderId);
        order.setMemberId(virtualMemberId);
        order.setFaceId(faceId);
        order.setOpenId("");
        order.setScenicId(scenicId);
        order.setType(3);

        batchSetUserPhotoListToPrinter(virtualMemberId, scenicId, printerId);

        List<MemberPrintResp> userPhotoList = getUserPhotoList(virtualMemberId, scenicId, faceId);
        List<OrderItemEntity> orderItems = userPhotoList.stream().map(goods -> {
            OrderItemEntity orderItem = new OrderItemEntity();
            orderItem.setOrderId(orderId);
            orderItem.setGoodsId(Long.valueOf(goods.getId()));
            orderItem.setGoodsType(3);
            return orderItem;
        }).collect(Collectors.toList());

        boolean actualPayment = Boolean.TRUE.equals(needActualPayment);

        if (actualPayment) {
            PriceCalculationRequest priceRequest = new PriceCalculationRequest();
            priceRequest.setUserId(virtualMemberId);
            priceRequest.setScenicId(scenicId);

            List<ProductItem> productItems = new ArrayList<>();
            ProductItem photoItem = new ProductItem();
            photoItem.setProductType(ProductType.PHOTO_PRINT);
            photoItem.setProductId(scenicId.toString());
            photoItem.setQuantity(1);
            photoItem.setPurchaseCount(sourceIds.size());
            photoItem.setScenicId(scenicId.toString());
            productItems.add(photoItem);

            priceRequest.setProducts(productItems);
            priceRequest.setAutoUseCoupon(false);
            priceRequest.setPreviewOnly(false);

            PriceCalculationResult priceResult = priceCalculationService.calculatePrice(priceRequest);

            order.setPrice(priceResult.getFinalAmount());
            order.setSlashPrice(priceResult.getOriginalAmount());
            order.setPayPrice(priceResult.getFinalAmount());
            order.setStatus(OrderStateEnum.UNPAID.getState());
            log.info("批量下单 - 待支付订单: orderId={}, price={}, count={}", orderId, priceResult.getFinalAmount(), sourceIds.size());

            if (needEnhance != null) {
                redisTemplate.opsForValue().set("virtual_order_enhance:" + orderId, needEnhance.toString(), 24, TimeUnit.HOURS);
            }
        } else {
            order.setPrice(BigDecimal.ZERO);
            order.setSlashPrice(BigDecimal.ZERO);
            order.setPayPrice(BigDecimal.ZERO);
            order.setStatus(OrderStateEnum.PAID.getState());
            order.setPayAt(new Date());
        }

        orderMapper.add(order);
        int addOrderItems = orderMapper.addOrderItems(orderItems);
        if (addOrderItems == NumberConstant.ZERO) {
            throw new BaseException("订单添加失败");
        }

        log.info("批量下单 - 订单创建成功: orderId={}, itemCount={}", orderId, orderItems.size());

        Map<String, Object> result = new HashMap<>();
        result.put("orderId", orderId);
        result.put("faceId", faceId);
        result.put("virtualMemberId", virtualMemberId);
        result.put("memberPrintIds", memberPrintIds);
        result.put("sourceIds", sourceIds);

        if (actualPayment) {
            if (order.getPayPrice().compareTo(BigDecimal.ZERO) <= 0) {
                order.setStatus(OrderStateEnum.PAID.getState());
                order.setPayAt(new Date());
                orderMapper.updateOrder(order);
                log.info("批量下单 - 价格为0直接完成: orderId={}", orderId);
                result.put("needPay", false);
            } else {
                IPayAdapter payAdapter = scenicService.getScenicPayAdapter(scenicId);
                if (payAdapter instanceof WxMpPayAdapter adapter) {
                    NativePayService nativePayService = new NativePayService.Builder().config(adapter.getConfig()).build();
                    PrepayRequest prepayRequest = new PrepayRequest();
                    prepayRequest.setAppid(adapter._config().getAppId());
                    prepayRequest.setMchid(adapter._config().getMerchantId());
                    prepayRequest.setDescription("照片打印 x" + sourceIds.size());
                    prepayRequest.setOutTradeNo(String.valueOf(orderId));
                    prepayRequest.setNotifyUrl("https://zhentuai.com/api/mobile/wx/pay/v1/" + scenicId + "/payNotify");
                    Amount amount = new Amount();
                    amount.setTotal(order.getPayPrice().multiply(new BigDecimal(100)).intValue());
                    prepayRequest.setAmount(amount);
                    PrepayResponse prepayResponse = nativePayService.prepay(prepayRequest);
                    result.put("payCode", prepayResponse.getCodeUrl());
                } else {
                    throw new BaseException("该景区不支持 Native 支付");
                }
                result.put("needPay", true);
                result.put("price", order.getPayPrice());
            }
        } else {
            result.put("needPay", false);
        }

        // 触发购买后逻辑（setUserIsBuyItem 内部遍历 orderItems 处理所有 memberPrint）
        setUserIsBuyItem(virtualMemberId, memberPrintIds.getFirst().longValue(), orderId, needEnhance);
        log.info("批量下单 - 购买后逻辑完成: orderId={}", orderId);

        return result;
    }

    @Override
    public PrinterEntity getByAccessKey(String accessKey) {
        if (accessKey == null) {
            throw new BaseException("accessKey不能为空");
        }
        PrinterEntity printer = printerMapper.findByAccessKey(accessKey);
        if (printer == null) {
            throw new BaseException("打印机不存在");
        }
        return printer;
    }

    @Override
    public Object getScenicBasicByAccessKey(String accessKey) {
        PrinterEntity printer = getByAccessKey(accessKey);
        if (printer.getScenicId() == null) {
            throw new BaseException("打印机未关联景区");
        }
        return scenicRepository.getScenicBasic(printer.getScenicId());
    }

    @Override
    public void openPrinter(String accessKey) {
        PrinterEntity printer = getByAccessKey(accessKey);
        printer.setStatus(1);
        printerMapper.update(printer);
    }

    @Override
    public void closePrinter(String accessKey) {
        PrinterEntity printer = getByAccessKey(accessKey);
        printer.setStatus(0);
        printerMapper.update(printer);
    }
}

