package com.ym.mec.biz.service.impl;

import static com.ym.mec.biz.dal.enums.DealStatusEnum.SUCCESS;
import static com.ym.mec.biz.dal.enums.GroupType.SUBJECT_CHANGE;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import com.ym.mec.biz.dal.dao.GoodsDao;
import com.ym.mec.biz.dal.dao.GoodsProcurementDao;
import com.ym.mec.biz.dal.dao.MusicGroupDao;
import com.ym.mec.biz.dal.dao.MusicGroupPurchaseListDao;
import com.ym.mec.biz.dal.dao.SellOrderDao;
import com.ym.mec.biz.dal.dao.StudentDao;
import com.ym.mec.biz.dal.dao.StudentPaymentOrderDao;
import com.ym.mec.biz.dal.dao.StudentPaymentOrderDetailDao;
import com.ym.mec.biz.dal.dao.StudentRegistrationDao;
import com.ym.mec.biz.dal.dao.SubjectChangeDao;
import com.ym.mec.biz.dal.dao.SysConfigDao;
import com.ym.mec.biz.dal.dao.SysUserCashAccountLogDao;
import com.ym.mec.biz.dal.dto.SubjectChangeParamDto;
import com.ym.mec.biz.dal.entity.Goods;
import com.ym.mec.biz.dal.entity.GoodsProcurement;
import com.ym.mec.biz.dal.entity.MusicGroup;
import com.ym.mec.biz.dal.entity.MusicGroupPurchaseList;
import com.ym.mec.biz.dal.entity.MusicGroupSubjectPlan;
import com.ym.mec.biz.dal.entity.SellOrder;
import com.ym.mec.biz.dal.entity.Student;
import com.ym.mec.biz.dal.entity.StudentPaymentOrder;
import com.ym.mec.biz.dal.entity.StudentPaymentOrderDetail;
import com.ym.mec.biz.dal.entity.StudentRegistration;
import com.ym.mec.biz.dal.entity.SubjectChange;
import com.ym.mec.biz.dal.entity.SysUserCashAccount;
import com.ym.mec.biz.dal.entity.SysUserCashAccountDetail;
import com.ym.mec.biz.dal.entity.SysUserCashAccountLog;
import com.ym.mec.biz.dal.enums.AccountType;
import com.ym.mec.biz.dal.enums.CouponDetailTypeEnum;
import com.ym.mec.biz.dal.enums.DealStatusEnum;
import com.ym.mec.biz.dal.enums.GoodsType;
import com.ym.mec.biz.dal.enums.KitGroupPurchaseTypeEnum;
import com.ym.mec.biz.dal.enums.MessageTypeEnum;
import com.ym.mec.biz.dal.enums.OrderDetailTypeEnum;
import com.ym.mec.biz.dal.enums.OrderTypeEnum;
import com.ym.mec.biz.dal.enums.PayStatus;
import com.ym.mec.biz.dal.enums.PaymentChannelEnum;
import com.ym.mec.biz.dal.enums.PlatformCashAccountDetailTypeEnum;
import com.ym.mec.biz.dal.enums.ReturnFeeEnum;
import com.ym.mec.biz.dal.enums.SellTypeEnum;
import com.ym.mec.biz.dal.enums.StudentMusicGroupStatusEnum;
import com.ym.mec.biz.dal.enums.SubjectChangeStatusEnum;
import com.ym.mec.biz.dal.page.SubjectChangeQueryInfo;
import com.ym.mec.biz.service.ContractService;
import com.ym.mec.biz.service.GoodsService;
import com.ym.mec.biz.service.MusicGroupSubjectPlanService;
import com.ym.mec.biz.service.PayService;
import com.ym.mec.biz.service.SellOrderService;
import com.ym.mec.biz.service.StudentInstrumentService;
import com.ym.mec.biz.service.StudentPaymentOrderDetailService;
import com.ym.mec.biz.service.StudentPaymentOrderService;
import com.ym.mec.biz.service.StudentPaymentRouteOrderService;
import com.ym.mec.biz.service.StudentRegistrationService;
import com.ym.mec.biz.service.SubjectChangeService;
import com.ym.mec.biz.service.SysConfigService;
import com.ym.mec.biz.service.SysCouponCodeService;
import com.ym.mec.biz.service.SysMessageService;
import com.ym.mec.biz.service.SysPaymentConfigService;
import com.ym.mec.biz.service.SysUserCashAccountDetailService;
import com.ym.mec.biz.service.SysUserCashAccountService;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.service.IdGeneratorService;
import com.ym.mec.common.service.impl.BaseServiceImpl;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;
import com.ym.mec.util.collection.MapUtil;
import com.ym.mec.util.date.DateUtil;

@Service
public class SubjectChangeServiceImpl extends BaseServiceImpl<Integer, SubjectChange> implements SubjectChangeService {

    @Autowired
    private SubjectChangeDao subjectChangeDao;
    @Autowired
    private GoodsDao goodsDao;
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private IdGeneratorService idGeneratorService;
    @Autowired
    private StudentPaymentOrderService studentPaymentOrderService;
    @Autowired
    private SysUserCashAccountService sysUserCashAccountService;
    @Autowired
    private StudentPaymentRouteOrderService studentPaymentRouteOrderService;
    @Autowired
    private SysConfigDao sysConfigDao;
    @Autowired
    private PayService payService;
    @Autowired
    private StudentPaymentOrderDetailDao studentPaymentOrderDetailDao;
    @Autowired
    private StudentPaymentOrderDetailService studentPaymentOrderDetailService;
    @Autowired
    private MusicGroupDao musicGroupDao;
    @Autowired
    private SellOrderDao sellOrderDao;
    @Autowired
    private SysUserCashAccountDetailService sysUserCashAccountDetailService;
    @Autowired
    private SysMessageService sysMessageService;
    @Autowired
    private StudentRegistrationService studentRegistrationService;
    @Autowired
    private ContractService contractService;
    @Autowired
    private SysPaymentConfigService sysPaymentConfigService;
    @Autowired
    private StudentPaymentOrderDao studentPaymentOrderDao;
    @Autowired
    private SellOrderService sellOrderService;
    @Autowired
    private GoodsService goodsService;
    @Autowired
    private StudentInstrumentService studentInstrumentService;
    @Autowired
    private MusicGroupPurchaseListDao musicGroupPurchaseListDao;
    @Autowired
    private StudentRegistrationDao studentRegistrationDao;
    @Autowired
    private MusicGroupSubjectPlanService musicGroupSubjectPlanService;
    @Autowired
    private SysCouponCodeService sysCouponCodeService;
    @Autowired
    private SysUserCashAccountLogDao sysUserCashAccountLogDao;
    @Autowired
    private GoodsProcurementDao goodsProcurementDao;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public BaseDAO<Integer, SubjectChange> getDAO() {
        return subjectChangeDao;
    }

