package com.liquidnet.service.goblin.service.impl.manage;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.github.pagehelper.PageInfo;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.candy.vo.CandyCouponVo;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.GoblinBackOrder;
import com.liquidnet.service.goblin.entity.GoblinBackOrderLog;
import com.liquidnet.service.goblin.entity.GoblinOrderOperationLog;
import com.liquidnet.service.goblin.param.BackCouponParam;
import com.liquidnet.service.goblin.service.GoblinCouponService;
import com.liquidnet.service.goblin.service.IGoblinMailTrackService;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreOrderService;
import com.liquidnet.service.goblin.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;

@Service
@Slf4j
public class GoblinStoreOrderServiceImpl implements IGoblinStoreOrderService {

    @Autowired
    GoblinRedisUtils redisUtils;
    @Autowired
    GoblinMongoUtils mongoUtils;
    @Autowired
    GoblinOrderUtils orderUtils;
    @Autowired
    QueueUtils queueUtils;
    @Autowired
    GoblinCouponService goblinCouponService;
    @Autowired
    IGoblinMailTrackService goblinMailTrackService;
    @Value("${liquidnet.service.order.url-pay.goblinRefundUrl}")
    private String synUrl;
    @Value("${liquidnet.service.dragon.urls.refundApply}")
    private String refundApply;
    @Value("${liquidnet.service.dragon.urls.refundResult}")
    private String refundApplyCallBack;
    @Value("${liquidnet.service.order.url-pay.goblinRefundUrl}")
    private String goblinRefundUrl;


    @Override
    public ResponseDto<GoblinStoreOrderListPageVo> orderList(Integer page, String orderCode, Integer type, String cst, String cet, String expressContacts, String phone, Integer status) {
        String uid = CurrentUtil.getCurrentUid();
        GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVoByUid(uid);
        if (storeInfoVo == null) {
            return ResponseDto.failure("无法查看");
        }
        String storeId = storeInfoVo.getStoreId();
        HashMap<String, Object> map = mongoUtils.storeOrderList(storeId, page, orderCode, cst, cet, expressContacts, phone, status);
        long total = (long) map.get("total");
        List<GoblinStoreOrderVo> voList = (List<GoblinStoreOrderVo>) map.get("data");
        List<GoblinStoreOrderListVo> listVos = buildStoreOrderListVos(voList, false);
        PageInfo<GoblinStoreOrderListVo> pageInfo = new PageInfo(listVos);
        pageInfo.setTotal(total);

        GoblinStoreOrderListPageVo result = new GoblinStoreOrderListPageVo();
        result.setPageInfo(pageInfo);
        // 状态 Tab 数量：与列表共用筛选条件，但不带 status 参数
        result.setStatusCount(mongoUtils.storeOrderStatusCount(storeId, orderCode, cst, cet, expressContacts, phone));
        return ResponseDto.success(result);
    }

    @Override
    public void exportOrderList(HttpServletResponse response, String orderCode, String cst, String cet, String expressContacts, String phone, Integer status) {
        String uid = CurrentUtil.getCurrentUid();
        GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVoByUid(uid);
        if (storeInfoVo == null) {
            log.warn("店铺-订单:订单列表导出:无法查看[uid:{}]", uid);
            return;
        }
        List<GoblinStoreOrderVo> voList = mongoUtils.storeOrderExportList(storeInfoVo.getStoreId(), orderCode, cst, cet, expressContacts, phone, status);
        List<GoblinStoreOrderListVo> listVos = buildStoreOrderListVos(voList, true);
        if (org.springframework.util.CollectionUtils.isEmpty(listVos)) {
            log.warn("店铺-订单:订单列表导出:无数据[uid:{},storeId:{},orderCode:{},cst:{},cet:{},expressContacts:{},phone:{},status:{}]",
                    uid, storeInfoVo.getStoreId(), orderCode, cst, cet, expressContacts, phone, status);
            return;
        }
        ServletOutputStream servletOutputStream = null;
        try {
            String fileName = DateUtil.Formatter.yyyyMMddHHmmssTrim.format(LocalDateTime.now())
                    .concat(new String(("商城订单信息").getBytes("gb2312"), StandardCharsets.ISO_8859_1));
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=".concat(fileName).concat(ExcelTypeEnum.XLSX.getValue()));
            response.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            servletOutputStream = response.getOutputStream();
            EasyExcel.write(servletOutputStream).head(exportOrderListHead(listVos)).sheet("商城订单信息").doWrite(exportOrderListRows(listVos));
        } catch (IOException e) {
            log.error("店铺-订单:订单列表导出:异常[uid:{},orderCode:{},cst:{},cet:{},expressContacts:{},phone:{},status:{},ex.msg={}]",
                    uid, orderCode, cst, cet, expressContacts, phone, status, e.getLocalizedMessage());
        } finally {
            if (null != servletOutputStream) {
                try {
                    servletOutputStream.close();
                } catch (Exception ignored) {
                }
            }
        }
    }

