This commit is contained in:
2026-04-20 20:42:57 +08:00
parent 7153c00d0c
commit 111631a426
7 changed files with 206 additions and 109 deletions

View File

@ -70,7 +70,7 @@ spring:
# 端口默认为6379 # 端口默认为6379
port: 6379 port: 6379
# 数据库索引 # 数据库索引
database: 0 database: 3
# 密码 # 密码
password: 12345678 password: 12345678
# 连接超时时间 # 连接超时时间
@ -92,7 +92,7 @@ spring:
druid: druid:
# 主库数据源 # 主库数据源
master: master:
url: jdbc:mysql://172.17.0.13:3306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 url: jdbc:mysql://172.17.0.13:3306/setri_ems_new?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ems username: ems
password: Aa112211! password: Aa112211!
# 从库数据源 # 从库数据源
@ -188,7 +188,7 @@ xss:
mqtt: mqtt:
broker.url: tcp://121.5.164.6:1883 broker.url: tcp://121.5.164.6:1883
client.id: ems-cloud client.id: ems-cloud-new
username: dmbroker username: dmbroker
password: qwer1234 password: qwer1234
connection-timeout: 15 connection-timeout: 15
@ -216,7 +216,7 @@ weather:
influxdb: influxdb:
enabled: true enabled: true
url: http://172.17.0.7:8086/ url: http://172.17.0.7:8086/
api-token: B_1HvHbUhubQQhLdI0XtNVw7maWS1aIjVZQ1a3PGD6b-VNg3_JUo_jHgZmjeBKYXnGATNdIqfpl-FAVbJ4UIPg== api-token: l_MUXGYFs15utEaLLLgGUUkHYVA84nweimAyeiHNRIg_FWy3ACcdA85LnxDIBKA8bKxbPp2isTkrqHzrhXtZYw==
write-method: POST write-method: POST
read-method: GET read-method: GET
write-path: /api/v2/write write-path: /api/v2/write

View File

@ -94,7 +94,7 @@ spring:
druid: druid:
# 主库数据源 # 主库数据源
master: master:
url: jdbc:mysql://122.51.194.184:13306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 url: jdbc:mysql://122.51.194.184:13306/setri_ems_new?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ems username: ems
password: 12345678 password: 12345678
# 从库数据源 # 从库数据源

View File

@ -70,6 +70,18 @@
<pattern>${log.pattern}</pattern> <pattern>${log.pattern}</pattern>
</encoder> </encoder>
</appender> </appender>
<!-- InfluxDB 写入成功日志输出 -->
<appender name="sys-influxdb" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-influxdb.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/sys-influxdb.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 --> <!-- 系统模块日志级别控制 -->
<logger name="com.xzzn" level="info" /> <logger name="com.xzzn" level="info" />
@ -90,4 +102,9 @@
<logger name="sys-user" level="info"> <logger name="sys-user" level="info">
<appender-ref ref="sys-user"/> <appender-ref ref="sys-user"/>
</logger> </logger>
<!-- InfluxDB 写入成功专用日志 -->
<logger name="sys-influxdb" level="info" additivity="false">
<appender-ref ref="sys-influxdb"/>
</logger>
</configuration> </configuration>

View File

