admin/src/views/business/ywelectricaldata.vue
@@ -80,6 +80,33 @@ :pagination="tableData.pagination" /> </template> <el-dialog title="ç«å³æè¡¨" :visible.sync="syncDialogVisible" width="520px" append-to-body @closed="resetSyncForm" > <el-form ref="syncFormRef" :model="syncForm" :rules="syncRules" label-width="110px"> <el-form-item label="æè¡¨æ¶é´æ®µ" prop="readTimeRange"> <el-date-picker v-model="syncForm.readTimeRange" type="datetimerange" value-format="yyyy-MM-dd HH:mm:ss" range-separator="-" start-placeholder="å¼å§æ¶é´" end-placeholder="ç»ææ¶é´" :picker-options="syncPickerOptions" style="width: 100%" /> </el-form-item> <p class="sync-tip">æ¶é´æ®µæé¿ä¸è¶ è¿7天</p> </el-form> <template v-slot:footer> <el-button @click="syncDialogVisible = false">åæ¶</el-button> <el-button type="primary" :loading="isReading" @click="submitSync">确认æè¡¨</el-button> </template> </el-dialog> </TableLayout> </template> @@ -108,7 +135,32 @@ readTimeRange: [] }, roomOptions: [], isReading: false isReading: false, syncDialogVisible: false, syncMinDate: null, syncForm: { readTimeRange: [] }, syncRules: { readTimeRange: [{ required: true, message: 'è¯·éæ©æè¡¨æ¶é´æ®µ', trigger: 'change' }] } } }, computed: { syncPickerOptions () { return { onPick: ({ minDate, maxDate }) => { this.syncMinDate = minDate && !maxDate ? minDate : null }, disabledDate: (time) => { if (!this.syncMinDate) return false const anchor = dayjs(this.syncMinDate) const min = anchor.subtract(7, 'day').startOf('day') const max = anchor.add(7, 'day').endOf('day') const current = dayjs(time) return current.isBefore(min) || current.isAfter(max) } } } }, created () { @@ -181,12 +233,60 @@ this.search() }, handleReadNow () { this.$dialog.actionConfirm('确认ç«å³ä»ç¬¬ä¸æ¹å¹³å°åæ¥æè¡¨æ°æ®åï¼', 'æä½ç¡®è®¤æé') .then(() => { this.syncForm.readTimeRange = this.defaultSyncRange() this.syncDialogVisible = true this.$nextTick(() => { if (this.$refs.syncFormRef) { this.$refs.syncFormRef.clearValidate() } }) }, defaultSyncRange () { const end = dayjs() const start = end.subtract(24, 'hour') return [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')] }, resetSyncForm () { this.syncMinDate = null this.syncForm.readTimeRange = [] if (this.$refs.syncFormRef) { this.$refs.syncFormRef.resetFields() } }, validateSyncRange (range) { if (!range || range.length !== 2) { this.$tip.warning('è¯·éæ©æè¡¨æ¶é´æ®µ') return false } const start = dayjs(range[0]) const end = dayjs(range[1]) if (!start.isValid() || !end.isValid()) { this.$tip.warning('æè¡¨æ¶é´æ ¼å¼ä¸æ£ç¡®') return false } if (!end.isAfter(start)) { this.$tip.warning('æè¡¨å¼å§æ¶é´å¿ é¡»æ©äºç»ææ¶é´') return false } if (end.diff(start, 'day', true) > 7) { this.$tip.warning('æè¡¨æ¶é´æ®µä¸è½è¶ è¿7天') return false } return true }, submitSync () { this.$refs.syncFormRef.validate(valid => { if (!valid) return const range = this.syncForm.readTimeRange if (!this.validateSyncRange(range)) return this.isReading = true dataApi.syncAll({}) dataApi.syncAll({ readTimeBegin: range[0], readTimeEnd: range[1] }) .then(res => { this.$tip.apiSuccess(res || 'æè¡¨åæ¥æå') this.syncDialogVisible = false this.search() }) .catch(e => { @@ -196,7 +296,6 @@ this.isReading = false }) }) .catch(() => {}) }, formatJsfs (val) { if (val == null || val === '') return '-' @@ -235,4 +334,10 @@ <style scoped> .red { color: #f56c6c; } .sync-tip { margin: 0 0 0 110px; color: #909399; font-size: 12px; line-height: 1.5; } </style> server/visits/dmvisit_admin/src/main/java/com/doumee/cloud/admin/YwElectricalDataCloudController.java
@@ -7,6 +7,7 @@ import com.doumee.core.model.ApiResponse; import com.doumee.core.model.PageData; import com.doumee.core.model.PageWrap; import com.doumee.dao.business.dto.YwElectricalDataSyncDTO; import com.doumee.dao.business.model.Device; import com.doumee.dao.business.model.YwElectricalData; import com.doumee.service.business.YwElectricalDataService; @@ -14,6 +15,7 @@ import com.doumee.config.annotation.CloudRequiredPermission; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; @@ -94,7 +96,11 @@ @ApiOperation("ç«å³æè¡¨ï¼åæ¥ç¬¬ä¸æ¹æè¡¨æ°æ®ï¼") @PostMapping("/syncAll") @CloudRequiredPermission("business:ywelectricaldata:sync") public ApiResponse<String> syncAll(@RequestHeader(Constants.HEADER_USER_TOKEN) String token) { public ApiResponse<String> syncAll(@RequestBody(required = false) YwElectricalDataSyncDTO dto, @RequestHeader(Constants.HEADER_USER_TOKEN) String token) { if (dto == null || (StringUtils.isBlank(dto.getReadTimeBegin()) && StringUtils.isBlank(dto.getReadTimeEnd()))) { return ApiResponse.success(ywElectricalDataService.syncFromPlatform()); } return ApiResponse.success(ywElectricalDataService.syncFromPlatform(dto.getReadTimeBegin(), dto.getReadTimeEnd())); } } server/visits/dmvisit_service/src/main/java/com/doumee/dao/business/dto/YwElectricalDataSyncDTO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,13 @@ package com.doumee.dao.business.dto; import lombok.Data; @Data public class YwElectricalDataSyncDTO { /** æè¡¨å¼å§æ¶é´ yyyy-MM-dd HH:mm:ss */ private String readTimeBegin; /** æè¡¨ç»ææ¶é´ yyyy-MM-dd HH:mm:ss */ private String readTimeEnd; } server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwElectricalBizService.java
@@ -35,6 +35,9 @@ /** æå¨ä»ç¬¬ä¸æ¹å¹³å°æåæè¡¨æ°æ®å ¥åº */ String syncMeterDataFromPlatform(); /** æå¨ä»ç¬¬ä¸æ¹å¹³å°æåæå®æ¶é´æ®µæè¡¨æ°æ®å ¥åº */ String syncMeterDataFromPlatform(String readTimeBegin, String readTimeEnd); void cleanLogBeforeThreeMonths(); void enrichList(List<YwElectrical> list); server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwElectricalDataService.java
@@ -113,4 +113,9 @@ * ä»ç¬¬ä¸æ¹å¹³å°åæ¥æè¡¨æ°æ® */ String syncFromPlatform(); /** * ä»ç¬¬ä¸æ¹å¹³å°åæ¥æå®æ¶é´æ®µæè¡¨æ°æ® */ String syncFromPlatform(String readTimeBegin, String readTimeEnd); } server/visits/dmvisit_service/src/main/java/com/doumee/service/business/YwWorkDeskEnergyService.java
@@ -12,4 +12,9 @@ List<DailyEnergyStatVO> electricalDailyStats(); List<DailyEnergyStatVO> conditionerDailyStats(); /** * æè¡¨åæ¥åï¼ææè¡¨æ¶é´æ®µåååæ©å±ä¸å¤©ï¼å·æ°æ¯æ¥çµé/çµè´¹ç»è®¡ */ String refreshElectricalDailyStatsForRange(String readTimeBegin, String readTimeEnd); } server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwElectricalBizServiceImpl.java
@@ -64,6 +64,12 @@ private static final int ELE_READ_TYPE_METER = 3; /** queryData çµè¡¨ç¶æè¯¦æ åè½ ID */ private static final String QUERY_DATA_FUNCTION_METER_STATUS = "253"; /** DataRequest 忬¡æå¤§æ¡æ°ï¼æ¥å£ limit ä¸é 1000ï¼ */ private static final int QUERY_DATA_PAGE_SIZE = 500; /** æè¡¨/DataRequest æ¥è¯¢æ¶é´è·¨åº¦ä¸éï¼å¤©ï¼ */ private static final int MAX_METER_QUERY_DAYS = 7; /** å页 total åçä¸éï¼é¿å è¯¯ææ¶é´æ³å½ä½æ»æ¡æ° */ private static final int QUERY_DATA_TOTAL_SANITY_MAX = 100_000; @Autowired private YwElectricalMapper ywElectricalMapper; @@ -829,7 +835,7 @@ @Override public void syncMeterDataScheduled() { try { syncMeterDataInternal(); syncMeterDataInternal(null, null); } catch (Exception e) { log.warn("syncMeterDataScheduled failed", e); } @@ -837,8 +843,36 @@ @Override public String syncMeterDataFromPlatform() { MeterDataSyncStats stats = syncMeterDataInternal(); MeterDataSyncStats stats = syncMeterDataInternal(null, null); return "æè¡¨åæ¥å®æï¼æ°å¢ã" + stats.addCount + "ãæ¡ï¼è·³è¿éå¤ã" + stats.skipCount + "ãæ¡"; } @Override public String syncMeterDataFromPlatform(String readTimeBegin, String readTimeEnd) { if (StringUtils.isBlank(readTimeBegin) || StringUtils.isBlank(readTimeEnd)) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "è¯·éæ©æè¡¨æ¶é´æ®µ"); } String startTime = readTimeBegin.trim(); String endTime = readTimeEnd.trim(); validateManualSyncTimeRange(startTime, endTime); String expandedStart = expandStartByOneDay(startTime); MeterDataSyncStats stats = syncMeterDataInternal(expandedStart, endTime); return "æè¡¨åæ¥å®æï¼æ°å¢ã" + stats.addCount + "ãæ¡ï¼è·³è¿éå¤ã" + stats.skipCount + "ãæ¡"; } private String expandStartByOneDay(String startTime) { try { Date start = DateUtil.StringToDate(startTime, "yyyy-MM-dd HH:mm:ss"); if (start == null) { return startTime; } Calendar cal = Calendar.getInstance(); cal.setTime(start); cal.add(Calendar.DAY_OF_MONTH, -1); return DateUtil.formatDate(cal.getTime(), "yyyy-MM-dd HH:mm:ss"); } catch (Exception e) { return startTime; } } private static class MeterDataSyncStats { @@ -846,12 +880,14 @@ private int skipCount; } private MeterDataSyncStats syncMeterDataInternal() { private MeterDataSyncStats syncMeterDataInternal(String startTime, String endTime) { MeterDataSyncStats stats = new MeterDataSyncStats(); String startTime = resolveSyncStartTime(); QueryDataRequest param = buildQueryDataRequest(startTime, DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss")); log.info("sync meter data, start_time={}, end_time={}", startTime, param.getEnd_time()); List<QueryDataInfoResponse> list = fetchQueryDataList(param); String resolvedStart = StringUtils.isNotBlank(startTime) ? startTime : resolveSyncStartTime(); String resolvedEnd = StringUtils.isNotBlank(endTime) ? endTime : DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"); resolvedStart = capQueryStartTime(resolvedStart, resolvedEnd, MAX_METER_QUERY_DAYS); QueryDataRequest param = buildQueryDataRequest(resolvedStart, resolvedEnd); log.info("sync meter data, start_time={}, end_time={}", resolvedStart, param.getEnd_time()); List<QueryDataInfoResponse> list = fetchAllQueryDataList(param); if (CollectionUtils.isEmpty(list)) { return stats; } @@ -875,6 +911,51 @@ return stats; } private void validateManualSyncTimeRange(String startTime, String endTime) { if (!startTime.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}") || !endTime.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "æè¡¨æ¶é´æ ¼å¼ä¸æ£ç¡®"); } Date start; Date end; try { start = DateUtil.StringToDate(startTime, "yyyy-MM-dd HH:mm:ss"); end = DateUtil.StringToDate(endTime, "yyyy-MM-dd HH:mm:ss"); } catch (Exception e) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "æè¡¨æ¶é´æ ¼å¼ä¸æ£ç¡®"); } if (start == null || end == null || !start.before(end)) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "æè¡¨å¼å§æ¶é´å¿ é¡»æ©äºç»ææ¶é´"); } long diffMs = end.getTime() - start.getTime(); if (diffMs > (long) MAX_METER_QUERY_DAYS * 24 * 60 * 60 * 1000) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "æè¡¨æ¶é´æ®µä¸è½è¶ è¿" + MAX_METER_QUERY_DAYS + "天"); } } private String capQueryStartTime(String startTime, String endTime, int maxDays) { if (StringUtils.isBlank(startTime) || StringUtils.isBlank(endTime)) { return startTime; } try { Date start = DateUtil.StringToDate(startTime, "yyyy-MM-dd HH:mm:ss"); Date end = DateUtil.StringToDate(endTime, "yyyy-MM-dd HH:mm:ss"); if (start == null || end == null || !start.before(end)) { return startTime; } long maxMs = (long) maxDays * 24 * 60 * 60 * 1000; if (end.getTime() - start.getTime() <= maxMs) { return startTime; } Calendar cal = Calendar.getInstance(); cal.setTime(end); cal.add(Calendar.DAY_OF_MONTH, -maxDays); return DateUtil.formatDate(cal.getTime(), "yyyy-MM-dd HH:mm:ss"); } catch (Exception e) { return startTime; } } /** å表æè¡¨åä»ç¬¬ä¸æ¹æåææ°æ°æ®å ¥åºï¼è¿åæ¯å¦ææ°è®°å½ */ private boolean syncMeterDataForElectrical(YwElectrical e) { if (e == null || StringUtils.isBlank(e.getAddress())) { @@ -887,7 +968,7 @@ DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss")); List<QueryDataInfoResponse> list; try { list = fetchQueryDataList(param); list = fetchAllQueryDataList(param); } catch (BusinessException ex) { log.warn("sync meter data for electricalId={} failed: {}", e.getId(), ex.getMessage()); return false; @@ -918,17 +999,66 @@ param.setStart_time(startTime); param.setEnd_time(endTime); param.setOffset(0); param.setLimit(500); param.setLimit(QUERY_DATA_PAGE_SIZE); return param; } private List<QueryDataInfoResponse> fetchQueryDataList(QueryDataRequest param) { /** æ DataRequest å页 total å¾ªç¯æåå ¨é¨æè¡¨æ°æ® */ private List<QueryDataInfoResponse> fetchAllQueryDataList(QueryDataRequest param) { if (param == null) { return Collections.emptyList(); } int limit = param.getLimit() > 0 ? param.getLimit() : QUERY_DATA_PAGE_SIZE; List<QueryDataInfoResponse> all = new ArrayList<>(); int offset = 0; Integer total = null; int pageNo = 0; while (true) { pageNo++; param.setOffset(offset); param.setLimit(limit); ElectronicDataResponse response = ElectronicToolUtil.queryDataRequest(param); if (!ElectronicToolUtil.isDataApiSuccess(response)) { throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), ElectronicToolUtil.dataApiErrorMessage(response, "æè¡¨æ°æ®åæ¥å¤±è´¥")); } return parseQueryDataList(response); List<QueryDataInfoResponse> page = parseQueryDataList(response); if (!CollectionUtils.isEmpty(page)) { all.addAll(page); } if (total == null) { total = resolveQueryTotal(response); } log.info("sync meter data page={}, offset={}, pageSize={}, accumulated={}, total={}", pageNo, offset, page.size(), all.size(), total); if (total != null && total > 0) { offset += limit; if (offset >= total) { break; } continue; } if (CollectionUtils.isEmpty(page) || page.size() < limit) { break; } offset += limit; if (pageNo >= 200) { log.warn("sync meter data pagination exceeded safety page limit, accumulated={}", all.size()); break; } } return all; } private Integer resolveQueryTotal(ElectronicDataResponse response) { if (response == null || response.getTotal() == null) { return null; } int total = response.getTotal(); if (total <= 0 || total > QUERY_DATA_TOTAL_SANITY_MAX) { return null; } return total; } private List<QueryDataInfoResponse> parseQueryDataList(ElectronicDataResponse response) { server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwElectricalDataServiceImpl.java
@@ -15,12 +15,14 @@ import com.doumee.dao.business.model.YwElectricalRoom; import com.doumee.service.business.YwElectricalBizService; import com.doumee.service.business.YwElectricalDataService; import com.doumee.service.business.YwWorkDeskEnergyService; import com.github.yulichang.wrapper.MPJLambdaWrapper; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.text.SimpleDateFormat; import java.util.List; /** @@ -33,6 +35,8 @@ private YwElectricalDataMapper ywElectricalDataMapper; @Autowired private YwElectricalBizService ywElectricalBizService; @Autowired private YwWorkDeskEnergyService ywWorkDeskEnergyService; @Override public Integer create(YwElectricalData ywElectricalData) { @@ -137,13 +141,28 @@ .eq(YwElectricalRoom::getRoomId, model.getRoomId()); } if (model.getReadTimeBegin() != null) { queryWrapper.ge(YwElectricalData::getCreateDate, Utils.Date.getStart(model.getReadTimeBegin())); String begin = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(Utils.Date.getStart(model.getReadTimeBegin())); queryWrapper.and(w -> w.and(w1 -> w1.isNotNull(YwElectricalData::getAddTime) .ne(YwElectricalData::getAddTime, "") .ge(YwElectricalData::getAddTime, begin)) .or(w2 -> w2.and(w3 -> w3.isNull(YwElectricalData::getAddTime) .or().eq(YwElectricalData::getAddTime, "")) .ge(YwElectricalData::getCreateDate, Utils.Date.getStart(model.getReadTimeBegin())))); } if (model.getReadTimeEnd() != null) { queryWrapper.le(YwElectricalData::getCreateDate, Utils.Date.getEnd(model.getReadTimeEnd())); String end = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(Utils.Date.getEnd(model.getReadTimeEnd())); queryWrapper.and(w -> w.and(w1 -> w1.isNotNull(YwElectricalData::getAddTime) .ne(YwElectricalData::getAddTime, "") .le(YwElectricalData::getAddTime, end)) .or(w2 -> w2.and(w3 -> w3.isNull(YwElectricalData::getAddTime) .or().eq(YwElectricalData::getAddTime, "")) .le(YwElectricalData::getCreateDate, Utils.Date.getEnd(model.getReadTimeEnd())))); } queryWrapper.orderByDesc(YwElectricalData::getCreateDate) queryWrapper.orderByDesc(YwElectricalData::getAddTime) .orderByDesc(YwElectricalData::getCreateDate) .orderByDesc(YwElectricalData::getId); IPage<YwElectricalData> result = ywElectricalDataMapper.selectJoinPage(page, YwElectricalData.class, queryWrapper); PageData<YwElectricalData> pageData = PageData.from(result); @@ -161,4 +180,11 @@ public String syncFromPlatform() { return ywElectricalBizService.syncMeterDataFromPlatform(); } @Override public String syncFromPlatform(String readTimeBegin, String readTimeEnd) { String syncMsg = ywElectricalBizService.syncMeterDataFromPlatform(readTimeBegin, readTimeEnd); String statsMsg = ywWorkDeskEnergyService.refreshElectricalDailyStatsForRange(readTimeBegin, readTimeEnd); return syncMsg + "ï¼" + statsMsg; } } server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwWorkDeskEnergyServiceImpl.java
@@ -1,7 +1,10 @@ package com.doumee.service.business.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.doumee.core.constants.ResponseStatus; import com.doumee.core.exception.BusinessException; import com.doumee.core.utils.Constants; import com.doumee.core.utils.DateUtil; import com.doumee.dao.business.YwConditionerUsageMapper; import com.doumee.dao.business.YwElectricalDataMapper; import com.doumee.dao.business.model.YwConditionerUsage; @@ -18,6 +21,7 @@ import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -42,13 +46,54 @@ public List<DailyEnergyStatVO> electricalDailyStats() { LocalDate end = LocalDate.now(); LocalDate start = end.minusDays(DAYS - 1L); LocalDate queryStart = start.minusDays(1L); return buildElectricalDailyStats(start, end); } @Override public String refreshElectricalDailyStatsForRange(String readTimeBegin, String readTimeEnd) { if (StringUtils.isBlank(readTimeBegin) || StringUtils.isBlank(readTimeEnd)) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "æè¡¨æ¶é´æ®µä¸è½ä¸ºç©º"); } LocalDate start = parseDateTime(readTimeBegin.trim()).minusDays(1); LocalDate end = parseDateTime(readTimeEnd.trim()).plusDays(1); if (start.isAfter(end)) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "æè¡¨æ¶é´æ®µæ æ"); } buildElectricalDailyStats(start, end); return "已巿°æ¯æ¥çµé/çµè´¹ç»è®¡ï¼" + start.format(DATE_FMT) + " ~ " + end.format(DATE_FMT) + "ï¼"; } @Override public List<DailyEnergyStatVO> conditionerDailyStats() { LocalDate end = LocalDate.now(); LocalDate start = end.minusDays(DAYS - 1L); Map<String, DailyEnergyStatVO> bucket = initDailyBucket(start, end); List<YwElectricalData> rows = ywElectricalDataMapper.selectList(new QueryWrapper<YwElectricalData>().lambda() .eq(YwElectricalData::getIsdeleted, Constants.ZERO) .ge(YwElectricalData::getCreateDate, java.sql.Date.valueOf(queryStart)) .le(YwElectricalData::getCreateDate, java.sql.Date.valueOf(end.plusDays(1)))); List<YwConditionerUsage> rows = ywConditionerUsageMapper.selectList(new QueryWrapper<YwConditionerUsage>().lambda() .eq(YwConditionerUsage::getIsdeleted, Constants.ZERO) .ge(YwConditionerUsage::getUsageDate, java.sql.Date.valueOf(start)) .le(YwConditionerUsage::getUsageDate, java.sql.Date.valueOf(end))); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); for (YwConditionerUsage row : rows) { if (row.getUsageDate() == null) { continue; } String dayKey = sdf.format(row.getUsageDate()); if (!bucket.containsKey(dayKey)) { continue; } DailyEnergyStatVO stat = bucket.get(dayKey); stat.setTotalKwh(stat.getTotalKwh().add(nullToZero(row.getSumDl()))); stat.setTotalFee(stat.getTotalFee().add(nullToZero(row.getSumDf()))); } return normalizeBucket(bucket); } private List<DailyEnergyStatVO> buildElectricalDailyStats(LocalDate start, LocalDate end) { LocalDate queryStart = start.minusDays(1L); Map<String, DailyEnergyStatVO> bucket = initDailyBucket(start, end); List<YwElectricalData> rows = loadElectricalRowsForStats(queryStart, end); SimpleDateFormat dayFmt = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat readTimeFmt = new SimpleDateFormat(READ_TIME_PATTERN); @@ -93,31 +138,36 @@ return normalizeBucket(bucket); } @Override public List<DailyEnergyStatVO> conditionerDailyStats() { LocalDate end = LocalDate.now(); LocalDate start = end.minusDays(DAYS - 1L); Map<String, DailyEnergyStatVO> bucket = initDailyBucket(start, end); private List<YwElectricalData> loadElectricalRowsForStats(LocalDate queryStart, LocalDate end) { String queryStartStr = queryStart.format(DATE_FMT) + " 00:00:00"; String queryEndStr = end.format(DATE_FMT) + " 23:59:59"; return ywElectricalDataMapper.selectList(new QueryWrapper<YwElectricalData>().lambda() .eq(YwElectricalData::getIsdeleted, Constants.ZERO) .and(w -> w.and(w1 -> w1.isNotNull(YwElectricalData::getAddTime) .ne(YwElectricalData::getAddTime, "") .ge(YwElectricalData::getAddTime, queryStartStr) .le(YwElectricalData::getAddTime, queryEndStr)) .or(w2 -> w2.and(w3 -> w3.isNull(YwElectricalData::getAddTime) .or().eq(YwElectricalData::getAddTime, "")) .ge(YwElectricalData::getCreateDate, java.sql.Date.valueOf(queryStart)) .le(YwElectricalData::getCreateDate, java.sql.Date.valueOf(end.plusDays(1)))))); } List<YwConditionerUsage> rows = ywConditionerUsageMapper.selectList(new QueryWrapper<YwConditionerUsage>().lambda() .eq(YwConditionerUsage::getIsdeleted, Constants.ZERO) .ge(YwConditionerUsage::getUsageDate, java.sql.Date.valueOf(start)) .le(YwConditionerUsage::getUsageDate, java.sql.Date.valueOf(end))); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); for (YwConditionerUsage row : rows) { if (row.getUsageDate() == null) { continue; private static LocalDate parseDateTime(String text) { if (text.length() >= 10) { try { return LocalDate.parse(text.substring(0, 10), DATE_FMT); } catch (DateTimeParseException ignored) { } String dayKey = sdf.format(row.getUsageDate()); if (!bucket.containsKey(dayKey)) { continue; } DailyEnergyStatVO stat = bucket.get(dayKey); stat.setTotalKwh(stat.getTotalKwh().add(nullToZero(row.getSumDl()))); stat.setTotalFee(stat.getTotalFee().add(nullToZero(row.getSumDf()))); try { Date date = DateUtil.StringToDate(text, READ_TIME_PATTERN); if (date != null) { return date.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(); } return normalizeBucket(bucket); } catch (Exception ignored) { } throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "æè¡¨æ¶é´æ ¼å¼ä¸æ£ç¡®"); } private static void upsertLatestReading(Map<String, Map<String, MeterDayReading>> meterDayLatest,