    private List<GoblinStoreOrderListVo> buildStoreOrderListVos(List<GoblinStoreOrderVo> voList, boolean includeExportFields) {
        List<GoblinStoreOrderListVo> listVos = ObjectUtil.getGoblinStoreOrderListVoArrayList();
        if (org.springframework.util.CollectionUtils.isEmpty(voList)) {
            return listVos;
        }
        for (GoblinStoreOrderVo item : voList) {
            GoblinStoreOrderListVo vo = GoblinStoreOrderListVo.getNew();
            vo.setCreatedAt(item.getCreatedAt());
            vo.setPayTime(item.getPayTime());
            GoblinOrderAttrVo orderAttrVo = item.getOrderAttrVo();
            if (orderAttrVo != null) {
                vo.setExpressAddressDetail(orderAttrVo.getExpressAddressDetail());
                vo.setExpressContacts(orderAttrVo.getExpressContacts());
                vo.setExpressPhone(orderAttrVo.getExpressPhone());
                vo.setExpressAddress(orderAttrVo.getExpressAddress());
            } else {
                vo.setExpressAddressDetail("");
                vo.setExpressContacts("");
                vo.setExpressPhone("");
                vo.setExpressAddress("");
            }
            vo.setOrderCode(item.getOrderCode());
            vo.setOrderId(item.getOrderId());
            vo.setPayType(item.getPayType());
            vo.setPriceActual(item.getPriceActual());
            vo.setStatus(item.getStatus());
            vo.setMarketType(item.getMarketType());
            vo.setPriceExpress(item.getPriceExpress());
            vo.setUserId(item.getUserId());
            vo.setUserName(item.getUserName());
            vo.setUserMobile(item.getUserMobile());
            if (includeExportFields) {
                vo.setPlatformUcouponPrice(item.getPriceCoupon());
                vo.setStoreUcouponPrice(item.getStorePriceCoupon());
            }
            if (item.getMarketId() == null || item.getMarketId().equals("")) {
                vo.setMarketName("");
            } else {
                GoblinSelfMarketingVo marketingVo = redisUtils.getSelfMarket(item.getMarketId().split("ZZ")[1]);
                vo.setMarketName(marketingVo != null ? marketingVo.getName() : "");
            }
            List<GoblinStoreOrderListSkuVo> orderListSkuVos = ObjectUtil.getGoblinStoreOrderListSkuVoArrayList();
            List<String> skuIds = item.getOrderSkuVoIds();
            if (skuIds == null) {
                skuIds = Collections.emptyList();
            }
            for (String skuId : skuIds) {
                GoblinOrderSkuVo orderSkuVo = redisUtils.getGoblinOrderSkuVo(skuId);
                if (orderSkuVo == null) {
                    continue;
                }
                GoblinStoreOrderListSkuVo itemSkuVo = GoblinStoreOrderListSkuVo.getNew();
                itemSkuVo.setNum(orderSkuVo.getNum());
                itemSkuVo.setOrderSkuId(orderSkuVo.getOrderSkuId());
                itemSkuVo.setSkuId(orderSkuVo.getSkuId());
                itemSkuVo.setSkuName(orderSkuVo.getSkuName());
                itemSkuVo.setSpuName(orderSkuVo.getSpuName());
                itemSkuVo.setSkuPriceActual(orderSkuVo.getSkuPriceActual());
                itemSkuVo.setSkuSpecs(orderSkuVo.getSkuSpecs());
                itemSkuVo.setSkuImage(orderSkuVo.getSkuImage());
                itemSkuVo.setStatus(orderSkuVo.getStatus());
                itemSkuVo.setPrice(orderSkuVo.getSkuPrice());
                itemSkuVo.setPriceVoucher(orderSkuVo.getPriceVoucher());
                orderListSkuVos.add(itemSkuVo);
            }
            vo.setStoreOrderListSkuVoList(orderListSkuVos);
            // 增加物流信息
            vo.setGoblinMailVoList(redisUtils.getGoblinMail(item.getOrderId()));
            String logisticsCompany = exportLogisticsCompany(vo.getGoblinMailVoList());
            if (StringUtils.isBlank(logisticsCompany)) {
                logisticsCompany = toExportString(item.getLogisticsCompany());
            }
            vo.setLogisticsCompany(logisticsCompany);
            listVos.add(vo);
        }
        return listVos;
    }

    private List<List<String>> exportOrderListHead(List<GoblinStoreOrderListVo> listVos) {
        List<String> heads = new ArrayList<>();
        heads.add("订单id");
        heads.add("订单编号");
        heads.add("购买人手机号");
        heads.add("快递费");
        heads.add("平台券优惠券金额");
        heads.add("店铺券优惠金额");
        heads.add("收货人");
        heads.add("收货人电话");
        heads.add("快递地址");
        heads.add("支付方式");
        heads.add("支付时间");
        heads.add("下单时间");
        heads.add("订单状态");
        heads.add("活动名称");
        heads.add("快递公司");
        heads.add("物流单号");
        int maxSkuCount = maxSkuCount(listVos);
        for (int i = 1; i <= maxSkuCount; i++) {
            heads.add("商品id" + i);
            heads.add("商品名" + i);
            heads.add("款式" + i);
            heads.add("数量" + i);
            heads.add("单价" + i);
            heads.add("价格" + i);
            heads.add("订单skuId" + i);
        }
        List<List<String>> headList = new ArrayList<>();
        for (String head : heads) {
            headList.add(Collections.singletonList(head));
        }
        return headList;
    }

    private List<List<String>> exportOrderListRows(List<GoblinStoreOrderListVo> listVos) {
        List<List<String>> rows = new ArrayList<>();
        for (GoblinStoreOrderListVo order : listVos) {
            rows.add(exportOrderListRow(order));
        }
        return rows;
    }

    private List<String> exportOrderListRow(GoblinStoreOrderListVo order) {
        List<String> row = new ArrayList<>();
        row.add(toExportString(order.getOrderId()));
        row.add(toExportString(order.getOrderCode()));
        row.add(toExportString(order.getUserMobile()));
        row.add(toExportString(order.getPriceExpress()));
        row.add(toExportString(order.getPlatformUcouponPrice()));
        row.add(toExportString(order.getStoreUcouponPrice()));
        row.add(toExportString(order.getExpressContacts()));
        row.add(toExportString(order.getExpressPhone()));
        row.add(toExportString(order.getExpressAddress()) + " " + toExportString(order.getExpressAddressDetail()));
        row.add(toExportString(order.getPayType()));
        row.add(toExportString(order.getPayTime()));
        row.add(toExportString(order.getCreatedAt()));
        row.add(toExportString(order.getStatus()));
        row.add(toExportString(order.getMarketName()));
        row.add(exportLogisticsCompany(order.getGoblinMailVoList()));
        row.add(exportMailNo(order.getGoblinMailVoList()));
        List<GoblinStoreOrderListSkuVo> skuVos = order.getStoreOrderListSkuVoList();
        if (!org.springframework.util.CollectionUtils.isEmpty(skuVos)) {
            for (GoblinStoreOrderListSkuVo skuVo : skuVos) {
                row.add(toExportString(skuVo.getSkuId()));
                row.add(toExportString(skuVo.getSpuName()));
                row.add(toExportString(skuVo.getSkuName()));
                row.add(toExportString(skuVo.getNum()));
                row.add(toExportString(skuVo.getPrice()));
                row.add(toExportString(skuVo.getSkuPriceActual()));
                row.add(toExportString(skuVo.getOrderSkuId()));
            }
        }
        return row;
    }

    private int maxSkuCount(List<GoblinStoreOrderListVo> listVos) {
        int max = 0;
        for (GoblinStoreOrderListVo order : listVos) {
            List<GoblinStoreOrderListSkuVo> skuVos = order.getStoreOrderListSkuVoList();
            if (!org.springframework.util.CollectionUtils.isEmpty(skuVos)) {
                max = Math.max(max, skuVos.size());
            }
        }
        return max;
    }

    private String exportLogisticsCompany(List<GoblinMailVo> mailVos) {
        if (org.springframework.util.CollectionUtils.isEmpty(mailVos)) {
            return "";
        }
        List<String> values = new ArrayList<>();
        for (GoblinMailVo mailVo : mailVos) {
            values.add(toExportString(mailVo.getLogisticsCompany()));
        }
        return String.join(",", values);
    }

