package com.jsowell.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.huifu.adapay.core.AdapayCore;
import com.huifu.adapay.core.exception.BaseAdaPayException;
import com.huifu.adapay.core.util.AdapaySign;
import com.huifu.adapay.model.Payment;
import com.jsowell.adapay.common.CreateAdaPaymentParam;
import com.jsowell.common.constant.CacheConstants;
import com.jsowell.common.constant.Constants;
import com.jsowell.common.core.domain.entity.SysDictData;
import com.jsowell.common.core.domain.vo.AuthorizedDeptVO;
import com.jsowell.common.core.domain.ykc.RealTimeMonitorData;
import com.jsowell.common.core.domain.ykc.TransactionRecordsData;
import com.jsowell.common.core.page.PageResponse;
import com.jsowell.common.core.redis.RedisCache;
import com.jsowell.common.enums.InvoiceRecordEnum;
import com.jsowell.common.enums.MemberWalletEnum;
import com.jsowell.common.enums.ykc.*;
import com.jsowell.common.exception.BusinessException;
import com.jsowell.common.util.*;
import com.jsowell.common.util.bean.BeanUtils;
import com.jsowell.common.util.id.IdUtils;
import com.jsowell.pile.domain.*;
import com.jsowell.pile.dto.*;
import com.jsowell.pile.service.*;
import com.jsowell.pile.transaction.dto.OrderTransactionDTO;
import com.jsowell.pile.transaction.service.TransactionService;
import com.jsowell.pile.vo.base.OrderAmountDetailVO;
import com.jsowell.pile.vo.base.OrderPeriodAmountVO;
import com.jsowell.pile.vo.base.PileInfoVO;
import com.jsowell.pile.vo.base.StationInfoVO;
import com.jsowell.pile.vo.uniapp.*;
import com.jsowell.pile.vo.web.BillingTemplateVO;
import com.jsowell.pile.vo.web.OrderDetailInfoVO;
import com.jsowell.pile.vo.web.PileStationVO;
import com.jsowell.pile.vo.web.UpdateMemberBalanceDTO;
import com.jsowell.wxpay.dto.WeChatRefundDTO;
import com.jsowell.wxpay.response.WechatPayNotifyParameter;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class OrderService {
	private final Logger log = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private TransactionService pileTransactionService;

	@Autowired
	private IPileBillingTemplateService pileBillingTemplateService;

	@Autowired
	private IOrderBasicInfoService orderBasicInfoService;

	@Autowired
	private PileRemoteService pileRemoteService;

	@Autowired
	private PileService pileService;

	@Autowired
	private MemberService memberService;

	@Autowired
	private IMemberBasicInfoService memberBasicInfoService;

	@Autowired
	private IOrderPayRecordService orderPayRecordService;

	@Autowired
	private WechatPayService wechatPayService;

	@Autowired
	private WxpayCallbackRecordService wxpayCallbackRecordService;

	@Autowired
	private IMemberTransactionRecordService memberTransactionRecordService;

	@Autowired
	private IPileStationInfoService pileStationInfoService;

	@Autowired
	private IOrderInvoiceRecordService orderInvoiceRecordService;

	@Autowired
	private IPileStationWhitelistService pileStationWhitelistService;

	@Autowired
	private RedisCache redisCache;

	@Autowired
	private AdapayCallbackRecordService adapayCallbackRecordService;

	@Autowired
	private IMemberInvoiceTitleService memberInvoiceTitleService;

	@Autowired
	private IAdapayRefundRecordService adapayRefundRecordService;

	@Value("${adapay.appId}")
	private String ADAPAY_APP_ID;

	@Value("${adapay.callback}")
	private String ADAPAY_CALLBACK_URL;


	@Autowired
	private IAdapayMemberAccountService adapayMemberAccountService;

	/**
	 * 生成订单
	 *
	 * @param dto
	 * @return
	 */
	public String generateOrder(GenerateOrderDTO dto) throws ParseException {
		log.info("generateOrder param:{}", JSONObject.toJSONString(dto));
		// 处理前端传的参数
		analysisPileParameter(dto);

		// 校验充电桩相关的信息
		checkPileInfo(dto);

		// 保存订单到数据库 saveOrder2Database
		String orderCode = saveOrder2Database(dto);
		return orderCode;
	}

	/**
	 * 订单支付
	 *
	 * @param dto
	 */
	public Map<String, Object> payOrder(PayOrderDTO dto) throws Exception {
		OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
		if (orderInfo == null) {
			throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_NULL_ERROR);
		}
		if (!StringUtils.equals(orderInfo.getPayStatus(), "0")) {
			// 订单已支付
			throw new BusinessException(ReturnCodeEnum.CODE_ORDER_IS_NOT_TO_BE_PAID_ERROR);
		}
		Map<String, Object> resultMap = Maps.newHashMap();
		if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue())) {
			// 余额支付
			balancePayOrder(dto);
		} else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())) {
			// 微信支付
			dto.setOrderBasicInfo(orderInfo);

			// 从字典中获取使用汇付支付的站点
			List<SysDictData> adapay_station = DictUtils.getDictCache("adapay_station");
			List<String> stationIdList = Lists.newArrayList();
			if (CollectionUtils.isNotEmpty(adapay_station)) {
				for (SysDictData sysDictData : adapay_station) {
					stationIdList.add(sysDictData.getDictValue());
				}
			}
			Map<String, Object> weixinMap;
			if (stationIdList.contains(orderInfo.getStationId())) {
				log.info("该站点:{}在字典中配置了使用汇付支付", orderInfo.getStationId());
				weixinMap = adapayPayOrder(dto);
			} else {
				log.info("该站点:{}使用微信支付", orderInfo.getStationId());
				weixinMap = wechatPayOrder(dto);
			}

			// 返回微信支付参数
			resultMap.put("weixinMap", weixinMap);
		} else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_ALIPAY.getValue())) { // 支付宝支付
			// TODO 返回支付宝支付参数
		} else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue())) { // 白名单支付
			// 白名单支付可以直接调支付回调方法
			dto.setPayAmount(new BigDecimal("500"));
			whiteListPayOrder(dto);
		}
		return resultMap;
	}

	/**
	 * 使用adapay支付订单
	 * @param dto
	 * @return
	 */
	public Map<String, Object> payOrderWithAdapay(PayOrderDTO dto) {
		OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
		if (orderInfo == null) {
			throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_NULL_ERROR);
		}
		if (!StringUtils.equals(orderInfo.getPayStatus(), "0")) {
			// 订单已支付
			throw new BusinessException(ReturnCodeEnum.CODE_ORDER_IS_NOT_TO_BE_PAID_ERROR);
		}
		Map<String, Object> resultMap = Maps.newHashMap();
		if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue())) {
			// 余额支付
			balancePayOrder(dto);
		} else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())) {
			// 微信支付 使用adapay
			dto.setOrderBasicInfo(orderInfo);
			Map<String, Object> weixinMap = adapayPayOrder(dto);
			// 返回微信支付参数
			resultMap.put("weixinMap", weixinMap);
		} else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_ALIPAY.getValue())) { // 支付宝支付
			// TODO 返回支付宝支付参数
		} else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue())) { // 白名单支付
			// 白名单支付可以直接调支付回调方法
			dto.setPayAmount(new BigDecimal("500"));
			whiteListPayOrder(dto);
		}
		return resultMap;
	}

	/**
	 * 白名单支付订单逻辑
	 * @param dto
	 */
	private void whiteListPayOrder(PayOrderDTO dto) {
		String orderCode = dto.getOrderCode();
		BigDecimal payAmount = dto.getPayAmount();
		String payMode = dto.getPayMode();
		PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
				.orderCode(orderCode)
				.payAmount(payAmount)
				.payMode(payMode)
				.build();
		payOrderSuccessCallback(callbackDTO);
	}

	/**
	 * 使用汇付支付
	 * @param dto
	 * @return
	 */
	private Map<String, Object> adapayPayOrder(PayOrderDTO dto) {
		log.info("===============使用汇付支付");
		// 相同参数重复请求，返回同一个支付对象
		String redisKey = CacheConstants.ADAPAY_ORDER_PARAM + dto.getOrderCode();
		Map<String, Object> cacheObject = redisCache.getCacheObject(redisKey);
		if (cacheObject != null) {
			return cacheObject;
		}

		OrderBasicInfo orderInfo = dto.getOrderBasicInfo();
		if (orderInfo == null) {
			// 订单为空重新查询
			orderInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
		}
		// 获取openId
		String openId = memberService.getOpenIdByCode(dto.getCode());
		if (StringUtils.isBlank(openId)) {
			throw new BusinessException(ReturnCodeEnum.CODE_GET_OPEN_ID_BY_CODE_ERROR);
		}

		// 封装对象
		String amount = AdapayUtil.formatAmount(dto.getPayAmount()); // 用户支付金额
		CreateAdaPaymentParam createAdaPaymentParam = new CreateAdaPaymentParam();
		createAdaPaymentParam.setOrder_no(orderInfo.getOrderCode());
		createAdaPaymentParam.setPay_amt(amount);
		createAdaPaymentParam.setApp_id(ADAPAY_APP_ID);
		createAdaPaymentParam.setPay_channel("wx_lite"); // todo 如果以后有支付宝等别的渠道，这里需要做修改，判断是什么渠道的请求
		createAdaPaymentParam.setGoods_title("充电费用");
		createAdaPaymentParam.setGoods_desc("充电桩预付款金额"); // 这个字段是微信支付凭证的商品名
		Map<String, String> map = Maps.newHashMap();
		map.put("type", ScenarioEnum.ORDER.getValue());
		map.put("orderCode", dto.getOrderCode());
		createAdaPaymentParam.setDescription(JSON.toJSONString(map));
		//异步通知地址，url为http/https路径，服务器POST回调，URL 上请勿附带参数
		createAdaPaymentParam.setNotify_url(ADAPAY_CALLBACK_URL);
		createAdaPaymentParam.setExpend(JSONObject.toJSONString( ImmutableMap.of("open_id", openId)));

		// 分账对象信息
		String adapayMemberId = adapayMemberAccountService.selectAdapayMemberIdByStationId(orderInfo.getStationId());
		if (StringUtils.isNotBlank(adapayMemberId)) {
			JSONObject jsonObject = new JSONObject();
			jsonObject.put("member_id", adapayMemberId);
			jsonObject.put("amount", amount);
			jsonObject.put("fee_flag", Constants.Y);

			// 分账对象信息列表，最多仅支持7个分账方，json 数组形式
			JSONArray jsonArray = new JSONArray();
			jsonArray.add(jsonObject);
			createAdaPaymentParam.setDiv_members(jsonArray.toString());
		}
		try {
			log.info("创建汇付支付参数:{}", JSONObject.toJSONString(createAdaPaymentParam));
			Map<String, Object> response = Payment.create(BeanMap.create(createAdaPaymentParam));
			if (response != null && !response.isEmpty()) {
				JSONObject expend = JSONObject.parseObject(response.get("expend").toString());
				JSONObject pay_info = expend.getJSONObject("pay_info");
				Map<String, Object> resultMap = JSONObject.parseObject(pay_info.toJSONString(), new TypeReference<Map<String, Object>>() {
				});
				if (resultMap != null) {
					// 表示已经获取到支付参数了，后续再有支付请求就拒绝
					redisCache.setCacheObject(redisKey, resultMap, 15, TimeUnit.MINUTES);
				}
				return resultMap;
			}
		} catch (BaseAdaPayException e) {
			log.error("汇付-获取支付对象发生异常", e);
		}
		return null;
	}

	/**
	 * 微信支付订单逻辑 获取支付参数
	 * @param dto
	 * @return
	 * @throws Exception
	 */
	private Map<String, Object> wechatPayOrder(PayOrderDTO dto) throws Exception {
		// 相同参数重复请求，返回同一个支付对象
		String redisKey = CacheConstants.WECHAT_PAY_ORDER_PARAM + dto.getOrderCode();
		Map<String, Object> cacheObject = redisCache.getCacheObject(redisKey);
		if (cacheObject != null) {
			return cacheObject;
		}

		OrderBasicInfo orderInfo = dto.getOrderBasicInfo();
		if (orderInfo == null) {
			orderInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
		}
		String openId = memberService.getOpenIdByCode(dto.getCode());
		if (StringUtils.isBlank(openId)) {
			throw new BusinessException(ReturnCodeEnum.CODE_GET_OPEN_ID_BY_CODE_ERROR);
		}
		WeixinPayDTO weixinPayDTO = new WeixinPayDTO();
		weixinPayDTO.setOpenId(openId);
		weixinPayDTO.setAmount(dto.getPayAmount().toString());
		// 支付订单 附加参数
		PaymentScenarioDTO paymentScenarioDTO = PaymentScenarioDTO.builder()
				.type(ScenarioEnum.ORDER.getValue())
				.orderCode(dto.getOrderCode())
				.memberId(orderInfo.getMemberId())
				.build();
		weixinPayDTO.setAttach(JSONObject.toJSONString(paymentScenarioDTO));
		weixinPayDTO.setDescription("充电费用");
		Map<String, Object> payV3 = this.weixinPayV3(weixinPayDTO);
		if (payV3 != null) {
			// 表示已经获取到支付参数了，后续再有支付请求就拒绝
			redisCache.setCacheObject(redisKey, payV3, 15, TimeUnit.MINUTES);
		}
		return payV3;
	}

	/**
	 * 余额支付订单逻辑
	 * @param dto
	 */
	private void balancePayOrder(PayOrderDTO dto) {
		// 记录支付流水
		List<OrderPayRecord> payRecordList = Lists.newArrayList();
		String orderCode = dto.getOrderCode();
		BigDecimal chargeAmount = dto.getPayAmount();
		// 查询该会员的余额
		MemberVO memberVO = memberService.getMemberInfoByMemberId(dto.getMemberId());
		BigDecimal totalAccountAmount = memberVO.getTotalAccountAmount();

		if (totalAccountAmount.compareTo(chargeAmount) < 0) {
			// 总余额小于充电金额
			throw new BusinessException(ReturnCodeEnum.CODE_BALANCE_IS_INSUFFICIENT);
		}
		BigDecimal principalAmount = memberVO.getPrincipalBalance(); // 会员剩余本金金额
		BigDecimal giftAmount = memberVO.getGiftBalance(); // 会员剩余赠送余额

		BigDecimal principalPay = null; // 30
		BigDecimal giftPay = null; // 10
		// 先扣除本金金额，再扣除赠送金额
		BigDecimal subtract = principalAmount.subtract(chargeAmount);
		if (subtract.compareTo(BigDecimal.ZERO) >= 0) {
			principalPay = chargeAmount;
		} else {
			if (principalAmount.compareTo(BigDecimal.ZERO) > 0) {
				principalPay = principalAmount;
			}
			giftPay = subtract.negate();
		}

		// 更新会员钱包
		UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
				.memberId(dto.getMemberId())
				.type(MemberWalletEnum.TYPE_OUT.getValue())
				.subType(MemberWalletEnum.SUBTYPE_PAYMENT_FOR_ORDER.getValue())
				.updatePrincipalBalance(principalPay)
				.updateGiftBalance(giftPay)
				.relatedOrderCode(orderCode)
				.build();
		memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);

		// 记录流水
		if (principalPay != null) {
			payRecordList.add(OrderPayRecord.builder()
					.orderCode(orderCode)
					.payMode(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue())
					.payAmount(principalPay)
					.createBy(dto.getMemberId())
					.build());
		}
		if (giftPay != null) {
			payRecordList.add(OrderPayRecord.builder()
					.orderCode(orderCode)
					.payMode(OrderPayRecordEnum.GIFT_BALANCE_PAYMENT.getValue())
					.payAmount(giftPay)
					.createBy(dto.getMemberId())
					.build());
		}
		// 余额支付可以直接调支付回调方法
		PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
				.orderCode(orderCode)
				.payAmount(chargeAmount)
				.payMode(dto.getPayMode())
				.build();
		payOrderSuccessCallback(callbackDTO);

		// 余额支付订单 记录会员交易流水
		MemberTransactionRecord record = MemberTransactionRecord.builder()
				.orderCode(orderCode)
				.scenarioType(ScenarioEnum.ORDER.getValue())
				.memberId(memberVO.getMemberId())
				.actionType(ActionTypeEnum.FORWARD.getValue())
				.payMode(PayModeEnum.PAYMENT_OF_BALANCE.getValue())
				.paymentInstitutions(PaymentInstitutionsEnum.LOCAL_ACCOUNTS.getValue())
				.amount(dto.getPayAmount()) // 单位元
				.build();
		memberTransactionRecordService.insertSelective(record);

		// 订单支付流水入库
		if (CollectionUtils.isNotEmpty(payRecordList)) {
			orderPayRecordService.batchInsert(payRecordList);
		}
	}

	/**
	 * 订单支付成功 支付回调
	 * 支付成功后掉用这个方法
	 * 1. 修改订单支付状态
	 * 2. 发送启动充电指令
	 */
	public void payOrderSuccessCallback(PayOrderSuccessCallbackDTO dto) {
		OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
		BigDecimal payAmount = dto.getPayAmount();

		// 修改订单
		orderInfo.setPayMode(dto.getPayMode());
		orderInfo.setPayStatus("1");
		orderInfo.setPayAmount(payAmount);
		orderInfo.setPayTime(new Date());
		orderBasicInfoService.updateOrderBasicInfo(orderInfo);

		if (StringUtils.equals(orderInfo.getStartType(), StartTypeEnum.NOW.getValue())) { // 立即启动充电
			String pileSn = orderInfo.getPileSn();
			// 发送启动充电指令前，再次下发计费模板
			BillingTemplateVO billingTemplateVO = pileBillingTemplateService.selectBillingTemplateDetailByPileSn(pileSn);
			if (billingTemplateVO != null) {
				pileRemoteService.publishPileBillingTemplate(pileSn, billingTemplateVO);
			}
			// 发送启动指令
			pileRemoteService.remoteStartCharging(pileSn, orderInfo.getConnectorCode(), orderInfo.getTransactionCode(), orderInfo.getPayAmount());
		} else { // 预约充电
			// 修改枪口状态为 占用预约

			// 下发修改充电桩设置指令

		}

	}

	/**
	 * 保存订单信息到数据库
	 *
	 * @param dto
	 * @return
	 */
	private String saveOrder2Database(GenerateOrderDTO dto) throws ParseException {
		String orderCode = IdUtils.getOrderCode();
		String transactionCode = IdUtils.generateTransactionCode(dto.getPileSn(), dto.getConnectorCode());

		if (StringUtils.isBlank(dto.getStartType())) {
			dto.setStartType(StartTypeEnum.NOW.getValue());
		}

		// 订单基本信息
		OrderBasicInfo orderBasicInfo = OrderBasicInfo.builder()
				.orderCode(orderCode)
				.transactionCode(transactionCode)
				.orderStatus(OrderStatusEnum.NOT_START.getValue())
				.memberId(dto.getMemberId())
				.stationId(dto.getPileConnector().getStationId())
				.pileSn(dto.getPileSn())
				.connectorCode(dto.getConnectorCode())
				.pileConnectorCode(dto.getPileSn() + dto.getConnectorCode())
				.startMode(dto.getStartMode())
				.payStatus(Constants.ZERO)
				.payAmount(dto.getChargeAmount())
				.payMode(dto.getPayMode())
				.orderAmount(BigDecimal.ZERO)
				.virtualAmount(BigDecimal.ZERO)
				.settleAmount(BigDecimal.ZERO)
				.startType(dto.getStartType())
				.build();
		if (StringUtils.equals(dto.getStartType(), StartTypeEnum.APPOINTMENT.getValue())) {
			orderBasicInfo.setAppointmentTime(DateUtils.parseDate(dto.getAppointmentTime(), DateUtils.YYYY_MM_DD_HH_MM_SS));
		}

		// 订单详情
		BillingTemplateVO billingTemplate = dto.getBillingTemplate();
		log.info("订单使用的计费模板-orderCode:{}, billingTemplate:{}", orderCode, JSONObject.toJSONString(billingTemplate));
		BigDecimal sharpElectricityPrice = billingTemplate.getSharpElectricityPrice() != null ? billingTemplate.getSharpElectricityPrice() : BigDecimal.ZERO;
		BigDecimal sharpServicePrice = billingTemplate.getSharpServicePrice() != null ? billingTemplate.getSharpServicePrice() : BigDecimal.ZERO;
		BigDecimal peakElectricityPrice = billingTemplate.getPeakElectricityPrice() != null ? billingTemplate.getPeakElectricityPrice() : BigDecimal.ZERO;
		BigDecimal peakServicePrice = billingTemplate.getPeakServicePrice() != null ? billingTemplate.getPeakServicePrice() : BigDecimal.ZERO;
		BigDecimal flatElectricityPrice = billingTemplate.getFlatElectricityPrice() != null ? billingTemplate.getFlatElectricityPrice() : BigDecimal.ZERO;
		BigDecimal flatServicePrice = billingTemplate.getFlatServicePrice() != null ? billingTemplate.getFlatServicePrice() : BigDecimal.ZERO;
		BigDecimal valleyElectricityPrice = billingTemplate.getValleyElectricityPrice() != null ? billingTemplate.getValleyElectricityPrice() : BigDecimal.ZERO;
		BigDecimal valleyServicePrice = billingTemplate.getValleyServicePrice() != null ? billingTemplate.getValleyServicePrice() : BigDecimal.ZERO;

		OrderDetail orderDetail = OrderDetail.builder()
				.orderCode(orderCode)
				.sharpPrice(sharpElectricityPrice.add(sharpServicePrice))
				.sharpElectricityPrice(sharpElectricityPrice)
				.sharpServicePrice(sharpServicePrice)
				.peakPrice(peakElectricityPrice.add(peakServicePrice))
				.peakElectricityPrice(peakElectricityPrice)
				.peakServicePrice(peakServicePrice)
				.flatPrice(flatElectricityPrice.add(flatServicePrice))
				.flatElectricityPrice(flatElectricityPrice)
				.flatServicePrice(flatServicePrice)
				.valleyPrice(valleyElectricityPrice.add(valleyServicePrice))
				.valleyElectricityPrice(valleyElectricityPrice)
				.valleyServicePrice(valleyServicePrice)
				.build();

		OrderTransactionDTO createOrderTransactionDTO = OrderTransactionDTO.builder()
				.orderBasicInfo(orderBasicInfo)
				.orderDetail(orderDetail)
				.build();
		pileTransactionService.doCreateOrder(createOrderTransactionDTO);
		return orderCode;
	}

	/**
	 * 校验充电桩相关的信息
	 *
	 * @param dto
	 */
	private void checkPileInfo(GenerateOrderDTO dto) {
		// 查询充电桩状态 是否空闲 枪口是否占用
		PileConnectorDetailVO pileConnector = pileService.queryPileConnectorDetail(dto.getPileSn() + dto.getConnectorCode());
		if (pileConnector == null) {
			log.error("checkPileInfo充电枪口为空 pileSn:{}, connectorCode:{}", dto.getPileSn(), dto.getConnectorCode());
			throw new BusinessException(ReturnCodeEnum.CODE_CONNECTOR_INFO_NULL_ERROR);
		}

		// 查询站点状态
		PileStationVO stationInfo = pileStationInfoService.getStationInfo(pileConnector.getStationId());
		if (stationInfo == null || StringUtils.equals(stationInfo.getOpenFlag(), Constants.ZERO)) {
			throw new BusinessException(ReturnCodeEnum.CODE_STATION_IS_NOT_OPEN);
		}

		// 判断枪口状态
		if (!(StringUtils.equals(pileConnector.getConnectorStatus(), PileConnectorDataBaseStatusEnum.FREE.getValue())
				|| StringUtils.equals(pileConnector.getConnectorStatus(), PileConnectorDataBaseStatusEnum.OCCUPIED_NOT_CHARGED.getValue()))) {
			log.error("checkPileInfo充电枪口状态不正确，当前状态为:{}", pileConnector.getConnectorStatus());
			throw new BusinessException(ReturnCodeEnum.CODE_PILE_CONNECTOR_STATUS_ERROR);
		}
		// 查询充电桩的计费模板
		BillingTemplateVO billingTemplateVO = pileBillingTemplateService.selectBillingTemplateDetailByPileSn(dto.getPileSn());
		if (billingTemplateVO == null) {
			throw new BusinessException(ReturnCodeEnum.CODE_BILLING_TEMPLATE_NULL_ERROR);
		}
		dto.setPileConnector(pileConnector);
		dto.setBillingTemplate(billingTemplateVO);
	}


	/**
	 * 处理前端传的参数
	 * pileConnectorCode = pileSn + connectorCode
	 *
	 * @param dto
	 */
	public void analysisPileParameter(BasicPileDTO dto) {
		if (StringUtils.isBlank(dto.getPileSn()) || StringUtils.isBlank(dto.getConnectorCode())) {
			// 从pileConnectorCode解析
			String pileConnectorCode = dto.getPileConnectorCode();
			if (StringUtils.isNotEmpty(pileConnectorCode) && pileConnectorCode.length() == Constants.PILE_CONNECTOR_CODE_LENGTH) {
				dto.setPileSn(StringUtils.substring(pileConnectorCode, 0, pileConnectorCode.length() - 2));
				dto.setConnectorCode(StringUtils.substring(pileConnectorCode, pileConnectorCode.length() - 2, pileConnectorCode.length()));
			} else {
				throw new BusinessException(ReturnCodeEnum.CODE_DATA_LENGTH_ERROR);
			}
		} else {
			// 说明pileSn 和 connectorCode前端传了，那就校验一下长度
			if (dto.getPileSn().length() != Constants.PILE_SN_LENGTH || dto.getConnectorCode().length() != Constants.CONNECTOR_CODE_LENGTH) {
				throw new BusinessException(ReturnCodeEnum.CODE_DATA_LENGTH_ERROR);
			}
		}
	}

	/**
	 * 结算订单
	 * endCharging
	 *
	 * @param dto 结算订单参数
	 */
	public void settleOrderForWeb(SettleOrderDTO dto) {
		analysisPileParameter(dto);
		// 查询订单详情，验证订单中的桩编号是否正确
		OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
		if (orderBasicInfo == null) {
			throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_NULL_ERROR);
		}
		if (!(StringUtils.equals(orderBasicInfo.getPileSn(), dto.getPileSn())
				&& StringUtils.equals(orderBasicInfo.getConnectorCode(), dto.getConnectorCode()))) {
			throw new BusinessException(ReturnCodeEnum.CODE_ORDER_PILE_MAPPING_ERROR);
		}
		// push远程停机指令
		pileRemoteService.remoteStopCharging(dto.getPileSn(), dto.getConnectorCode());

	}

	/**
	 * 通过会员Id查询订单列表
	 *
	 * @param memberId 会员Id
	 * @return 订单信息集合
	 */
	public PageResponse getListByMemberIdAndOrderStatus(String memberId, UniAppQueryOrderDTO dto) throws ParseException {
		String orderStatus = dto.getOrderStatus();
		if (StringUtils.isBlank(orderStatus)) {
			throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
		}
		ArrayList<String> orderStatusList = Lists.newArrayList();
		if (StringUtils.equals("2", orderStatus)) {
			// 查未完成订单
			CollectionUtils.addAll(orderStatusList, "0", "1", "2", "3", "4", "5");
		} else if (StringUtils.equals("3", orderStatus)) {
			// 查已完成订单
			orderStatusList.add("6");
		}else if (StringUtils.equals("4", orderStatus)) {
			// 查询正在进行中的订单
			orderStatusList.add("1");
		}
		// 分页
		PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
		List<OrderVO> list = orderBasicInfoService.getListByMemberIdAndOrderStatus(memberId, orderStatusList);

		PageInfo<OrderVO> pageInfo = new PageInfo<>(list);

		for (OrderVO orderVO : pageInfo.getList()) {
			orderVO.setPileConnectorCode(orderVO.getPileSn() + orderVO.getConnectorCode());
			String chargingTime = "0分钟";
			if (orderVO.getStartTime() != null) {
				Date startTimeDate = DateUtils.parseDate(orderVO.getStartTime());
				Date endTimeDate;
				if (orderVO.getEndTime() != null) {
					endTimeDate = DateUtils.parseDate(orderVO.getEndTime());
				} else {
					endTimeDate = new Date();
				}
				// 计算出两个时间差
				chargingTime = DateUtils.getDatePoor(endTimeDate, startTimeDate);
			}
			orderVO.setChargingTime(chargingTime);
		}

		// 返回结果集
		PageResponse pageResponse = PageResponse.builder()
				.pageNum(dto.getPageNum())
				.pageSize(dto.getPageSize())
				.list(pageInfo.getList())
				.pages(pageInfo.getPages())
				.total(pageInfo.getTotal())
				.build();
		return pageResponse;
	}

	/**
	 * 微信支付v3
	 *
	 * @param dto
	 * @return
	 * @throws Exception
	 */
	public Map<String, Object> weixinPayV3(WeixinPayDTO dto) throws Exception {
		return wechatPayService.weixinPayV3(dto);
	}

	/**
	 * 用户停止充电
	 *
	 * @param dto
	 */
	public void stopCharging(StopChargingDTO dto) {
		// 查订单
		OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
		if (orderInfo == null) {
			throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_NULL_ERROR);
		}
		// 校验订单中的会员与操作会员是否一致
		if (!StringUtils.equals(orderInfo.getMemberId(), dto.getMemberId())) {
			throw new BusinessException(ReturnCodeEnum.CODE_ORDER_MEMBER_NOT_MATCH_ERROR);
		}
		// 发送停止指令
		pileRemoteService.remoteStopCharging(orderInfo.getPileSn(), orderInfo.getConnectorCode());
		log.info("订单号:{}发送停机指令成功", dto.getOrderCode());
	}

	/**
	 * 微信支付回调
	 *
	 * @param request
	 * @param body
	 * @throws Exception
	 */
	public void wechatPayCallback(HttpServletRequest request, WechatPayNotifyParameter body) throws Exception {
		// 获取微信支付成功返回的信息
		Map<String, Object> map = wechatPayService.wechatPayCallbackInfo(request, body);
		String type = (String) map.get("type");
		BigDecimal amount = (BigDecimal) map.get("amount"); // 微信给的amount单位是分
		amount = amount.divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP); // 转换为元

		String orderCode = (String) map.get("orderCode");
		String memberId = (String) map.get("memberId");
		if (StringUtils.equals(type, ScenarioEnum.ORDER.getValue())) { // 1-订单支付
			// 支付订单成功
			// orderCode = (String) map.get("orderCode");
			PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
					.orderCode(orderCode)
					.payAmount(amount)
					.payMode(OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())
					.build();
			// 订单支付成功 支付回调
			payOrderSuccessCallback(callbackDTO);

			// 记录订单支付流水
			OrderPayRecord orderPayRecord = OrderPayRecord.builder()
					.orderCode(orderCode)
					.payMode(OrderPayRecordEnum.WECHATPAY_PAYMENT.getValue())
					.payAmount(amount)
					.createBy(null)
					.build();
			orderPayRecordService.batchInsert(Lists.newArrayList(orderPayRecord));
		} else if (StringUtils.equals(type, ScenarioEnum.BALANCE.getValue())) { // 2-充值余额
			// 充值余额成功
			// memberId = (String) map.get("memberId");
			UpdateMemberBalanceDTO dto = new UpdateMemberBalanceDTO();
			dto.setMemberId(memberId);
			dto.setType(MemberWalletEnum.TYPE_IN.getValue());
			dto.setSubType(MemberWalletEnum.SUBTYPE_TOP_UP.getValue());
			dto.setUpdatePrincipalBalance(amount);
			memberBasicInfoService.updateMemberBalance(dto);
		}

		// 微信支付订单 记录会员交易流水
		MemberTransactionRecord record = MemberTransactionRecord.builder()
				.orderCode(orderCode)
				.scenarioType(type)
				.memberId(memberId)
				.actionType(ActionTypeEnum.FORWARD.getValue())
				.payMode(PayModeEnum.PAYMENT_OF_WECHATPAY.getValue())
				.paymentInstitutions(PaymentInstitutionsEnum.WECHAT_PAY.getValue())
				.amount(amount) // 单位元
				.outTradeNo(String.valueOf(map.get("out_trade_no")))
				.transactionId(String.valueOf(map.get("transaction_id")))
				.build();
		memberTransactionRecordService.insertSelective(record);
	}

	public void weChatRefund(WeChatRefundDTO dto) {
		log.info("微信退款接口 param:{}", JSONObject.toJSONString(dto));
		orderBasicInfoService.weChatRefund(dto);
	}

	public void adapayRefund(WeChatRefundDTO dto) {
		log.info("汇付退款 param:{}", JSON.toJSONString(dto));
		// 退款有两种情况 1-订单结算退款  2-用户余额退款
		String refundType = dto.getRefundType();
		if (StringUtils.equals(refundType, "1")) {
			orderBasicInfoService.refundForOrderWithAdapay(dto);
		} else if (StringUtils.equals(refundType, "2")) {
			orderBasicInfoService.refundForBalanceWithAdapay(dto);
		} else {
			log.warn("没有找到退款处理逻辑");
		}
	}

	/**
	 * 查询订单详情信息
	 * @param orderCode 订单编号
	 * @return
	 */
	public OrderDetailInfoVO queryOrderDetailInfo(String orderCode) {
		OrderDetailInfoVO vo = new OrderDetailInfoVO();

		// 订单信息
		OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
		if (orderBasicInfo == null) {
			return vo;
		}
		OrderDetailInfoVO.OrderInfo order = new OrderDetailInfoVO.OrderInfo();
		order.setOrderCode(orderBasicInfo.getOrderCode());
		order.setTransactionCode(orderBasicInfo.getTransactionCode());
		order.setOrderStatus(orderBasicInfo.getOrderStatus());
		order.setLogicCard(orderBasicInfo.getLogicCard());
		String describe = orderBasicInfoService.transformOrderStatusDescribe(orderBasicInfo.getOrderStatus(), orderBasicInfo.getPayStatus());
		order.setOrderStatusDescribe(describe);
		order.setStartTime(DateUtils.formatDateTime(orderBasicInfo.getChargeStartTime()));
		order.setEndTime(DateUtils.formatDateTime(orderBasicInfo.getChargeEndTime()));
		order.setCreateTime(DateUtils.formatDateTime(orderBasicInfo.getCreateTime()));
		order.setStopReasonMsg(orderBasicInfo.getReason());
		order.setStartSOC(orderBasicInfo.getStartSOC());
		order.setEndSOC(orderBasicInfo.getEndSOC());
		vo.setOrderInfo(order);

		// 设备信息
		PileInfoVO pileInfoVO = pileService.selectPileInfoBySn(orderBasicInfo.getPileSn());
		vo.setPileInfo(pileInfoVO);

		// 枪口实时数据信息
		String pileConnectorCode = orderBasicInfo.getPileSn() + orderBasicInfo.getConnectorCode();
		QueryConnectorListDTO dto = new QueryConnectorListDTO();
		dto.setConnectorCodeList(Lists.newArrayList(pileConnectorCode));
		List<RealTimeMonitorData> chargingRealTimeDataList = orderBasicInfoService.getChargingRealTimeData(orderBasicInfo.getTransactionCode());
		if (CollectionUtils.isNotEmpty(chargingRealTimeDataList)) {
			List<OrderDetailInfoVO.PileMonitorData> infoList = Lists.newArrayList();
			for (RealTimeMonitorData realTimeMonitorData : chargingRealTimeDataList) {
				OrderDetailInfoVO.PileMonitorData info = new OrderDetailInfoVO.PileMonitorData();
				info.setInstantCurrent(realTimeMonitorData.getOutputCurrent()); // 电流
				info.setInstantVoltage(realTimeMonitorData.getOutputVoltage()); // 电压
				info.setInstantPower(realTimeMonitorData.getOutputPower());  // 功率
				info.setSOC(realTimeMonitorData.getSOC());
				info.setTime(realTimeMonitorData.getDateTime());  // 时间
				infoList.add(info);
			}

			// 监控信息
			OrderDetailInfoVO.OrderRealTimeInfo realTimeInfo = new OrderDetailInfoVO.OrderRealTimeInfo();
			RealTimeMonitorData realTimeMonitorData = chargingRealTimeDataList.get(0);
			realTimeInfo.setOrderAmount(realTimeMonitorData.getChargingAmount());
			realTimeInfo.setChargedDegree(realTimeMonitorData.getChargingDegree());
			realTimeInfo.setChargingTime(realTimeMonitorData.getSumChargingTime());
			vo.setOrderRealTimeInfo(realTimeInfo);

			// 根据时间进行正序排序
			infoList = infoList.stream()
					.sorted(Comparator.comparing(OrderDetailInfoVO.PileMonitorData::getTime))
					.collect(Collectors.toList());
			vo.setRealTimeMonitorDataList(infoList);

			// 最后一次实时数据
			vo.setLastMonitorData(chargingRealTimeDataList.get(0));
		}

		// 支付流水
		List<OrderPayRecord> orderPayRecordList = orderPayRecordService.getOrderPayRecordList(orderCode);
		if (CollectionUtils.isNotEmpty(orderPayRecordList)) {
			List<OrderDetailInfoVO.PayRecord> payRecordList = Lists.newArrayList();
			for (OrderPayRecord orderPayRecord : orderPayRecordList) {
				OrderDetailInfoVO.PayRecord payInfo = new OrderDetailInfoVO.PayRecord();
				// 余额支付如果是由本金和赠送一起支付的，合并为一个
				BigDecimal bigDecimal = orderPayRecordList.stream()
						.map(OrderPayRecord::getPayAmount)
						.reduce(BigDecimal.ZERO, BigDecimal::add);
				payInfo.setPayAmount(bigDecimal.toString());
				payInfo.setPayStatus(orderBasicInfo.getPayStatus());
				payInfo.setPayTime(DateUtils.formatDateTime(orderBasicInfo.getPayTime()));
				if (orderPayRecord.getRefundAmount() != null) {
					payInfo.setRefundAmount(orderPayRecord.getRefundAmount().toString());
				}
				String payMode = orderPayRecord.getPayMode();
				if (StringUtils.equals(payMode, OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue())
						|| StringUtils.equals(payMode, OrderPayRecordEnum.GIFT_BALANCE_PAYMENT.getValue())) {
					// 使用余额支付
					payInfo.setPayMode(OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue());
					payInfo.setPayModeDesc(OrderPayModeEnum.PAYMENT_OF_BALANCE.getLabel());
				} else if (StringUtils.equals(payMode, OrderPayRecordEnum.WECHATPAY_PAYMENT.getValue())){
					// 使用微信支付
					payInfo.setPayMode(OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue());
					payInfo.setPayModeDesc(OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getLabel());
					// 查微信支付回调记录
					WxpayCallbackRecord wxpayCallbackRecord = wxpayCallbackRecordService.selectByOrderCode(orderCode);
					if (wxpayCallbackRecord != null) {
						payInfo.setOutTradeNo(wxpayCallbackRecord.getOutTradeNo());
						payInfo.setTransactionId(wxpayCallbackRecord.getTransactionId());
					}
				} else if (StringUtils.equals(payMode, OrderPayRecordEnum.WHITELIST_PAYMENT.getValue())){
					// 使用白名单支付
					payInfo.setPayMode(OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue());
					payInfo.setPayModeDesc(OrderPayModeEnum.PAYMENT_OF_WHITELIST.getLabel());
				}
				payRecordList.add(payInfo);
			}
			vo.setPayRecordList(payRecordList);
		}

		// 查订单明细
		OrderDetail orderDetail = orderBasicInfoService.getOrderDetailByOrderCode(orderCode);
		if (orderDetail != null) {
			OrderAmountDetailVO billingDetails = new OrderAmountDetailVO();
			BeanUtils.copyBeanProp(billingDetails, orderDetail);
			vo.setBillingDetails(billingDetails);

			List<OrderPeriodAmountVO> orderPeriodAmountVOS = orderBasicInfoService.transformPeriodAmountByOrderDetail(orderDetail);
			vo.setChargeDetails(orderPeriodAmountVOS);
		}

		// 用户信息
		MemberVO memberVO = memberService.getMemberInfoByMemberId(orderBasicInfo.getMemberId());
		vo.setMemberInfo(memberVO);

		try {
			// 因为原来的数据在redis中是永久保存，所以这里做下查询详情的时候，发现已经是完成的订单，redis数据存到表中
			if (StringUtils.equals(orderBasicInfo.getOrderStatus(), OrderStatusEnum.ORDER_COMPLETE.getValue())) {
				// 如果是已完成的订单，把redis中的实时数据存到表中
				orderBasicInfoService.realTimeMonitorDataRedis2DB(orderBasicInfo.getTransactionCode(), orderBasicInfo.getOrderCode());
			}
		} catch (Exception e) {
			log.error("后管查询订单详情时把redis中的实时数据存到表发生异常", e);
		}
		return vo;
	}

	public void wechatPayRefundCallback(HttpServletRequest request, WechatPayNotifyParameter body) throws Exception {
		// 获取微信退款成功返回的信息
		Map<String, Object> map = wechatPayService.wechatPayRefundCallbackInfo(request, body);
	}

	/**
	 * 获取小程序订单详情
	 * @param orderCode
	 * @return
	 */
	public UniAppOrderVO getUniAppOrderDetail(String orderCode) {
		OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
		if (orderBasicInfo == null) {
			throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_NULL_ERROR);
		}
		UniAppOrderVO vo = new UniAppOrderVO();
		vo.setOrderCode(orderBasicInfo.getOrderCode());
		vo.setPileSn(orderBasicInfo.getPileSn());
		vo.setConnectorCode(orderBasicInfo.getConnectorCode());
		vo.setPileConnectorCode(orderBasicInfo.getPileSn() + orderBasicInfo.getConnectorCode());
		String orderStatus = orderBasicInfo.getOrderStatus();
		vo.setOrderStatus(orderStatus);
		if (Objects.nonNull(orderBasicInfo.getChargeStartTime())) {
			vo.setStartChargingTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, orderBasicInfo.getChargeStartTime()));
		}
		Date chargeEndTime = orderBasicInfo.getChargeEndTime();
		if (Objects.nonNull(chargeEndTime)) {
			vo.setEndChargingTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, chargeEndTime));
		}
		PileStationVO stationInfo = pileStationInfoService.getStationInfo(orderBasicInfo.getStationId());
		vo.setStationName(stationInfo.getStationName());
		vo.setReason(orderBasicInfo.getReason());
		vo.setOrderAmount(String.valueOf(orderBasicInfo.getOrderAmount()));
		vo.setPayAmount(String.valueOf(orderBasicInfo.getPayAmount()));
		vo.setPayMode(orderBasicInfo.getPayMode());
		// 订单状态描述
		String orderStatusDescribe = orderBasicInfoService.transformOrderStatusDescribe(orderStatus, orderBasicInfo.getPayStatus());
		vo.setOrderStatusDescribe(orderStatusDescribe);

		// 获取充电桩枪口信息
		PileConnectorDetailVO pileConnectorDetailVO = pileService.queryPileConnectorDetail(vo.getPileConnectorCode());
		if (pileConnectorDetailVO != null) {
			vo.setPileConnectorStatus(pileConnectorDetailVO.getConnectorStatus());
		}

		// 获取订单充电数据
		List<RealTimeMonitorData> monitorDataList = orderBasicInfoService.getChargingRealTimeData(orderBasicInfo.getTransactionCode());
		if (CollectionUtils.isNotEmpty(monitorDataList)) {
			List<UniAppOrderVO.ChargingData> chargingDataList = Lists.newArrayList();
			UniAppOrderVO.ChargingData data = null;
			for (int i = 0; i < monitorDataList.size(); i++) {
				RealTimeMonitorData monitorData = monitorDataList.get(i);
				data = new UniAppOrderVO.ChargingData();
				data.setDateTime(monitorData.getDateTime());
				String outputVoltage = monitorData.getOutputVoltage();
				data.setOutputVoltage(outputVoltage);
				String outputCurrent = monitorData.getOutputCurrent();
				data.setOutputCurrent(outputCurrent);
				BigDecimal power = new BigDecimal(outputCurrent).multiply(new BigDecimal(outputVoltage))
						.divide(new BigDecimal("1000"), 2, BigDecimal.ROUND_HALF_UP);
				data.setPower(power.toString());
				data.setSOC(monitorData.getSOC());
				data.setBatteryMaxTemperature(monitorData.getBatteryMaxTemperature());
				chargingDataList.add(data);
				// vo中的实时数据，最新一条就取monitorDataList第一个
				if (i == 0) {
					vo.setBatteryMaxTemperature(data.getBatteryMaxTemperature());
					vo.setOutputPower(data.getPower());
					vo.setOutputCurrent(data.getOutputCurrent());
					vo.setOutputVoltage(data.getOutputVoltage());
					vo.setSOC(data.getSOC());
					BigDecimal chargingAmount = new BigDecimal(monitorData.getChargingAmount()).setScale(2, BigDecimal.ROUND_HALF_UP); // 充电金额
					vo.setChargingAmount(chargingAmount.toString());
					BigDecimal chargingDegree = new BigDecimal(monitorData.getChargingDegree()).setScale(2, BigDecimal.ROUND_HALF_UP); // 充电度数
					vo.setChargingDegree(chargingDegree.toString());
					vo.setSumChargingTime(monitorData.getSumChargingTime());
					vo.setTimeRemaining(monitorData.getTimeRemaining());
				}
			}
			// monitorDataList是按照时间倒序的，chargingDataList需要按照时间正序
			Collections.reverse(chargingDataList);
			vo.setChargingDataList(chargingDataList);
 		}

		OrderDetail orderDetail = orderBasicInfoService.getOrderDetailByOrderCode(orderCode);
		if (orderDetail != null) {
			OrderAmountDetailVO billingDetails = new OrderAmountDetailVO();
			BeanUtils.copyBeanProp(billingDetails, orderDetail);
			vo.setBillingDetails(billingDetails);
		}

		try {
			// 因为原来的数据在redis中是永久保存，所以这里做下查询详情的时候，发现已经是完成的订单，redis数据存到表中
			if (StringUtils.equals(orderBasicInfo.getOrderStatus(), OrderStatusEnum.ORDER_COMPLETE.getValue())) {
				// 如果是已完成的订单，把redis中的实时数据存到表中
				orderBasicInfoService.realTimeMonitorDataRedis2DB(orderBasicInfo.getTransactionCode(), orderBasicInfo.getOrderCode());
			}
		} catch (Exception e) {
			log.error("后管查询订单详情时把redis中的实时数据存到表发生异常", e);
		}
		return vo;
	}

	/**
	 * 根据订单号查询充电桩启动状态
	 * @param orderCode
	 * @return
	 */
	public String selectPileStarterStatus(String orderCode) {
		OrderBasicInfo orderInfoByOrderCode = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
		if (orderInfoByOrderCode == null) {
			return Constants.ZERO;
		}
		List<RealTimeMonitorData> chargingRealTimeData = orderBasicInfoService.getChargingRealTimeData(orderInfoByOrderCode.getTransactionCode());
		// 只有充电桩上传的实时数据中的状态为充电，才能查到实时数据列表
		return CollectionUtils.isNotEmpty(chargingRealTimeData) ? Constants.ONE : Constants.ZERO;
	}

	public void closeStartFailedOrder(QueryOrderDTO dto) {
		orderBasicInfoService.closeStartFailedOrder(dto.getStartTime(), dto.getEndTime());
	}

	/**
	 * 人工结算订单
	 * @param dto
	 */
	public boolean manualSettlementOrder(ManualSettlementDTO dto) {
		log.info("人工结算订单-begin orderCode:{}", dto.getOrderCode());
		// 查询订单
		OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
		if (orderBasicInfo == null) {
			log.info("人工结算订单-根据订单号:{}, 查询为null", dto.getOrderCode());
			return false;
		}
		// 当前登录用户，是否可以结算该订单
		AuthorizedDeptVO authorizedMap = SecurityUtils.getAuthorizedMap();
		if (authorizedMap == null) {
			log.info("人工结算订单-当前登录管理员无权限");
			return false;
		}

		List<String> stationDeptIds = authorizedMap.getStationDeptIds();
		if (CollectionUtils.isEmpty(stationDeptIds)) {
			// 运营商管理员
			List<String> merchantDeptIds = authorizedMap.getMerchantDeptIds();
		}

		String chargingAmount = dto.getChargingAmount();
		String chargingDegree = dto.getChargingDegree();
		if (StringUtils.equals(chargingAmount, Constants.ZERO) || StringUtils.equals(chargingDegree, Constants.ZERO)) {
			// 获取最后一次实时数据
			List<RealTimeMonitorData> chargingRealTimeData = orderBasicInfoService.getChargingRealTimeData(orderBasicInfo.getTransactionCode());
			if (CollectionUtils.isNotEmpty(chargingRealTimeData)) {
				RealTimeMonitorData realTimeMonitorData = chargingRealTimeData.get(0);
				chargingAmount = realTimeMonitorData.getChargingAmount();
				chargingDegree = realTimeMonitorData.getChargingDegree();
			}
		}

		// 组装交易记录数据
		TransactionRecordsData data = new TransactionRecordsData();
		data.setConsumptionAmount(chargingAmount); // 总消费金额
		data.setTotalElectricity(chargingDegree); // 总用电量
		data.setStopReasonMsg("人工结算订单，操作人:" + SecurityUtils.getUsername()); // 停止原因

		// 结算订单
		orderBasicInfoService.settleOrder(data, orderBasicInfo);

		// 发送停止充电指令
		pileRemoteService.remoteStopCharging(orderBasicInfo.getPileSn(), orderBasicInfo.getConnectorCode());

		log.info("人工结算订单-end orderCode:{}", dto.getOrderCode());
		return true;
	}

	/**
	 * 当前登录会员 获取支付方式
	 * @param dto
	 */
	public List<PayModeVO> getPayMode(GetPayModeDTO dto) {
		List<PayModeVO> result = Lists.newArrayList();
		// 查询会员在站点是否是白名单用户
		PileStationWhitelist whitelist = pileStationWhitelistService.queryWhitelistByMemberId(dto.getStationId(), dto.getMemberId());
		boolean flag = whitelist != null;
		if (flag) {
			result.add(
					PayModeVO.builder()
							.payModeCode(OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue())
							.payModeName(OrderPayModeEnum.PAYMENT_OF_WHITELIST.getLabel())
							.build()
			);
		} else {
			// 微信支付
			result.add(
					PayModeVO.builder()
							.payModeCode(OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())
							.payModeName(OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getLabel())
							.build()
			);
			// 余额支付
			result.add(
					PayModeVO.builder()
							.payModeCode(OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue())
							.payModeName(OrderPayModeEnum.PAYMENT_OF_BALANCE.getLabel())
							.build()
			);
		}
		return result;
	}

	public List<OrderVO> queryUninvoicedOrderList(QueryOrderDTO dto) {
		int i = 15;

		// 查询最近15天完成的订单
		LocalDateTime dateTime = LocalDateTime.now().plusDays(-i);
		List<OrderVO> orderList = orderBasicInfoService.getListByMemberIdAndOrderStatus(dto.getMemberId(), Lists.newArrayList("6"), dateTime);

		// 过滤掉订单金额为0的
		orderList = orderList.stream()
				.filter(x -> x.getOrderAmount().compareTo(BigDecimal.ZERO) > 0)
				.collect(Collectors.toList());
		if (CollectionUtils.isEmpty(orderList)) {
			return orderList;
		}

		// 查询最近60天申请开票记录
		QueryInvoiceRecordDTO build = QueryInvoiceRecordDTO.builder()
				.memberId(dto.getMemberId())
				.startTime(dateTime)
				.build();
		List<OrderInvoiceRecord> orderInvoiceRecords = orderInvoiceRecordService.selectInvoiceRecordList(build);
		if (CollectionUtils.isEmpty(orderInvoiceRecords)) {
			return orderList;
		}

		// 排除掉已经申请过的订单
		List<String> orderCodeList = orderInvoiceRecords.stream() // 转化为 Stream
				.map(OrderInvoiceRecord::getOrderCodes) // 获取 OrderInvoiceRecord 中的 orderCodes 字符串
				.flatMap(str -> Arrays.stream(str.split(","))) // 分隔逗号并转化为 Stream
				.collect(Collectors.toList()); // 收集为 List<String>

		orderList = orderList.stream()
				.filter(x -> !orderCodeList.contains(x.getOrderCode()))
				.collect(Collectors.toList());

		return orderList;
	}

	public List<StationInfoVO> queryUninvoicedStationList(QueryOrderDTO dto) {
		return Lists.newArrayList();
	}

	/**
	 * 申请开票
	 * @param dto
	 */
	public void applyOrderInvoice(ApplyOrderInvoiceDTO dto) {
		int i = 15;
		// 查询最近15天完成的订单
		LocalDateTime dateTime = LocalDateTime.now().plusDays(-i);
		// 查询最近15天申请开票记录
		QueryInvoiceRecordDTO build = QueryInvoiceRecordDTO.builder()
				.memberId(dto.getMemberId())
				.startTime(dateTime)
				.build();
		List<OrderInvoiceRecord> orderInvoiceRecords = orderInvoiceRecordService.selectInvoiceRecordList(build);
		List<String> orderCodeList = orderInvoiceRecords.stream() // 转化为 Stream
				.map(OrderInvoiceRecord::getOrderCodes) // 获取 OrderInvoiceRecord 中的 orderCodes 字符串
				.flatMap(str -> Arrays.stream(str.split(","))) // 分隔逗号并转化为 Stream
				.collect(Collectors.toList()); // 收集为 List<String>

		// 取交集 校验订单是否已经开票
		Set<String> intersection = Sets.intersection(Sets.newHashSet(orderCodeList), Sets.newHashSet(dto.getOrderCodes()));
		if (CollectionUtils.isNotEmpty(intersection)) {
			log.info("申请开票前端传的:{}订单号list, 包含已经申请的订单:{}", dto.getOrderCodes(), intersection);
			return;
		}

		// 整理数据
		List<OrderVO> orderVOList = orderBasicInfoService.getListByOrderCodes(dto.getOrderCodes());
		BigDecimal totalAmount = BigDecimal.ZERO;
		BigDecimal totalElecAmount = BigDecimal.ZERO;
		BigDecimal totalServiceAmount = BigDecimal.ZERO;
		for (OrderVO orderVO : orderVOList) {
			totalAmount = totalAmount.add(orderVO.getOrderAmount());
			totalElecAmount = totalElecAmount.add(orderVO.getTotalElectricityAmount());
			totalServiceAmount = totalServiceAmount.add(orderVO.getTotalServiceAmount());
		}

		// 查抬头信息
		MemberInvoiceTitle invoiceTitle = memberInvoiceTitleService.selectMemberInvoiceTitleById(Long.parseLong(dto.getTitleId()));
		if (StringUtils.isNotEmpty(dto.getReception())) {
			boolean b = false;
			if (StringUtils.isEmail(dto.getReception()) && !StringUtils.equals(dto.getReception(), invoiceTitle.getEmail())) {
				invoiceTitle.setEmail(dto.getReception());
				b = true;
			}
			if (StringUtils.isPhoneNumber(dto.getReception()) && !StringUtils.equals(dto.getReception(), invoiceTitle.getPhoneNumber())) {
				invoiceTitle.setPhoneNumber(dto.getReception());
				b = true;
			}
			if (b) {
				memberInvoiceTitleService.updateMemberInvoiceTitle(invoiceTitle);
			}
		}

		// 入库
		OrderInvoiceRecord orderInvoiceRecord = new OrderInvoiceRecord();
		orderInvoiceRecord.setStatus("0");
		orderInvoiceRecord.setMemberId(dto.getMemberId());
		orderInvoiceRecord.setTitleId(dto.getTitleId());
		orderInvoiceRecord.setOrderCodes(String.join(",", dto.getOrderCodes()));
		orderInvoiceRecord.setTotalAmount(totalAmount);
		orderInvoiceRecord.setTotalElecAmount(totalElecAmount);
		orderInvoiceRecord.setTotalServiceAmount(totalServiceAmount);
		orderInvoiceRecordService.insertOrderInvoiceRecord(orderInvoiceRecord);
	}

	public PageResponse queryInvoiceRecord(QueryInvoiceRecordDTO dto) {

		QueryInvoiceRecordDTO build = QueryInvoiceRecordDTO.builder()
				.memberId(dto.getMemberId())
				.build();
		int pageNo = dto.getPageNo() == null ? 1 : dto.getPageNo();
		int pageSize = dto.getPageSize() == null ? 10 : dto.getPageSize();
		PageHelper.startPage(pageNo, pageSize);
		List<OrderInvoiceRecord> orderInvoiceRecords = orderInvoiceRecordService.selectInvoiceRecordList(build);
		PageInfo<OrderInvoiceRecord> pageInfo = new PageInfo<>(orderInvoiceRecords);
		List<InvoiceRecordVO> volist = Lists.newArrayList();
		for (OrderInvoiceRecord orderInvoiceRecord : orderInvoiceRecords) {
			volist.add(
					InvoiceRecordVO.builder()
							.status(InvoiceRecordEnum.getLabel(orderInvoiceRecord.getStatus()))
							.totalAmount(orderInvoiceRecord.getTotalAmount())
							.totalElecAmount(orderInvoiceRecord.getTotalElecAmount())
							.totalServiceAmount(orderInvoiceRecord.getTotalServiceAmount())
							.createTime(DateUtils.formatDateTime(orderInvoiceRecord.getCreateTime()))
							.build()
			);
		}
		return PageResponse.builder()
				.total(pageInfo.getTotal())
				.pages(pageInfo.getPages())
				.pageNum(pageInfo.getPageNum())
				.pageSize(pageInfo.getPageSize())
				.list(volist)
				.build();
	}

	/**
	 * 汇付支付 支付回调
	 * @param request
	 * @throws Exception
	 */
	public void adapayCallback(HttpServletRequest request) throws Exception {
		//验签请参data
		String data = request.getParameter("data");
		//验签请参sign
		String sign = request.getParameter("sign");
		//验签请参publicKey
		String publicKey = AdapayCore.PUBLIC_KEY;
		log.info("汇付支付回调验签请参data={}, sign={}", data, sign);
		//验签标记
		boolean checkSign = AdapaySign.verifySign(data, sign, publicKey);
		if (!checkSign) {
			log.info("汇付支付回调验签失败:{}", data);
			return;
		}
		//验签成功 保存到回调记录表中
		JSONObject jsonObject = JSON.parseObject(data);
		ObjectMapper mapper = new ObjectMapper();
		mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
		AdapayCallbackRecord adapayCallbackRecord = mapper.readValue(jsonObject.toJSONString(), AdapayCallbackRecord.class);
		adapayCallbackRecord.setExpend(jsonObject.getString("expend"));
		adapayCallbackRecord.setPaymentId(jsonObject.getString("id"));

		JSONObject descJson = JSON.parseObject(adapayCallbackRecord.getDescription());
		String type = descJson.getString("type");
		adapayCallbackRecord.setPayScenario(type);
		String memberId = descJson.getString("memberId");
		adapayCallbackRecord.setMemberId(memberId);
		String orderCode = descJson.getString("orderCode");
		adapayCallbackRecord.setOrderCode(orderCode);
		BigDecimal amount = adapayCallbackRecord.getPayAmt();

		// 保存到数据库
		adapayCallbackRecordService.saveAdapayCallbackRecord(adapayCallbackRecord);

		if (StringUtils.equals(type, ScenarioEnum.ORDER.getValue())) { // 1-订单支付
			// 支付订单成功
			PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
					.orderCode(orderCode)
					.payAmount(amount)
					.payMode(OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())
					.build();
			// 订单支付成功 支付回调
			payOrderSuccessCallback(callbackDTO);

			// 记录订单支付流水
			OrderPayRecord orderPayRecord = OrderPayRecord.builder()
					.orderCode(orderCode)
					.payMode(OrderPayRecordEnum.WECHATPAY_PAYMENT.getValue())
					.payAmount(amount)
					.createBy(null)
					.build();
			orderPayRecordService.batchInsert(Lists.newArrayList(orderPayRecord));
		} else if (StringUtils.equals(type, ScenarioEnum.BALANCE.getValue())) { // 2-充值余额
			// 充值余额成功
			UpdateMemberBalanceDTO dto = new UpdateMemberBalanceDTO();
			dto.setMemberId(memberId);
			dto.setType(MemberWalletEnum.TYPE_IN.getValue());
			dto.setSubType(MemberWalletEnum.SUBTYPE_TOP_UP.getValue());
			dto.setUpdatePrincipalBalance(amount);
			memberBasicInfoService.updateMemberBalance(dto);
		}

		// 微信支付订单 记录会员交易流水
		MemberTransactionRecord record = MemberTransactionRecord.builder()
				.orderCode(orderCode)
				.scenarioType(type)
				.memberId(memberId)
				.actionType(ActionTypeEnum.FORWARD.getValue())
				.payMode(PayModeEnum.PAYMENT_OF_WECHATPAY.getValue())
				.paymentInstitutions(PaymentInstitutionsEnum.ADAPAY.getValue())
				.amount(amount) // 单位元
				.outTradeNo(adapayCallbackRecord.getPartyOrderId())
				.transactionId(adapayCallbackRecord.getOutTransId())
				.build();
		memberTransactionRecordService.insertSelective(record);
	}

	/**
	 * 汇付支付 退款回调
	 * @param request
	 * @throws Exception
	 */
	public void adapayRefundCallback(HttpServletRequest request) throws Exception {
		//验签请参data
		String data = request.getParameter("data");
		//验签请参sign
		String sign = request.getParameter("sign");
		//验签请参publicKey
		String publicKey = AdapayCore.PUBLIC_KEY;
		log.info("汇付支付退款回调验签请参data={}, sign={}", data, sign);
		//验签标记
		boolean checkSign = AdapaySign.verifySign(data, sign, publicKey);
		if (!checkSign) {
			log.info("汇付支付退款回调验签失败:{}", data);
			return;
		}

		adapayRefundRecordService.saveAdapayRefundRecord(data);

	}

}