    @Override
    public SubjectChange getChangeInfo(Integer id) {
        SubjectChange subjectChange = subjectChangeDao.getChangeInfo(id);
        if (subjectChange == null) {
            return null;
        }
        List<StudentPaymentOrder> orders = studentPaymentOrderDao.findMusicGroupApplyOrderByStatus(subjectChange.getStudentId(), subjectChange.getMusicGroupId(), SUCCESS);
        //获取原报名订单的乐器费用
        if (subjectChange.getOriginalMusical() != null) {
            if (orders != null && orders.size() > 0) {
                List<Long> collect = orders.stream().map(e -> e.getId()).collect(Collectors.toList());
                List<Goods> musical = studentPaymentOrderDetailDao.getGoodsSellPrice(collect, "MUSICAL");
                if (musical != null && musical.size() > 0) {
                    subjectChange.setOriginalMusicalGoods(musical.get(0));
                }
            }
        }
        if (subjectChange.getOriginalAccessories() != null) {
            if (orders != null && orders.size() > 0) {
                List<Long> collect = orders.stream().map(e -> e.getId()).collect(Collectors.toList());
                subjectChange.setOriginalAccessoriesGoods(studentPaymentOrderDetailDao.getGoodsSellPrice(collect, "ACCESSORIES"));
            }
        }

        if (subjectChange.getChangeMusical() != null) {
            subjectChange.setChangeMusicalGoods(goodsDao.get(subjectChange.getChangeMusical()));
        }
        if (subjectChange.getChangeAccessories() != null) {
            subjectChange.setChangeAccessoriesGoods(goodsDao.findGoodsByIds(subjectChange.getChangeAccessories()));
        }

        return subjectChange;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map payChange(SubjectChangeParamDto subjectChangeParamDto) throws Exception {
        Date date = new Date();
        SubjectChange subjectChange = subjectChangeDao.get(subjectChangeParamDto.getId());
        if (subjectChangeParamDto.getRepay()) {
            StudentPaymentOrder studentPaymentOrder = studentPaymentOrderService.get(subjectChange.getOrderId().longValue());

            if (studentPaymentOrder != null && studentPaymentOrder.getStatus() == DealStatusEnum.ING) {
                // 查询订单状态
                PayStatus payStatus = studentPaymentOrderService.queryPayStatus(studentPaymentOrder);
                if (payStatus != PayStatus.FAILED) {
                    if (payStatus == PayStatus.SUCCESSED) {
                        throw new BizException("订单已支付成功，请勿重复支付");
                    }
                }
                studentPaymentOrder.setStatus(DealStatusEnum.CLOSE);
                studentPaymentOrder.setUpdateTime(date);
                studentPaymentOrderService.update(studentPaymentOrder);
                subjectChange.setStatus(SubjectChangeStatusEnum.WAIT_PAY);
                //增加商品库存
                List<StudentPaymentOrderDetail> studentPaymentOrderDetailList = studentPaymentOrderDetailService.queryOrderDetail(studentPaymentOrder.getId());
                String goodsIds = studentPaymentOrderDetailList.stream().filter(t -> StringUtils.isNotBlank(t.getMinuendStockGoodsIdList())).map(t -> t.getMinuendStockGoodsIdList()).collect(Collectors.joining(","));
                if(StringUtils.isNotBlank(goodsIds)){
                	GoodsProcurement goodsProcurement = null;
                	List<Goods> goodsList = goodsService.getGoodsWithLocked(goodsIds);
                	Map<Integer,Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(Goods :: getId, t -> t));
                	Goods goods = null;
        			Map<Integer, Goods> batchUpdateGoodsMap = new HashMap<Integer, Goods>();
        			Map<Long, GoodsProcurement> goodsProcurementMap = new HashMap<Long, GoodsProcurement>();
                	
                	for(String goodsIdStr : goodsIds.split(",")){
                		if(StringUtils.isBlank(goodsIdStr)){
        					continue;
        				}
        				goods = goodsMap.get(Integer.parseInt(goodsIdStr));
        				if(batchUpdateGoodsMap.get(goods.getId()) != null){
        					goods = batchUpdateGoodsMap.get(goods.getId());
        				}
        				goods.setStockCount(new AtomicInteger(goods.getStockCount()).incrementAndGet());
        				goods.setSellCount(new AtomicInteger(goods.getSellCount()).decrementAndGet());
        				goods.setUpdateTime(date);
        				
        				batchUpdateGoodsMap.put(goods.getId(), goods);
        				
        				// 进货清单
        				goodsProcurement = goodsProcurementDao.getWithStockSurplusProcurement(goods.getId());
        				if(goodsProcurement != null){
        					if(goodsProcurementMap.get(goodsProcurement.getId()) != null){
        						goodsProcurement = goodsProcurementMap.get(goodsProcurement.getId());
        					}
        					goodsProcurement.setStockSoldNum(new AtomicInteger(goodsProcurement.getStockSoldNum()).decrementAndGet());
        					goodsProcurement.setUpdateTime(date);
        					goodsProcurementMap.put(goodsProcurement.getId(), goodsProcurement);
        				}
        				
                	}
                	
                	if(goodsProcurementMap.size() > 0){
                		goodsProcurementDao.batchUpdate(new ArrayList<GoodsProcurement>(goodsProcurementMap.values()));
                	}
                    
                    if(batchUpdateGoodsMap.size() > 0){
                    	goodsService.batchUpdate(new ArrayList<Goods>(batchUpdateGoodsMap.values()));
                    }
                }
            }
        }

        if (subjectChange.getStatus().equals(SubjectChangeStatusEnum.CANCELED)) {
            throw new BizException("声部更改已取消,请勿缴费");
        }
        if (subjectChange.getStatus().equals(SubjectChangeStatusEnum.SUCCESSED)) {
            throw new BizException("声部更改已缴费,请勿重复缴费");
        }

        HashSet<String> musicGroupIds = new HashSet<>();
        musicGroupIds.add(subjectChange.getMusicGroupId());
        List<MusicGroupPurchaseList> musicGroupPurchaseListCount = musicGroupPurchaseListDao.getCount(musicGroupIds);
        if (musicGroupPurchaseListCount.size() > 0) {
            throw new BizException("乐团当前状态不能缴声部更改费用");
        }
        BigDecimal amount = subjectChange.getGoodsMargin().add(subjectChange.getCourseMargin());

        studentDao.lockUser(subjectChange.getStudentId());

        String orderNo = idGeneratorService.generatorId("payment") + "";

        String channelType = "";

        Integer tenantId = subjectChangeParamDto.getTenantId();

        //只允许使用这类型的优惠券
        String[] checkCoupon = CouponDetailTypeEnum.getAllowType(CouponDetailTypeEnum.ACCESSORIES, CouponDetailTypeEnum.MUSICAL);
        StudentPaymentOrder studentPaymentOrder = sysCouponCodeService.use(subjectChangeParamDto.getCouponIdList(), amount, true, checkCoupon);
        amount = studentPaymentOrder.getActualAmount();
        studentPaymentOrder.setUserId(subjectChange.getStudentId());
        studentPaymentOrder.setGroupType(SUBJECT_CHANGE);
        studentPaymentOrder.setOrderNo(orderNo);
        studentPaymentOrder.setType(OrderTypeEnum.SUBJECT_CHANGE);
        studentPaymentOrder.setStatus(DealStatusEnum.ING);
        studentPaymentOrder.setMusicGroupId(subjectChange.getId().toString());
        studentPaymentOrder.setPaymentChannel("BALANCE");
        studentPaymentOrder.setUpdateTime(date);
        studentPaymentOrder.setOrganId(subjectChange.getOrganId());
        studentPaymentOrder.setRoutingOrganId(subjectChange.getOrganId());
        studentPaymentOrder.setTenantId(tenantId);
        studentPaymentOrderService.insert(studentPaymentOrder);

        subjectChange.setOrderId(studentPaymentOrder.getId().intValue());
        subjectChange.setOrderNo(orderNo);
        subjectChange.setOrganId(studentPaymentOrder.getOrganId());
        subjectChange.setUpdateTime(date);
        subjectChange.setStatus(SubjectChangeStatusEnum.ING);
        if (subjectChangeDao.update(subjectChange) <= 0) {
            throw new BizException("下单失败，请重试");
        }

        studentPaymentOrder.setVersion(0);
        if (amount.compareTo(subjectChangeParamDto.getAmount()) != 0) {
            throw new BizException("价格异常");
        }

        BigDecimal balance = BigDecimal.ZERO;
        if (subjectChangeParamDto.getUseBalancePayment() && amount.compareTo(BigDecimal.ZERO) > 0) {
            SysUserCashAccount userCashAccount = sysUserCashAccountService.getLocked(subjectChange.getStudentId());
            if (userCashAccount == null) {
                throw new BizException("用户账户找不到");
            }
            if (userCashAccount.getBalance() != null && userCashAccount.getBalance().compareTo(BigDecimal.ZERO) > 0) {
                balance = amount.compareTo(userCashAccount.getBalance()) >= 0 ? userCashAccount.getBalance() : amount;
                amount = amount.subtract(balance);
                studentPaymentOrder.setActualAmount(amount);
                studentPaymentOrder.setBalancePaymentAmount(balance);
                sysUserCashAccountService.updateBalance(subjectChange.getStudentId(), balance.negate(), PlatformCashAccountDetailTypeEnum.PAY_FEE, "乐器维修");
            }
        }
        if (amount.compareTo(subjectChangeParamDto.getAmount()) != 0) {
            throw new BizException("价格异常");
        }
        studentPaymentOrderService.update(studentPaymentOrder);

        studentPaymentOrder.setVersion(studentPaymentOrder.getVersion() + 1);
        
        //添加订单详情
        List<StudentPaymentOrderDetail> studentPaymentOrderDetailList = new ArrayList<StudentPaymentOrderDetail>();
        // 添加studentPaymentOrderDetail
        StudentPaymentOrderDetail studentPaymentOrderDetail = null;

        BigDecimal totalPrice = BigDecimal.ZERO;
        
        List<String> changeGoodsList = new ArrayList<String>();
        
        if(StringUtils.isNotBlank(subjectChange.getChangeAccessories())){
        	for(String id : subjectChange.getChangeAccessories().split(",")){
        		changeGoodsList.add(id);
        	}
        	
        	if(subjectChange.getChangeMusical() != subjectChange.getOriginalMusical()){
        		changeGoodsList.add(subjectChange.getChangeMusical()+"");
        	}
        }
        
        if(changeGoodsList != null){
            String goodsIdsStr = changeGoodsList.stream().map(t -> t.toString()).collect(Collectors.joining(","));
        	List<Goods> goodsList = goodsService.findGoodsByIds(goodsIdsStr);
        	Map<Integer,Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(Goods :: getId, t -> t));
        	Goods goods = null;
        	BigDecimal goodsTotalGroupPurchasePrice = BigDecimal.ZERO;
        	
        	for(String goodsIdStr : goodsIdsStr.split(",")){
        		if(StringUtils.isBlank(goodsIdStr)){
					continue;
				}
				goods = goodsMap.get(Integer.parseInt(goodsIdStr));
				goodsTotalGroupPurchasePrice = goodsTotalGroupPurchasePrice.add(goods.getGroupPurchasePrice());
        	}
        	
        	for(String goodsIdStr : goodsIdsStr.split(",")){
        		if(StringUtils.isBlank(goodsIdStr)){
					continue;
				}
				goods = goodsMap.get(Integer.parseInt(goodsIdStr));
				
				studentPaymentOrderDetail = new StudentPaymentOrderDetail();
                studentPaymentOrderDetail.setCreateTime(date);
                if(goods.getType() == GoodsType.INSTRUMENT){
                	studentPaymentOrderDetail.setType(OrderDetailTypeEnum.MUSICAL);
                }else if(goods.getType() == GoodsType.ACCESSORIES){
                	studentPaymentOrderDetail.setType(OrderDetailTypeEnum.ACCESSORIES);
                }else if(goods.getType() == GoodsType.TEACHING || goods.getType() == GoodsType.STAFF){
                	studentPaymentOrderDetail.setType(OrderDetailTypeEnum.TEACHING);
                }else{
                	studentPaymentOrderDetail.setType(OrderDetailTypeEnum.OTHER);
                }
                studentPaymentOrderDetail.setGoodsIdList(goods.getId() + "");
                
                BigDecimal tempPrice = studentPaymentOrder.getExpectAmount().multiply(goods.getGroupPurchasePrice()).divide(goodsTotalGroupPurchasePrice, BigDecimal.ROUND_DOWN).setScale(2, BigDecimal.ROUND_DOWN);
                
                if(totalPrice.add(tempPrice).compareTo(studentPaymentOrder.getExpectAmount()) > 0 || goodsIdsStr.split(",").length == studentPaymentOrderDetailList.size() + 1){
                	studentPaymentOrderDetail.setPrice(studentPaymentOrder.getExpectAmount().subtract(totalPrice));
                }else{
                	studentPaymentOrderDetail.setPrice(tempPrice);
                }
                studentPaymentOrderDetail.setUpdateTime(date);
                studentPaymentOrderDetail.setPaymentOrderId(studentPaymentOrder.getId());
                
                totalPrice = totalPrice.add(studentPaymentOrderDetail.getPrice());
                studentPaymentOrderDetailList.add(studentPaymentOrderDetail);
        	}
            
            if(studentPaymentOrderDetailList.size() > 0){
            	studentPaymentOrderDetailService.batchAdd(studentPaymentOrderDetailList);
            }
        }

