package com.ai.da.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.enums.CollectionLevel1TypeEnum;
import com.ai.da.common.enums.SingleOverallEnum;
import com.ai.da.common.enums.SysFileLevel2TypeEnum;
import com.ai.da.common.utils.*;
import com.ai.da.mapper.primary.DesignItemMapper;
import com.ai.da.mapper.primary.DesignMapper;
import com.ai.da.mapper.primary.UserLikeMapper;
import com.ai.da.mapper.primary.entity.*;
import com.ai.da.model.dto.*;
import com.ai.da.model.enums.ModelType;
import com.ai.da.model.vo.*;
import com.ai.da.python.PythonService;
import com.ai.da.python.vo.*;
import com.ai.da.service.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.collect.Lists;
import io.netty.util.internal.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SerializationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 服务实现类
 *
 * @author yanglei
 * @since 2022-09-30
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DesignItemServiceImpl extends ServiceImpl<DesignItemMapper, DesignItem> implements DesignItemService {

    private final CollectionElementService collectionElementService;
    private final DesignMapper designMapper;
    private final DesignItemMapper designItemMapper;
    private final DesignItemDetailService designItemDetailService;
    private final DesignItemDetailPrintService designItemDetailPrintService;
    private final ITDesignPythonOutfitService designPythonOutfitService;
    private final ITDesignPythonOutfitDetailService designPythonOutfitDetailService;
    private final LibraryService libraryService;
    private final LibraryModelPointService libraryModelPointService;
    private final MinioUtil minioUtil;
    private final PythonService pythonService;
    private final PanToneService panToneService;
    private final SysFileService sysFileService;
    private final UserLikeService userLikeService;
    private final UserLikeGroupService userLikeGroupService;
    private final WorkspaceService workspaceService;

    @Value("${minio.bucketName.modifiedSketch}")
    private String modifiedSketchBucket;
    @Value("${minio.bucketName.clothing}")
    private String clothingBucket;
    @Value("${minio.bucketName.partialDesign}")
    private String partialDesignBucket;
    @Value("${minio.endpoint}")
    private String minioEndpoint;
    @Autowired
    private RedisUtil redisUtil;
    @Resource
    private ProjectService projectService;

    @Override
    public Long saveOne(DesignItem designItem) {
        if (designItemMapper.insertDesignItem(designItem) <= 0) {
            throw new BusinessException("save.designItem.failed");
        }
        return designItem.getId();
    }

    @Override
    public int deleteByCollectionId(Long collectionId) {
        QueryWrapper<DesignItem> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("collection_id", collectionId);

        DesignItem designItem = new DesignItem();
        designItem.setCollectionId(0L);
        return designItemMapper.update(designItem, queryWrapper);
    }

    @Override
    public List<DesignItem> getByDesignId(Long designId) {
        QueryWrapper<DesignItem> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("design_id", designId);
        queryWrapper.eq("is_like", (byte) 0);
        return designItemMapper.selectList(queryWrapper);
    }

    @Override
    public void updateLikeStatus(Long designItemId, Byte hasLike) {
        QueryWrapper<DesignItem> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", designItemId);

        DesignItem designItem = new DesignItem();
        designItem.setHasLike(hasLike);
        designItemMapper.update(designItem, queryWrapper);
    }

    @Override
    public void updateDesignHighUrl(Long designItemId, String highUrl, String timeZone) {
        QueryWrapper<DesignItem> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", designItemId);

        DesignItem designItem = new DesignItem();
        designItem.setHighDesignUrl(highUrl);
        designItem.setUpdateDate(DateUtil.getByTimeZone(timeZone));
        designItemMapper.update(designItem, queryWrapper);
    }

    @Override
    public GetNextSysElementVO getNextSysElement(Long id, String level2Type, String operateType) {
        //校验
        if (!DesignPythonItem.SYS_HAIRSTYLE_SHOES_BODY.contains(level2Type)) {
            throw new BusinessException("unknown.type");
        }
        List<String> operateTypes = Arrays.asList("PREV", "NEXT");
        if (!operateTypes.contains(operateType)) {
            throw new BusinessException("unknown.operateType");
        }
        if (null == id) {
            throw new BusinessException("id.cannot.be.empty");
        }
        Long maxId = sysFileService.getMaxIdByLevel2Type(level2Type, null);
        Long minId = sysFileService.getMinIdByLevel2Type(level2Type, null);

        if (id > maxId || id < minId) {
            throw new BusinessException("the.id.value.is.out.of.range");
        }
        Long idValue = null;
        if ("PREV".equals(operateType)) {
            if (minId.equals(id)) {
                idValue = minId;
            } else {
                idValue = id - 1;
            }
        }
        if ("NEXT".equals(operateType)) {
            if (maxId.equals(id)) {
                idValue = maxId;
            } else {
                idValue = id + 1;
            }
        }
        SysFileVO sysFileVO = sysFileService.getById(idValue);
        if (Objects.nonNull(sysFileVO)) {
            return new GetNextSysElementVO(sysFileVO.getId(), level2Type, sysFileVO.getUrl());
        } else {
            return new GetNextSysElementVO();
        }
    }

    @Override
    public DesignCollectionItemVO designSingle(DesignSingleDTO designSingleDTO) {
        AuthPrincipalVo userInfo = UserContext.getUserHolder();
        DesignItem designItem = selectById(designSingleDTO.getDesignItemId());
        if (Objects.isNull(designItem)) {
            throw new BusinessException("designItem.not.found");
        }
        Design design = designMapper.selectById(designItem.getDesignId());
        if (Objects.isNull(design)) {
            throw new BusinessException("design.not.found");
        }
        DesignLibraryModelPointVO designLibraryModelPointVO = null;
        if (Objects.nonNull(design.getTemplateId())) {
            LibraryModelPoint modelPoint = libraryModelPointService.getById(design.getTemplateId());
            if (Objects.isNull(modelPoint)) {
                throw new BusinessException("modelPoint.not.found");
            }
            Library library = libraryService.getById(modelPoint.getRelationId());
            // ??和上面重复
            if (Objects.isNull(library)) {
                throw new BusinessException("library.not.found");
            }
            designLibraryModelPointVO = collectionElementService.calculateTemplatePoint(modelPoint, library.getHigh(), library.getWidth(), library.getUrl());
        }

        Set<String> newTypes = designSingleDTO.getClothes().stream().map(DesignSingleItemDTO::getType).collect(Collectors.toSet());
        //校验category
        validateCategory(newTypes, design.getSingleOverall(), design.getSwitchCategory());
        DesignPythonObjects objects = pythonService.covertDesignSingleParam(
                designSingleDTO, design.getSingleOverall(), design.getSwitchCategory(), designLibraryModelPointVO);
        pythonService.design(objects);
        //designSingle
        DesignCollectionItemVO response = saveSingleDesignItemAndDetail(objects, design.getId(), designSingleDTO.getDesignItemId(),
                design.getCollectionId(), userInfo, designSingleDTO.getTimeZone());
        // ??
        designItem.setDesignUrl(response.getDesignItemUrl());
        return response;
    }

    private LibraryModelPointVO resloveVoByModelPoint(LibraryModelPoint modelPoint, Library library) {
        LibraryModelPointVO libraryModelPointVO = CopyUtil.copyObject(modelPoint, LibraryModelPointVO.class);
        libraryModelPointVO.setTemplateId(modelPoint.getId());
        libraryModelPointVO.setHandLeft(JSON.parseObject(modelPoint.getHandLeft(), List.class));
        libraryModelPointVO.setHandRight(JSON.parseObject(modelPoint.getHandRight(), List.class));
        libraryModelPointVO.setShoulderRight(JSON.parseObject(modelPoint.getShoulderRight(), List.class));
        libraryModelPointVO.setShoulderLeft(JSON.parseObject(modelPoint.getShoulderLeft(), List.class));
        libraryModelPointVO.setWaistbandRight(JSON.parseObject(modelPoint.getWaistbandRight(), List.class));
        libraryModelPointVO.setWaistbandLeft(JSON.parseObject(modelPoint.getWaistbandLeft(), List.class));
        return libraryModelPointVO;
    }

    @Override
    public String printDot(DesignSingleDTO designSingleDTO) {
        DesignItem designItem = selectById(designSingleDTO.getDesignItemId());
        if (Objects.isNull(designItem)) {
            throw new BusinessException("designItem.not.found");
        }
        Design design = designMapper.selectById(designItem.getDesignId());
        if (Objects.isNull(design)) {
            throw new BusinessException("design.not.found");
        }
        DesignLibraryModelPointVO designLibraryModelPointVO = null;
        if (Objects.nonNull(design.getTemplateId())) {
            LibraryModelPoint modelPoint = libraryModelPointService.getById(design.getTemplateId());
            if (Objects.isNull(modelPoint)) {
                throw new BusinessException("modelPoint.not.found");
            }
            Library library = libraryService.getById(modelPoint.getRelationId());
            if (Objects.isNull(library)) {
                throw new BusinessException("library.not.found");
            }
            designLibraryModelPointVO = collectionElementService.calculateTemplatePoint(modelPoint, library.getHigh(), library.getWidth(), library.getUrl());
        }
        Set<String> newTypes = designSingleDTO.getClothes().stream().map(DesignSingleItemDTO::getType).collect(Collectors.toSet());
        //校验category
        validateCategory(newTypes, design.getSingleOverall(), design.getSwitchCategory());
        DesignPythonObjects objects = pythonService.covertDesignSingleParam(
                designSingleDTO, design.getSingleOverall(), design.getSwitchCategory(), designLibraryModelPointVO);
        pythonService.design(objects);

        return objects.getObjects().get(0).getBasic().getSave_name();
    }

    private void validateCategory(Set<String> newTypes, String singleOverall, String switchCategory) {
        if (SingleOverallEnum.SINGLE.getRealName().equals(singleOverall)) {
            if (newTypes.size() > 1) {
                throw new BusinessException("wrong.clothes.type");
            }
            //一个的时候
            if (!switchCategory.equals(CollectionUtil.newArrayList(newTypes).get(0))) {
                throw new BusinessException("wrong.clothes.type");
            }
        }
    }

    // todo check这个方法是否还在用
    private DesignCollectionItemVO saveSingleDesignItemAndDetail(DesignPythonObjects pythonObjects
            , Long designId, Long designItemId, Long collectionId, AuthPrincipalVo userInfo, String timeZone) {
        DesignCollectionItemVO response = new DesignCollectionItemVO();
        response.setDesignItemId(designItemId);

        DesignItem designItem = new DesignItem();
        String url = pythonObjects.getObjects().get(0).getBasic().getSave_name();
        designItem.setUpdateDate(DateUtil.getByTimeZone(timeZone));
        designItem.setDesignUrl(url);
        designItem.setId(designItemId);
        response.setDesignItemUrl(url);
        //更新item
        updateById(designItem);

        // 查询现有的记录，用于 saveOrUpdate
        List<DesignItemDetail> existingDetails = designItemDetailService.selectByDesignItemId(designItemId);
        // 创建 Map，以 priority 为 key，方便快速查找
        Map<Integer, DesignItemDetail> existingDetailMap = existingDetails.stream()
                .filter(detail -> Objects.nonNull(detail.getPriority()))
                .collect(Collectors.toMap(DesignItemDetail::getPriority, detail -> detail, (old, newVal) -> old));
        Map<String, Deque<DesignItemDetail>> detailBuckets = buildDetailTypeBuckets(existingDetails);
        Set<Long> reusedDetailIds = new HashSet<>();

        List<DesignItemDetail> designItemDetails = Lists.newArrayList();
        pythonObjects.getObjects().get(0).getItems().forEach(detail -> {
            if (null == detail) {
                return;
            }
            Integer priority = SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType()) ? 0 : detail.getPriority();

            DesignItemDetail designItemDetail = reuseDesignItemDetail(priority, detail.getType(),
                    existingDetailMap, detailBuckets, reusedDetailIds);
            if (Objects.nonNull(designItemDetail)) {
                designItemDetail.setUpdateDate(DateUtil.getByTimeZone(timeZone));
            } else {
                designItemDetail = new DesignItemDetail();
                designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
            }

            // 设置/更新字段
            designItemDetail.setAccountId(userInfo.getId());
            designItemDetail.setDesignId(designId);
            designItemDetail.setDesignItemId(designItemId);
            designItemDetail.setCollectionElementId(detail.getElementId());
            designItemDetail.setType(detail.getType());
            designItemDetail.setPriority(priority);
            if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
                designItemDetail.setPath(detail.getBody_path());
                //BODY不关联businessId
                designItemDetail.setBusinessId(0L);
            } else {
                // 非BODY类型使用path字段
                designItemDetail.setPath(detail.getPath());
            }
            designItemDetail.setColor(detail.getColor());
            designItemDetail.setIconPath(detail.getIcon());
            // todo
            DesignPythonItemPrint printObject = detail.getPrint().getSingle();
            designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath());
            designItemDetail.setPrintJson(JSON.toJSONString(printObject));
            designItemDetails.add(designItemDetail);
        });

        // 逻辑删除未复用的旧记录
        existingDetails.stream()
                .filter(existingDetail -> Objects.nonNull(existingDetail.getId()) && !reusedDetailIds.contains(existingDetail.getId()))
                .forEach(existingDetail -> {
                    UpdateWrapper<DesignItemDetail> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.eq("id", existingDetail.getId());
                    updateWrapper.set("is_deleted", (byte) 1);
                    updateWrapper.set("update_date", DateUtil.getByTimeZone(timeZone));
                    designItemDetailService.update(null, updateWrapper);
                });

        designItemDetailService.saveOrUpdateBatch(designItemDetails);
        //封装item信息
        List<DesignCollectionItemVO> designCollectionItems = CopyUtil.copyList(getByDesignId(designId)
                , DesignCollectionItemVO.class, (o, d) -> {
                    d.setDesignItemId(o.getId());
                    d.setDesignItemUrl(o.getDesignUrl());
                });
        return response;
    }

    public List<TDesignPythonOutfitDetail> saveDesignSingleItemDetailAndLayers(DesignPythonObjects pythonObjects
            , Long designId, Long designItemId, Long userId
            , JSONObject outfit, String timeZone, List<DesignSingleItemDTO> designSingleItemDTOList
            , Map<String, List<String>> priorityAndUndividedLayer
            , boolean changeModelFlag
            , Long modelId, String modelType, boolean isSingleCollectionFlag) {

        DesignItem designItem = new DesignItem();
//        String url = pythonObjects.getObjects().get(0).getBasic().getSave_name();
        designItem.setUpdateDate(DateUtil.getByTimeZone(timeZone));
        designItem.setDesignUrl(outfit.getString("synthesis_url"));
        designItem.setId(designItemId);
        if (changeModelFlag) {
            designItem.setModelId(modelId);
            designItem.setModelType(modelType);
        }
        // 1、更新designItem
//        updateById(designItem);
        updateDesignItem(designItem);

        // 2、查询现有的记录，用于 saveOrUpdate
        List<DesignItemDetail> existingDetails = designItemDetailService.selectByDesignItemId(designItemId);
        // 创建 Map，以 priority 为 key，方便快速查找
        Map<Integer, DesignItemDetail> existingDetailMap = existingDetails.stream()
                .filter(detail -> Objects.nonNull(detail.getPriority()))
                .collect(Collectors.toMap(DesignItemDetail::getPriority, detail -> detail, (old, newVal) -> old));
        Map<String, Deque<DesignItemDetail>> detailBuckets = buildDetailTypeBuckets(existingDetails);
        Set<Long> reusedDetailIds = new HashSet<>();

        List<DesignItemDetail> designItemDetails = Lists.newArrayList();
        // 3、保存新的designItemDetail（使用 saveOrUpdate 方式）
        pythonObjects.getObjects().get(0).getItems().forEach(detail -> {
            if (null == detail) {
                return;
            }
            Integer priority = SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType()) ? 0 : detail.getPriority();

            DesignItemDetail designItemDetail = reuseDesignItemDetail(priority, detail.getType(),
                    existingDetailMap, detailBuckets, reusedDetailIds);
            if (Objects.nonNull(designItemDetail)) {
                designItemDetail.setUpdateDate(DateUtil.getByTimeZone(timeZone));
            } else {
                designItemDetail = new DesignItemDetail();
                designItemDetail.setCreateDate(DateUtil.getByTimeZone(timeZone));
            }

            // 设置/更新字段
            designItemDetail.setAccountId(userId);
            designItemDetail.setDesignId(designId);
            designItemDetail.setDesignItemId(designItemId);
            designItemDetail.setCollectionElementId(detail.getElementId());
            designItemDetail.setType(detail.getType());
            designItemDetail.setPriority(priority);
            if (SysFileLevel2TypeEnum.BODY.getRealName().equals(detail.getType())) {
                designItemDetail.setPath(detail.getBody_path());
                //BODY不关联businessId
                designItemDetail.setBusinessId(0L);
            } else {
                // 非BODY类型使用path字段
                designItemDetail.setPath(detail.getPath());
            }
            designItemDetail.setColor(detail.getColor());
            designItemDetail.setIconPath(detail.getIcon());
//            designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getType().toLowerCase()));
            if (!detail.getType().equals("Body")) {
                designItemDetail.setUndividedLayer(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(0));
                designItemDetail.setUndividedLayerWithSinglePrint(priorityAndUndividedLayer.get(detail.getPriority().toString()).get(1));

            }
            // 印花存储在design_item_detail_print表中 这里还要存吗？
//            DesignPythonItemPrint printObject = detail.getPrintToPython();
//            designItemDetail.setPrintPath(Objects.isNull(printObject) ? "" : printObject.getPath());
//            当有多个印花后，返回的printObject太长，导致存储到数据库时报错
//            designItemDetail.setPrintJson(JSON.toJSONString(printObject));

            designItemDetail.setPartialDesign(Objects.isNull(detail.getPrint()) ? null : detail.getPrint().getPartial());
            designItemDetails.add(designItemDetail);
        });

        // 逻辑删除未复用的旧记录
        existingDetails.stream()
                .filter(existingDetail -> Objects.nonNull(existingDetail.getId()) && !reusedDetailIds.contains(existingDetail.getId()))
                .forEach(existingDetail -> {
                    UpdateWrapper<DesignItemDetail> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.eq("id", existingDetail.getId());
                    updateWrapper.set("is_deleted", (byte) 1);
                    updateWrapper.set("update_date", DateUtil.getByTimeZone(timeZone));
                    designItemDetailService.update(null, updateWrapper);
                });

        // businessId 来自t_sys_file或者t_library
        designItemDetailService.saveOrUpdateBatch(designItemDetails);

        // 4、保存印花信息到designItemDetailPrint（使用 saveOrUpdate 方式）
        List<DesignItemDetailPrint> designItemDetailPrints = setDesignItemDetailPrintList(designItemDetails, designSingleItemDTOList, timeZone);
        designItemDetailPrintService.saveOrUpdateBatch(designItemDetailPrints);

        // 5、覆盖designPythonOutfit表中的模特全身图
        TDesignPythonOutfit designPythonOutfit = new TDesignPythonOutfit();
        designPythonOutfit.setDesignUrl(outfit.getString("synthesis_url"));
        designPythonOutfit.setId(designPythonOutfitService.getByDesignItemId(designItemId).getId());
        designPythonOutfit.setUpdateDate(LocalDateTime.now(ZoneId.of(timeZone)));
        designPythonOutfitService.updateById(designPythonOutfit);

        // 6、查询现有的图层信息，用于 saveOrUpdate
        List<TDesignPythonOutfitDetail> existingLayers = designPythonOutfitDetailService.getDetailByDesignPythonOutfitId(designPythonOutfit.getId());
        // 创建 Map，以 priority 为 key，方便快速查找
        Map<Integer, TDesignPythonOutfitDetail> existingLayerMap = existingLayers.stream()
                .filter(layer -> Objects.nonNull(layer.getPriority()))
                .collect(Collectors.toMap(layer -> Math.abs(layer.getPriority()), layer -> layer, (old, newVal) -> old));
        Map<String, Deque<TDesignPythonOutfitDetail>> layerBuckets = buildOutfitCategoryBuckets(existingLayers);
        Set<Long> reusedLayerIds = new HashSet<>();

        // 7、将新生成的图层信息存入designPythonOutfitDetail表（使用 saveOrUpdate 方式）
        JSONArray layers = outfit.getJSONArray("layers");

        Map<Integer, List<Long>> priorityOffset = null;
        if (!isSingleCollectionFlag) {
            priorityOffset = designSingleItemDTOList.stream()
                    .collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset));
        }
        List<TDesignPythonOutfitDetail> list = new ArrayList<>();
        for (int i = 0; i < layers.size(); i++) {
            JSONObject jsonObject = layers.getJSONObject(i);
            Integer priority = (Integer) jsonObject.get("priority");

            TDesignPythonOutfitDetail designPythonOutfitDetail = reuseOutfitDetail(priority,
                    jsonObject.getString("image_category"), existingLayerMap, layerBuckets, reusedLayerIds);
            if (Objects.nonNull(designPythonOutfitDetail)) {
                designPythonOutfitDetail.setUpdateDate(LocalDateTime.now(ZoneId.of(timeZone)));
            } else {
                designPythonOutfitDetail = new TDesignPythonOutfitDetail();
                designPythonOutfitDetail.setCreateDate(LocalDateTime.now(ZoneId.of(timeZone)));
            }

            // 设置/更新字段
            designPythonOutfitDetail.setDesignId(designId);
            designPythonOutfitDetail.setDesignPythonOutfitId(designPythonOutfit.getId());
            designPythonOutfitDetail.setPosition(jsonObject.getString("position"));
            designPythonOutfitDetail.setImageUrl(jsonObject.getString("image_url"));
            designPythonOutfitDetail.setImageSize(jsonObject.getString("image_size"));
            designPythonOutfitDetail.setImageCategory(jsonObject.getString("image_category"));
            designPythonOutfitDetail.setMaskUrl(jsonObject.getString("mask_url"));
            designPythonOutfitDetail.setScale(Objects.isNull(jsonObject.getString("resize_scale")) ? "1.0" : jsonObject.getString("resize_scale"));
            designPythonOutfitDetail.setUserId(userId);
            if (priorityOffset != null && !priorityOffset.isEmpty()){
                designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(priority))));
            }
            designPythonOutfitDetail.setPriority(priority);

            list.add(designPythonOutfitDetail);
        }

        // 逻辑删除未复用的旧记录
        existingLayers.stream()
                .filter(existingLayer -> Objects.nonNull(existingLayer.getId()) && !reusedLayerIds.contains(existingLayer.getId()))
                .forEach(existingLayer -> {
                    UpdateWrapper<TDesignPythonOutfitDetail> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.eq("id", existingLayer.getId());
                    updateWrapper.set("is_deleted", 1);
                    updateWrapper.set("update_date", LocalDateTime.now(ZoneId.of(timeZone)));
                    designPythonOutfitDetailService.update(null, updateWrapper);
                });

        designPythonOutfitDetailService.saveOrUpdateBatch(list);

        return list;
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateDesignItem(DesignItem designItem) {
        // 拆分长事务为小事务
        updateById(designItem);
    }

    public List<TDesignPythonOutfitDetail> setTDesignPythonOutfitDetailList(JSONArray layers, Long designId,
                                                                            Long designPythonOutfitId, Long userId,
                                                                            Map<Integer, List<Long>> priorityOffset) {
        // 设置图层信息;
        List<TDesignPythonOutfitDetail> list = new ArrayList<>();
        for (int i = 0; i < layers.size(); i++) {
            JSONObject jsonObject = layers.getJSONObject(i);
            TDesignPythonOutfitDetail designPythonOutfitDetail = new TDesignPythonOutfitDetail();
            designPythonOutfitDetail.setDesignId(designId);
            designPythonOutfitDetail.setDesignPythonOutfitId(designPythonOutfitId);
            designPythonOutfitDetail.setPosition(jsonObject.getString("position"));
            designPythonOutfitDetail.setImageUrl(jsonObject.getString("image_url"));
            designPythonOutfitDetail.setImageSize(jsonObject.getString("image_size"));
            designPythonOutfitDetail.setImageCategory(jsonObject.getString("image_category"));
            designPythonOutfitDetail.setMaskUrl(jsonObject.getString("mask_url"));
            designPythonOutfitDetail.setScale(Objects.isNull(jsonObject.getString("resize_scale")) ? "1.0" : jsonObject.getString("resize_scale"));
            designPythonOutfitDetail.setUserId(userId);
            if (priorityOffset != null && !priorityOffset.isEmpty()) {
                designPythonOutfitDetail.setOffset(String.valueOf(priorityOffset.get(Math.abs(Integer.parseInt(jsonObject.getString("priority"))))));
            }
            designPythonOutfitDetail.setPriority((Integer) jsonObject.get("priority"));
            designPythonOutfitDetail.setCreateDate(LocalDateTime.now());
            list.add(designPythonOutfitDetail);
        }
        return list;
    }

    private Map<String, Deque<DesignItemDetail>> buildDetailTypeBuckets(List<DesignItemDetail> details) {
        return details.stream()
                .collect(Collectors.groupingBy(DesignItemDetail::getType,
                        Collectors.collectingAndThen(Collectors.toList(), list -> {
                            list.sort(Comparator
                                    .comparingInt(this::extractDetailPriorityForSort)
                                    .thenComparing(DesignItemDetail::getId, Comparator.nullsLast(Long::compareTo)));
                            return new ArrayDeque<>(list);
                        })));
    }

    private DesignItemDetail reuseDesignItemDetail(Integer priority, String type,
                                                   Map<Integer, DesignItemDetail> priorityMap,
                                                   Map<String, Deque<DesignItemDetail>> typeBuckets,
                                                   Set<Long> reusedIds) {
        DesignItemDetail reused = null;
        if (Objects.nonNull(priority)) {
            reused = priorityMap.remove(priority);
        }
        if (Objects.nonNull(reused)) {
            markReusedDetail(reused, reusedIds);
            return reused;
        }
        Deque<DesignItemDetail> bucket = typeBuckets.get(type);
        while (bucket != null && !bucket.isEmpty()) {
            DesignItemDetail candidate = bucket.pollFirst();
            if (markReusedDetail(candidate, reusedIds)) {
                Integer candidatePriority = candidate.getPriority();
                if (Objects.nonNull(candidatePriority)) {
                    priorityMap.remove(candidatePriority, candidate);
                }
                return candidate;
            }
        }
        return null;
    }

    private Map<String, Deque<TDesignPythonOutfitDetail>> buildOutfitCategoryBuckets(List<TDesignPythonOutfitDetail> details) {
        return details.stream()
                .collect(Collectors.groupingBy(TDesignPythonOutfitDetail::getImageCategory,
                        Collectors.collectingAndThen(Collectors.toList(), list -> {
                            list.sort(Comparator
                                    .comparingInt(this::extractOutfitPriorityForSort)
                                    .thenComparing(TDesignPythonOutfitDetail::getId, Comparator.nullsLast(Long::compareTo)));
                            return new ArrayDeque<>(list);
                        })));
    }

    private TDesignPythonOutfitDetail reuseOutfitDetail(Integer priority, String imageCategory,
                                                        Map<Integer, TDesignPythonOutfitDetail> priorityMap,
                                                        Map<String, Deque<TDesignPythonOutfitDetail>> categoryBuckets,
                                                        Set<Long> reusedIds) {
        TDesignPythonOutfitDetail reused = null;
        if (Objects.nonNull(priority)) {
            reused = priorityMap.remove(Math.abs(priority));
        }
        if (Objects.nonNull(reused)) {
            markReusedLayer(reused, reusedIds);
            return reused;
        }
        Deque<TDesignPythonOutfitDetail> bucket = categoryBuckets.get(imageCategory);
        while (bucket != null && !bucket.isEmpty()) {
            TDesignPythonOutfitDetail candidate = bucket.pollFirst();
            if (markReusedLayer(candidate, reusedIds)) {
                Integer candidatePriority = candidate.getPriority();
                if (Objects.nonNull(candidatePriority)) {
                    priorityMap.remove(Math.abs(candidatePriority), candidate);
                }
                return candidate;
            }
        }
        return null;
    }

    private boolean markReusedDetail(DesignItemDetail detail, Set<Long> reusedIds) {
        if (Objects.isNull(detail)) {
            return false;
        }
        Long id = detail.getId();
        return Objects.isNull(id) || reusedIds.add(id);
    }

    private boolean markReusedLayer(TDesignPythonOutfitDetail detail, Set<Long> reusedIds) {
        if (Objects.isNull(detail)) {
            return false;
        }
        Long id = detail.getId();
        return Objects.isNull(id) || reusedIds.add(id);
    }

    private int safePriorityValue(Integer priority) {
        return Objects.isNull(priority) ? Integer.MAX_VALUE : priority;
    }

    private int safeAbsPriorityValue(Integer priority) {
        return Objects.isNull(priority) ? Integer.MAX_VALUE : Math.abs(priority);
    }

    private int extractDetailPriorityForSort(DesignItemDetail detail) {
        return safePriorityValue(detail.getPriority());
    }

    private int extractOutfitPriorityForSort(TDesignPythonOutfitDetail detail) {
        return safeAbsPriorityValue(detail.getPriority());
    }

    private DesignItem selectById(Long id) {
        QueryWrapper<DesignItem> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        return designItemMapper.selectOne(queryWrapper);
    }

    private Boolean exists(Long id) {
        QueryWrapper<DesignItem> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        return designItemMapper.exists(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DesignSingleVO designSingleIncludeLayers(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {
        // 记录入参 base64数据太长，所以这里去掉
        DesignSingleIncludeLayersDTO clone = SerializationUtils.clone(designSingleIncludeLayersDTO);
        clone.getDesignSingleItemDTOList().forEach(i -> {
            // 渐变色
            if (!Objects.isNull(i.getGradient()) && !StringUtil.isNullOrEmpty(i.getGradient().getColorImg())) {
                log.info("set gradient colorImage为空，便于日志打印");
                i.getGradient().setColorImg(null);
            }
            // 画笔修改过的sketch
            if (!StringUtil.isNullOrEmpty(i.getSketchString())) {
                log.info("set sketchString为空，便于日志打印");
                i.setSketchString(null);
            }
            // 标注过的mask
            if (!StringUtil.isNullOrEmpty(i.getMaskUrl())) {
                log.info("set labelingMask为空，便于日志打印");
                i.setMaskUrl(null);
            }
            if (!Objects.isNull(i.getPartialDesign()) &&
                    !StringUtil.isNullOrEmpty(i.getPartialDesign().getPartialDesignBase64())) {
                log.info("set partialDesignBase64为空，便于日志打印");
                i.getPartialDesign().setPartialDesignBase64(null);
            }
        });

        log.info("designSingle request入参  ==>  " + JSONObject.toJSONString(clone));

        Long userId = UserContext.getUserHolder().getId();
        DesignItem designItem = selectById(designSingleIncludeLayersDTO.getDesignItemId());
        if (Objects.isNull(designItem)) {
            throw new BusinessException("designItem.not.found");
        }
        Design design = designMapper.selectById(designItem.getDesignId());
        if (Objects.isNull(design)) {
            throw new BusinessException("design.not.found");
        }

        DesignLibraryModelPointVO designLibraryModelPointVO = null;
        // overall模式下 设置模特点位
        boolean changeModelFlag = false;
        Long modelId = null;
        String modelType = null;
        Map<Integer, List<Long>> priorityOffset = new HashMap<>();
        // 单品设计标志
        boolean isSingleCollectionFlag = true;
        if (design.getSingleOverall().equals("overall")) {
            isSingleCollectionFlag = false;
            // 优先级 1、判断当前入参中是否有model数据  无 -> 2、判断design item 中是否有model数据 无 -> 3、从design表中拿model数据 仍然没有 报错
            if (!Objects.isNull(designSingleIncludeLayersDTO.getModelId()) && !StringUtil.isNullOrEmpty(designSingleIncludeLayersDTO.getModelType())) {
                modelId = designSingleIncludeLayersDTO.getModelId();
                modelType = designSingleIncludeLayersDTO.getModelType();
                changeModelFlag = true;
            } else if (!Objects.isNull(designItem.getModelId()) && !StringUtil.isNullOrEmpty(designItem.getModelType())) {
                modelId = designItem.getModelId();
                modelType = designItem.getModelType();
            } else if (!Objects.isNull(design.getTemplateId()) && !StringUtil.isNullOrEmpty(design.getModelType())) {
                modelId = design.getTemplateId();
                modelType = design.getModelType();
            } else {
                // 报错
                throw new BusinessException("model.not.found");
            }

            String modelUrl;
            Integer high;
            Integer width;
            if (modelType.equals(ModelType.SYSTEM.getValue())) {
                SysFileVO sysFile = sysFileService.getById(modelId);
                if (Objects.isNull(sysFile)) {
                    throw new BusinessException("model.not.found");
                }
                modelUrl = sysFile.getUrl();
                high = 700;
                width = 320;
            } else if (modelType.equals(ModelType.LIBRARY.getValue())) {
                Library libFile = libraryService.getById(modelId);
                if (Objects.isNull(libFile)) {
                    throw new BusinessException("model.not.found");
                }
                modelUrl = libFile.getUrl();
                high = libFile.getHigh();
                width = libFile.getWidth();
            } else {
                throw new BusinessException("unknown.modelType");
            }
            LibraryModelPoint modelPoint = libraryModelPointService.getByRelationId(modelId, modelType);
            if (Objects.isNull(modelPoint)) {
                throw new BusinessException("modelPoint.not.found");
            }
            designLibraryModelPointVO = collectionElementService.calculateTemplatePoint(modelPoint, high, width, modelUrl);
            // 通过priority将offset关联到layers
            try {
                priorityOffset = designSingleIncludeLayersDTO.getDesignSingleItemDTOList().stream()
                        .collect(Collectors.toMap(DesignSingleItemDTO::getPriority, DesignSingleItemDTO::getOffset));
            } catch (IllegalStateException e) {
                // priority重复
                log.info("服装的priority重复");
                throw new BusinessException("priority.cannot.be.repeated");
            }
        }

        // 画笔修改的sketch截图 上传后替换path
        // 由于用户在系统或自己上传sketch的基础上修改过的衣服，再次修改后，第一次修改的衣服不会回到某个池子被再次利用，
        // 所以，这里选择使用system或collection相同的地址，只是放在不同的桶，这样能保证图片服务器上,类似的sketch不会存在很多
        sketchBase64ToPath(designSingleIncludeLayersDTO, userId);

        // 将标注后的mask上传到minio,并将minio地址传给python端
        // todo 如果当前design使用的仍是旧的mask 则置maskUrl为null
        // 1、查询当前design item在不在history中
        UserLike userLikeDesign = userLikeService.getByDesignItemId(designItem.getId());
        Boolean setNull = Boolean.FALSE;
        if (!Objects.isNull(userLikeDesign) && userLikeDesign.getConverted() == 0) {
            setNull = Boolean.TRUE;
        }
        maskBase64ToPath(designSingleIncludeLayersDTO, setNull);
//        maskBase64ToPath(designSingleIncludeLayersDTO, Boolean.TRUE);

        partialDesignBase64ToImage(designSingleIncludeLayersDTO, userId, designSingleIncludeLayersDTO.getIsPreview());

        // 组装入参
        DesignPythonObjects objects = pythonService.covertDesignSingleParam(
                designSingleIncludeLayersDTO, design.getSingleOverall(),
                design.getSwitchCategory(), designLibraryModelPointVO,
                designSingleIncludeLayersDTO.getIsPreview() ? "preview" : "submit");
        // design
        JSONObject jsonObject = pythonService.designNew(objects);
        // preview -> 不存数据库    submit -> 存数据库
        List<TDesignPythonOutfitDetail> tDesignPythonOutfitDetails;
        JSONObject data = jsonObject.getJSONObject("data");
        if (data == null || data.toJSONString().equals("{}")) {
            throw new BusinessException("python response data is null");
        }
        JSONObject outfit = data.getJSONObject("0");

        JSONArray layers = outfit.getJSONArray("layers");
        Map<String, List<String>> priorityAndUndividedLayer = setPriorityAndUndividedLayer(layers);
        if (!designSingleIncludeLayersDTO.getIsPreview()) {
            // 更新及保存图层信息
            tDesignPythonOutfitDetails = saveDesignSingleItemDetailAndLayers(objects, design.getId(), designSingleIncludeLayersDTO.getDesignItemId()
                    , userId, outfit, designSingleIncludeLayersDTO.getTimeZone()
                    , designSingleIncludeLayersDTO.getDesignSingleItemDTOList()
                    , priorityAndUndividedLayer, changeModelFlag, modelId, modelType, isSingleCollectionFlag);

            saveCollectionElement(designSingleIncludeLayersDTO);
        } else {
            tDesignPythonOutfitDetails = setTDesignPythonOutfitDetailList(layers, designItem.getDesignId(), null, userId, priorityOffset);
        }

        List<DesignPythonOutfitVO> detailsVO = new ArrayList<>();
        tDesignPythonOutfitDetails.forEach(detail -> {
            String type = detail.getImageCategory().split("_")[0];
            detailsVO.add(designPythonOutfitDetailService.convertToDesignPythonOutfitVO(detail, null));
        });

        TDesignPythonOutfit designPythonOutfit = designPythonOutfitService.getByDesignItemId(designSingleIncludeLayersDTO.getDesignItemId());
//        Assert.notNull(designItem, "design item detail layers does not exists!");

        // 如果当前item被like过，需要更新t_user_like表和t_user_like_group表
        if (!designSingleIncludeLayersDTO.getIsPreview()) {
            updateUserLikeDate(designSingleIncludeLayersDTO.getDesignItemId(), designSingleIncludeLayersDTO.getTimeZone());
            // 更新项目更新时间
            if (Objects.nonNull(designSingleIncludeLayersDTO.getProjectId())) {
                projectService.modifyProjectUpdateTime(designSingleIncludeLayersDTO.getProjectId());
            } else {
                log.error("design single projectId传入空值，导致项目修改时间没有更新");
            }
        } else {
            updateUserLikeConvertStatus(designSingleIncludeLayersDTO.getDesignItemId(), designSingleIncludeLayersDTO.getTimeZone());
        }

        return assembleDesignSingleResponse(designItem.getId(),
                minioUtil.getPreSignedUrl(designPythonOutfit.getDesignUrl(), 24 * 60),
                outfit.getString("synthesis_url"),
                designSingleIncludeLayersDTO.getDesignSingleItemDTOList(),
                detailsVO,
                design.getSingleOverall(),
                priorityAndUndividedLayer);
    }

    // 方法1：仅查询（无事务）
    public DesignItem getDesignItem(Long designItemId) {
        return selectById(designItemId); // 普通查询，不加锁
    }

    private void sketchBase64ToPath(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO, Long userId) {
        designSingleIncludeLayersDTO.getDesignSingleItemDTOList().forEach(item -> {
            // 如果sketch截图不为空，则将该截图上传，并替换path
            if (!StringUtil.isNullOrEmpty(item.getSketchString())) {
                if (StringUtil.isNullOrEmpty(item.getPath())) {
                    throw new BusinessException("path.cannot.be.empty");
                }
                String sourcePath = item.getPath();
                String path = sourcePath.substring(sourcePath.lastIndexOf("/") + 1, sourcePath.lastIndexOf("."));
                // 路径组成：用户id + sketchboard + temp | modified + 原图所在不同桶的原路径
                if (designSingleIncludeLayersDTO.getIsPreview()) {
                    path = userId + "/sketchboard/temp/" + path;
                } else {
                    path = userId + "/sketchboard/modified/" + path;
                }

                // 将原图地址作为修改后的图片地址，放在不同的桶
                String newPath = minioUtil.base64UploadToPath(item.getSketchString(), modifiedSketchBucket, path);
                if (StringUtil.isNullOrEmpty(newPath)) {
                    log.error("修改过的sketch图片上传失败");
                    throw new BusinessException("image.modify.failed");
                }
                item.setPath(newPath);
            }
        });
    }

    private void maskBase64ToPath(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO, Boolean setNull) {
        designSingleIncludeLayersDTO.getDesignSingleItemDTOList().forEach(item -> {
            // 如果sketch截图不为空，则将该截图上传，并替换path
            if (setNull) {
                log.info("服装类型为{}， set maskUrl为null", item.getType());
                item.setMaskUrl(null);
            } else {
                if (!StringUtil.isNullOrEmpty(item.getMaskUrl())) {
                    log.info("服装{} 的maskUrl不为null", item.getType());
                    // 由于前端不好处理这块，所以，当mask没有做任何修改的时候，仍然会传原始mask的minio地址
                    if (!item.getMaskUrl().startsWith("data:image") && item.getMaskUrl().startsWith("https://")) {
                        // 当没有修改mask时，还是用之前的mask地址
                        item.setMaskUrl(item.getMaskMinioUrl());
                        log.info("服装{} 的maskUrl没有被编辑", item.getType());
                    } else {
                        // 将原图地址作为修改后的图片地址，放在不同的桶
                        String path = minioUtil.base64UploadToPath(item.getMaskUrl(), clothingBucket, "labelingMask/" + UUID.randomUUID());
                        log.info("服装{} 的maskUrl已被编辑， 新的path为{}", item.getType(), path);
                        if (StringUtil.isNullOrEmpty(path)) {
                            log.error("标注的mask图片上传失败");
                            throw new BusinessException("image.modify.failed");
                        }
                        item.setMaskUrl(path);
                    }
                }
                log.info("服装{} 的maskUrl为null", item.getType());
            }
        });
    }

    private void partialDesignBase64ToImage(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO, Long accountId, boolean preview) {
        designSingleIncludeLayersDTO.getDesignSingleItemDTOList().forEach(item -> {
            PartialDesignDTO partialDesignDTO = item.getPartialDesign();
            if (!Objects.isNull(item.getPartialDesign())
                    && !StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignBase64())) {
                String path;
                if (!StringUtil.isNullOrEmpty(partialDesignDTO.getPartialDesignMinioPath()) && !preview) {
                    String sourcePath = partialDesignDTO.getPartialDesignMinioPath();
                    path = sourcePath.substring(sourcePath.indexOf("/") + 1, sourcePath.lastIndexOf("."));
                } else if (preview) {
                    path = accountId + "/" + CommonConstant.PARTIAL_DESIGN_PREVIEW_FILENAME + "/" + UUID.randomUUID();
                } else {
                    path = accountId + "/" + CommonConstant.PARTIAL_DESIGN_FILENAME + "/" + UUID.randomUUID();
                }
                String newPath = minioUtil.base64UploadToPath(partialDesignDTO.getPartialDesignBase64(), partialDesignBucket, path);
                if (StringUtil.isNullOrEmpty(newPath)) {
                    log.error("局部design图片上传失败");
                    throw new BusinessException("partial.design.failed");
                }
                item.getPartialDesign().setPartialDesignMinioPath(newPath);
            } else if (Objects.isNull(item.getPartialDesign())
                    || StringUtil.isNullOrEmpty(item.getPartialDesign().getPartialDesignMinioPath())) {
                item.setPartialDesign(new PartialDesignDTO(null));
            }
        });
    }

    @Override
    public Map<String, List<String>> setPriorityAndUndividedLayer(JSONArray layers) {
        HashMap<String, List<String>> priorityAndLayer = new HashMap<>();
        for (int i = 0; i < layers.size(); i++) {
            JSONObject jsonObject = layers.getJSONObject(i);
            String priority = jsonObject.getString("priority");
            String category = jsonObject.getString("image_category").split("_")[0];
            if (!category.equals("body") && !priorityAndLayer.containsKey(priority))
                priorityAndLayer.put(priority, Arrays.asList(jsonObject.getString("pattern_overall_image_url"), jsonObject.getString("pattern_print_image_url")));
        }
        return priorityAndLayer;
    }

    // 由于在design过程中没有priority 优先级的概念，并且在design时，不会出现上下两件使用相同服装类型的情况，所以这里依然保留这个方法。
    @Override
    public Map<String, String> setTypeAndUndividedLayer(JSONArray layers) {
        HashMap<String, String> typeAndLayer = new HashMap<>();
        for (int i = 0; i < layers.size(); i++) {
            JSONObject jsonObject = layers.getJSONObject(i);
            String category = jsonObject.getString("image_category").split("_")[0];
            if (!category.equals("body") && !typeAndLayer.containsKey(category))
                typeAndLayer.put(category, jsonObject.getString("pattern_image_url"));
        }
        return typeAndLayer;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ComposeLayersVO editLayersPositionAndScale(EditLayersPositionAndScaleVO positionAndScaleVO) throws IOException {
        ComposeLayersVO designItemLayer = positionAndScaleVO.getLayers();
        // 1、校验designItem是是否存在
        DesignItem designItem = selectById(designItemLayer.getDesignItemId());
        if (Objects.isNull(designItem)) {
            throw new BusinessException("design.item.does.not.exist");
        }
        TDesignPythonOutfit designPythonOutfit = designPythonOutfitService.getByDesignItemId(designItemLayer.getDesignItemId());
        if (Objects.isNull(designPythonOutfit)) {
            throw new BusinessException("layers.does.not.exists");
        }

        // 2、校验layers是否存在
        designItemLayer.getLayers().forEach(layer -> {
            TDesignPythonOutfitDetail detail = designPythonOutfitDetailService.getById(layer.getId());
            if (Objects.isNull(detail)) {
                log.error(layer.getImageCategory() + " layer does not exists!");
                throw new BusinessException("layers.does.not.exists");
            }
        });

        // 3、组装python入参
        List<OutfitDetailPythonItem> outfitDetailPythonItems = convertToOutfitDetailPythonItemList(designItemLayer.getLayers());

        // 4、合成图层
        String synthesisUrl = pythonService.composeLayers(outfitDetailPythonItems);
        designItemLayer.setDesignItemUrl(minioUtil.getPreSignedUrl(synthesisUrl, 24 * 60));

        // 5、更新数据库，根据designItemId更新designItemUrl
        designItem.setUpdateDate(DateUtil.getByTimeZone(positionAndScaleVO.getTimeZone()));
        designItem.setDesignUrl(synthesisUrl);
        designItem.setId(designItemLayer.getDesignItemId());
        designItem.setUpdateDate(DateUtil.getByTimeZone(positionAndScaleVO.getTimeZone()));
        updateById(designItem);

        // 6、更新数据库，根据designItemId更新t_design_python_outfit的designUrl
        TDesignPythonOutfit tDesignPythonOutfit = new TDesignPythonOutfit();
        tDesignPythonOutfit.setDesignUrl(synthesisUrl);
        tDesignPythonOutfit.setId(designPythonOutfit.getId());
        tDesignPythonOutfit.setUpdateDate(LocalDateTime.now(ZoneId.of(positionAndScaleVO.getTimeZone())));
        designPythonOutfitService.updateById(tDesignPythonOutfit);

        // 7、将图层信息position和scale更新到t_design_python_outfit_detail表
        ArrayList<TDesignPythonOutfitDetail> details = new ArrayList<>();
        designItemLayer.getLayers().forEach(layer -> {
            TDesignPythonOutfitDetail layerDetail = new TDesignPythonOutfitDetail();
            layerDetail.setPosition(layer.getPosition().toString());
            layerDetail.setScale(layer.getScale().toString());
            layerDetail.setId(layer.getId());
            details.add(layerDetail);
        });
        designPythonOutfitDetailService.updateBatchById(details);

        // 8、返回图层及合成图信息
        designItemLayer.getLayers().forEach(layer -> {
            ArrayList<Long> imageSize = new ArrayList<>();
            for (int i = 0; i < layer.getImageSize().size(); i++) {
                // todo check这里的计算是否正确
                imageSize.add((long) (layer.getImageSize().get(i) * layer.getScale()[i]));
            }
            layer.setImageSize(imageSize);
            if (!StringUtil.isNullOrEmpty(layer.getImageUrl())) {
                layer.setImageUrl(minioUtil.getPreSignedUrl(layer.getImageUrl(), 24 * 60));
            }
            if (!StringUtil.isNullOrEmpty(layer.getMaskUrl())) {
                layer.setMaskUrl(minioUtil.getPreSignedUrl(layer.getMaskUrl(), 24 * 60));
            }
        });

        // 9、如果当前item被like过，需要更新t_user_like表和t_user_like_group表
        updateUserLikeDate(designItemLayer.getDesignItemId(), positionAndScaleVO.getTimeZone());
        return designItemLayer;
    }

    private void updateUserLikeDate(Long designItemId, String timeZone) {
        UserLike userLike = userLikeService.getByDesignItemId(designItemId);
        if (!ObjectUtil.isEmpty(userLike)) {
            Long userLikeGroupId = userLike.getUserLikeGroupId();
            // 更新t_user_like和t_user_like_group表的update_date
            userLikeService.updateDate(designItemId, timeZone);
            userLikeGroupService.updateDate(userLikeGroupId, timeZone);
        }
    }

    private void updateUserLikeConvertStatus(Long designItemId, String timeZone) {
        UserLike userLike = userLikeService.getByDesignItemId(designItemId);
        if (!ObjectUtil.isEmpty(userLike) && userLike.getConverted() == 0) {
            // 更新t_user_like和t_user_like_group表的update_date
            userLikeService.updateDate(designItemId, timeZone);
        }
    }

    private DesignSingleVO assembleDesignSingleResponse(Long designItemId, String designItemUrl,
                                                        String currentFullBodyView,
                                                        List<DesignSingleItemDTO> designSingleItemDTOList,
                                                        List<DesignPythonOutfitVO> layersObject,
                                                        String singleOrOverall,
                                                        Map<String, List<String>> priorityAndUndividedLayer) {

        DesignSingleVO designSingleVO = new DesignSingleVO();
        ArrayList<DesignItemClothesDetailVO> clothes = new ArrayList<>();
        DesignItemClothesDetailVO body = new DesignItemClothesDetailVO("body");
        designSingleVO.setDesignItemId(designItemId);
        // 图片用于修改前后的一键对比
        designSingleVO.setDesignItemUrl(designItemUrl);
        // 当前全身图
        designSingleVO.setCurrentFullBodyView(minioUtil.getPreSignedUrl(currentFullBodyView, 24 * 60));
        designSingleVO.setClothes(clothes);

        // 获取每个单品的id是否被改变过，该状态需要再返回给前端
        Map<Long, Boolean> idChanged = designSingleItemDTOList.stream().collect(Collectors.toMap(DesignSingleItemDTO::getId, DesignSingleItemDTO::getChanged));
        // 获取每个单品的designType，该状态需要再返回给前端
        Map<Long, String> idDesignType = designSingleItemDTOList.stream().collect(Collectors.toMap(DesignSingleItemDTO::getId, DesignSingleItemDTO::getDesignType));

        boolean flag = singleOrOverall.equals("single");
        designSingleItemDTOList.forEach(singleItem -> {
            DesignItemClothesDetailVO designItemClothesDetailVO = new DesignItemClothesDetailVO();
            designItemClothesDetailVO.setId(singleItem.getId());
            designItemClothesDetailVO.setChanged(idChanged.get(singleItem.getId()));
            designItemClothesDetailVO.setDesignType(idDesignType.get(singleItem.getId()));
            designItemClothesDetailVO.setType(singleItem.getType());
            // 重置sketch的minio临时访问凭证
            designItemClothesDetailVO.setPath(minioUtil.getPreSignedUrl(singleItem.getPath(), 24 * 60, true));
            designItemClothesDetailVO.setMinIOPath(singleItem.getPath());
            if (!StringUtil.isNullOrEmpty(singleItem.getColor()))
                designItemClothesDetailVO.setColor(panToneService.getPantoneByRgb(singleItem.getColor()));
            designItemClothesDetailVO.setPrintObject(singleItem.getPrintObject());
            designItemClothesDetailVO.setTrims(singleItem.getTrims());
            designItemClothesDetailVO.setLayersObject(layersObject.stream().filter(
                    layers -> (singleItem.getType().toLowerCase().equals(layers.getImageCategory().split("_")[0])
                            && (flag ? Boolean.TRUE : singleItem.getPriority().equals(layers.getPriority())))
            ).collect(Collectors.toList()));
            designItemClothesDetailVO.setGradient(singleItem.getGradient());
            String partialDesignMinioPath = singleItem.getPartialDesign().getPartialDesignMinioPath();
            String preSignedUrl = StringUtil.isNullOrEmpty(partialDesignMinioPath) ? null : minioUtil.getPreSignedUrl(partialDesignMinioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true);
            designItemClothesDetailVO.setPartialDesign(new PartialDesignDTO(partialDesignMinioPath, preSignedUrl));

            if (priorityAndUndividedLayer.containsKey(singleItem.getPriority().toString())) {
                designItemClothesDetailVO.setUndividedLayer(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(0), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
                designItemClothesDetailVO.setUndividedLayerWithSinglePrint(minioUtil.getPreSignedUrl(priorityAndUndividedLayer.get(singleItem.getPriority().toString()).get(1), CommonConstant.MINIO_IMAGE_EXPIRE_TIME, true));
            }
            body.setLayersObject(layersObject.stream().filter(layers -> layers.getImageCategory().equals("body")).collect(Collectors.toList()));

            clothes.add(designItemClothesDetailVO);
        });
        clothes.add(body);

        return designSingleVO;
    }

    private List<DesignItemDetailPrint> setDesignItemDetailPrintList(List<DesignItemDetail> designItemDetails,
                                                                     List<DesignSingleItemDTO> designSingleItemDTOList,
                                                                     String timeZone) {

        Map<Integer, Long> designItemDetailTypeIdMap = designItemDetails.stream().collect(Collectors.toMap(DesignItemDetail::getPriority, DesignItemDetail::getId));
        ArrayList<DesignItemDetailPrint> designItemDetailPrints = new ArrayList<>();

        designSingleItemDTOList.forEach(designSingleItem -> {
            // 1、判断有无印花，无印花则跳过
            setDesignItemDetailPrintList(designSingleItem, designItemDetailPrints, "print", designItemDetailTypeIdMap, timeZone);

            // 2、判断有无装饰配件
            setDesignItemDetailPrintList(designSingleItem, designItemDetailPrints, "trims", designItemDetailTypeIdMap, timeZone);
        });
        return designItemDetailPrints;
    }

    private void setDesignItemDetailPrintList(DesignSingleItemDTO designSingleItem,
                                              ArrayList<DesignItemDetailPrint> designItemDetailPrints,
                                              String printType,
                                              Map<Integer, Long> designItemDetailTypeIdMap,
                                              String timeZone) {

        DesignSinglePrintDTO printObject;
        if (printType.equals("print")) {
            printObject = designSingleItem.getPrintObject();
        } else {
            printObject = designSingleItem.getTrims();
        }

        Long designItemDetailId = designItemDetailTypeIdMap.get(designSingleItem.getPriority());

        // 查询现有的记录，用于 saveOrUpdate
        List<DesignItemDetailPrint> existingPrints = designItemDetailPrintService.getByDesignItemDetailId(designItemDetailId, printType);
        // 创建 Map，以 priority 为 key，方便快速查找
        Map<Integer, DesignItemDetailPrint> existingPrintMap = existingPrints.stream()
                .collect(Collectors.toMap(DesignItemDetailPrint::getPriority, print -> print, (old, newVal) -> old));

        // 收集新 print 的 priority，用于后续删除不存在的旧记录
        Set<Integer> newPrintPriorities = new HashSet<>();

        if (!CollectionUtil.isEmpty(printObject.getPrints())) {
            // 处理新的印花列表
            printObject.getPrints().forEach(print -> {
                setUriToMinioPath(print);

                Integer priority = print.getPriority();
                newPrintPriorities.add(priority);

                DesignItemDetailPrint designItemDetailPrint;
                // 如果存在相同 priority 的记录，则更新；否则创建新记录
                if (existingPrintMap.containsKey(priority)) {
                    // 更新现有记录
                    designItemDetailPrint = existingPrintMap.get(priority);
                    designItemDetailPrint.setUpdateDate(LocalDateTime.now(ZoneId.of(timeZone)));
                } else {
                    // 创建新记录
                    designItemDetailPrint = new DesignItemDetailPrint();
                    designItemDetailPrint.setCreateDate(LocalDateTime.now(ZoneId.of(timeZone)));
                }

                // 设置/更新字段
                designItemDetailPrint.setDesignItemDetailId(designItemDetailId);
                designItemDetailPrint.setPrintType(printType);
                designItemDetailPrint.setLevel2Type(print.getLevel2Type());
                designItemDetailPrint.setPath(print.getMinIOPath());
                designItemDetailPrint.setScale(print.getScale().toString());
                designItemDetailPrint.setSingleOrOverall(printType.equals("print") ? print.getIfSingle() ? "single" : "overall" : "single");
                // single、overall模式下都有position、angle和priority
                designItemDetailPrint.setPosition(print.getLocation().toString());
                designItemDetailPrint.setAngle(print.getAngle());
                designItemDetailPrint.setPriority(priority);

                designItemDetailPrints.add(designItemDetailPrint);
            });
        }

        // 逻辑删除不在新列表中的旧记录
        existingPrints.forEach(existingPrint -> {
            if (!newPrintPriorities.contains(existingPrint.getPriority())) {
                // 该记录不在新列表中，需要逻辑删除
                UpdateWrapper<DesignItemDetailPrint> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("id", existingPrint.getId());
                updateWrapper.set("is_deleted", (byte) 1);
                updateWrapper.set("update_date", LocalDateTime.now(ZoneId.of(timeZone)));
                designItemDetailPrintService.update(null, updateWrapper);
            }
        });
    }

    // 对印花类型为Generate的图片路径进行特殊处理
    private void setUriToMinioPath(DesignSinglePrint print) {
        if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Generate")) {
            if (!StringUtil.isNullOrEmpty(print.getPath())) {
                try {
                    URI uri = new URI(print.getPath());
                    String path = uri.getPath(); // 获取路径部分: /aida-users/87/print/9ac32f65-6043-424d-a146-92c9c6d204ee-4-87.png
                    String substring = path.substring(1);
                    print.setMinIOPath(substring);
                } catch (URISyntaxException e) {
                    throw new BusinessException(e.getMessage());
                }
            } else {
                log.error("designType为Generate的印花path传值为空 ：{}", print.getPath());
                throw new BusinessException("path.cannot.be.empty");
            }
        }
    }

    private List<OutfitDetailPythonItem> convertToOutfitDetailPythonItemList(List<DesignPythonOutfitVO> layers) {
        List<OutfitDetailPythonItem> composeLayerPythonItem = Arrays.asList(new OutfitDetailPythonItem[layers.size()]);
        layers.forEach(layer -> {
            composeLayerPythonItem.set(layer.getPriority() - 1, new OutfitDetailPythonItem(layer.getImageCategory(),
                    layer.getPosition(),
                    layer.getImageSize(),
                    layer.getScale(),
                    layer.getImageUrl(),
                    layer.getMaskUrl()));
        });
        return composeLayerPythonItem;
    }

    @Override
    public List<DesignItem> selectDesignIdById(List<Long> designItemIdList) {
        QueryWrapper<DesignItem> queryWrapper = new QueryWrapper<>();

        queryWrapper.in("id", designItemIdList);
        return designItemMapper.selectList(queryWrapper);
    }

    private void saveCollectionElement(DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {

        ArrayList<Library> libraries = new ArrayList<>();
        // 添加sketch到library
        designSingleIncludeLayersDTO.getDesignSingleItemDTOList().forEach(designSingleItem -> {

            if (!StringUtil.isNullOrEmpty(designSingleItem.getDesignType()) && designSingleItem.getDesignType().equals("Collection")) {
                String path = minioUtil.getPreSignedUrl(designSingleItem.getPath(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
                try {
                    String md5 = MD5Utils.encryptFile(path, false);
                    // 先判断是否需要被加入到library
                    Boolean needAdd = libraryService.checkMd5(CollectionLevel1TypeEnum.SKETCH_BOARD.getRealName(),
                            designSingleItem.getType(),
                            designSingleIncludeLayersDTO.getGender(),
                            md5);
                    // 加入到library
                    if (needAdd) {
                        Library library = new Library();
                        library.setAccountId(UserContext.getUserHolder().getId());
                        library.setLevel1Type(CollectionLevel1TypeEnum.SKETCH_BOARD.getRealName());
                        library.setLevel2Type(designSingleItem.getType());
                        library.setLevel3Type(designSingleIncludeLayersDTO.getGender());
                        String ageGroup = workspaceService.getAgeGroupByProjectOrCollectionId(designSingleIncludeLayersDTO.getProjectId(), null);
                        library.setAgeGroup(ageGroup);
                        library.setUrl(designSingleItem.getPath());
                        library.setName(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
                        library.setMd5(md5);
                        library.setCreateDate(DateUtil.getByTimeZone(designSingleIncludeLayersDTO.getTimeZone()));
                        libraries.add(library);
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

            // 添加print到library
            designSingleItem.getPrintObject().getPrints().forEach(print -> {
                if (!StringUtil.isNullOrEmpty(print.getDesignType()) && print.getDesignType().equals("Collection")) {
                    String path = minioUtil.getPreSignedUrl(print.getMinIOPath(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
                    try {
                        String md5 = MD5Utils.encryptFile(path, false);
                        // 先判断是否已被加入到library
                        Boolean needAdd = libraryService.checkMd5(CollectionLevel1TypeEnum.PRINT_BOARD.getRealName(),
                                null,
                                null,
                                md5);
                        // 加入到library
                        if (needAdd) {
                            Library library = new Library();
                            library.setAccountId(UserContext.getUserHolder().getId());
                            library.setLevel1Type(CollectionLevel1TypeEnum.PRINT_BOARD.getRealName());
                            library.setLevel2Type(print.getLevel2Type());
                            library.setUrl(print.getMinIOPath());
                            library.setName(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
                            library.setMd5(md5);
                            library.setCreateDate(DateUtil.getByTimeZone(designSingleIncludeLayersDTO.getTimeZone()));
                            libraries.add(library);
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        });
        libraryService.saveBatch(libraries);
    }

    public Long getCountByUserAndTime(String startTime, String endTime, List<Long> accountIds) {
        QueryWrapper<DesignItem> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("create_date", startTime)
                .lt("create_date", endTime)
                .select("count(distinct design_id) as count");

        if (!accountIds.isEmpty()) {
            queryWrapper.in("account_id", accountIds);
        }

        List<Map<String, Object>> result = baseMapper.selectMaps(queryWrapper);
        if (result != null && !result.isEmpty()) {
            Object countObj = result.get(0).get("count");
            return (Long) countObj;
        } else {
            return 0L;
        }
    }


    @Resource
    private UserLikeMapper userLikeMapper;

    @Override
    public void convertHistoryMaskWithoutGradient() {
        // 1、获取全部需要转换的designOutfitId
        // 1、获取t_user_like表中design_item_id不为-1的所有design_item_id, design_outfit_id
        QueryWrapper<UserLike> queryWrapper = new QueryWrapper<>();
        queryWrapper.ne("design_item_id", -1)
                .ne("converted", 1)
                .groupBy(Arrays.asList("design_outfit_id", " design_item_id")).select(" design_item_id", "design_outfit_id");

        List<UserLike> userLikes = userLikeMapper.selectList(queryWrapper);
//        List<Map<String, Object>> userLikes = userLikeMapper.selectMaps(queryWrapper);
        log.info("userLike 总数 ：{}", userLikes.size());
        // 用于存储结果
        List<String[]> pairs = new ArrayList<>();
        AtomicInteger count = new AtomicInteger();

        // 2、查询指定designOutfitId对应的mask
        userLikes.forEach(item -> {
            // 存储数据，按前缀分组
            Map<Integer, Map<String, String>> groupedData = new HashMap<>();
            Long designItemId = item.getDesignItemId();
            Long designOutfitId = item.getDesignOutfitId();

            List<TDesignPythonOutfitDetail> designPythonOutfitDetails = designPythonOutfitDetailService.getDetailByDesignPythonOutfitId(designOutfitId);
            List<DesignItemDetail> designItemDetails = designItemDetailService.selectByDesignItemId(designItemId);
            // 为没有优先级的sketch添加优先级
            designItemDetailService.setDesignItemDetailPriority(designItemDetails);
            // 为没有优先级的图层添加优先级
            designPythonOutfitDetailService.setDesignPythonOutfitDetailPriority(designPythonOutfitDetails);
            Map<Integer, String> designItemPath = designItemDetails.stream()
                    .filter(designItemDetail -> !designItemDetail.getType().equals("Body"))
                    .collect(Collectors.toMap(
                            DesignItemDetail::getPriority, // key: category 转为小写
                            DesignItemDetail::getPath // value: path 字段
                    ));
            // 遍历列表，拿出每个种类的前后片
            designPythonOutfitDetails.forEach(designPythonOutfitDetail -> {
                String imageCategory = designPythonOutfitDetail.getImageCategory();
                String maskPath = designPythonOutfitDetail.getMaskUrl();
                Integer priority = Math.abs(designPythonOutfitDetail.getPriority());

                // 提取前缀和后缀
                if (imageCategory.contains("_")) {
                    String[] parts = imageCategory.split("_");
//                    String prefix = parts[0];
                    String suffix = parts[1];

                    // 初始化前缀组
                    groupedData.putIfAbsent(priority, new HashMap<>());
                    groupedData.get(priority).put(suffix, maskPath);
                }
            });

            // 查找成对的front和back
            for (Map.Entry<Integer, Map<String, String>> entry : groupedData.entrySet()) {
                Map<String, String> suffixes = entry.getValue();
                if (suffixes.containsKey("front") && suffixes.containsKey("back")) {
                    String frontMask = suffixes.get("front");
                    String backMask = suffixes.get("back");
                    pairs.add(new String[]{designItemPath.get(entry.getKey()), frontMask, backMask, frontMask});
                }
            }
            log.info("总数 count + current : {} + {} = {}", count, groupedData.size(), count.get() + groupedData.size());
            count.addAndGet(groupedData.size());

        });

        // 将结果写入JSON文件
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT); // 格式化JSON输出

        try {
            // 写入JSON文件
            objectMapper.writeValue(new File("mask_pairs.json"), pairs);
            log.info("待合成mask的数量：{}", pairs.size());
            System.out.println("数据已成功写入mask_pairs.json文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateMaskUrl() {
        QueryWrapper<UserLike> queryWrapper = new QueryWrapper<>();
        queryWrapper.ne("design_item_id", -1)
                .ne("converted", 1)
                .groupBy(Arrays.asList("design_outfit_id", " design_item_id")).select(" design_item_id", "design_outfit_id");

        List<UserLike> userLikes = userLikeMapper.selectList(queryWrapper);
        log.info("userLike 总数 ：{}", userLikes.size());
        ArrayList<TDesignPythonOutfitDetail> tDesignPythonOutfitDetails = new ArrayList<>();


        // 2、查询指定designOutfitId对应的mask
        userLikes.forEach(item -> {
            Long designOutfitId = item.getDesignOutfitId();

            // 存储以front结尾的mask路径
            Map<String, String> frontMaskMap = new HashMap<>();
            HashMap<String, Long> backMaskIdMap = new HashMap<>();
            List<TDesignPythonOutfitDetail> designPythonOutfitDetails = designPythonOutfitDetailService.getDetailByDesignPythonOutfitId(designOutfitId);
            // 遍历列表，拿出每个种类的前后片
            designPythonOutfitDetails.forEach(designPythonOutfitDetail -> {
                String imageCategory = designPythonOutfitDetail.getImageCategory();
                String maskUrl = designPythonOutfitDetail.getMaskUrl();

                // 检查是否以'front'结尾
                if (imageCategory.endsWith("front")) {
                    String maskPath = maskUrl.replace("aida-clothing", "test");
                    String prefix = imageCategory.substring(0, imageCategory.lastIndexOf("_"));
                    if (backMaskIdMap.containsKey(prefix)) {
                        TDesignPythonOutfitDetail tDesignPythonOutfitDetail = new TDesignPythonOutfitDetail();
                        tDesignPythonOutfitDetail.setMaskUrl(maskPath);
                        tDesignPythonOutfitDetail.setUpdateDate(LocalDateTime.now());
                        tDesignPythonOutfitDetail.setId(backMaskIdMap.get(prefix));
                        tDesignPythonOutfitDetails.add(tDesignPythonOutfitDetail);
                    } else {
                        frontMaskMap.put(prefix, maskPath);
                    }

                    TDesignPythonOutfitDetail tDesignPythonOutfitDetail = new TDesignPythonOutfitDetail();
                    tDesignPythonOutfitDetail.setMaskUrl(maskPath);
                    tDesignPythonOutfitDetail.setUpdateDate(LocalDateTime.now());
                    tDesignPythonOutfitDetail.setId(designPythonOutfitDetail.getId());
                    tDesignPythonOutfitDetails.add(tDesignPythonOutfitDetail);
                }

                if (imageCategory.endsWith("back")) {
                    String prefix = imageCategory.substring(0, imageCategory.lastIndexOf("_"));
                    if (frontMaskMap.containsKey(prefix)) {
                        // 更新back的mask路径为front的mask路径
                        String frontMaskPath = frontMaskMap.get(prefix);

                        TDesignPythonOutfitDetail tDesignPythonOutfitDetail = new TDesignPythonOutfitDetail();
                        tDesignPythonOutfitDetail.setMaskUrl(frontMaskPath);
                        tDesignPythonOutfitDetail.setUpdateDate(LocalDateTime.now());
                        tDesignPythonOutfitDetail.setId(designPythonOutfitDetail.getId());
                        tDesignPythonOutfitDetails.add(tDesignPythonOutfitDetail);
                    } else {
                        backMaskIdMap.put(prefix, designPythonOutfitDetail.getId());
                    }
                }
            });
        });

        log.info("更新总数：{}", tDesignPythonOutfitDetails.size());
        designPythonOutfitDetailService.updateBatchById(tDesignPythonOutfitDetails);
        log.info("更新完成");
    }

}