    private String exportMailNo(List<GoblinMailVo> mailVos) {
        if (org.springframework.util.CollectionUtils.isEmpty(mailVos)) {
            return "";
        }
        List<String> values = new ArrayList<>();
        for (GoblinMailVo mailVo : mailVos) {
            values.add(toExportString(mailVo.getMailNo()));
        }
        return String.join(",", values);
    }

    private String toExportString(Object value) {
        return null == value ? "" : String.valueOf(value);
    }

    @Override
    public ResponseDto<GoblinStoreOrderListVo> orderDetails(String orderId) {
        String uid = CurrentUtil.getCurrentUid();
        GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVoByUid(uid);
        if (storeInfoVo == null) {
            return ResponseDto.failure("无法查看");
        }
        GoblinStoreOrderVo orderVo = redisUtils.getGoblinOrder(orderId);
        if (orderVo == null || orderVo.getStoreId() == null
                || !orderVo.getStoreId().equals(storeInfoVo.getStoreId())) {
            return ResponseDto.failure("无法查看");
        }
        GoblinStoreOrderListVo vo = GoblinStoreOrderListVo.getNew();
        vo.setCreatedAt(orderVo.getCreatedAt());
        // 订单详情增加下单账号信息
        vo.setUserId(orderVo.getUserId());
        vo.setUserMobile(orderVo.getUserMobile());
        vo.setUserName(orderVo.getUserName());
        GoblinOrderAttrVo orderAttrVo = orderVo.getOrderAttrVo();
        if (orderAttrVo != null) {
            vo.setExpressAddressDetail(orderAttrVo.getExpressAddressDetail());
            vo.setExpressContacts(orderAttrVo.getExpressContacts());
            vo.setExpressPhone(orderAttrVo.getExpressPhone());
            vo.setExpressAddress(orderAttrVo.getExpressAddress());
        } else {
            vo.setExpressAddressDetail("");
            vo.setExpressContacts("");
            vo.setExpressPhone("");
            vo.setExpressAddress("");
        }
        vo.setOrderCode(orderVo.getOrderCode());
        vo.setOrderId(orderVo.getOrderId());
        vo.setPayType(orderVo.getPayType());
        vo.setPriceActual(orderVo.getPriceActual());
        vo.setStatus(orderVo.getStatus());
        vo.setPayTime(orderVo.getPayTime());
        vo.setPriceExpress(orderVo.getPriceExpress());
        if (!(orderVo.getUcouponId() == null || orderVo.getUcouponId().equals(""))) {
            CandyCouponVo candyCouponVo = orderUtils.getCouponDetails(orderVo.getUcouponId(), orderVo.getUserId());
            if (candyCouponVo != null) {
                vo.setPlatformUcouponId(candyCouponVo.getUcouponId());
                vo.setPlatformUcouponName(candyCouponVo.getTitle());
                vo.setPlatformUcouponPrice(orderVo.getPriceCoupon());
            } else {
                vo.setPlatformUcouponId("");
                vo.setPlatformUcouponName("");
                vo.setPlatformUcouponPrice(BigDecimal.ZERO);
            }
        } else {
            vo.setPlatformUcouponId("");
            vo.setPlatformUcouponName("");
            vo.setPlatformUcouponPrice(BigDecimal.ZERO);
        }
        if (!(orderVo.getStoreCouponId() == null || orderVo.getStoreCouponId().equals(""))) {
            List<GoblinUserCouponVo> voList = redisUtils.getUserCouponVos(orderVo.getUserId());
            if (voList != null) {
                for (GoblinUserCouponVo goblinUserCouponVo : voList) {
                    if (goblinUserCouponVo != null && goblinUserCouponVo.getUcouponId() != null
                            && goblinUserCouponVo.getUcouponId().equals(orderVo.getStoreCouponId())) {
                        vo.setStoreUcouponId(goblinUserCouponVo.getStoreCouponId());
                        vo.setStoreUcouponName(goblinUserCouponVo.getTitle());
                        vo.setStoreUcouponPrice(orderVo.getStorePriceCoupon());
                        break;
                    }
                }
            }
            if (null == vo.getStoreUcouponId()) {
                List<CouponOutLineVo> outLineVos = redisUtils.getOutLineCoupon();
                if (outLineVos != null) {
                    for (CouponOutLineVo outLineVo : outLineVos) {
                        if (outLineVo != null && outLineVo.getStoreCouponId() != null
                                && outLineVo.getStoreCouponId().equals(orderVo.getStoreCouponId())) {
                            vo.setStoreUcouponId(outLineVo.getStoreCouponId());
                            vo.setStoreUcouponName(outLineVo.getName());
                            vo.setStoreUcouponPrice(orderVo.getStorePriceCoupon());
                            break;
                        }
                    }
                }
            }

        } else {
            vo.setStoreUcouponId("");
            vo.setStoreUcouponName("");
            vo.setStoreUcouponPrice(BigDecimal.ZERO);
        }
        if (orderVo.getMarketId() == null || orderVo.getMarketId().equals("")) {
            vo.setMarketName("");
        } else {
            GoblinSelfMarketingVo marketingVo = redisUtils.getSelfMarket(orderVo.getMarketId().split("ZZ")[1]);
            vo.setMarketName(marketingVo != null ? marketingVo.getName() : "");
        }

        vo.setGoblinMailVoList(redisUtils.getGoblinMail(orderId));
        List<GoblinStoreOrderListSkuVo> orderListSkuVos = ObjectUtil.getGoblinStoreOrderListSkuVoArrayList();
        List<String> skuIds = orderVo.getOrderSkuVoIds();
        if (skuIds == null) {
            skuIds = Collections.emptyList();
        }
        for (String skuId : skuIds) {
            GoblinOrderSkuVo orderSkuVo = redisUtils.getGoblinOrderSkuVo(skuId);
            if (orderSkuVo == null) {
                continue;
            }
            GoblinStoreOrderListSkuVo itemSkuVo = GoblinStoreOrderListSkuVo.getNew();
            itemSkuVo.setNum(orderSkuVo.getNum());
            itemSkuVo.setOrderSkuId(orderSkuVo.getOrderSkuId());
            itemSkuVo.setSkuId(orderSkuVo.getSkuId());
            itemSkuVo.setSkuName(orderSkuVo.getSkuName());
            itemSkuVo.setSpuName(orderSkuVo.getSpuName());
            itemSkuVo.setSkuPriceActual(orderSkuVo.getSkuPriceActual());
            itemSkuVo.setSkuSpecs(orderSkuVo.getSkuSpecs());
            itemSkuVo.setSkuImage(orderSkuVo.getSkuImage());
            itemSkuVo.setStatus(orderSkuVo.getStatus());
            itemSkuVo.setPrice(orderSkuVo.getSkuPrice());
            itemSkuVo.setPriceVoucher(orderSkuVo.getPriceVoucher());

            orderSkuVo.desensitize();// 商品订单页面展示-关联人信息脱敏
            itemSkuVo.setSkuType(orderSkuVo.getSkuType());
            itemSkuVo.setIsTrueName(orderSkuVo.getIsTrueName());
            itemSkuVo.setIdType(orderSkuVo.getIdType());
            itemSkuVo.setIdName(orderSkuVo.getIdName());
            itemSkuVo.setIdNo(orderSkuVo.getIdNo());
            orderListSkuVos.add(itemSkuVo);
        }
        vo.setStoreOrderListSkuVoList(orderListSkuVos);
        return ResponseDto.success(vo);
    }