        if (amount.compareTo(BigDecimal.ZERO) == 0) {
            studentPaymentRouteOrderService.addRouteOrder(orderNo, subjectChange.getOrganId(), studentPaymentOrder.getExpectAmount());
            Map<String, String> notifyMap = new HashMap<>();
            notifyMap.put("tradeState", "1");
            notifyMap.put("merOrderNo", studentPaymentOrder.getOrderNo());
            notifyMap.put("channelType", channelType);
            notifyMap.put("orderNo", "");
            studentPaymentOrderService.updateOrder(notifyMap);
            notifyMap.put("orderNo", orderNo);
            return notifyMap;
        }

        String baseApiUrl = sysConfigDao.findConfigValue(SysConfigService.BASE_API_URL);

        Map<String, Object> payMap = payService.getPayMap(
                amount,
                balance,
                orderNo,
                baseApiUrl + "/api-student/studentOrder/callback",
                baseApiUrl + "/api-student/studentOrder/paymentResult?orderNo=" + orderNo,
                "声部更换",
                "声部更换",
                subjectChange.getOrganId(),
                "subjectChange"
        );

        studentPaymentOrder.setMerNos((String) payMap.get("routingMerNos"));
        studentPaymentOrder.setPaymentChannel((String) payMap.get("type"));
        studentPaymentOrder.setUpdateTime(date);
        studentPaymentOrderService.update(studentPaymentOrder);
        return payMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SubjectChange addChange(SubjectChange subjectChange) {
        String musicGroupId = subjectChange.getMusicGroupId();
        SubjectChange studentWaitPay = subjectChangeDao.getStudentWaitPay(subjectChange.getStudentId(), musicGroupId);
        if (studentWaitPay != null) {
            throw new BizException("已有未支付的声部更改，请勿重复创建");
        }

        studentWaitPay = subjectChangeDao.getStudentLastChange(subjectChange.getStudentId(), musicGroupId);
        if (studentWaitPay != null) {
            throw new BizException("声部更换只能操作一次，请勿重复操作");
        }

        Set<String> musicGroupIds = new HashSet<String>();
        musicGroupIds.add(musicGroupId);

        List<MusicGroupPurchaseList> musicGroupPurchaseListCount = musicGroupPurchaseListDao.getCount(musicGroupIds);
        if (musicGroupPurchaseListCount.size() > 0) {
            throw new BizException("乐器清单已确认，不能做声部更改");
        }

        //判断新声部人数是否已满
        MusicGroupSubjectPlan changeSubjectPlan = musicGroupSubjectPlanService.getMusicOneSubjectClassPlan(musicGroupId, subjectChange.getChangeSubjectId());
        if (changeSubjectPlan.getExpectedStudentNum() <= changeSubjectPlan.getPaidStudentNum()) {
            throw new BizException("当前声部人数已满");
        }

        StudentRegistration studentRegistration = studentRegistrationDao.getStudentRegister(musicGroupId, subjectChange.getStudentId());
        if (studentRegistration == null) {
            throw new BizException("用户乐团报名信息查询失败");
        }

        if (studentRegistration.getNoneNeedCloudTeacher() != null && studentRegistration.getNoneNeedCloudTeacher() == 1) {
            if (changeSubjectPlan.getPaidZeroNum() != null && changeSubjectPlan.getPaidZeroNum() > 0) {
                throw new BizException("声部更换失败，当前用户是0元入团，且当前声部已有0元入团学生");
            }
        }
        Date nowDate = new Date();
        SubjectChange studentOriginal = getStudentOriginal(subjectChange.getStudentId(), subjectChange.getMusicGroupId());
        if (studentOriginal != null) {
            subjectChange.setOriginalOrderId(studentOriginal.getOrderId());
            subjectChange.setOriginalCost(studentOriginal.getOriginalCost());
            subjectChange.setOriginalAccessories(studentOriginal.getOriginalAccessories());
            subjectChange.setOriginalKitGroupPurchaseType(
                    studentOriginal.getOriginalKitGroupPurchaseType() == null ? KitGroupPurchaseTypeEnum.OWNED : studentOriginal.getOriginalKitGroupPurchaseType());
        }
        subjectChange.setStatus(SubjectChangeStatusEnum.WAIT_PAY);
        MusicGroup musicGroup = musicGroupDao.get(subjectChange.getMusicGroupId());
        subjectChange.setOrganId(musicGroup.getOrganId());
        subjectChange.setCooperationOrganId(musicGroup.getCooperationOrganId());
        subjectChange.setEduTeacherId(musicGroup.getEducationalTeacherId());
        subjectChange.setCourseMargin(subjectChange.getChangeCourseFee().subtract(subjectChange.getOriginalCourseFee()));
        BigDecimal goodsMargin = subjectChange.getChangeMusicalPrice().add(subjectChange.getChangeAccessoriesPrice())
                .subtract(subjectChange.getOriginalMusicalPrice()).subtract(subjectChange.getOriginalAccessoriesPrice());
        subjectChange.setGoodsMargin(goodsMargin);
        String goodsIds = "";
        if (subjectChange.getKitGroupPurchaseType().equals(KitGroupPurchaseTypeEnum.OWNED)) {
            subjectChange.setChangeMusical(null);
        }
        if (subjectChange.getChangeMusical() != null) {
            goodsIds += subjectChange.getChangeMusical() + "";
        }
        if (StringUtils.isNotBlank(subjectChange.getChangeAccessories())) {
            goodsIds += StringUtils.isNotBlank(goodsIds) ? "," + subjectChange.getChangeAccessories() : subjectChange.getChangeAccessories();
        } else {
            subjectChange.setChangeAccessories(null);
        }
        BigDecimal goodsPrice = BigDecimal.ZERO;
        List<Goods> goodies = goodsDao.findGoodsByIds(goodsIds);
        for (Goods goods : goodies) {
            goodsPrice = goodsPrice.add(goods.getDiscountPrice());
        }
        
        Map<Integer,Goods> goodsMap = goodies.stream().collect(Collectors.toMap(Goods :: getId, t -> t));
        
        subjectChange.setChangeCost(goodsPrice);
        subjectChange.setCostMargin(subjectChange.getChangeCost().subtract(subjectChange.getOriginalCost()));
        //差价 <= 0
        BigDecimal amountMargin = subjectChange.getGoodsMargin().add(subjectChange.getCourseMargin());
        if (amountMargin.compareTo(BigDecimal.ZERO) <= 0) {
            subjectChange.setStatus(SubjectChangeStatusEnum.SUCCESSED);
            subjectChange.setSellAmount(BigDecimal.ZERO);
            subjectChange.setSellTime(nowDate);
        }
        subjectChange.setCreateTime(nowDate);
        subjectChange.setUpdateTime(nowDate);
        subjectChange.setVersion(0);
        subjectChangeDao.insert(subjectChange);
        //差价小于0退到余额
        if (amountMargin.compareTo(BigDecimal.ZERO) <= 0) {
//            sysUserCashAccountService.updateBalance(subjectChange.getStudentId(), amountMargin.negate(), PlatformCashAccountDetailTypeEnum.REFUNDS, "声部更换退还");
            if (amountMargin.compareTo(BigDecimal.ZERO) < 0) {
                SysUserCashAccountLog sysUserCashAccountLog = new SysUserCashAccountLog();
                sysUserCashAccountLog.setUserId(subjectChange.getStudentId());
                sysUserCashAccountLog.setGroupType(SUBJECT_CHANGE);
                sysUserCashAccountLog.setOrganId(subjectChange.getOrganId());
                sysUserCashAccountLog.setGroupId(subjectChange.getId().toString());
                sysUserCashAccountLog.setAmount(amountMargin.negate());
                sysUserCashAccountLog.setReturnFeeType(ReturnFeeEnum.SUBJECT_CHANGE);
                sysUserCashAccountLog.setComment("声部更换退还");
                sysUserCashAccountLogDao.insert(sysUserCashAccountLog);
            }
            studentRegistrationService.changeStudentSubject(subjectChange.getStudentId(), subjectChange.getMusicGroupId(), subjectChange.getOriginalSubjectId(), subjectChange.getChangeSubjectId());
        }

        if (amountMargin.compareTo(BigDecimal.ZERO) <= 0) {
            String orderNo = idGeneratorService.generatorId("payment") + "";
            StudentPaymentOrder studentPaymentOrder = new StudentPaymentOrder();
            studentPaymentOrder.setUserId(subjectChange.getStudentId());
            studentPaymentOrder.setGroupType(SUBJECT_CHANGE);
            studentPaymentOrder.setOrderNo(orderNo);
            studentPaymentOrder.setType(OrderTypeEnum.SUBJECT_CHANGE);
            studentPaymentOrder.setExpectAmount(BigDecimal.ZERO);
            studentPaymentOrder.setActualAmount(BigDecimal.ZERO);
            studentPaymentOrder.setBalancePaymentAmount(BigDecimal.ZERO);
            studentPaymentOrder.setStatus(DealStatusEnum.SUCCESS);
            studentPaymentOrder.setMusicGroupId(subjectChange.getId().toString());
            studentPaymentOrder.setPaymentChannel("BALANCE");
            studentPaymentOrder.setUpdateTime(nowDate);
            studentPaymentOrder.setOrganId(subjectChange.getOrganId());
            studentPaymentOrder.setRoutingOrganId(subjectChange.getOrganId());
            studentPaymentOrderService.insert(studentPaymentOrder);

            subjectChange.setOrderId(studentPaymentOrder.getId().intValue());
            subjectChange.setOrderNo(orderNo);
            subjectChangeDao.update(subjectChange);

            //添加订单详情
            List<StudentPaymentOrderDetail> studentPaymentOrderDetailList = new ArrayList<StudentPaymentOrderDetail>();
            StudentPaymentOrderDetail studentPaymentOrderDetail = null;
        	Goods goods = null;
			List<Integer> minuendStockGoodsIdList = null;
			GoodsProcurement goodsProcurement = null;
			Map<Long, GoodsProcurement> goodsProcurementMap = new HashMap<Long, GoodsProcurement>();
			Map<Integer, Goods> batchUpdateGoodsMap = new HashMap<Integer, Goods>();
			
            for(String goodsIdStr : goodsIds.split(",")){
        		if(StringUtils.isBlank(goodsIdStr)){
					continue;
				}
				goods = goodsMap.get(Integer.parseInt(goodsIdStr));
				
				if(goods != null){
					studentPaymentOrderDetail = new StudentPaymentOrderDetail();
	                studentPaymentOrderDetail.setCreateTime(nowDate);
	                if(goods.getType() == GoodsType.INSTRUMENT){
	                	studentPaymentOrderDetail.setType(OrderDetailTypeEnum.MUSICAL);
	                }else if(goods.getType() == GoodsType.ACCESSORIES){
	                	studentPaymentOrderDetail.setType(OrderDetailTypeEnum.ACCESSORIES);
	                }else if(goods.getType() == GoodsType.TEACHING || goods.getType() == GoodsType.STAFF){
	                	studentPaymentOrderDetail.setType(OrderDetailTypeEnum.TEACHING);
	                }else{
	                	studentPaymentOrderDetail.setType(OrderDetailTypeEnum.OTHER);
	                }
	                studentPaymentOrderDetail.setGoodsIdList(goodsIdStr);
                	studentPaymentOrderDetail.setPrice(BigDecimal.ZERO);
	                studentPaymentOrderDetail.setUpdateTime(nowDate);
	                studentPaymentOrderDetail.setPaymentOrderId(studentPaymentOrder.getId());
	              
	                //忽略的分部
	        		List<Integer> ignoreOrganList = Arrays.asList(4, 55, 59);
	        		
	        		//查询商品收款账户配置
	                String goodsSellReceiptMerNo = sysConfigDao.findConfigValue("goodsSellReceiptMerNo");
	                
	                if(!ignoreOrganList.contains(subjectChange.getOrganId()) && StringUtils.isNotBlank(goodsSellReceiptMerNo)){
	                	//扣减库存
						
						BigDecimal totalGroupPurchaseAmount = BigDecimal.ZERO;
						
						BigDecimal groupPurchaseAmount = BigDecimal.ZERO;
						
						minuendStockGoodsIdList = new ArrayList<Integer>();
						
						// 是否是组合商品
						if(StringUtils.isNotBlank(goods.getComplementGoodsIdList())){
							List<Goods> goodsList = goodsService.getGoodsWithLocked(goods.getComplementGoodsIdList());
							totalGroupPurchaseAmount =  totalGroupPurchaseAmount.add(goodsList.stream().map(Goods :: getGroupPurchasePrice).reduce(BigDecimal.ZERO,BigDecimal :: add));
							
							for(Goods subGoods : goodsList){
								
								if(batchUpdateGoodsMap.get(subGoods.getId()) != null){
									subGoods = batchUpdateGoodsMap.get(subGoods.getId());
								}
								//判断是否有内部库存
								if(subGoods.getStockCount() > 0){
									
									groupPurchaseAmount = groupPurchaseAmount.add(subGoods.getGroupPurchasePrice());
									subGoods.setStockCount(new AtomicInteger(subGoods.getStockCount()).decrementAndGet());
									subGoods.setSellCount(new AtomicInteger(subGoods.getSellCount()).incrementAndGet());
									subGoods.setUpdateTime(nowDate);
									
									batchUpdateGoodsMap.put(subGoods.getId(), subGoods);
									
									minuendStockGoodsIdList.add(subGoods.getId());
									
									goodsProcurement = goodsProcurementDao.getWithStockSurplusProcurement(subGoods.getId());
									if(goodsProcurement != null){
										if(goodsProcurementMap.get(goodsProcurement.getId()) != null){
											goodsProcurement = goodsProcurementMap.get(goodsProcurement.getId());
										}
										goodsProcurement.setStockSoldNum(new AtomicInteger(goodsProcurement.getStockSoldNum()).incrementAndGet());
										goodsProcurement.setUpdateTime(nowDate);
										goodsProcurementMap.put(goodsProcurement.getId(), goodsProcurement);
									}
								}
							}
						}else{
							totalGroupPurchaseAmount = totalGroupPurchaseAmount.add(goods.getGroupPurchasePrice());
							
							if(batchUpdateGoodsMap.get(goods.getId()) != null){
								goods = batchUpdateGoodsMap.get(goods.getId());
							}
							
							//判断是否有内部库存
							if(goods.getStockCount() > 0){
								
								groupPurchaseAmount = groupPurchaseAmount.add(goods.getGroupPurchasePrice());
								goods.setStockCount(new AtomicInteger(goods.getStockCount()).decrementAndGet());
								goods.setSellCount(new AtomicInteger(goods.getSellCount()).incrementAndGet());
								goods.setUpdateTime(nowDate);
								batchUpdateGoodsMap.put(goods.getId(), goods);
								
								minuendStockGoodsIdList.add(goods.getId());
								
								goodsProcurement = goodsProcurementDao.getWithStockSurplusProcurement(goods.getId());
								if(goodsProcurement != null){
									if(goodsProcurementMap.get(goodsProcurement.getId()) != null){
										goodsProcurement = goodsProcurementMap.get(goodsProcurement.getId());
									}
									goodsProcurement.setStockSoldNum(new AtomicInteger(goodsProcurement.getStockSoldNum()).incrementAndGet());
									goodsProcurement.setUpdateTime(nowDate);
									goodsProcurementMap.put(goodsProcurement.getId(), goodsProcurement);
								}
							}
						}
						
						if (minuendStockGoodsIdList.size() > 0) {
							studentPaymentOrderDetail.setIncomeItem(OrderDetailTypeEnum.CLOUD_TEACHER.name());
							studentPaymentOrderDetail.setIncome(BigDecimal.ZERO);
							studentPaymentOrderDetail.setBalanceIncome(BigDecimal.ZERO);
							if(minuendStockGoodsIdList.size() > 0){
								studentPaymentOrderDetail.setMinuendStockGoodsIdList(minuendStockGoodsIdList.stream().map(t -> t.toString()).collect(Collectors.joining(",")));
							}
						}
	                }
	                
	                studentPaymentOrderDetailList.add(studentPaymentOrderDetail);
				}
        	}
            
            //原始订单的库存要归还
            List<StudentPaymentOrderDetail> originalStudentPaymentOrderDetailList = studentPaymentOrderDetailService.queryOrderDetail(subjectChange.getOriginalOrderId().longValue());
            String originalGoodsIds = originalStudentPaymentOrderDetailList.stream().filter(t -> StringUtils.isNotBlank(t.getMinuendStockGoodsIdList())).map(t -> t.getMinuendStockGoodsIdList()).collect(Collectors.joining(","));
            if(StringUtils.isNotBlank(originalGoodsIds)){
            	List<Goods> goodsList = goodsService.getGoodsWithLocked(originalGoodsIds);
            	goodsMap = goodsList.stream().collect(Collectors.toMap(Goods :: getId, t -> t));
            	
            	for(String goodsIdStr : originalGoodsIds.split(",")){
            		if(StringUtils.isBlank(goodsIdStr)){
						continue;
					}
					goods = goodsMap.get(Integer.parseInt(goodsIdStr));
					if(batchUpdateGoodsMap.get(goods.getId()) != null){
						goods = batchUpdateGoodsMap.get(goods.getId());
					}
					goods.setStockCount(new AtomicInteger(goods.getStockCount()).incrementAndGet());
					goods.setSellCount(new AtomicInteger(goods.getSellCount()).decrementAndGet());
					goods.setUpdateTime(nowDate);
					
					batchUpdateGoodsMap.put(goods.getId(), goods);
					
					// 进货清单
					goodsProcurement = goodsProcurementDao.getWithStockSurplusProcurement(goods.getId());
					if(goodsProcurement != null){
						if(goodsProcurementMap.get(goodsProcurement.getId()) != null){
							goodsProcurement = goodsProcurementMap.get(goodsProcurement.getId());
						}
						goodsProcurement.setStockSoldNum(new AtomicInteger(goodsProcurement.getStockSoldNum()).decrementAndGet());
						goodsProcurement.setUpdateTime(nowDate);
						goodsProcurementMap.put(goodsProcurement.getId(), goodsProcurement);
					}
					
            	}
            }
            
            if(studentPaymentOrderDetailList.size() > 0){
            	studentPaymentOrderDetailService.batchAdd(studentPaymentOrderDetailList);
            }
			
			if(goodsProcurementMap.size() > 0){
				goodsProcurementDao.batchUpdate(new ArrayList<GoodsProcurement>(goodsProcurementMap.values()));
			}
    		
    		if(batchUpdateGoodsMap.size() > 0){
    			goodsService.batchUpdate(new ArrayList<Goods>(batchUpdateGoodsMap.values()));
    		}

            //退原订单商品
            sellOrderService.refundByOrderId(subjectChange.getOriginalOrderId().longValue(), false);

            if (StringUtils.isNotBlank(goodsIds)) {
                List<Integer> goodsIdList = Arrays.stream(goodsIds.split(",")).map(Integer::parseInt).collect(Collectors.toList());
                //添加新订单
                this.addSellOrder(studentPaymentOrder,
                        subjectChange.getMusicGroupId(),
                        goodsIdList,
                        subjectChange.getKitGroupPurchaseType());
            }
            //乐保处理
            studentInstrumentService.subjectChangeUpdateInstrument(subjectChange);
        }
        return subjectChange;
    }

