package com.jsowell.service; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.google.common.collect.Lists; import com.jsowell.common.constant.Constants; import com.jsowell.common.core.domain.ykc.RealTimeMonitorData; import com.jsowell.common.core.page.PageResponse; import com.jsowell.common.enums.ykc.BusinessTypeEnum; import com.jsowell.common.enums.ykc.OrderPayModeEnum; import com.jsowell.common.enums.ykc.OrderStatusEnum; import com.jsowell.common.enums.ykc.PileStatusEnum; import com.jsowell.common.enums.ykc.ReturnCodeEnum; import com.jsowell.common.exception.BusinessException; import com.jsowell.common.util.DateUtils; import com.jsowell.common.util.SecurityUtils; import com.jsowell.common.util.StringUtils; import com.jsowell.common.util.id.SnUtils; import com.jsowell.pile.service.*; import com.jsowell.pile.domain.MemberBasicInfo; import com.jsowell.pile.domain.OrderBasicInfo; import com.jsowell.pile.domain.PileBasicInfo; import com.jsowell.pile.domain.PileConnectorInfo; import com.jsowell.pile.domain.PileMemberRelation; import com.jsowell.pile.domain.PileStationInfo; import com.jsowell.pile.dto.BatchCreatePileDTO; import com.jsowell.pile.dto.MemberRegisterAndLoginDTO; import com.jsowell.pile.dto.PileMemberBindingDTO; import com.jsowell.pile.dto.QueryPersonPileDTO; import com.jsowell.pile.dto.QueryStationDTO; import com.jsowell.pile.transaction.dto.PileTransactionDTO; import com.jsowell.pile.transaction.service.TransactionService; import com.jsowell.pile.vo.base.ConnectorInfoVO; import com.jsowell.pile.vo.base.MerchantInfoVO; import com.jsowell.pile.vo.base.PileInfoVO; import com.jsowell.pile.vo.uniapp.BillingPriceVO; import com.jsowell.pile.vo.uniapp.PersonPileConnectorSumInfoVO; import com.jsowell.pile.vo.uniapp.PersonPileRealTimeVO; import com.jsowell.pile.vo.uniapp.PileConnectorDetailVO; import com.jsowell.pile.vo.uniapp.PileConnectorVO; import com.jsowell.pile.vo.web.PileDetailVO; import com.jsowell.pile.vo.web.PileStationVO; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.RandomStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.math.BigDecimal; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @Service public class PileService { private final Logger log = LoggerFactory.getLogger(this.getClass()); @Autowired private TransactionService pileTransactionService; @Resource private SnUtils snUtils; @Autowired private IPileBasicInfoService pileBasicInfoService; @Autowired private IPileConnectorInfoService pileConnectorInfoService; @Autowired private IPileStationInfoService pileStationInfoService; @Autowired private IPileMerchantInfoService pileMerchantInfoService; @Autowired private IPileBillingTemplateService pileBillingTemplateService; @Autowired private IPileMemberRelationService pileMemberRelationService; @Autowired private IMemberBasicInfoService memberBasicInfoService; @Autowired private MemberService memberService; @Autowired private IOrderBasicInfoService orderBasicInfoService; @Autowired private YKCPushCommandService ykcPushCommandService; @Autowired private PileRemoteService pileRemoteService; /** * 查询设备信息 * * @param pileConnectorCode 充电枪编号 880000000000000101 * @return 设备信息集合 */ public PileConnectorDetailVO queryPileConnectorDetail(String pileConnectorCode) { return pileBasicInfoService.queryPileConnectorDetail(pileConnectorCode); } /** * 查询设备信息 * * @param pileSn 充电枪编号 8800000000000001 * @param connectorCode 充电枪口号 01 * @return 设备信息集合 */ public PileConnectorDetailVO queryPileConnectorDetail(String pileSn, String connectorCode) { return queryPileConnectorDetail(pileSn + connectorCode); } public int batchCreatePile(BatchCreatePileDTO dto) { // 批量生成sn号 List snList = snUtils.generateSN(dto.getNum()); // List basicInfoList = Lists.newArrayList(); List connectorInfoList = Lists.newArrayList(); PileBasicInfo basicInfo = null; PileConnectorInfo connectorInfo = null; for (String sn : snList) { // 组装pile_basic_info表数据 basicInfo = new PileBasicInfo(); basicInfo.setSn(sn); // sn号 basicInfo.setBusinessType(dto.getChargerPileType()); // 经营类型 1-运营桩;2-个人桩 if (StringUtils.equals(BusinessTypeEnum.INDIVIDUAL_PILE.getValue(), dto.getChargerPileType())) { // 个人桩生成一个八位的密钥(字母加数字) String secretKey = RandomStringUtils.randomAlphanumeric(8).toUpperCase(Locale.ROOT); basicInfo.setSecretKey(secretKey); } basicInfo.setSoftwareProtocol(dto.getSoftwareProtocol()); // 软件协议 basicInfo.setMerchantId(Long.valueOf(dto.getMerchantId())); // 运营商id basicInfo.setStationId(Long.valueOf(dto.getStationId())); // 站点id basicInfo.setModelId(Long.valueOf(dto.getModelId())); // 型号id basicInfo.setProductionDate(dto.getProductionDate()); // 生产日期 basicInfo.setLicenceId(null); // TODO 证书编号 basicInfo.setSimId(null); // TODO sim卡 basicInfo.setRemark(dto.getRemark()); // 备注 basicInfo.setCreateBy(SecurityUtils.getUsername()); // 创建人 basicInfo.setDelFlag(Constants.DEL_FLAG_NORMAL); // 删除标识 basicInfoList.add(basicInfo); for (int i = 1; i < dto.getConnectorNum() + 1; i++) { // 组装pile_connector_info表数据 connectorInfo = new PileConnectorInfo(); connectorInfo.setPileSn(sn); // sn号 String connectorCode = String.format("%1$02d", i); connectorInfo.setPileConnectorCode(sn + connectorCode); // 枪口号 connectorInfo.setStatus(Constants.ZERO); //状态,默认 0-离网 connectorInfo.setCreateBy(SecurityUtils.getUsername()); // 创建人 connectorInfo.setDelFlag(Constants.DEL_FLAG_NORMAL); // 删除标识 connectorInfoList.add(connectorInfo); } } // 批量入库 PileTransactionDTO transactionDTO = PileTransactionDTO.builder() .pileBasicInfoList(basicInfoList) .pileConnectorInfoList(connectorInfoList) .build(); return pileTransactionService.doCreatePileTransaction(transactionDTO); } /** * 前端扫码跳转接口 */ public PileConnectorVO getPileDetailByPileSn(String param) throws ExecutionException, InterruptedException { if (StringUtils.isBlank(param)) { throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR); } String pileSn; if (param.length() == 16) { pileSn = StringUtils.substring(param, 0, param.length() - 2); } else { pileSn = param; } // 查询充电桩信息 PileInfoVO pileInfoVO = pileBasicInfoService.selectPileInfoBySn(pileSn); if (pileInfoVO == null) { return null; } // 查询站点信息 PileStationVO stationInfo = pileStationInfoService.getStationInfo(pileInfoVO.getStationId()); if (stationInfo == null || StringUtils.equals(stationInfo.getOpenFlag(), Constants.ZERO)) { throw new BusinessException(ReturnCodeEnum.CODE_STATION_IS_NOT_OPEN); } // 查询充电桩下枪口信息 CompletableFuture> connectorInfoListFuture = CompletableFuture.supplyAsync(() -> pileConnectorInfoService.selectConnectorInfoList(pileSn)); // 查计费模板信息 CompletableFuture> billingPriceFuture = CompletableFuture.supplyAsync(() -> pileBillingTemplateService.queryBillingPrice(pileInfoVO.getStationId())); // 查询运营商信息 CompletableFuture merchantInfoVOFuture = CompletableFuture.supplyAsync(() -> pileMerchantInfoService.getMerchantInfo(pileInfoVO.getMerchantId())); CompletableFuture all = CompletableFuture.allOf(connectorInfoListFuture, merchantInfoVOFuture, billingPriceFuture); // .join()和.get()都会阻塞并获取线程的执行情况 // .join()会抛出未经检查的异常,不会强制开发者处理异常 .get()会抛出检查异常,需要开发者处理 all.join(); all.get(); List connectorInfoList = connectorInfoListFuture.get(); // PileStationVO pileStationVO = pileStationVOFuture.get(); MerchantInfoVO merchantInfoVO = merchantInfoVOFuture.get(); List billingPriceVOS = billingPriceFuture.get(); PileConnectorVO vo = PileConnectorVO.builder() .pileInfo(pileInfoVO) .connectorInfoList(connectorInfoList) .merchantInfo(merchantInfoVO) .stationInfo(stationInfo) .billingPriceList(billingPriceVOS) .build(); return vo; } public PileConnectorVO getConnectorDetail(String pileConnectorCode) throws ExecutionException, InterruptedException { PileConnectorDetailVO pileConnectorDetailVO = queryPileConnectorDetail(pileConnectorCode); if (pileConnectorDetailVO == null) { return null; } String pileSn = pileConnectorDetailVO.getPileSn(); PileConnectorVO resultVO = getPileDetailByPileSn(pileSn); List connectorInfoList = resultVO.getConnectorInfoList(); if (connectorInfoList.size() > 1) { List list = Lists.newArrayList(); // 枪口大于1个,此充电桩非单枪设备,根据参数展示对应枪口的信息 for (ConnectorInfoVO connectorInfoVO : connectorInfoList) { if (StringUtils.equals(pileConnectorCode, pileSn + connectorInfoVO.getConnectorCode())) { list.add(connectorInfoVO); } } resultVO.setConnectorInfoList(list); } return resultVO; } public PileInfoVO selectPileInfoBySn(String pileSn) { return pileBasicInfoService.selectPileInfoBySn(pileSn); } /** * 用户绑定个人桩 * * @param dto * @return */ public int pileMemberBinding(PileMemberBindingDTO dto){ // 校验短信验证码 MemberRegisterAndLoginDTO registerAndLoginDTO = MemberRegisterAndLoginDTO.builder() .mobileNumber(dto.getPhoneNumber()) .verificationCode(dto.getVerificationCode()) .build(); memberService.checkVerificationCode(registerAndLoginDTO); // 判断桩是否为个人桩 PileBasicInfo pileBasicInfo = pileBasicInfoService.selectPileBasicInfoBySN(dto.getPileSn()); if (pileBasicInfo == null) { // 未查到则说明没有此桩 throw new BusinessException(ReturnCodeEnum.CODE_PILE_NOT_INFO); } String businessType = pileBasicInfo.getBusinessType(); if (!StringUtils.equals(BusinessTypeEnum.INDIVIDUAL_PILE.getValue(), businessType)){ // 不一致则此桩不是个人桩 throw new BusinessException(ReturnCodeEnum.CODE_THIS_PILE_NOT_PERSONAL_PILE); } // 检查桩是否已经被绑定 PileMemberRelation pileMemberRelation = new PileMemberRelation(); pileMemberRelation.setPileSn(dto.getPileSn()); List list = pileMemberRelationService.selectPileMemberRelationList(pileMemberRelation); if (CollectionUtils.isNotEmpty(list)){ // 说明已经被绑定过,抛出异常 throw new BusinessException(ReturnCodeEnum.CODE_PILE_HAS_BEEN_BINDING_ERROR); } // 如果没被绑定,先校验桩密钥是否一致 if (!StringUtils.equals(pileBasicInfo.getSecretKey(), dto.getSecretKey())) { // 错误的密钥信息 throw new BusinessException(ReturnCodeEnum.CODE_SECRET_KEY_ERROR); } // 密钥正确,且桩未被绑定,则此用户为管理员 pileMemberRelation.setMemberId(dto.getMemberId()); pileMemberRelation.setType("1"); // 1-管理员 return pileMemberRelationService.insertPileMemberRelation(pileMemberRelation); } /** * 个人桩管理员下发给其他用户使用 * * @param dto */ public void adminIssuePile(PileMemberBindingDTO dto) { List relationList = pileMemberRelationService.selectPileMemberRelationByPileSn(dto.getPileSn()); if (CollectionUtils.isEmpty(relationList)) { // 充电桩没有绑定任何人 } List adminList = relationList.stream() .filter(x -> x.getType().equals("1")) .map(PileMemberRelation::getMemberId) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(adminList)) { // 没有管理员 } // 校验身份 if (!adminList.contains(dto.getMemberId())) { // 如果为空,说明此用户身份有误,不是管理员,抛出异常 throw new BusinessException(ReturnCodeEnum.CODE_AUTHENTICATION_ERROR); } List userList = relationList.stream() .filter(x -> !x.getType().equals("1")) .map(PileMemberRelation::getMemberId) .collect(Collectors.toList()); // 通过前端传的手机号查询是否有此用户 MemberBasicInfo memberBasicInfo = memberBasicInfoService.selectInfoByMobileNumber(dto.getPhoneNumber()); if (memberBasicInfo == null) { // 为空说明此用户未注册平台账号 throw new BusinessException(ReturnCodeEnum.CODE_USER_IS_NOT_REGISTER); } if (userList.contains(memberBasicInfo.getMemberId())) { // 不为空说明已绑定 throw new BusinessException(ReturnCodeEnum.CODE_USER_HAS_BEEN_THIS_PILE); } else { // 进行绑定,此用户为普通用户 PileMemberRelation info = new PileMemberRelation(); info.setPileSn(dto.getPileSn()); info.setMemberId(memberBasicInfo.getMemberId()); info.setType("2"); pileMemberRelationService.insertPileMemberRelation(info); } } /** * 获取枪口实时数据 * @param dto * @return */ public PersonPileRealTimeVO getConnectorRealTimeInfo(QueryPersonPileDTO dto) { // 根据memberId查出该用户 正在充电、个人桩启动(白名单支付方式)的订单号 OrderBasicInfo orderBasicInfo = OrderBasicInfo.builder() .memberId(dto.getMemberId()) .orderStatus(OrderStatusEnum.IN_THE_CHARGING.getValue()) .pileConnectorCode(dto.getPileConnectorCode()) .payMode(OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue()) // 3- 白名单支付 .build(); OrderBasicInfo basicInfo = orderBasicInfoService.getOrderBasicInfo(orderBasicInfo); if (basicInfo == null){ throw new BusinessException(ReturnCodeEnum.CODE_NO_CHARGING_ORDER_ERROR); } String orderCode = basicInfo.getOrderCode(); // 根据订单号从redis中获取实时数据信息(默认时间倒叙排列,所以取第一条) List chargingRealTimeData = orderBasicInfoService.getChargingRealTimeData(basicInfo.getTransactionCode()); if (CollectionUtils.isEmpty(chargingRealTimeData)) { throw new BusinessException(ReturnCodeEnum.CODE_NO_REAL_TIME_INFO); } RealTimeMonitorData realTimeMonitorData = chargingRealTimeData.get(0); // 将需要的数据set PersonPileRealTimeVO vo = PersonPileRealTimeVO.builder() .chargingDegree(realTimeMonitorData.getChargingDegree()) .chargingTime(realTimeMonitorData.getSumChargingTime()) .startTime(DateUtils.formatDateTime(orderBasicInfo.getChargeStartTime())) .instantCurrent(realTimeMonitorData.getOutputCurrent()) .instantVoltage(realTimeMonitorData.getOutputVoltage()) .instantPower(realTimeMonitorData.getOutputPower()) .build(); return vo; } /** * 获取某枪口某段时间内累计信息 * @param dto * @return */ public PersonPileConnectorSumInfoVO getAccumulativeInfo(QueryPersonPileDTO dto) { List accumulativeInfo = orderBasicInfoService.getAccumulativeInfo(dto); if (CollectionUtils.isEmpty(accumulativeInfo)) { throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_INFO_IS_NULL); } // BigDecimal sumChargingTime = BigDecimal.ZERO; // BigDecimal sumUsedElectricity = BigDecimal.ZERO; // // 将返回的结果进行for循环,将总充电量、总充电时长进行累加(充电时长需要通过 充电开始时间 和 充电结束时间 进行计算) // for (PersonPileConnectorSumInfoVO personPileConnectorSumInfoVO : accumulativeInfo) { // if (StringUtils.isNotBlank(personPileConnectorSumInfoVO.getChargeStartTime()) && StringUtils.isNotBlank(personPileConnectorSumInfoVO.getChargeEndTime())){ // long longChargingTime = DateUtils.intervalTime(personPileConnectorSumInfoVO.getChargeStartTime(), personPileConnectorSumInfoVO.getChargeEndTime()); // BigDecimal chargingTime = new BigDecimal(String.valueOf(longChargingTime)); // sumChargingTime = sumChargingTime.add(chargingTime); // } // BigDecimal chargingDegree = StringUtils.isBlank(personPileConnectorSumInfoVO.getSumChargingDegree()) // ? BigDecimal.ZERO // : new BigDecimal(personPileConnectorSumInfoVO.getSumChargingDegree()); // sumUsedElectricity = sumUsedElectricity.add(chargingDegree); // } Map sumInfo = getSumInfo(accumulativeInfo); // set 对象 PersonPileConnectorSumInfoVO vo = new PersonPileConnectorSumInfoVO(); vo.setStartTime(dto.getStartTime()); vo.setEndTime(dto.getEndTime()); vo.setMemberId(dto.getMemberId()); vo.setSumChargingDegree(sumInfo.get("sumUsedElectricity")); vo.setSumChargingTime(sumInfo.get("sumChargingTime")); return vo; } /** * 获取充电记录(默认30天) * * @param dto */ public PageResponse getChargingRecord(QueryPersonPileDTO dto) { int pageNum = dto.getPageNum() == 0 ? 1 : dto.getPageNum(); int pageSize = dto.getPageSize() == 0 ? 10 : dto.getPageSize(); // 获取三十天前的数据 Date date = DateUtils.addMonths(new Date(), -1); String dateStr = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, date); dto.setStartTime(dateStr); dto.setEndTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, new Date())); // 分页查询 PageHelper.startPage(pageNum, pageSize); List accumulativeInfo = orderBasicInfoService.getAccumulativeInfo(dto); if (CollectionUtils.isEmpty(accumulativeInfo)) { throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_INFO_IS_NULL); } PageInfo pageInfo = new PageInfo<>(accumulativeInfo); for (PersonPileConnectorSumInfoVO personPileConnectorSumInfoVO : pageInfo.getList()) { if (StringUtils.isNotBlank(personPileConnectorSumInfoVO.getChargeStartTime()) && StringUtils.isNotBlank(personPileConnectorSumInfoVO.getChargeEndTime())){ String datePoor = DateUtils.getDatePoor(DateUtils.parseDate(personPileConnectorSumInfoVO.getChargeEndTime()), DateUtils.parseDate(personPileConnectorSumInfoVO.getChargeStartTime())); personPileConnectorSumInfoVO.setSumChargingTime(datePoor); } } return PageResponse.builder() .pageNum(pageInfo.getPageNum()) .pageSize(pageInfo.getPageSize()) .list(pageInfo.getList()) .pages(pageInfo.getPages()) .total(pageInfo.getTotal()) .build(); } /** * 获取总充电时长、总充电量 * * @param accumulativeInfo * @return */ private Map getSumInfo(List accumulativeInfo){ Map resultMap = new HashMap<>(); BigDecimal sumChargingTime = BigDecimal.ZERO; BigDecimal sumUsedElectricity = BigDecimal.ZERO; // 将返回的结果进行for循环,将总充电量、总充电时长进行累加(充电时长需要通过 充电开始时间 和 充电结束时间 进行计算) for (PersonPileConnectorSumInfoVO personPileConnectorSumInfoVO : accumulativeInfo) { if (StringUtils.isNotBlank(personPileConnectorSumInfoVO.getChargeStartTime()) && StringUtils.isNotBlank(personPileConnectorSumInfoVO.getChargeEndTime())){ long longChargingTime = DateUtils.intervalTime(personPileConnectorSumInfoVO.getChargeStartTime(), personPileConnectorSumInfoVO.getChargeEndTime()); BigDecimal chargingTime = new BigDecimal(String.valueOf(longChargingTime)); sumChargingTime = sumChargingTime.add(chargingTime); } BigDecimal chargingDegree = StringUtils.isBlank(personPileConnectorSumInfoVO.getSumChargingDegree()) ? BigDecimal.ZERO : new BigDecimal(personPileConnectorSumInfoVO.getSumChargingDegree()); sumUsedElectricity = sumUsedElectricity.add(chargingDegree); } resultMap.put("sumChargingTime", String.valueOf(sumChargingTime)); resultMap.put("sumUsedElectricity", String.valueOf(sumUsedElectricity)); return resultMap; } /** * 后管调用,给站点下所有桩下发二维码 * @param dto * @return */ public int updateStationQRCodePrefix(QueryStationDTO dto) { String stationId = dto.getStationId(); // 通过站点id查询数据 PileStationInfo pileStationInfo = pileStationInfoService.selectPileStationInfoById(Long.parseLong(stationId)); if (pileStationInfo == null) { return 0; } // 修改站点列表的 qrcodePrefix 字段 pileStationInfo.setQrcodePrefix(dto.getQrcodePrefix()); int i = pileStationInfoService.updatePileStationInfo(pileStationInfo); if (i == 0) { return 0; } // 查出这个站点下的所有桩 List pileDetailVOS = pileBasicInfoService.selectPileListByStationIds(Lists.newArrayList(Long.parseLong(stationId))); List pileSnList = pileDetailVOS.stream() .filter(x -> StringUtils.equals(x.getStatus(), PileStatusEnum.ON_LINE.getValue())) .map(PileDetailVO::getPileSn) .collect(Collectors.toList()); // IssueQRCodeCommand command; for (String pileSn : pileSnList) { // 给桩下发二维码 pileRemoteService.issueQRCode(pileSn, pileStationInfo.getQrcodePrefix()); } return 1; } }