    @Override
    public ResponseDto<Boolean> orderCancel(String orderId) {
        String uid = CurrentUtil.getCurrentUid();
        LocalDateTime now = LocalDateTime.now();
        String nowStr = DateUtil.getNowTime();
        LinkedList<String> sqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> sqlsOrder = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> sqlsOrderSku = CollectionUtil.linkedListObjectArr();
        sqls.add(SqlMapping.get("goblin_order.store.cancel"));
        sqls.add(SqlMapping.get("goblin_order.store.orderSkuStatus"));
        GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVoByUid(uid);
        if (storeInfoVo == null) {
            return ResponseDto.failure("无法查看");
        }
        GoblinStoreOrderVo orderVo = redisUtils.getGoblinOrder(orderId);
        if (orderVo == null || !orderVo.getStoreId().equals(storeInfoVo.getStoreId())) {
            return ResponseDto.failure("无法查看");
        }
        if (!orderVo.getStatus().equals(GoblinStatusConst.Status.ORDER_STATUS_0.getValue())) {
            return ResponseDto.failure("无法取消");
        }
        orderVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_5.getValue());
        orderVo.setCancelReason("商铺取消");
        orderVo.setCancelTime(nowStr);
        for (String orderSkuId : orderVo.getOrderSkuVoIds()) {
            GoblinOrderSkuVo orderSkuVo = redisUtils.getGoblinOrderSkuVo(orderSkuId);
            orderSkuVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_5.getValue());
            //redis
            String pre = GoblinStatusConst.MarketPreStatus.getPre(orderSkuVo.getSkuId());