    @Override
    public SubjectChange cancel(Integer id) {
        SubjectChange subjectChange = subjectChangeDao.get(id);
        if (!subjectChange.getStatus().equals(SubjectChangeStatusEnum.WAIT_PAY)) {
            throw new BizException("不是待支付状态，不能取消");
        }
        subjectChange.setStatus(SubjectChangeStatusEnum.CANCELED);
        subjectChange.setUpdateTime(new Date());
        if (subjectChangeDao.update(subjectChange) <= 0) {
            throw new BizException("取消失败,请重试");
        }
        return subjectChange;
    }

    @Override
    public SubjectChange getStudentOriginal(Integer studentId, String musicGroupId) {

        //1、存在历史的更换
        SubjectChange subjectChange = new SubjectChange();
        SubjectChange studentLastChange = subjectChangeDao.getStudentLastChange(studentId, musicGroupId);
        if (studentLastChange != null) {
            throw new BizException("声部更换只能操作一次，请勿重复操作");
        	/*List<Long> orderIdList = new ArrayList<Long>();
        	orderIdList.add(studentLastChange.getOrderId().longValue());
        	Set<Integer> refundSellOrderGoodsIds = getRefundGoodsId(orderIdList);
            subjectChange.setStudentId(studentLastChange.getStudentId());
            subjectChange.setOrderId(studentLastChange.getOrderId());
            subjectChange.setCooperationOrganId(studentLastChange.getCooperationOrganId());
            subjectChange.setMusicGroupId(studentLastChange.getMusicGroupId());
            subjectChange.setOriginalCourseFee(studentLastChange.getChangeCourseFee());
            subjectChange.setKitGroupPurchaseType(studentLastChange.getKitGroupPurchaseType());

            BigDecimal originalCost = BigDecimal.ZERO;
            if (studentLastChange.getChangeMusical() != null && !refundSellOrderGoodsIds.contains(studentLastChange.getChangeMusical())) {
                subjectChange.setOriginalMusical(studentLastChange.getChangeMusical());
                subjectChange.setOriginalMusicalGoods(goodsDao.get(studentLastChange.getChangeMusical()));
                subjectChange.setOriginalMusicalPrice(studentLastChange.getChangeMusicalPrice());
            }

            if (StringUtils.isNotBlank(studentLastChange.getChangeAccessories())) {
                String[] goodsIdArr = studentLastChange.getChangeAccessories().split(",");
                String accessoriesId = "";
                for (String goodsId : goodsIdArr) {
                    if (refundSellOrderGoodsIds.contains(Integer.parseInt(goodsId))) {
                        continue;
                    }
                    accessoriesId += StringUtils.isBlank(accessoriesId) ? goodsId : "," + goodsId;
                }
                if (StringUtils.isNotBlank(accessoriesId)) {
                    subjectChange.setOriginalAccessories(accessoriesId);
                    List<Goods> accessoriesGoods = goodsDao.findGoodsByIds(accessoriesId);
                    subjectChange.setOriginalAccessoriesGoods(accessoriesGoods);
                    BigDecimal price = accessoriesGoods.stream().map(Goods::getGroupPurchasePrice).reduce(BigDecimal.ZERO, BigDecimal::add);
                    subjectChange.setOriginalAccessoriesPrice(price);
                }
            }
            subjectChange.setOriginalCost(originalCost);
            return subjectChange;*/
        }
        //2.1不存在历史的更换
        StudentRegistration studentRegistration = studentRegistrationDao.queryByUserIdAndMusicGroupId(studentId, musicGroupId);
        if (studentRegistration == null) {
            throw new BizException("用户注册信息不存在");
        }

        List<StudentPaymentOrder> studentPaymentOrderList = new ArrayList<StudentPaymentOrder>();

        if (studentRegistration.getMusicGroupPaymentCalenderId() == null) {
            if (studentRegistration.getMusicGroupStatus() == StudentMusicGroupStatusEnum.NORMAL) {
                studentPaymentOrderList = studentPaymentOrderService.findMusicGroupApplyOrderByStatus(studentId, musicGroupId, SUCCESS);
            }
        } else {
            studentPaymentOrderList = studentPaymentOrderService.queryByBatchNo(studentId, studentRegistration.getMusicGroupPaymentCalenderId() + "",
                    DealStatusEnum.SUCCESS);
        }

        List<Long> paymentOrderIdList = studentPaymentOrderList.stream().map(t -> t.getId()).collect(Collectors.toList());

        List<StudentPaymentOrderDetail> details = new ArrayList<StudentPaymentOrderDetail>();
        if (paymentOrderIdList != null && paymentOrderIdList.size() > 0) {
            details = studentPaymentOrderDetailDao.getWithIds(paymentOrderIdList);
        }

        //查询乐器订单
        Long paymentOrderId = null;

        for (StudentPaymentOrderDetail detail : details) {
            if (detail.getType() == OrderDetailTypeEnum.ACCESSORIES || detail.getType() == OrderDetailTypeEnum.MUSICAL) {
                if (paymentOrderId == null || paymentOrderId < detail.getPaymentOrderId()) {
                    paymentOrderId = detail.getPaymentOrderId();
                }
            }
        }

        Set<Integer> refundSellOrderGoodsIds = new HashSet<Integer>();
        if (details.size() > 0) {
            refundSellOrderGoodsIds = getRefundGoodsId(details.stream().map(t -> t.getPaymentOrderId()).collect(Collectors.toList()));
        }

        MusicGroup musicGroup = musicGroupDao.get(musicGroupId);
        subjectChange.setStudentId(studentId);
        subjectChange.setOrganId(musicGroup.getOrganId());
        subjectChange.setCooperationOrganId(musicGroup.getCooperationOrganId());
        subjectChange.setMusicGroupId(musicGroupId);

        if (paymentOrderId != null) {
            subjectChange.setOrderId(paymentOrderId.intValue());
        }

        String accessoriesIds = "";
        BigDecimal accessoriesPrice = BigDecimal.ZERO;
        for (StudentPaymentOrderDetail detail : details) {
            if (detail.getType().equals(OrderDetailTypeEnum.COURSE)) {
                subjectChange.setOriginalCourseFee(detail.getPrice());
            } else if (detail.getType().equals(OrderDetailTypeEnum.MUSICAL)) {
                subjectChange.setOriginalKitGroupPurchaseType(detail.getKitGroupPurchaseType());
                Integer goodsId = Integer.parseInt(detail.getGoodsIdList());
                if (!refundSellOrderGoodsIds.contains(goodsId)) {
                    subjectChange.setOriginalMusical(goodsId);
                    subjectChange.setOriginalMusicalGoods(goodsDao.get(goodsId));
                    subjectChange.setOriginalMusicalPrice(detail.getPrice());
                }
            } else if (detail.getType().equals(OrderDetailTypeEnum.ACCESSORIES)) {
                if (StringUtils.isNotBlank(detail.getGoodsIdList())) {
                    accessoriesPrice = accessoriesPrice.add(detail.getPrice());
                    accessoriesIds = accessoriesIds.length() > 0 ? accessoriesIds + "," + detail.getGoodsIdList() : detail.getGoodsIdList();
                }
            }
        }
        if (StringUtils.isNotBlank(accessoriesIds)) {
            String[] goodsIdArr = accessoriesIds.split(",");
            String accessoriesId = "";
            for (String goodsId : goodsIdArr) {
                if (refundSellOrderGoodsIds.contains(Integer.parseInt(goodsId))) {
                    continue;
                }
                accessoriesId += StringUtils.isBlank(accessoriesId) ? goodsId : "," + goodsId;
            }

            subjectChange.setOriginalAccessories(accessoriesId);

            Map<Integer, Goods> goodsMap = goodsService.findGoodsByIds(accessoriesId).stream().collect(Collectors.toMap(Goods::getId, Goods -> Goods));

            String[] accessoriesIdList = StringUtils.split(accessoriesId, ',');

            for (String s : accessoriesIdList) {
                if (StringUtils.isBlank(s)) {
                    continue;
                }

                if (subjectChange.getOriginalAccessoriesGoods() == null) {
                    subjectChange.setOriginalAccessoriesGoods(new ArrayList<Goods>());
                }
                subjectChange.getOriginalAccessoriesGoods().add(goodsMap.get(Integer.parseInt(s)));
            }

            //BigDecimal price = accessoriesGoods.stream().map(Goods::getGroupPurchasePrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            subjectChange.setOriginalAccessoriesPrice(accessoriesPrice);
        }
        //2.2 计算销售成本
        BigDecimal orderSellCost = BigDecimal.ZERO;
        if (paymentOrderIdList != null && paymentOrderIdList.size() > 0) {
            orderSellCost = sellOrderDao.getOrderSellCost(paymentOrderIdList);
        }
        subjectChange.setOriginalCost(orderSellCost == null ? BigDecimal.ZERO : orderSellCost);
        return subjectChange;
    }