@ -28,6 +28,7 @@ import java.util.Map;
public class InfluxPointDataWriter { public class InfluxPointDataWriter {
private static final Logger log = LoggerFactory.getLogger(InfluxPointDataWriter.class); private static final Logger log = LoggerFactory.getLogger(InfluxPointDataWriter.class);
private static final Logger influxWriteSuccessLog = LoggerFactory.getLogger("sys-influxdb");
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Value("${influxdb.enabled:true}") @Value("${influxdb.enabled:true}")
@ -91,6 +92,7 @@ public class InfluxPointDataWriter {
} }
try { try {
StringBuilder body = new StringBuilder(); StringBuilder body = new StringBuilder();
List<String> pointSummaries = new ArrayList<>();
for (PointWritePayload payload : payloads) { for (PointWritePayload payload : payloads) {
if (payload == null || payload.getPointValue() == null) { if (payload == null || payload.getPointValue() == null) {
continue; continue;
@ -103,6 +105,12 @@ public class InfluxPointDataWriter {
.append(" value=").append(payload.getPointValue().toPlainString()) .append(" value=").append(payload.getPointValue().toPlainString())
.append(" ").append(time) .append(" ").append(time)
.append("\n"); .append("\n");
pointSummaries.add(String.format("siteId=%s, deviceId=%s, pointKey=%s, value=%s, time=%s",
safe(payload.getSiteId()),
safe(payload.getDeviceId()),
safe(payload.getPointKey()),
payload.getPointValue(),
new Date(time)));
} }
if (body.length() == 0) { if (body.length() == 0) {
return; return;
@ -112,6 +120,8 @@ public class InfluxPointDataWriter {
log.warn("写入 InfluxDB 失败v2 写入地址未构建成功,请检查 influxdb.org / influxdb.bucket 配置"); log.warn("写入 InfluxDB 失败v2 写入地址未构建成功,请检查 influxdb.org / influxdb.bucket 配置");
return; return;
} }
long startMillis = System.currentTimeMillis();
int writeCount = countWriteLines(body);
HttpResult result = executeRequest(methodOrDefault(writeMethod, "POST"), writeUrl, body.toString()); HttpResult result = executeRequest(methodOrDefault(writeMethod, "POST"), writeUrl, body.toString());
if (result.code < 200 || result.code >= 300) { if (result.code < 200 || result.code >= 300) {
if (result.code == 404 && isV2WritePath() && isOrgOrBucketMissing(result.body)) { if (result.code == 404 && isV2WritePath() && isOrgOrBucketMissing(result.body)) {
@ -119,6 +129,7 @@ public class InfluxPointDataWriter {
HttpResult retryResult = executeRequest(methodOrDefault(writeMethod, "POST"), writeUrl, body.toString()); HttpResult retryResult = executeRequest(methodOrDefault(writeMethod, "POST"), writeUrl, body.toString());
if (retryResult.code >= 200 && retryResult.code < 300) { if (retryResult.code >= 200 && retryResult.code < 300) {
log.info("InfluxDB org/bucket 自动创建成功,写入已恢复"); log.info("InfluxDB org/bucket 自动创建成功,写入已恢复");
logWriteSuccess(writeCount, writeUrl, startMillis, pointSummaries);
return; return;
} }
log.warn("InfluxDB 重试写入失败HTTP状态码: {}, url: {}, body: {}", retryResult.code, writeUrl, safeLog(retryResult.body)); log.warn("InfluxDB 重试写入失败HTTP状态码: {}, url: {}, body: {}", retryResult.code, writeUrl, safeLog(retryResult.body));
@ -126,7 +137,9 @@ public class InfluxPointDataWriter {
} }
} }
log.warn("写入 InfluxDB 失败HTTP状态码: {}, url: {}, body: {}", result.code, writeUrl, safeLog(result.body)); log.warn("写入 InfluxDB 失败HTTP状态码: {}, url: {}, body: {}", result.code, writeUrl, safeLog(result.body));
return;
} }
logWriteSuccess(writeCount, writeUrl, startMillis, pointSummaries);
} catch (Exception e) { } catch (Exception e) {
log.warn("写入 InfluxDB 失败: {}", e.getMessage()); log.warn("写入 InfluxDB 失败: {}", e.getMessage());
} }
@ -173,7 +186,8 @@ public class InfluxPointDataWriter {
startTime.getTime(), startTime.getTime(),
endTime.getTime() endTime.getTime()
); );
values = parseInfluxQlResponse(executeRequestWithResponse(methodOrDefault(readMethod, "GET"), buildQueryUrl(regexQuery))); String regexQueryUrl = buildQueryUrl(regexQuery);
values = parseInfluxQlResponse(executeRequestWithResponse(methodOrDefault(readMethod, "GET"), regexQueryUrl));
return values; return values;
} catch (Exception e) { } catch (Exception e) {
log.warn("查询 InfluxDB 曲线失败: {}", e.getMessage()); log.warn("查询 InfluxDB 曲线失败: {}", e.getMessage());
@ -204,7 +218,8 @@ public class InfluxPointDataWriter {
try { try {
String queryUrl = buildQueryUrl(influxQl); String queryUrl = buildQueryUrl(influxQl);
return parseInfluxQlResponse(executeRequestWithResponse(methodOrDefault(readMethod, "GET"), queryUrl)); List<PointValue> values = parseInfluxQlResponse(executeRequestWithResponse(methodOrDefault(readMethod, "GET"), queryUrl));
return values;
} catch (Exception e) { } catch (Exception e) {
log.warn("按 pointKey 查询 InfluxDB 曲线失败: {}", e.getMessage()); log.warn("按 pointKey 查询 InfluxDB 曲线失败: {}", e.getMessage());
return Collections.emptyList(); return Collections.emptyList();
@ -234,24 +249,9 @@ public class InfluxPointDataWriter {
); );
try { try {
String queryUrl = buildQueryUrl(influxQl);
List<PointValue> values = parseInfluxQlResponse( List<PointValue> values = parseInfluxQlResponse(
executeRequestWithResponse(methodOrDefault(readMethod, "GET"), buildQueryUrl(influxQl)) executeRequestWithResponse(methodOrDefault(readMethod, "GET"), queryUrl)
);
if (!values.isEmpty()) {
return values.get(0);
}
String regexQuery = String.format(
"SELECT \"value\" FROM \"%s\" WHERE \"site_id\" = '%s' AND \"point_key\" =~ /(?i)^%s$/ " +
"AND time >= %dms AND time <= %dms ORDER BY time DESC LIMIT 1",
measurement,
escapeTagValue(normalizedSiteId),
escapeRegex(normalizedPointKey),
startTime.getTime(),
endTime.getTime()
);
values = parseInfluxQlResponse(
executeRequestWithResponse(methodOrDefault(readMethod, "GET"), buildQueryUrl(regexQuery))
); );
return values.isEmpty() ? null : values.get(0); return values.isEmpty() ? null : values.get(0);
} catch (Exception e) { } catch (Exception e) {
@ -260,6 +260,21 @@ public class InfluxPointDataWriter {
} }
} }
private void logWriteSuccess(int writeCount, String writeUrl, long startMillis, List<String> pointSummaries) {
influxWriteSuccessLog.info("InfluxDB写入成功, count: {}, points: {}, url: {}, costMs: {}",
writeCount, pointSummaries, writeUrl, System.currentTimeMillis() - startMillis);
}
private int countWriteLines(StringBuilder body) {
int count = 0;
for (int i = 0; i < body.length(); i++) {
if (body.charAt(i) == '\n') {
count++;
}
}
return count;
}
private String buildWriteUrl() { private String buildWriteUrl() {
if (isV2WritePath()) { if (isV2WritePath()) {
return buildV2WriteUrl(); return buildV2WriteUrl();

View File

@ -2816,15 +2816,16 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
private JSONArray parseJsonData(String message) { private JSONArray parseJsonData(String message) {
try { try {
JSONObject object = JSONObject.parseObject(message); Object parsed = JSON.parse(message);
return object.getJSONArray("payload"); if (parsed instanceof JSONArray) {
} catch (JSONException e) { return (JSONArray) parsed;
log.info("mqtt message is not a json object: " + e.getMessage());
try {
return JSONArray.parseArray(message);
} catch (Exception arrayException) {
log.info("mqtt message is not a json array: " + e.getMessage());
} }
if (parsed instanceof JSONObject) {
return ((JSONObject) parsed).getJSONArray("payload");
}
log.info("mqtt message root type is unsupported: {}", parsed == null ? "null" : parsed.getClass().getName());
} catch (JSONException e) {
log.info("mqtt message parse failed: {}", e.getMessage());
} }
return null; return null;
} }
@ -3954,15 +3955,22 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
log.info("deviceId:" + deviceId); log.info("deviceId:" + deviceId);
if (checkJsonDataEmpty(jsonData)) {
log.warn("设备告警Data为空跳过告警处理siteId: {}deviceId: {}", siteId, deviceId);
continue;
}
// 存放mqtt原始每个设备最晚一次数据便于后面点位获取数据 // 存放mqtt原始每个设备最晚一次数据便于后面点位获取数据
redisCache.setCacheObject(RedisKeyConstants.ORIGINAL_MQTT_DATA_ALARM + siteId + "_" + deviceId, obj); redisCache.setCacheObject(RedisKeyConstants.ORIGINAL_MQTT_DATA_ALARM + siteId + "_" + deviceId, obj);
// 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据 // 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据
redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA_ALARM + siteId + "_" + deviceId, obj, 1, TimeUnit.MINUTES); redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA_ALARM + siteId + "_" + deviceId, obj, 1, TimeUnit.MINUTES);
// 处理设备数据,根据不同设备类型进行不同的数据处理 // 处理设备数据,根据不同设备类型进行不同的数据处理
String deviceCategory = processingDeviceAlarmData(siteId, deviceId, jsonData, dataUpdateTime); String deviceCategory = processingDeviceAlarmData(siteId, deviceId, jsonData, dataUpdateTime);
if (StringUtils.isEmpty(deviceCategory)) { if (StringUtils.isNotEmpty(deviceCategory)) {
// 处理告警信息 // 处理告警信息
alarmDataProcess(siteId, deviceId, jsonData, alarmMatchInfo, deviceCategory); alarmDataProcess(siteId, deviceId, jsonData, alarmMatchInfo, deviceCategory);
} else {
log.warn("设备告警数据未识别到设备类型跳过告警处理siteId: {}deviceId: {}", siteId, deviceId);
} }
} }
} }
@ -4097,8 +4105,21 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
private void alarmDataProcess(String siteId, String deviceId, String jsonData, private void alarmDataProcess(String siteId, String deviceId, String jsonData,
Map<String, EmsAlarmMatchData> alarmInfoData, String category) { Map<String, EmsAlarmMatchData> alarmInfoData, String category) {
if (StringUtils.isEmpty(category)) {
log.warn("设备类型为空跳过告警处理siteId: {}deviceId: {}", siteId, deviceId);
return;
}
if (alarmInfoData == null || alarmInfoData.isEmpty()) {
log.warn("告警匹配缓存为空跳过告警处理siteId: {}deviceId: {}category: {}", siteId, deviceId, category);
return;
}
Map<String, Object> obj = JSON.parseObject(jsonData, new TypeReference<Map<String, Object>>() { Map<String, Object> obj = JSON.parseObject(jsonData, new TypeReference<Map<String, Object>>() {
}); });
if (obj == null || obj.isEmpty()) {
log.warn("设备告警Data解析结果为空跳过告警处理siteId: {}deviceId: {}category: {}data: {}",
siteId, deviceId, category, jsonData);
return;
}
String redisKey = RedisKeyConstants.LATEST_ALARM_RECORD + "_" + siteId + "_" + deviceId; String redisKey = RedisKeyConstants.LATEST_ALARM_RECORD + "_" + siteId + "_" + deviceId;
// 获取redis里面的当前有效告警遍历添加到已存在告警key里面 // 获取redis里面的当前有效告警遍历添加到已存在告警key里面
@ -4114,12 +4135,8 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
// 结合同步数据,筛选簇需要更新的告警信息 // 结合同步数据,筛选簇需要更新的告警信息
List<String> needUpdateKeys = obj.entrySet().stream() List<String> needUpdateKeys = obj.entrySet().stream()
.filter(entry -> { .filter(entry -> {
Object valueObj = entry.getValue(); Long value = convertToLong(entry.getValue());
if (valueObj == null) { return value != null && value == 0L && currentAlarmKeys.contains(entry.getKey());
return false;
}
int value = Integer.parseInt(valueObj.toString());
return value == 0 && currentAlarmKeys.contains(entry.getKey());
}) })
.map(Map.Entry::getKey) .map(Map.Entry::getKey)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -4129,11 +4146,13 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
if (!needUpdateKeys.isEmpty()) { if (!needUpdateKeys.isEmpty()) {
List<EmsAlarmRecords> records = iEmsAlarmRecordsService.getAllUnfinishedRecords(needUpdateKeys, siteId, deviceId); List<EmsAlarmRecords> records = iEmsAlarmRecordsService.getAllUnfinishedRecords(needUpdateKeys, siteId, deviceId);
// 转为Map便于快速获取 // 转为Map便于快速获取
needUpdateMap = records.stream() if (CollectionUtils.isNotEmpty(records)) {
.collect(Collectors.toMap( needUpdateMap = records.stream()
EmsAlarmRecords::getAlarmPoint, .collect(Collectors.toMap(
record -> record EmsAlarmRecords::getAlarmPoint,
)); record -> record
));
}
} }
@ -4147,20 +4166,28 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
continue; continue;
} }
String key = entry.getKey(); String key = entry.getKey();
Long value = (Long) entry.getValue(); Long value = convertToLong(entry.getValue());
if (value == null) {
log.warn("告警值不是有效数字忽略处理siteId: {}deviceId: {}point: {}value: {}",
siteId, deviceId, key, entry.getValue());
continue;
}
Boolean isCurrentAlarm = currentAlarmKeys.contains(key); Boolean isCurrentAlarm = currentAlarmKeys.contains(key);
String matchRedisKey = siteId + "_" + category + "_" + key; String matchRedisKey = siteId + "_" + category + "_" + key;
// 默认告警值0是正常1是异常 // 默认告警值0是正常1是异常
Long alarmData = 1L; Long alarmData = 1L;
Object cacheObj = alarmInfoData.get(matchRedisKey); EmsAlarmMatchData matchInfo = alarmInfoData.get(matchRedisKey);
if (cacheObj != null) { if (matchInfo != null) {
EmsAlarmMatchData matchInfo = JSON.toJavaObject(cacheObj, EmsAlarmMatchData.class);
alarmData = matchInfo.getAlarmData(); alarmData = matchInfo.getAlarmData();
} }
// 处理告警 // 处理告警
if (value.equals(alarmData) && !isCurrentAlarm) { if (value.equals(alarmData) && !isCurrentAlarm) {
// 上送值和匹配值相同,新增告警 // 上送值和匹配值相同,新增告警
EmsAlarmMatchData matchInfo = JSON.toJavaObject(cacheObj, EmsAlarmMatchData.class); if (matchInfo == null) {
log.warn("未找到告警匹配配置忽略新增告警siteId: {}deviceId: {}category: {}point: {}",
siteId, deviceId, category, key);
continue;
}
EmsAlarmRecords emsAlarmRecord = convertAlarmRecord(siteId, deviceId, matchInfo); EmsAlarmRecords emsAlarmRecord = convertAlarmRecord(siteId, deviceId, matchInfo);
saveOrUpdateList.add(emsAlarmRecord); saveOrUpdateList.add(emsAlarmRecord);
newAddRecordList.add(emsAlarmRecord); newAddRecordList.add(emsAlarmRecord);
@ -4219,6 +4246,24 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
} }
} }
private Long convertToLong(Object value) {
if (value == null) {
return null;
}
if (value instanceof Number) {
return ((Number) value).longValue();
}
String text = String.valueOf(value);
if (StringUtils.isBlank(text)) {
return null;
}
try {
return new BigDecimal(text.trim()).longValue();
} catch (NumberFormatException ex) {
return null;
}
}
private EmsAlarmRecords convertAlarmRecord(String siteId, String deviceId, EmsAlarmMatchData matchInfo) { private EmsAlarmRecords convertAlarmRecord(String siteId, String deviceId, EmsAlarmMatchData matchInfo) {
EmsAlarmRecords emsAlarmRecords = new EmsAlarmRecords(); EmsAlarmRecords emsAlarmRecords = new EmsAlarmRecords();
emsAlarmRecords.setSiteId(siteId); emsAlarmRecords.setSiteId(siteId);

View File

@ -125,7 +125,10 @@
t.alarm_content as alarmContent, t.alarm_content as alarmContent,
t.ticket_no as ticketNo, t.ticket_no as ticketNo,
t.id t.id
from ems_alarm_records t INNER JOIN ems_devices_setting t2 on t.site_id = t2.site_id and t.device_id = t2.device_id from ems_alarm_records t
INNER JOIN ems_devices_setting t2
on t.site_id COLLATE utf8mb4_general_ci = t2.site_id
and t.device_id COLLATE utf8mb4_general_ci = t2.device_id
where t.site_id = #{siteId} where t.site_id = #{siteId}
and t.status != 1 and t.status != 1
and t.alarm_level in ('C','D') and t.alarm_level in ('C','D')
@ -156,7 +159,9 @@
t.ticket_no as ticketNo, t.ticket_no as ticketNo,
t.id t.id
from ems_alarm_records t from ems_alarm_records t
LEFT JOIN ems_devices_setting t2 on t.site_id = t2.site_id and t.device_id = t2.device_id LEFT JOIN ems_devices_setting t2
on t.site_id = t2.site_id
and t.device_id = t2.device_id
where t.site_id = #{siteId} where t.site_id = #{siteId}
<if test="deviceType != null and deviceType != ''"> <if test="deviceType != null and deviceType != ''">
AND t.device_type = #{deviceType} AND t.device_type = #{deviceType}
@ -265,4 +270,4 @@
and device_id = #{deviceId} and device_id = #{deviceId}
and alarm_point is not null and alarm_point is not null
</select> </select>
</mapper> </mapper>

View File

@ -288,65 +288,80 @@
<select id="getRevenueDataBySiteId" resultType="com.xzzn.ems.domain.vo.AmmeterRevenueStatisListVo"> <select id="getRevenueDataBySiteId" resultType="com.xzzn.ems.domain.vo.AmmeterRevenueStatisListVo">
select select
DATE_FORMAT(t.data_date, '%Y-%m-%d') as dataTime, revenue.dataTime as dataTime,
COALESCE(c.is_workday, CASE WHEN WEEKDAY(t.data_date) &lt; 5 THEN 1 ELSE 0 END) as isWorkday, revenue.isWorkday as isWorkday,
CASE case
WHEN COALESCE(c.is_workday, CASE WHEN WEEKDAY(t.data_date) &lt; 5 THEN 1 ELSE 0 END) = 1 THEN '工作日' when revenue.isWorkday = 1 then '工作日'
ELSE '节假日' else '节假日'
END as dayType, end as dayType,
COALESCE(NULLIF(TRIM(w.weather_desc), ''), '--') as weatherDesc, revenue.weatherDesc as weatherDesc,
ROUND(SUM(IFNULL(t.peak_charge_diff, 0) * IFNULL(pc.peak, 0)), 3) as activePeakPrice, revenue.activePeakPrice as activePeakPrice,
ROUND(SUM(IFNULL(t.peak_discharge_diff, 0) * IFNULL(pc.peak, 0)), 3) as reActivePeakPrice, revenue.reActivePeakPrice as reActivePeakPrice,
ROUND(SUM(IFNULL(t.high_charge_diff, 0) * IFNULL(pc.high, 0)), 3) as activeHighPrice, revenue.activeHighPrice as activeHighPrice,
ROUND(SUM(IFNULL(t.high_discharge_diff, 0) * IFNULL(pc.high, 0)), 3) as reActiveHighPrice, revenue.reActiveHighPrice as reActiveHighPrice,
ROUND(SUM(IFNULL(t.flat_charge_diff, 0) * IFNULL(pc.flat, 0)), 3) as activeFlatPrice, revenue.activeFlatPrice as activeFlatPrice,
ROUND(SUM(IFNULL(t.flat_discharge_diff, 0) * IFNULL(pc.flat, 0)), 3) as reActiveFlatPrice, revenue.reActiveFlatPrice as reActiveFlatPrice,
ROUND(SUM(IFNULL(t.valley_charge_diff, 0) * IFNULL(pc.valley, 0)), 3) as activeValleyPrice, revenue.activeValleyPrice as activeValleyPrice,
ROUND(SUM(IFNULL(t.valley_discharge_diff, 0) * IFNULL(pc.valley, 0)), 3) as reActiveValleyPrice, revenue.reActiveValleyPrice as reActiveValleyPrice,
ROUND( revenue.actualRevenue as actualRevenue
SUM( from (
(IFNULL(t.peak_discharge_diff, 0) - IFNULL(t.peak_charge_diff, 0)) * IFNULL(pc.peak, 0) select
+ (IFNULL(t.high_discharge_diff, 0) - IFNULL(t.high_charge_diff, 0)) * IFNULL(pc.high, 0) DATE_FORMAT(t.data_date, '%Y-%m-%d') as dataTime,
+ (IFNULL(t.flat_discharge_diff, 0) - IFNULL(t.flat_charge_diff, 0)) * IFNULL(pc.flat, 0) COALESCE(c.is_workday, CASE WHEN WEEKDAY(t.data_date) &lt; 5 THEN 1 ELSE 0 END) as isWorkday,
+ (IFNULL(t.valley_discharge_diff, 0) - IFNULL(t.valley_charge_diff, 0)) * IFNULL(pc.valley, 0) COALESCE(NULLIF(TRIM(w.weather_desc), ''), '--') as weatherDesc,
ROUND(SUM(IFNULL(t.peak_charge_diff, 0) * IFNULL(pc.peak, 0)), 3) as activePeakPrice,
ROUND(SUM(IFNULL(t.peak_discharge_diff, 0) * IFNULL(pc.peak, 0)), 3) as reActivePeakPrice,
ROUND(SUM(IFNULL(t.high_charge_diff, 0) * IFNULL(pc.high, 0)), 3) as activeHighPrice,
ROUND(SUM(IFNULL(t.high_discharge_diff, 0) * IFNULL(pc.high, 0)), 3) as reActiveHighPrice,
ROUND(SUM(IFNULL(t.flat_charge_diff, 0) * IFNULL(pc.flat, 0)), 3) as activeFlatPrice,
ROUND(SUM(IFNULL(t.flat_discharge_diff, 0) * IFNULL(pc.flat, 0)), 3) as reActiveFlatPrice,
ROUND(SUM(IFNULL(t.valley_charge_diff, 0) * IFNULL(pc.valley, 0)), 3) as activeValleyPrice,
ROUND(SUM(IFNULL(t.valley_discharge_diff, 0) * IFNULL(pc.valley, 0)), 3) as reActiveValleyPrice,
ROUND(
SUM(
(IFNULL(t.peak_discharge_diff, 0) - IFNULL(t.peak_charge_diff, 0)) * IFNULL(pc.peak, 0)
+ (IFNULL(t.high_discharge_diff, 0) - IFNULL(t.high_charge_diff, 0)) * IFNULL(pc.high, 0)
+ (IFNULL(t.flat_discharge_diff, 0) - IFNULL(t.flat_charge_diff, 0)) * IFNULL(pc.flat, 0)
+ (IFNULL(t.valley_discharge_diff, 0) - IFNULL(t.valley_charge_diff, 0)) * IFNULL(pc.valley, 0)
),
3
) as actualRevenue
from ems_daily_energy_data t
left join ems_calendar_day c on c.calendar_date = t.data_date
left join ems_site_weather_day w on w.site_id = t.site_id and w.calendar_date = t.data_date
left join ems_energy_price_config pc on pc.id = COALESCE(
(
select p.id
from ems_energy_price_config p
where p.site_id = t.site_id
and STR_TO_DATE(CONCAT(p.year, '-', LPAD(p.month, 2, '0'), '-01'), '%Y-%m-%d') &lt;= DATE_FORMAT(t.data_date, '%Y-%m-01')
order by STR_TO_DATE(CONCAT(p.year, '-', LPAD(p.month, 2, '0'), '-01'), '%Y-%m-%d') desc
limit 1
), ),
3 (
) as actualRevenue select p2.id
from ems_daily_energy_data t from ems_energy_price_config p2
left join ems_calendar_day c on c.calendar_date = t.data_date where p2.site_id = t.site_id
left join ems_site_weather_day w on w.site_id = t.site_id and w.calendar_date = t.data_date order by STR_TO_DATE(CONCAT(p2.year, '-', LPAD(p2.month, 2, '0'), '-01'), '%Y-%m-%d') asc
left join ems_energy_price_config pc on pc.id = COALESCE( limit 1
( )
select p.id
from ems_energy_price_config p
where p.site_id = t.site_id
and STR_TO_DATE(CONCAT(p.year, '-', LPAD(p.month, 2, '0'), '-01'), '%Y-%m-%d') &lt;= DATE_FORMAT(t.data_date, '%Y-%m-01')
order by STR_TO_DATE(CONCAT(p.year, '-', LPAD(p.month, 2, '0'), '-01'), '%Y-%m-%d') desc
limit 1
),
(
select p2.id
from ems_energy_price_config p2
where p2.site_id = t.site_id
order by STR_TO_DATE(CONCAT(p2.year, '-', LPAD(p2.month, 2, '0'), '-01'), '%Y-%m-%d') asc
limit 1
) )
) where 1=1
where 1=1 and t.data_hour is not null
and t.data_hour is not null <if test="siteId != null">
<if test="siteId != null"> and t.site_id = #{siteId}
and t.site_id = #{siteId} </if>
</if> <if test="startTime != null">
<if test="startTime != null"> and t.data_date &gt;= #{startTime}
and t.data_date &gt;= #{startTime} </if>
</if> <if test="endTime != null">
<if test="endTime != null"> and t.data_date &lt;= #{endTime}
and t.data_date &lt;= #{endTime} </if>
</if> group by t.data_date,
group by t.data_date, COALESCE(c.is_workday, CASE WHEN WEEKDAY(t.data_date) &lt; 5 THEN 1 ELSE 0 END),
COALESCE(c.is_workday, CASE WHEN WEEKDAY(t.data_date) &lt; 5 THEN 1 ELSE 0 END), COALESCE(NULLIF(TRIM(w.weather_desc), ''), '--')
COALESCE(NULLIF(TRIM(w.weather_desc), ''), '--') ) revenue
order by t.data_date asc order by revenue.dataTime asc
</select> </select>
</mapper> </mapper>