package com.ruoyi.other.service.impl; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.web.page.PageInfo; import com.ruoyi.other.api.domain.TStationInfo; import com.ruoyi.other.mapper.TStationInfoMapper; import com.ruoyi.other.service.TStationCollectService; import com.ruoyi.other.service.TStationEarnService; import com.ruoyi.other.service.TStationInfoService; import com.ruoyi.other.query.StationInfoQuery; import com.ruoyi.other.util.KsolarUtils; import com.ruoyi.other.util.HuWeiUtils; import com.ruoyi.other.vo.StationInfoStatusVO; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; /** * 电站信息服务实现。 */ @Service @Slf4j public class TStationInfoServiceImpl extends ServiceImpl implements TStationInfoService { @Autowired private TStationCollectService collectService; @Autowired private TStationEarnService stationEarnService; @Autowired private HuWeiUtils huWeiUtils; /** * 每天同步科士达数据到数据库 */ @Transactional(rollbackFor = Exception.class) public void syncKsolarDataTask() { String token = KsolarUtils.token(); //JSONObject statusJsonObj = KsolarUtils.getListStatus(token); log.info("-------1开始同步科士达-电站列表信息----"); // 电站列表 JSONArray listInfoJsonArray = KsolarUtils.getStationListInfo(1, 1000, token); // 电站基本信息列表 JSONArray listBaseJsonArray = KsolarUtils.getStationListBase(1, 1000, token); List stationInfoList = build(listInfoJsonArray, listBaseJsonArray); if(CollectionUtils.isNotEmpty(stationInfoList)) { // 去除stationInfoList中的重复stationId Map stationMap = stationInfoList.stream().filter(item -> StringUtils.isNotEmpty(item.getStationId())) .collect(Collectors.toMap(TStationInfo::getStationId, Function.identity(), (o1, o2) -> o2)); stationInfoList = new ArrayList<>(stationMap.values()); // 查询数据库已有数据 List existingStationList = lambdaQuery().in(TStationInfo::getStationId, stationMap.keySet()).list(); // 转Map,方便查找 Map existingMap = existingStationList.stream().collect(Collectors.toMap(TStationInfo::getStationId, Function.identity())); List insertList = new ArrayList<>(); List updateList = new ArrayList<>(); for (TStationInfo station : stationInfoList) { TStationInfo dbStation = existingMap.get(station.getStationId()); if (dbStation == null) { // 数据库不存在 insertList.add(station); } else { // 数据库存在,需要更新;保留数据库主键,否则updateBatchById无法更新 station.setId(dbStation.getId()); updateList.add(station); } } // 批量新增 if (CollectionUtils.isNotEmpty(insertList)) { saveBatch(insertList); } // 批量更新 if (CollectionUtils.isNotEmpty(updateList)) { updateBatchById(updateList); } } log.info("-------1结束同步科士达-电站列表信息----"); log.info("-------2开始同步科士达-逆变器列表----"); // 逆变器列表 JSONArray collectBaseList = KsolarUtils.getCollectBaseList(1, 1000, token); collectService.saveCollectBaseList(collectBaseList); log.info("-------2结束同步科士达-逆变器列表----"); log.info("-------3开始同步科士达-电站的电量和收益统计查询----"); String startDateStr = buildSyncStartDate(LocalDate.now()); String endDateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); JSONArray stationEarnList = KsolarUtils.getStationElecList(startDateStr, endDateStr, token); stationEarnService.saveStationEarnList(stationEarnList); log.info("-------3结束同步科士达-电站的电量和收益统计查询----"); } /** * 根据电站列表信息和电站基础信息批量构建电站对象。 * 两个数组通过 stationId 进行关联。 */ public List build(JSONArray listInfoJsonArray, JSONArray listBaseJsonArray) { if (listInfoJsonArray == null || listInfoJsonArray.isEmpty()) { log.error("电站列表信息获取第三方API数据为空!"); return null; } Map baseMap = buildBaseMap(listBaseJsonArray); List stationInfoList = new ArrayList<>(listInfoJsonArray.size()); for (Object item : listInfoJsonArray) { JSONObject info = JSONObject.parseObject(item.toString()); String stationId = info.getString("stationId"); if (StringUtils.isBlank(stationId)) { log.error("电站列表信息中的stationId不能为空!"); continue; } stationInfoList.add(buildStationInfo(info, baseMap.get(stationId))); } return stationInfoList; } private TStationInfo buildStationInfo(JSONObject info, JSONObject base) { String stationId = info.getString("stationId"); TStationInfo stationInfo = new TStationInfo(); stationInfo.setPlatformId(0L); stationInfo.setStationId(stationId); stationInfo.setStationName(info.getString("stationName")); stationInfo.setStationType(info.getString("stationType")); stationInfo.setRunStatus(info.getString("status")); stationInfo.setRealtimePower(toBigDecimal(info, "powerInter")); stationInfo.setInstalledCapacity(toBigDecimal(info, "installCapacity")); stationInfo.setDailyGeneration(toBigDecimal(info, "dayGeneration")); stationInfo.setDeviceCount(info.getInteger("collects")); stationInfo.setAddress(info.getString("address")); stationInfo.setStationImage(info.getString("stationImage")); if (base != null) { stationInfo.setStationName(firstNotBlank(base.getString("stationName"), stationInfo.getStationName())); stationInfo.setInstalledCapacity(firstNotNull( toBigDecimal(base, "installCapacity"), stationInfo.getInstalledCapacity())); stationInfo.setAreaName(base.getString("areaName")); stationInfo.setCityName(base.getString("cityName")); stationInfo.setElecPrice(base.getString("elecPrice")); stationInfo.setLatitude(base.getString("latitude")); stationInfo.setLongitude(base.getString("longitude")); stationInfo.setPhone(firstNotBlank(base.getString("Phone"), base.getString("phone"))); stationInfo.setProvinceName(base.getString("provinceName")); stationInfo.setAddress(firstNotBlank( base.getString("stationAddressDetail"), stationInfo.getAddress())); stationInfo.setLoanStatus(base.getInteger("loanStatus")); stationInfo.setGridStatus(Objects.equals(stationInfo.getLoanStatus(), 3) ? "1" : "0"); stationInfo.setDataTime(parseDataTime(base.getString("saveTime"))); } stationInfo.setDataSource(2); stationInfo.setSyncTime(LocalDateTime.now()); fillDefaults(stationInfo); return stationInfo; } @Override @Transactional(rollbackFor = Exception.class) public boolean addStationInfo(TStationInfo stationInfo) { validate(stationInfo); stationInfo.setStationId("1"+System.currentTimeMillis()); stationInfo.setDataSource(1); if (lambdaQuery().eq(TStationInfo::getPlatformId, stationInfo.getPlatformId() == null ? 0L : stationInfo.getPlatformId()) .eq(TStationInfo::getStationId, stationInfo.getStationId()).count() > 0) { throw new ServiceException("该平台下的电站信息已存在"); } fillDefaults(stationInfo); stationInfo.setId(null); stationInfo.setCreateTime(LocalDateTime.now()); stationInfo.setUpdateTime(LocalDateTime.now()); return save(stationInfo); } @Override public PageInfo pageList(StationInfoQuery query) { if (query == null) query = new StationInfoQuery(); PageInfo page = new PageInfo<>(query.getPageCurr(), query.getPageSize()); lambdaQuery() .eq(query.getPlatformId() != null, TStationInfo::getPlatformId, query.getPlatformId()) .like(StringUtils.isNotBlank(query.getStationName()), TStationInfo::getStationName, query.getStationName()) .eq(StringUtils.isNotBlank(query.getStationType()), TStationInfo::getStationType, query.getStationType()) .eq(StringUtils.isNotBlank(query.getRunStatus()), TStationInfo::getRunStatus, query.getRunStatus()) .eq(StringUtils.isNotBlank(query.getGridStatus()), TStationInfo::getGridStatus, query.getGridStatus()) .orderByAsc(TStationInfo::getSortOrder).orderByDesc(TStationInfo::getCreateTime).page(page); return page; } @Override @Transactional(rollbackFor = Exception.class) public boolean updateStationInfo(TStationInfo stationInfo) { if (stationInfo == null || stationInfo.getId() == null) throw new ServiceException("电站信息ID不能为空"); TStationInfo old = getById(stationInfo.getId()); if (old == null) throw new ServiceException("电站信息不存在"); if (stationInfo.getStationId() != null && lambdaQuery() .eq(TStationInfo::getPlatformId, stationInfo.getPlatformId() == null ? old.getPlatformId() : stationInfo.getPlatformId()) .eq(TStationInfo::getStationId, stationInfo.getStationId()).ne(TStationInfo::getId, stationInfo.getId()).count() > 0) { throw new ServiceException("该平台下的电站信息已存在"); } stationInfo.setCreateBy(null); stationInfo.setCreateTime(null); stationInfo.setDelFlag(null); stationInfo.setUpdateTime(LocalDateTime.now()); return updateById(stationInfo); } private void validate(TStationInfo stationInfo) { if (stationInfo == null) { throw new ServiceException("电站信息参数不能为空"); } if (StringUtils.isBlank(stationInfo.getStationName())) { throw new ServiceException("电站名称不能为空"); } } private void fillDefaults(TStationInfo stationInfo) { stationInfo.setPlatformId(stationInfo.getPlatformId() == null ? 0L : stationInfo.getPlatformId()); stationInfo.setStationType(StringUtils.isBlank(stationInfo.getStationType()) ? "1" : stationInfo.getStationType()); stationInfo.setRunStatus(StringUtils.isBlank(stationInfo.getRunStatus()) ? "0" : stationInfo.getRunStatus()); stationInfo.setInstalledCapacity(stationInfo.getInstalledCapacity() == null ? BigDecimal.ZERO : stationInfo.getInstalledCapacity()); stationInfo.setRealtimePower(stationInfo.getRealtimePower() == null ? BigDecimal.ZERO : stationInfo.getRealtimePower()); stationInfo.setDeviceCount(stationInfo.getDeviceCount() == null ? 0 : stationInfo.getDeviceCount()); stationInfo.setDailyGeneration(stationInfo.getDailyGeneration() == null ? BigDecimal.ZERO : stationInfo.getDailyGeneration()); stationInfo.setGridStatus(StringUtils.isBlank(stationInfo.getGridStatus()) ? "0" : stationInfo.getGridStatus()); stationInfo.setSortOrder(stationInfo.getSortOrder() == null ? 0 : stationInfo.getSortOrder()); stationInfo.setCreateBy(StringUtils.defaultString(stationInfo.getCreateBy())); stationInfo.setUpdateBy(StringUtils.defaultString(stationInfo.getUpdateBy())); stationInfo.setDelFlag("0"); } private Map buildBaseMap(JSONArray jsonArray) { Map baseMap = new HashMap<>(); if (jsonArray != null) { for (Object item : jsonArray) { JSONObject object = JSONObject.parseObject(item.toString()); String stationId = object.getString("stationId"); if (StringUtils.isNotBlank(stationId)) { baseMap.put(stationId, object); } } } return baseMap; } static String buildSyncStartDate(LocalDate date) { return date.withDayOfYear(1).format(DateTimeFormatter.ISO_LOCAL_DATE) + " 00:00:00"; } private Long parseStationId(String stationId) { try { return Long.valueOf(stationId); } catch (NumberFormatException exception) { log.error("stationId格式不正确:" + stationId); return null; } } private BigDecimal toBigDecimal(JSONObject object, String key) { if (object == null || object.get(key) == null || StringUtils.isBlank(object.getString(key))) { return null; } try { return new BigDecimal(object.getString(key)); } catch (NumberFormatException exception) { log.error(key + "格式不正确:" + object.getString(key)); return null; } } private LocalDateTime parseDataTime(String value) { if (StringUtils.isBlank(value)) { return null; } DateTimeFormatter[] dateTimeFormatters = { DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss") }; for (DateTimeFormatter formatter : dateTimeFormatters) { try { return LocalDateTime.parse(value, formatter); } catch (DateTimeParseException ignored) { // 尝试下一个时间格式 } } try { return LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay(); } catch (DateTimeParseException exception) { log.error("saveTime格式不正确:" + value); return null; } } private LocalDateTime parseHuaweiDateTime(String value) { if (StringUtils.isBlank(value)) { return null; } try { return OffsetDateTime.parse(value).toLocalDateTime(); } catch (DateTimeParseException ignored) { try { return ZonedDateTime.parse(value).toLocalDateTime(); } catch (DateTimeParseException ignoredAgain) { return parseDataTime(value); } } } private String firstNotBlank(String preferred, String fallback) { return StringUtils.isNotBlank(preferred) ? preferred : fallback; } private T firstNotNull(T preferred, T fallback) { return preferred != null ? preferred : fallback; } /** * 实时获取电站状态 */ public StationInfoStatusVO getStationStatus() { StationInfoStatusVO statusVO = new StationInfoStatusVO(); String token = KsolarUtils.token(); JSONObject statusJsonObj = KsolarUtils.getListStatus(token); JSONObject earnJsonObj = KsolarUtils.getListEarn("", token); if(Objects.nonNull(statusJsonObj)) { statusVO.setAlarm(statusJsonObj.getInteger("alarm")); statusVO.setOffline(statusJsonObj.getInteger("offline")); statusVO.setOnline(statusJsonObj.getInteger("online")); statusVO.setTotal(statusJsonObj.getInteger("total")); } if(Objects.nonNull(earnJsonObj)) { statusVO.setRealPower(earnJsonObj.getDouble("realPower")); statusVO.setDayGeneration(earnJsonObj.getDouble("dayGeneration")); } // 统计电站数量 List stationInfoList = lambdaQuery().eq(TStationInfo::getDelFlag, "0").list(); if(CollectionUtils.isNotEmpty(stationInfoList)) { statusVO.setStationCount(stationInfoList.size()); List onStatusList = stationInfoList.stream().filter(s -> Objects.nonNull(s.getLoanStatus()) && s.getLoanStatus() == 3).collect(Collectors.toList()); statusVO.setOnStatusCount(onStatusList.size()); statusVO.setOffStatusCount(stationInfoList.size() - onStatusList.size()); } // 电站状态数量加上非科士达的数据 List noKeShiDaList = stationInfoList.stream().filter(s -> s.getPlatformId() != 0).collect(Collectors.toList()); statusVO.setTotal(statusVO.getTotal() + noKeShiDaList.size()); statusVO.setOnline(statusVO.getOnline() + noKeShiDaList.size()); return statusVO; } /** * 根据ids删除数据时,校验示范存在第三方同步的数据,第三方同步的数据不允许删除 * @param ids 数据id * @return true可以删除,false不可以删除 */ public Boolean validateDelete(Long[] ids) { if(ids != null && ids.length > 0) { List idList = Arrays.stream(ids).collect(Collectors.toList()); List infoList = lambdaQuery().eq(TStationInfo::getDataSource, 2).in(TStationInfo::getId, idList).list(); if(CollectionUtils.isNotEmpty(infoList) && !infoList.isEmpty()) { return false; } } return true; } /** * 同步华为 FusionSolar 电站数据。 * 电站编号 plantCode 作为系统中的 stationId,仅插入数据库中不存在的电站。 */ @Transactional(rollbackFor = Exception.class) public void syncHuaWeiDataTask() { log.info("-------1、开始同步华为电站列表信息----"); String token = huWeiUtils.getXsrfToken(); JSONArray stationArray = huWeiUtils.getStations(1, token); Set stationIds = new HashSet<>(); if (CollectionUtils.isNotEmpty(stationArray)) { List insertList = new ArrayList<>(); huaWeiBuild(stationArray, stationIds, insertList); if (CollectionUtils.isNotEmpty(insertList)) { List existing = lambdaQuery().eq(TStationInfo::getPlatformId, 1L).in(TStationInfo::getStationId, stationIds).list(); Set existingIds = existing.stream().map(TStationInfo::getStationId).collect(Collectors.toSet()); insertList.removeIf(item -> existingIds.contains(item.getStationId())); if (CollectionUtils.isNotEmpty(insertList)) { saveBatch(insertList); } } log.info("-------1、结束同步华为电站列表信息,本次新增{}条----", insertList.size()); } log.info("-------2、开始同步华为设备列表信息----"); if(CollectionUtils.isNotEmpty(stationIds)) { String codes = String.join(",", stationIds); JSONArray devList = huWeiUtils.getDevList(codes, token); collectService.saveCollectHuaWeiList(devList); } log.info("-------3、开始同步华为电站日数据信息----"); List existInfos = lambdaQuery().eq(TStationInfo::getPlatformId, 1L).list(); Set existIds = existInfos.stream().map(TStationInfo::getStationId).collect(Collectors.toSet()); if(CollectionUtils.isNotEmpty(existIds)) { String codes = String.join(",", existIds); JSONArray stationDayList = huWeiUtils.getKpiStationDay(codes, System.currentTimeMillis(), token); stationEarnService.saveStationHuaWeiList(stationDayList); } log.info("-------3、结束同步华为电站日数据信息----"); } public void huaWeiBuild(JSONArray stationArray, Set stationIds, List insertList) { for (Object item : stationArray) { JSONObject json = JSONObject.parseObject(item.toString()); String stationId = json.getString("plantCode"); if (StringUtils.isBlank(stationId) || !stationIds.add(stationId)) { continue; } TStationInfo info = new TStationInfo(); info.setPlatformId(1L); info.setStationId(stationId); info.setStationName(json.getString("plantName")); info.setStationType("1"); info.setRunStatus("0"); info.setInstalledCapacity(toBigDecimal(json, "capacity")); info.setContactName(json.getString("contactPerson")); info.setPhone(json.getString("contactMethod")); info.setLatitude(json.getString("latitude")); info.setLongitude(json.getString("longitude")); info.setAddress(json.getString("plantAddress")); LocalDateTime gridTime = parseHuaweiDateTime(json.getString("gridConnectionDate")); info.setDataTime(gridTime); info.setLoanStatus(gridTime != null && gridTime.isBefore(LocalDateTime.now()) ? 3 : 1); info.setGridStatus(info.getLoanStatus() == 3 ? "1" : "0"); info.setDataSource(2); info.setSyncTime(LocalDateTime.now()); info.setCreateTime(LocalDateTime.now()); info.setUpdateTime(LocalDateTime.now()); fillDefaults(info); insertList.add(info); } } }