    private Set<Integer> getRefundGoodsId(List<Long> orderIdList) {
        List<SellOrder> refundSellOrders = sellOrderService.getRefundSellOrder(orderIdList);
        Set<Integer> refundSellOrderGoodsIds = refundSellOrders.stream().filter(e -> e.getParentGoodsId() == null).map(SellOrder::getGoodsId).collect(Collectors.toSet());
        Set<Integer> refundSellOrderParentGoodsId = refundSellOrders.stream().filter(e -> e.getParentGoodsId() != null).map(SellOrder::getParentGoodsId).collect(Collectors.toSet());
        refundSellOrderGoodsIds.addAll(refundSellOrderParentGoodsId);
        return refundSellOrderGoodsIds;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean orderCallback(StudentPaymentOrder studentPaymentOrder) {
        Date nowDate = new Date();
        //更新订单信息
        studentPaymentOrder.setUpdateTime(nowDate);
        int updateCount = studentPaymentOrderService.update(studentPaymentOrder);
        if (updateCount <= 0) {
            throw new BizException("订单更新失败");
        }

        //更新声部更改信息状态
        SubjectChange subjectChange = subjectChangeDao.get(Integer.parseInt(studentPaymentOrder.getMusicGroupId()));
        if (subjectChange == null) {
            throw new BizException("声部更换订单不存在");
        }
        if (!subjectChange.getStatus().equals(SubjectChangeStatusEnum.ING)) {
            return true;
        }

        subjectChange.setUpdateTime(nowDate);
        subjectChange.setTransNo(studentPaymentOrder.getTransNo());
        subjectChange.setMerNo(studentPaymentOrder.getMerNos());
        subjectChange.setPaymentChannel(studentPaymentOrder.getPaymentChannel());

        Integer userId = studentPaymentOrder.getUserId();

        Map<Integer, String> map = new HashMap<>();
        map.put(userId, userId.toString());

        if (studentPaymentOrder.getStatus() == DealStatusEnum.SUCCESS) {
            try {
                contractService.transferProduceContract(userId, subjectChange.getMusicGroupId(), null);
            } catch (Exception e) {
                logger.error("产品协议生成失败", e);
            }
            //更新声部信息报名数量
            studentRegistrationService.changeStudentSubject(subjectChange.getStudentId(), subjectChange.getMusicGroupId(), subjectChange.getOriginalSubjectId(), subjectChange.getChangeSubjectId());

            subjectChange.setStatus(SubjectChangeStatusEnum.SUCCESSED);
            subjectChange.setSellTime(nowDate);

            if (this.update(subjectChange) <= 0) {
                throw new BizException("维修单更新失败");
            }
            String goodsIds = "";
            if (subjectChange.getChangeMusical() != null) {
                goodsIds += subjectChange.getChangeMusical() + "";
            }
            if (subjectChange.getChangeAccessories() != null) {
                goodsIds += StringUtils.isNotBlank(goodsIds) ? "," + subjectChange.getChangeAccessories() : subjectChange.getChangeAccessories();
            }
            if (StringUtils.isNotBlank(goodsIds)) {
                List<Integer> goodsIdList = Arrays.stream(goodsIds.split(",")).map(Integer::parseInt).collect(Collectors.toList());
                //退原订单商品
                if (subjectChange.getOriginalOrderId() != null) {
                    sellOrderService.refundByOrderId(subjectChange.getOriginalOrderId().longValue(), false);
                }
                //添加新订单
                List<SellOrder> sellOrders = this.addSellOrder(studentPaymentOrder,subjectChange.getMusicGroupId(),
                        goodsIdList,
                        subjectChange.getKitGroupPurchaseType());
                if (sellOrders != null && sellOrders.size() > 0) {
                    SubjectChange change = subjectChangeDao.get(subjectChange.getId());
                    BigDecimal instrumentAmount = sellOrders.stream().filter(e -> e.getType() == SellTypeEnum.INSTRUMENT).map(e -> e.getExpectAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);
                    BigDecimal accessoriesAmount = sellOrders.stream().filter(e -> e.getType() == SellTypeEnum.ACCESSORIES).map(e -> e.getExpectAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);
                    change.setChangeMusicalPrice(instrumentAmount.add(change.getOriginalMusicalPrice()));
                    change.setChangeAccessoriesPrice(accessoriesAmount.add(change.getOriginalAccessoriesPrice()));
                    change.setSellAmount(instrumentAmount.add(accessoriesAmount));
                    subjectChangeDao.update(change);
                }
            }
            
            //更新学生声部
            Student student = studentDao.get(userId);
            if(student == null){
            	throw new BizException("学生[{}]信息查询失败", userId);
            }
            student.setSubjectIdList(subjectChange.getChangeSubjectId() + "");
            student.setUpdateTime(nowDate);
            studentDao.update(student);

            //乐保处理
            studentInstrumentService.subjectChangeUpdateInstrument(subjectChange);

            //插入交易明细
            BigDecimal amount = studentPaymentOrder.getActualAmount();
            SysUserCashAccount cashAccount = sysUserCashAccountService.get(userId);
            //充值
            SysUserCashAccountDetail rechargeDetail = new SysUserCashAccountDetail();
            rechargeDetail.setAmount(amount);
            rechargeDetail.setBalance(cashAccount.getBalance().add(amount));
            rechargeDetail.setComment("缴费前充值");
            rechargeDetail.setCreateTime(nowDate);
            rechargeDetail.setStatus(DealStatusEnum.SUCCESS);
            rechargeDetail.setTransNo(studentPaymentOrder.getTransNo());
            rechargeDetail.setType(PlatformCashAccountDetailTypeEnum.RECHARGE);
            rechargeDetail.setUpdateTime(nowDate);
            rechargeDetail.setUserId(userId);
            rechargeDetail.setChannel(studentPaymentOrder.getPaymentChannel());
            rechargeDetail.setComAmount(studentPaymentOrder.getComAmount());
            rechargeDetail.setPerAmount(studentPaymentOrder.getPerAmount());
            sysUserCashAccountDetailService.insert(rechargeDetail);

            //缴费
            SysUserCashAccountDetail paymentDetail = new SysUserCashAccountDetail();
            paymentDetail.setAmount(amount.negate());
            paymentDetail.setBalance(cashAccount.getBalance());
            paymentDetail.setComment("声部更改");
            paymentDetail.setCreateTime(nowDate);
            paymentDetail.setStatus(DealStatusEnum.SUCCESS);
            paymentDetail.setTransNo(studentPaymentOrder.getTransNo());
            paymentDetail.setType(PlatformCashAccountDetailTypeEnum.PAY_FEE);
            paymentDetail.setUpdateTime(nowDate);
            paymentDetail.setUserId(userId);
            rechargeDetail.setChannel(studentPaymentOrder.getPaymentChannel());
            if (studentPaymentOrder.getComAmount() != null) {
                rechargeDetail.setComAmount(studentPaymentOrder.getComAmount().negate());
                rechargeDetail.setPerAmount(studentPaymentOrder.getPerAmount().negate());
            }
            sysUserCashAccountDetailService.insert(paymentDetail);
            
            //增加商品库存
            List<StudentPaymentOrderDetail> studentPaymentOrderDetailList = studentPaymentOrderDetailService.queryOrderDetail(subjectChange.getOriginalOrderId().longValue());
            goodsIds = studentPaymentOrderDetailList.stream().filter(t -> StringUtils.isNotBlank(t.getMinuendStockGoodsIdList())).map(t -> t.getMinuendStockGoodsIdList()).collect(Collectors.joining(","));
            if(StringUtils.isNotBlank(goodsIds)){
            	GoodsProcurement goodsProcurement = null;
            	List<Goods> goodsList = goodsService.getGoodsWithLocked(goodsIds);
            	Map<Integer,Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(Goods :: getId, t -> t));
            	Goods goods = null;
    			Map<Integer, Goods> batchUpdateGoodsMap = new HashMap<Integer, Goods>();
    			Map<Long, GoodsProcurement> goodsProcurementMap = new HashMap<Long, GoodsProcurement>();
            	
            	for(String goodsIdStr : goodsIds.split(",")){
            		if(StringUtils.isBlank(goodsIdStr)){
						continue;
					}
					goods = goodsMap.get(Integer.parseInt(goodsIdStr));
					if(batchUpdateGoodsMap.get(goods.getId()) != null){
						goods = batchUpdateGoodsMap.get(goods.getId());
					}
					goods.setStockCount(new AtomicInteger(goods.getStockCount()).incrementAndGet());
					goods.setSellCount(new AtomicInteger(goods.getSellCount()).decrementAndGet());
					goods.setUpdateTime(nowDate);
					
					batchUpdateGoodsMap.put(goods.getId(), goods);
					
					// 进货清单
					goodsProcurement = goodsProcurementDao.getWithStockSurplusProcurement(goods.getId());
					if(goodsProcurement != null){
						if(goodsProcurementMap.get(goodsProcurement.getId()) != null){
							goodsProcurement = goodsProcurementMap.get(goodsProcurement.getId());
						}
						goodsProcurement.setStockSoldNum(new AtomicInteger(goodsProcurement.getStockSoldNum()).decrementAndGet());
						goodsProcurement.setUpdateTime(nowDate);
						goodsProcurementMap.put(goodsProcurement.getId(), goodsProcurement);
					}
					
            	}
            	
            	if(goodsProcurementMap.size() > 0){
            		goodsProcurementDao.batchUpdate(new ArrayList<GoodsProcurement>(goodsProcurementMap.values()));
            	}
                
                if(batchUpdateGoodsMap.size() > 0){
                	goodsService.batchUpdate(new ArrayList<Goods>(batchUpdateGoodsMap.values()));
                }
            }
        }

        if (studentPaymentOrder.getStatus() == DealStatusEnum.CLOSE || studentPaymentOrder.getStatus() == DealStatusEnum.FAILED) {
            subjectChange.setStatus(SubjectChangeStatusEnum.WAIT_PAY);
            if (this.update(subjectChange) <= 0) {
                throw new BizException("声部更改订单更新失败");
            }
            
            //增加商品库存
            List<StudentPaymentOrderDetail> studentPaymentOrderDetailList = studentPaymentOrderDetailService.queryOrderDetail(studentPaymentOrder.getId());
            String goodsIds = studentPaymentOrderDetailList.stream().filter(t -> StringUtils.isNotBlank(t.getMinuendStockGoodsIdList())).map(t -> t.getMinuendStockGoodsIdList()).collect(Collectors.joining(","));
            if(StringUtils.isNotBlank(goodsIds)){
            	GoodsProcurement goodsProcurement = null;
            	List<Goods> goodsList = goodsService.getGoodsWithLocked(goodsIds);
            	Map<Integer,Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(Goods :: getId, t -> t));
            	Goods goods = null;
    			Map<Integer, Goods> batchUpdateGoodsMap = new HashMap<Integer, Goods>();
    			Map<Long, GoodsProcurement> goodsProcurementMap = new HashMap<Long, GoodsProcurement>();
            	
            	for(String goodsIdStr : goodsIds.split(",")){
            		if(StringUtils.isBlank(goodsIdStr)){
						continue;
					}
					goods = goodsMap.get(Integer.parseInt(goodsIdStr));
					if(batchUpdateGoodsMap.get(goods.getId()) != null){
						goods = batchUpdateGoodsMap.get(goods.getId());
					}
					goods.setStockCount(new AtomicInteger(goods.getStockCount()).incrementAndGet());
					goods.setSellCount(new AtomicInteger(goods.getSellCount()).decrementAndGet());
					goods.setUpdateTime(nowDate);
					
					batchUpdateGoodsMap.put(goods.getId(), goods);
					
					// 进货清单
					goodsProcurement = goodsProcurementDao.getWithStockSurplusProcurement(goods.getId());
					if(goodsProcurement != null){
						if(goodsProcurementMap.get(goodsProcurement.getId()) != null){
							goodsProcurement = goodsProcurementMap.get(goodsProcurement.getId());
						}
						goodsProcurement.setStockSoldNum(new AtomicInteger(goodsProcurement.getStockSoldNum()).decrementAndGet());
						goodsProcurement.setUpdateTime(nowDate);
						goodsProcurementMap.put(goodsProcurement.getId(), goodsProcurement);
					}
					
            	}
            	
            	if(goodsProcurementMap.size() > 0){
            		goodsProcurementDao.batchUpdate(new ArrayList<GoodsProcurement>(goodsProcurementMap.values()));
            	}
                
                if(batchUpdateGoodsMap.size() > 0){
                	goodsService.batchUpdate(new ArrayList<Goods>(batchUpdateGoodsMap.values()));
                }
            }
            
            if (studentPaymentOrder.getBalancePaymentAmount() != null && studentPaymentOrder.getBalancePaymentAmount().compareTo(BigDecimal.ZERO) > 0) {
                sysUserCashAccountService.updateBalance(studentPaymentOrder.getUserId(), studentPaymentOrder.getBalancePaymentAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "声部更改支付失败");
            }
            sysCouponCodeService.quit(studentPaymentOrder.getCouponCodeId());
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.STUDENT_SMS_PUSH_SPORADIC_PAYMENT_FAILED, map, null, 0, null, "STUDENT",
                    studentPaymentOrder.getActualAmount(), "声部更换");
            return false;
        }
        return true;
    }

    @Override
    public PageInfo<SubjectChange> getPageList(SubjectChangeQueryInfo queryInfo) {
        if (queryInfo.getEndTime() != null) {
            queryInfo.setEndTime(DateUtil.getLastTimeWithDay(queryInfo.getEndTime()));
        }
        PageInfo<SubjectChange> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<String, Object>();
        MapUtil.populateMap(params, queryInfo);

        List<SubjectChange> dataList = new ArrayList<>();
        int count = subjectChangeDao.queryCount(params);

        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = subjectChangeDao.getPageList(params);
            for (SubjectChange subjectChange : dataList) {
                if (subjectChange.getOriginalMusical() != null) {
                    subjectChange.setOriginalMusicalGoods(goodsDao.get(subjectChange.getOriginalMusical()));
                }
                if (subjectChange.getOriginalAccessories() != null) {
                    subjectChange.setOriginalAccessoriesGoods(goodsDao.findGoodsByIds(subjectChange.getOriginalAccessories()));
                }
                if (subjectChange.getChangeMusical() != null) {
                    subjectChange.setChangeMusicalGoods(goodsDao.get(subjectChange.getChangeMusical()));
                }
                if (subjectChange.getChangeAccessories() != null) {
                    subjectChange.setChangeAccessoriesGoods(goodsDao.findGoodsByIds(subjectChange.getChangeAccessories()));
                }
            }
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public SubjectChange updateCostMargin(Integer id, BigDecimal costMargin) {
        SubjectChange subjectChange = subjectChangeDao.get(id);
        subjectChange.setCostMargin(costMargin);
        subjectChange.setUpdateTime(new Date());
        if (subjectChangeDao.update(subjectChange) <= 0) {
            throw new BizException("更新成本差失败,请重试");
        }
        return subjectChange;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public List<SellOrder> addSellOrder(StudentPaymentOrder order,
                                        String musicGroupId,
                                        List<Integer> goodsIds,
                                        KitGroupPurchaseTypeEnum kitGroupPurchaseType) {
        if (goodsIds == null || goodsIds.size() <= 0) {
            return null;
        }
        BigDecimal balance = order.getBalancePaymentAmount();
        if (balance == null) {
            balance = BigDecimal.ZERO;
        }
        Long orderId = order.getId();
        BigDecimal totalAmount = order.getExpectAmount();
        BigDecimal couponRemitAmount = order.getCouponRemitFee();
        Integer tenantId = order.getTenantId();
        MusicGroup musicGroup = new MusicGroup();
        if (StringUtils.isNotBlank(musicGroupId)) {
            musicGroup = musicGroupDao.get(musicGroupId);
        }
        int goodsNum = goodsIds.size();
        BigDecimal goodsTotalPrice = BigDecimal.ZERO;
        List<Goods> goodies = goodsDao.getGoodies(goodsIds);
        int i = 1;
        for (Integer goodsId : goodsIds) {
            for (Goods goods : goodies) {
                if (goods.getId().equals(goodsId)) {
                    goodsTotalPrice = goodsTotalPrice.add(goods.getGroupPurchasePrice());
                    break;
                }
            }
        }

        BigDecimal hasRouteBalance = BigDecimal.ZERO;
        BigDecimal hasRouteExpectAmount = BigDecimal.ZERO;

        AccountType accountType = sysPaymentConfigService.checkAccountType(PaymentChannelEnum.valueOf(order.getPaymentChannel()), order.getMerNos(), order.getTenantId());
        List<SellOrder> sellOrderList = goodsService.subtractStock(goodsIds, accountType);

        for (Integer goodsId : goodsIds) {
            BigDecimal goodsPrice = BigDecimal.ZERO;
            Goods nowGoods = new Goods();
            for (Goods goods : goodies) {
                if (goods.getId().equals(goodsId)) {
                    nowGoods = goods;
                    break;
                }
            }
            goodsPrice = nowGoods.getGroupPurchasePrice();
            BigDecimal expectAmount = BigDecimal.ZERO;
            BigDecimal goodsBalance = BigDecimal.ZERO;

            if (goodsTotalPrice.compareTo(BigDecimal.ZERO) > 0) {
                goodsBalance = balance.multiply(goodsPrice).divide(goodsTotalPrice, 2, BigDecimal.ROUND_DOWN);
                expectAmount = totalAmount.multiply(goodsPrice).divide(goodsTotalPrice, 2, BigDecimal.ROUND_DOWN);
            }
            if (i == goodsNum) {
                goodsBalance = balance.subtract(hasRouteBalance);
                expectAmount = totalAmount.subtract(hasRouteExpectAmount);
            }
            //租赁、免费的没有销售收入，有销售成本
            if (nowGoods.getType().equals(GoodsType.INSTRUMENT) && (kitGroupPurchaseType.equals(KitGroupPurchaseTypeEnum.LEASE) || kitGroupPurchaseType.equals(KitGroupPurchaseTypeEnum.FREE))) {
                goodsBalance = BigDecimal.ZERO;
                expectAmount = BigDecimal.ZERO;
            }

            hasRouteBalance = hasRouteBalance.add(goodsBalance);
            hasRouteExpectAmount = hasRouteExpectAmount.add(expectAmount);
            i++;

            int complementGoodsNum = nowGoods.getComplementGoodsIdList() == null ? 1 : nowGoods.getComplementGoodsIdList().split(",").length;
            BigDecimal complementPrice = nowGoods.getGroupPurchasePrice();
            Map<Integer, BigDecimal> complementGoodsPrice = new HashMap<>();
            complementGoodsPrice.put(nowGoods.getId(), nowGoods.getGroupPurchasePrice());
            if (nowGoods.getComplementGoodsIdList() != null) {
                List<Goods> complementGoodies = goodsDao.findGoodsByIds(nowGoods.getComplementGoodsIdList());
                complementGoodsPrice = complementGoodies.stream().collect(Collectors.toMap(Goods::getId, Goods::getGroupPurchasePrice));
                complementPrice = complementGoodies.stream().map(Goods::getGroupPurchasePrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            }

            BigDecimal hasRouteSellOrderExpectAmount = BigDecimal.ZERO;
            BigDecimal hasRouteSellOrderBalance = BigDecimal.ZERO;
            for (SellOrder sellOrder : sellOrderList) {
                if (!goodsId.equals(sellOrder.getParentGoodsId()) && !goodsId.equals(sellOrder.getGoodsId())) {
                    continue;
                }
                if (sellOrder.getHasRoute()) {
                    continue;
                }
                BigDecimal sellOrderExpectAmount = BigDecimal.ZERO;
                BigDecimal sellOrderBalance = BigDecimal.ZERO;
                if (complementPrice.compareTo(BigDecimal.ZERO) > 0) {
                    sellOrderExpectAmount = complementGoodsPrice.get(sellOrder.getGoodsId()).multiply(new BigDecimal(sellOrder.getNum())).multiply(expectAmount).divide(complementPrice, 2, BigDecimal.ROUND_DOWN);
                    sellOrderBalance = complementGoodsPrice.get(sellOrder.getGoodsId()).multiply(new BigDecimal(sellOrder.getNum())).multiply(goodsBalance).divide(complementPrice, 2, BigDecimal.ROUND_DOWN);
                }
                complementGoodsNum = complementGoodsNum - sellOrder.getNum();
                if (complementGoodsNum <= 0) {
                    sellOrderExpectAmount = expectAmount.subtract(hasRouteSellOrderExpectAmount);
                    sellOrderBalance = goodsBalance.subtract(hasRouteSellOrderBalance);
                }
                hasRouteSellOrderExpectAmount = hasRouteSellOrderExpectAmount.add(sellOrderExpectAmount);
                hasRouteSellOrderBalance = hasRouteSellOrderBalance.add(sellOrderBalance);

                sellOrder.setExpectAmount(sellOrderExpectAmount);
                sellOrder.setBalanceAmount(sellOrderBalance);
                sellOrder.setActualAmount(sellOrderExpectAmount.subtract(sellOrderBalance));
                sellOrder.setOrganId(order.getOrganId());
                sellOrder.setCooperationOrganId(musicGroup.getCooperationOrganId());
                sellOrder.setEduTeacherId(musicGroup.getEducationalTeacherId());
                sellOrder.setTransNo(order.getTransNo());
                sellOrder.setOrderId(order.getId());
                sellOrder.setOrderNo(order.getOrderNo());
                sellOrder.setUserId(order.getUserId());
                sellOrder.setPaymentChannel(order.getPaymentChannel());
                sellOrder.setMerNo(order.getMerNos());
                sellOrder.setSellTime(order.getCreateTime());
                sellOrder.setCreateIme(new Date());
                sellOrder.setUpdateTime(new Date());
                sellOrder.setTenantId(tenantId);
                if (nowGoods.getType().equals(GoodsType.INSTRUMENT)) {
                    sellOrder.setType(SellTypeEnum.INSTRUMENT);
                } else if (nowGoods.getType().equals(GoodsType.ACCESSORIES)) {
                    sellOrder.setType(SellTypeEnum.ACCESSORIES);
                } else {
                    sellOrder.setType(SellTypeEnum.OTHER);
                }
                sellOrder.setHasRoute(true);
                if (complementGoodsNum <= 0) {
                    break;
                }
            }
        }
        if (sellOrderList.size() > 0) {
            //如果有优惠券金额
            if (couponRemitAmount.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal reduce = sellOrderList.stream().map(e -> e.getExpectAmount()).reduce(BigDecimal.ZERO, BigDecimal::add);
                List<SellOrder> sellOrders = sellOrderList.stream().filter(e -> e.getExpectAmount().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
                //可用优惠券金额
                BigDecimal subCouponMarketAmount = couponRemitAmount;
                for (int j = 0; j < sellOrders.size(); j++) {
                    SellOrder sellOrder = sellOrders.get(j);
                    //如果是最后一件商品
                    if (j == sellOrders.size() - 1) {
                        sellOrder.setCouponRemitAmount(subCouponMarketAmount);
                    } else {
                        //获取比例
                        BigDecimal ratioAmount = sellOrder.getExpectAmount().divide(reduce, 6, BigDecimal.ROUND_HALF_UP);
                        //获取分配的减免金额
                        BigDecimal multiply = couponRemitAmount.multiply(ratioAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
                        subCouponMarketAmount = subCouponMarketAmount.subtract(multiply);
                        sellOrder.setCouponRemitAmount(multiply);
                    }
                }
            }
            sellOrderService.batchInsert(sellOrderList);
        }
        return sellOrderList;
    }
}