            redisUtils.setGoblinOrderSku(orderSkuId, orderSkuVo);
            redisUtils.incrSkuStock(pre, orderSkuVo.getSkuId(), orderSkuVo.getNum());
            redisUtils.decrSkuCountByUid(orderVo.getUserId(), orderSkuVo.getSkuId(), orderSkuVo.getNum());
            //mongo
            mongoUtils.updateGoblinOrderSkuVo(orderSkuId, orderSkuVo);
            //mysql
            sqlsOrderSku.add(new Object[]{
                    orderSkuVo.getStatus(), now,
                    orderSkuVo.getOrderSkuId(), now, now
            });
        }
        GoblinOrderOperationLog log = initLog(orderId, uid, now);
        log.setType(GoblinStatusConst.Status.ORDER_LOG_STATUS_14.getValue());
        log.setRemark("商铺取消订单");

        //回退平台券
        if (!(orderVo.getUcouponId() == null || orderVo.getUcouponId().equals(""))) {
            orderUtils.backCoupon(orderVo.getUcouponId(), orderVo.getUserId());
        }
        //回退店铺券
        if (!(orderVo.getStoreCouponId() == null || orderVo.getStoreCouponId().equals(""))) {
            List<BackCouponParam> params = ObjectUtil.getBackCouponParam();
            BackCouponParam backCouponParam = BackCouponParam.getNew();
            backCouponParam.setuCouponIds(orderVo.getStoreCouponId());
            backCouponParam.setUid(orderVo.getUserId());
            params.add(backCouponParam);
            goblinCouponService.backCoupon(params);
        }

        //redis
        redisUtils.setGoblinOrder(orderId, orderVo);
        //mongo
        mongoUtils.updateGoblinStoreOrderVo(orderId, orderVo);
        //mysql
        sqlsOrder.add(new Object[]{
                orderVo.getStatus(), now, orderVo.getCancelReason(), now,
                orderId, now, now
        });
        queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.gets(sqls, sqlsOrder, sqlsOrderSku));
        return ResponseDto.success();
    }

    @Override
    public ResponseDto<Boolean> changeExpressPrice(String orderId, BigDecimal price) {
        String uid = CurrentUtil.getCurrentUid();
        LocalDateTime now = LocalDateTime.now();
        GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVoByUid(uid);
        if (storeInfoVo == null) {
            return ResponseDto.failure("无法查看");
        }
        GoblinStoreOrderVo orderVo = redisUtils.getGoblinOrder(orderId);
        if (orderVo == null || !orderVo.getStoreId().equals(storeInfoVo.getStoreId())) {
            return ResponseDto.failure("无法查看");
        }
        if (orderVo.getStatus() == GoblinStatusConst.Status.ORDER_STATUS_3.getValue()) {
            return ResponseDto.failure("已发货");
        }
        BigDecimal voucherExpress = orderVo.getPriceExpress().subtract(price);
        orderVo.setPriceTotal(orderVo.getPriceTotal().subtract(voucherExpress));
        orderVo.setPriceModify(orderVo.getPriceModify().add(voucherExpress));
//        orderVo.setPriceVoucher(orderVo.getPriceVoucher().add(voucherExpress));
        orderVo.setPriceActual(orderVo.getPriceActual().subtract(voucherExpress));
        orderVo.setPriceExpress(price);
        GoblinOrderOperationLog log = initLog(orderId, uid, now);
        log.setType(GoblinStatusConst.Status.ORDER_LOG_STATUS_17.getValue());
        log.setRemark("修改快递价格，expressPress=[" + price + "]");
        //redis
        redisUtils.setGoblinOrder(orderId, orderVo);
        //mongo
        mongoUtils.updateGoblinStoreOrderVo(orderId, orderVo);
        //mysql
        queueUtils.sendMsgByRedis(
                MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.get("goblin_order.store.orderExpressPrice",
                        orderVo.getPriceTotal(), orderVo.getPriceModify(), orderVo.getPriceVoucher(), orderVo.getPriceActual(), orderVo.getPriceExpress(), now,
                        orderId, now, now
                )
        );
        //添加日志
        queueUtils.sendMsgByRedis(
                MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.get("goblin_order.store.log",
                        log.getOrderLogId(), log.getOrderId(), log.getType(), log.getRemark(), log.getOperationName(), now
                )
        );
        return ResponseDto.success();
    }

    @Override
    public ResponseDto<Boolean> changeAddress(String orderId, String expressContacts, String expressPhone, String expressAddressDetail) {
        String uid = CurrentUtil.getCurrentUid();
        LocalDateTime now = LocalDateTime.now();
        GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVoByUid(uid);
        if (storeInfoVo == null) {
            return ResponseDto.failure("无法查看");
        }
        GoblinStoreOrderVo orderVo = redisUtils.getGoblinOrder(orderId);
        if (orderVo == null || !orderVo.getStoreId().equals(storeInfoVo.getStoreId())) {
            return ResponseDto.failure("无法查看");
        }
        if (orderVo.getStatus() == GoblinStatusConst.Status.ORDER_STATUS_3.getValue()) {
            return ResponseDto.failure("已发货");
        }
        orderVo.getOrderAttrVo().setExpressAddressDetail(expressAddressDetail);
        orderVo.getOrderAttrVo().setExpressContacts(expressContacts);
        orderVo.getOrderAttrVo().setExpressPhone(expressPhone);
        GoblinOrderOperationLog log = initLog(orderId, uid, now);
        log.setType(GoblinStatusConst.Status.ORDER_LOG_STATUS_17.getValue());
        log.setRemark("修改收货地址，expressContacts=[" + expressContacts + "],expressPhone=[" + expressPhone + "],expressAddressDetail=[" + expressAddressDetail + "]");
        //redis
        redisUtils.setGoblinOrder(orderId, orderVo);
        //mongo
        mongoUtils.updateGoblinStoreOrderVo(orderId, orderVo);
        //mysql
        queueUtils.sendMsgByRedis(
                MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.get("goblin_order.store.address",
                        expressContacts, expressAddressDetail, expressPhone, now, orderId, now, now
                )
        );
        //添加日志
        queueUtils.sendMsgByRedis(
                MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.get("goblin_order.store.log",
                        log.getOrderLogId(), log.getOrderId(), log.getType(), log.getRemark(), log.getOperationName(), now
                )
        );
        return ResponseDto.success();
    }

    @Override
    public ResponseDto<Boolean> changeSkuPrice(String orderId, String orderSkuId, BigDecimal price) {
        String uid = CurrentUtil.getCurrentUid();
        LocalDateTime now = LocalDateTime.now();
        GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVoByUid(uid);
        if (storeInfoVo == null) {
            return ResponseDto.failure("无法查看");
        }
        GoblinStoreOrderVo orderVo = redisUtils.getGoblinOrder(orderId);
        if (orderVo == null || !orderVo.getStoreId().equals(storeInfoVo.getStoreId())) {
            return ResponseDto.failure("无法查看");
        }
        if (orderVo.getStatus() == GoblinStatusConst.Status.ORDER_STATUS_3.getValue()) {
            return ResponseDto.failure("已发货");
        }
        GoblinOrderSkuVo orderSkuVo = redisUtils.getGoblinOrderSkuVo(orderSkuId);
        if (orderSkuVo == null) {
            return ResponseDto.failure("不存在");
        }
        BigDecimal voucherPrice = orderSkuVo.getSkuPriceActual().subtract(price);
        orderSkuVo.setPriceModify(orderSkuVo.getPriceModify() == null ? price : orderSkuVo.getPriceModify().add(price));
//        orderSkuVo.setPriceVoucher(orderSkuVo.getPriceVoucher().add(voucherPrice));
        orderSkuVo.setSkuPriceActual(orderSkuVo.getSkuPriceActual().subtract(voucherPrice));
        orderVo.setPriceTotal(orderVo.getPriceTotal().subtract(voucherPrice));
        orderVo.setPriceModify(orderVo.getPriceModify().add(voucherPrice));
//        orderVo.setPriceVoucher(orderVo.getPriceVoucher().add(voucherPrice));
        orderVo.setPriceActual(orderVo.getPriceActual().subtract(voucherPrice));
        GoblinOrderOperationLog log = initLog(orderId, uid, now);
        log.setType(GoblinStatusConst.Status.ORDER_LOG_STATUS_12.getValue());
        log.setRemark("修改金额，orderSkuId=[" + orderSkuId + "],price=[" + price + "]");
        //redis
        redisUtils.setGoblinOrder(orderId, orderVo);
        redisUtils.setGoblinOrderSku(orderSkuId, orderSkuVo);
        //mongo
        mongoUtils.updateGoblinStoreOrderVo(orderId, orderVo);
        mongoUtils.updateGoblinOrderSkuVo(orderSkuId, orderSkuVo);
        //mysql
        LinkedList<String> sqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> sqlsOrder = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> sqlsOrderSku = CollectionUtil.linkedListObjectArr();
        sqls.add(SqlMapping.get("goblin_order.store.orderPrice"));
        sqls.add(SqlMapping.get("goblin_order.store.orderSkuPrice"));
        sqlsOrder.add(new Object[]{
                orderVo.getPriceModify(), orderVo.getPriceVoucher(), orderVo.getPriceActual(), now,
                orderId, now, now
        });
        sqlsOrderSku.add(new Object[]{
                orderSkuVo.getPriceModify(), orderSkuVo.getPriceVoucher(), orderSkuVo.getSkuPriceActual(), now,
                orderSkuId, now, now
        });
        queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.gets(sqls, sqlsOrder, sqlsOrderSku));
        //添加日志
        queueUtils.sendMsgByRedis(
                MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.get("goblin_order.store.log",
                        log.getOrderLogId(), log.getOrderId(), log.getType(), log.getRemark(), log.getOperationName(), now
                )
        );
        return ResponseDto.success();
    }

    @Override
    public ResponseDto<Boolean> refundOrderSku(String orderId, String orderSkuId, BigDecimal price) {
        String uid = CurrentUtil.getCurrentUid();
        LocalDateTime now = LocalDateTime.now();
        String nowStr = DateUtil.getNowTime();
        List<GoblinBackOrderSkuVo> orderSkuVoList = ObjectUtil.goblinBackOrderSkuVoArrayList();
        BigDecimal refundPrice = price;
        GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVoByUid(uid);
        if (storeInfoVo == null) {
            return ResponseDto.failure("无法查看");
        }
        GoblinStoreOrderVo orderVo = redisUtils.getGoblinOrder(orderId);
        if (orderVo == null || !orderVo.getStoreId().equals(storeInfoVo.getStoreId())) {
            return ResponseDto.failure("无法查看");
        }
        if (!orderVo.getMixId().equals("")) {
            return ResponseDto.failure("组合购商品暂不支持退款");
        }
        if (orderSkuId != null) {
            GoblinOrderSkuVo orderSkuVo = redisUtils.getGoblinOrderSkuVo(orderSkuId);
            if (orderSkuVo == null) {
                return ResponseDto.failure("不存在");
            }
            GoblinBackOrderSkuVo backOrderSkuVo = GoblinBackOrderSkuVo.getNew();
            backOrderSkuVo.setOrderSkuId(orderSkuId);
            backOrderSkuVo.setSpuId(orderSkuVo.getSpuId());
            backOrderSkuVo.setSpuName(orderSkuVo.getSpuName());
            backOrderSkuVo.setSkuId(orderSkuVo.getSkuId());
            backOrderSkuVo.setSkuName(orderSkuVo.getSkuName());
            backOrderSkuVo.setRefundPrice(price);
            backOrderSkuVo.setSkuSpecs(orderSkuVo.getSkuSpecs());
            backOrderSkuVo.setCreatedAt(nowStr);
            backOrderSkuVo.setSkuPic(orderSkuVo.getSkuImage());
            orderSkuVoList.add(backOrderSkuVo);
            BigDecimal backOrderSkuVoPrice = mongoUtils.getRefundOrderSkuVoPrice(orderSkuId);
            if (price.compareTo(orderSkuVo.getSkuPriceActual().subtract(backOrderSkuVoPrice)) > 0) {
                return ResponseDto.failure("退款价格超过商品可退价格");
            }
        } else {
            refundPrice = BigDecimal.ZERO;
            for (String orderSkuIdItem : orderVo.getOrderSkuVoIds()) {
                GoblinOrderSkuVo orderSkuVo = redisUtils.getGoblinOrderSkuVo(orderSkuIdItem);
                GoblinBackOrderSkuVo backOrderSkuVo = GoblinBackOrderSkuVo.getNew();
                backOrderSkuVo.setOrderSkuId(orderSkuIdItem);
                backOrderSkuVo.setSpuId(orderSkuVo.getSpuId());
                backOrderSkuVo.setSpuName(orderSkuVo.getSpuName());
                backOrderSkuVo.setSkuId(orderSkuVo.getSkuId());
                backOrderSkuVo.setSkuName(orderSkuVo.getSkuName());
                backOrderSkuVo.setRefundPrice(orderSkuVo.getSkuPriceActual());
                backOrderSkuVo.setSkuSpecs(orderSkuVo.getSkuSpecs());
                backOrderSkuVo.setCreatedAt(nowStr);
                backOrderSkuVo.setSkuPic(orderSkuVo.getSkuImage());
                orderSkuVoList.add(backOrderSkuVo);
                refundPrice = refundPrice.add(orderSkuVo.getSkuPriceActual().subtract(orderSkuVo.getPriceRefund()));
            }
        }
        //记录退款单
        String refundCode = IDGenerator.storeRefundCode(orderVo.getMasterOrderCode());
        GoblinBackOrder goblinBackOrder = GoblinBackOrder.getNew();
        goblinBackOrder.setBackOrderId(IDGenerator.nextTimeId2());
        goblinBackOrder.setBackCode(refundCode);
        goblinBackOrder.setOrderId(orderVo.getOrderId());
        goblinBackOrder.setOrderCode(orderVo.getOrderCode());
        goblinBackOrder.setStoreId(orderVo.getStoreId());
        goblinBackOrder.setUserId(orderVo.getUserId());
        goblinBackOrder.setSkuIdNums(orderSkuId);
        goblinBackOrder.setType(GoblinStatusConst.Type.BACK_TYPE_1.getValue());
        goblinBackOrder.setReason(GoblinStatusConst.Type.BACK_REASON_TYPE_8.getDesc());
        goblinBackOrder.setDescribes("店铺退款");
        refundPrice = refundPrice.add(orderVo.getPriceExpress());
        goblinBackOrder.setRealBackPrice(refundPrice);
        goblinBackOrder.setStatus(GoblinStatusConst.Status.ORDER_BACK_STATUS_0.getValue());
        goblinBackOrder.setAuditAt(now);
        goblinBackOrder.setCreatedAt(now);
        GoblinBackOrderVo backOrderVo = GoblinBackOrderVo.getNew();
        BeanUtils.copyProperties(goblinBackOrder, backOrderVo);
        backOrderVo.setCreatedAt(nowStr);
        backOrderVo.setBackOrderSkuVos(orderSkuVoList);
        //添加日志
        GoblinBackOrderLog backOrderLog = initBackLog(goblinBackOrder.getBackOrderId(), uid, now);
        backOrderLog.setStatus(GoblinStatusConst.Status.ORDER_LOG_STATUS_22.getValue());
        backOrderLog.setOperationType(GoblinStatusConst.Type.OPERATION_TYPE_2.getValue());
        backOrderLog.setMessage("商户发起：orderSkuId=[" + orderSkuId + "],refundPrice=[" + refundPrice + "],[refundCode=" + refundCode + "]");
        //调用退款
        String returnString = initRefund(orderVo, refundPrice, refundCode);
        HashMap hashMapResult = JsonUtils.fromJson(returnString, HashMap.class);
        Boolean success = (Boolean) hashMapResult.get("success");
        String message = (String) hashMapResult.get("message");
        if (!success) {
            if (!(backOrderVo.getStatus() == GoblinStatusConst.Status.ORDER_BACK_STATUS_8.getValue() || backOrderVo.getStatus() == GoblinStatusConst.Status.ORDER_BACK_STATUS_2.getValue())) {
                backOrderVo.setStatus(GoblinStatusConst.Status.ORDER_BACK_STATUS_10.getValue());
                backOrderVo.setErrorReason(backOrderVo.getErrorReason() + ",失败原因：" + message);
                log.error("REFUND DATA = " + returnString);
            }
        }
        //redis
        redisUtils.setBackOrderVo(backOrderVo.getBackOrderId(), backOrderVo);
        redisUtils.addBackOrderByOrderId(orderVo.getOrderId(), backOrderVo.getBackOrderId());
        //mongo
        mongoUtils.insertGoblinBackOrderVo(backOrderVo);
        //添加退款
        queueUtils.sendMsgByRedis(
                MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.get("goblin_order.store.backOrder",
                        goblinBackOrder.getBackOrderId(), goblinBackOrder.getBackCode(), goblinBackOrder.getOrderId(),
                        goblinBackOrder.getOrderCode(), goblinBackOrder.getStoreId(), goblinBackOrder.getUserId(),
                        goblinBackOrder.getSkuIdNums(), goblinBackOrder.getType(), goblinBackOrder.getReason(),
                        goblinBackOrder.getDescribes(), goblinBackOrder.getRealBackPrice(), goblinBackOrder.getStatus(),
                        goblinBackOrder.getCreatedAt(), goblinBackOrder.getAuditAt(), goblinBackOrder.getErrorReason()
                )
        );
        //添加日志
        queueUtils.sendMsgByRedis(
                MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.get("goblin_order.store.refundLog",
                        backOrderLog.getBackOrderLogId(), backOrderLog.getBackOrderId(), backOrderLog.getOperationType(),
                        backOrderLog.getMessage(), backOrderLog.getOperationName(), backOrderLog.getStatus(), now
                )
        );
        if (success) {
            if (orderVo.getPaymentType().equals(DragonConstant.REFUND_TYPE_MICROPAY_ALIPAY)) {
                alipayCallBack(orderVo, refundCode);
            }
            return ResponseDto.success();
        }
        return ResponseDto.failure("退款失败:" + message);
    }

    @Override
    public ResponseDto<Boolean> express(String orderId, String orderSkuIds, String mailNo, String uid, String orderCode,
                                        String logisticsCode, String logisticsCompany) {
        LocalDateTime now = LocalDateTime.now();
        String nowStr = DateUtil.getNowTime();
        LinkedList<String> sqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> sqlsOrder = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> sqlsOrderSku = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> sqlsMail = CollectionUtil.linkedListObjectArr();
        sqls.add(SqlMapping.get("goblin_order.store.express"));
        sqls.add(SqlMapping.get("goblin_order.store.orderSkuStatus"));
        sqls.add(SqlMapping.get("goblin_order.mail"));
        GoblinStoreOrderVo orderVo = null;
        if (null == orderCode || "".equals(orderCode)) {
            orderVo = redisUtils.getGoblinOrder(orderId);
        } else {
            orderVo = mongoUtils.getGoblinStoreOrderVoByCode(orderCode);
        }
        if (orderVo == null) {
            return ResponseDto.failure("无法查看");
        }
        orderId = orderVo.getOrderId();
        if (orderVo.getStatus() != GoblinStatusConst.Status.ORDER_STATUS_2.getValue()) {
            return ResponseDto.failure("无法发货");
        }
        List<String> backOrderIds = redisUtils.getBackOrderByOrderId(orderId);
        for (String backOrderId : backOrderIds) {
            GoblinBackOrderVo backOrderVo = redisUtils.getBackOrderVo(backOrderId);
            if (!(backOrderVo.getStatus().equals(GoblinStatusConst.Status.ORDER_BACK_STATUS_3.getValue()) || backOrderVo.getStatus().equals(GoblinStatusConst.Status.ORDER_BACK_STATUS_5.getValue()) || backOrderVo.getStatus().equals(GoblinStatusConst.Status.ORDER_BACK_STATUS_9.getValue()))) {
                return ResponseDto.failure("申请失败");
            }
        }
        List<String> skuIds;
        if (orderSkuIds == null) {
            skuIds = orderVo.getOrderSkuVoIds();
        } else {
            skuIds = Arrays.asList(orderSkuIds.split(","));
        }
        GoblinMailVo mailVo = GoblinMailVo.getNew();
        mailVo.setMailId(IDGenerator.nextTimeId2());
        mailVo.setOrderId(orderVo.getOrderId());
        mailVo.setMailNo(mailNo);
        mailVo.setDeliveryTime(nowStr);
        mailVo.setLogisticsCompany(StringUtils.isNotBlank(logisticsCompany) ? logisticsCompany : "");
        mailVo.setLogisticsCode(StringUtils.isNotBlank(logisticsCode) ? logisticsCode.toLowerCase() : "");
        mailVo.setOrderSkuVoIds(skuIds);

        //redis
        redisUtils.addGoblinMail(orderId, mailVo);
        //mongo
        mongoUtils.insertGoblinMailVo(mailVo);
        //mysql
        sqlsMail.add(new Object[]{
                mailVo.getMailId(), mailVo.getOrderId(), mailVo.getMailNo(), mailVo.getDeliveryTime(),
                mailVo.getLogisticsCompany(), mailVo.getLogisticsCode(), StringUtils.join(mailVo.getOrderSkuVoIds(), ","), now
        });

        for (String orderSkuId : skuIds) {
            GoblinOrderSkuVo orderSkuVo = redisUtils.getGoblinOrderSkuVo(orderSkuId);
            if (orderSkuVo == null) {
                log.error("调过子订单号:" + orderSkuId);
                continue;
            }
            orderSkuVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_3.getValue());
            //redis
            redisUtils.setGoblinOrderSku(orderSkuId, orderSkuVo);
            //mongo
            mongoUtils.updateGoblinOrderSkuVo(orderSkuId, orderSkuVo);
            //mysql
            sqlsOrderSku.add(new Object[]{
                    orderSkuVo.getStatus(), now,
                    orderSkuVo.getOrderSkuId(), now, now
            });
        }
        int sendSize = 0;
        for (String orderSkuId : orderVo.getOrderSkuVoIds()) {
            GoblinOrderSkuVo orderSkuVo = redisUtils.getGoblinOrderSkuVo(orderSkuId);
            if (orderSkuVo.getStatus() == GoblinStatusConst.Status.ORDER_STATUS_3.getValue() || orderSkuVo.getStatus() == GoblinStatusConst.Status.ORDER_STATUS_4.getValue()) {
                sendSize++;
            }
        }
        if (sendSize == orderVo.getOrderSkuVoIds().size()) {
            orderVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_3.getValue());
        }

        GoblinOrderOperationLog log = initLog(orderId, uid, now);
        log.setType(GoblinStatusConst.Status.ORDER_LOG_STATUS_13.getValue());
        log.setRemark("发货：orderId=[" + orderId + "],orderSkuId=[" + StringUtils.join(skuIds, ",") + "],mailNo=[" + mailNo + "],logisticsCode=[" + mailVo.getLogisticsCode() + "]");


        //redis
        redisUtils.setGoblinOrder(orderId, orderVo);
        //mongo
        mongoUtils.updateGoblinStoreOrderVo(orderId, orderVo);
        //mysql
        sqlsOrder.add(new Object[]{
                orderVo.getStatus(), now, orderId, now, now
        });
        queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.gets(sqls, sqlsOrder, sqlsOrderSku, sqlsMail));
        //添加日志
        queueUtils.sendMsgByRedis(
                MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.get("goblin_order.store.log",
                        log.getOrderLogId(), log.getOrderId(), log.getType(), log.getRemark(), log.getOperationName(), now
                )
        );
        // 快递100订阅
        goblinMailTrackService.initAndSubscribeAsync(orderId, mailVo.getMailId(), mailVo.getMailNo(),
                mailVo.getLogisticsCode(), mailVo.getLogisticsCompany());
        return ResponseDto.success();
    }

    @Override
    public ResponseDto<Boolean> changeExpressMailNo(String orderId, String mailId, String mailNo,
                                                    String logisticsCode, String logisticsCompany) {
        String uid = CurrentUtil.getCurrentUid();
        GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVoByUid(uid);
        if (storeInfoVo == null) {
            return ResponseDto.failure("无法查看");
        }
        GoblinStoreOrderVo orderVo = redisUtils.getGoblinOrder(orderId);
        if (orderVo == null || !orderVo.getStoreId().equals(storeInfoVo.getStoreId())) {
            return ResponseDto.failure("无法查看");
        }
        GoblinMailVo origin = null;
        for (GoblinMailVo m : redisUtils.getGoblinMail(orderId)) {
            if (mailId.equals(m.getMailId())) {
                origin = m;
                break;
            }
        }
        if (origin == null) {
            return ResponseDto.failure("快递信息不存在");
        }
        String newMailNo = mailNo == null ? "" : mailNo.trim();
        String newCode = StringUtils.isNotBlank(logisticsCode) ? logisticsCode.trim().toLowerCase() : StringUtils.defaultString(origin.getLogisticsCode());
        String oldMailNo = StringUtils.defaultString(origin.getMailNo());
        String oldCode = StringUtils.defaultString(origin.getLogisticsCode());
        boolean mailChanged = !oldMailNo.equals(newMailNo)
                || !oldCode.equalsIgnoreCase(StringUtils.defaultString(newCode));

        GoblinMailVo vo = GoblinMailVo.getNew();
        vo.setMailNo(newMailNo);
        vo.setMailId(mailId);
        vo.setOrderId(orderId);
        if (StringUtils.isNotBlank(logisticsCompany)) {
            vo.setLogisticsCompany(logisticsCompany);
        }
        if (StringUtils.isNotBlank(logisticsCode)) {
            vo.setLogisticsCode(logisticsCode.toLowerCase());
        }
        redisUtils.changeGoblinMail(orderId, vo);
        mongoUtils.updateGoblinMailVo(mailId, vo);
        GoblinMailVo cached = null;
        for (GoblinMailVo m : redisUtils.getGoblinMail(orderId)) {
            if (mailId.equals(m.getMailId())) {
                cached = m;
                break;
            }
        }
        queueUtils.sendMsgByRedis(
                MQConst.GoblinQueue.GOBLIN_STORE_ORDER_OPERA.getKey(),
                SqlMapping.get("goblin_order.mail.update",
                        vo.getMailNo(),
                        cached != null ? cached.getLogisticsCompany() : "",
                        cached != null ? cached.getLogisticsCode() : "",
                        LocalDateTime.now(), vo.getMailId()
                )
        );
        if (mailChanged && cached != null) {
            // 单号或公司编码变了才清缓存并重新订阅，避免无变化重复占用订阅次数
            redisUtils.delMailTrackCache(mailId);
            goblinMailTrackService.initAndSubscribeAsync(orderId, mailId, cached.getMailNo(),
                    cached.getLogisticsCode(), cached.getLogisticsCompany());
        }
        return ResponseDto.success();
    }

    private GoblinOrderOperationLog initLog(String orderId, String uid, LocalDateTime now) {
        GoblinOrderOperationLog log = GoblinOrderOperationLog.getNew();
        log.setOrderId(orderId);
        log.setOperationName(uid);
        log.setOrderLogId(IDGenerator.nextTimeId2());
        log.setCreatedAt(now);
        return log;
    }

    private GoblinBackOrderLog initBackLog(String orderId, String uid, LocalDateTime now) {
        GoblinBackOrderLog log = GoblinBackOrderLog.getNew();
        log.setBackOrderId(orderId);
        log.setOperationName(uid);
        log.setBackOrderLogId(IDGenerator.nextTimeId2());
        log.setCreatedAt(now);
        return log;
    }

    private String initRefund(GoblinStoreOrderVo orderVo, BigDecimal price, String refundCode) {
        MultiValueMap<String, String> params = CollectionUtil.linkedMultiValueMapStringString();
        params.add("code", orderVo.getPayCode());
        params.add("notifyUrl", synUrl);
        params.add("orderCode", orderVo.getMasterOrderCode());
        params.add("orderRefundCode", refundCode);
        params.add("paymentId", orderVo.getPaymentId());
        params.add("paymentType", orderVo.getPaymentType());
        params.add("price", String.valueOf(price));


        BigDecimal totalPrice = BigDecimal.ZERO;
        String[] orderIds = redisUtils.getMasterCode(orderVo.getMasterOrderCode());
        for (String orderId : orderIds) {
            GoblinStoreOrderVo vo = redisUtils.getGoblinOrder(orderId);
            totalPrice = totalPrice.add(vo.getPriceActual());
        }
        params.add("priceTotal", String.valueOf(totalPrice));
        params.add("reason", "按需退款");
        MultiValueMap<String, String> headers = CollectionUtil.linkedMultiValueMapStringString();
        headers.add("Accept", "application/json;charset=UTF-8");
        String returnString = HttpUtil.post(refundApply, params, headers);
        log.debug("REFUND BACK DATA = " + returnString);
        return returnString;
    }

    private String alipayCallBack(GoblinStoreOrderVo orderVo, String refundCode) {
        MultiValueMap<String, String> params = CollectionUtil.linkedMultiValueMapStringString();
        params.add("callBackUrl", goblinRefundUrl);
        params.add("orderCode", orderVo.getMasterOrderCode());
        params.add("orderRefundCode", refundCode);
        params.add("paymentId", orderVo.getPaymentId());
        MultiValueMap<String, String> headers = CollectionUtil.linkedMultiValueMapStringString();
        headers.add("Accept", "application/json;charset=UTF-8");
        log.debug("REFUND CALLBACK params = " + params);
        String returnString = HttpUtil.post(refundApplyCallBack, params, headers);
        log.debug("REFUND CALLBACK DATA = " + returnString);
        return returnString;
    }
}
