Commit 645df86586393e4105547b52cf7dc0afe9399731

Authored by 杨鸣坤
1 parent a021b725

refactor: 重构设备同步服务支持多公司动态数据源

... ... @@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.GetMapping;
9 9 import org.springframework.web.bind.annotation.RequestParam;
10 10 import org.springframework.web.bind.annotation.RestController;
11 11
  12 +import java.util.Collections;
12 13 import java.util.Map;
13 14
14 15 @RestController
... ... @@ -23,217 +24,208 @@ public class HealthController {
23 24 @Resource
24 25 private EnergyPullService energyPullService;
25 26
  27 + /**
  28 + * corpCode为空时的统一空数据响应
  29 + */
  30 + private static Map<String, Object> emptyResult() {
  31 + return Collections.emptyMap();
  32 + }
  33 +
26 34 @GetMapping("/health")
27   - public String health() throws Exception {
28   - devicePullService.pullDeviceAndPushToIot();
  35 + public String health(@RequestParam(required = false) String corpCode) throws Exception {
  36 + if (!isValidCorpCode(corpCode)) return "corpCode不能为空";
  37 + devicePullService.pullDeviceAndPushToIot(corpCode);
29 38 return "IoT Scheduler is running...";
30 39 }
31 40
32 41 @GetMapping("/device/list")
33 42 public Map<String, Object> deviceList(
  43 + @RequestParam(required = false) String corpCode,
34 44 @RequestParam(required = false) String deviceName,
35 45 @RequestParam(required = false) String lampState,
36 46 @RequestParam(defaultValue = "1") Integer pageNo,
37 47 @RequestParam(defaultValue = "10") Integer pageSize) {
38   - return deviceSearchService.queryDeviceList(deviceName, lampState, pageNo, pageSize);
  48 + if (!isValidCorpCode(corpCode)) return emptyResult();
  49 + return deviceSearchService.queryDeviceList(corpCode, deviceName, lampState, pageNo, pageSize);
39 50 }
40 51
41 52 @GetMapping("/device/stats")
42   - public Map<String, Object> deviceStats() {
43   - return deviceSearchService.queryDeviceStats();
  53 + public Map<String, Object> deviceStats(@RequestParam(required = false) String corpCode) {
  54 + if (!isValidCorpCode(corpCode)) return emptyResult();
  55 + return deviceSearchService.queryDeviceStats(corpCode);
44 56 }
45 57
46 58 @GetMapping("/device/lampData")
47 59 public Map<String, Object> lampData(
  60 + @RequestParam(required = false) String corpCode,
48 61 @RequestParam String dtuSn,
49 62 @RequestParam String date) {
50   - return deviceSearchService.queryLampData(dtuSn, date);
  63 + if (!isValidCorpCode(corpCode)) return emptyResult();
  64 + return deviceSearchService.queryLampData(corpCode, dtuSn, date);
51 65 }
52 66
53 67 @GetMapping("/device/syncDevUtil")
54   - public String syncDevUtil() {
55   - devicePullService.pullDevUtilAndSave();
  68 + public String syncDevUtil(@RequestParam(required = false) String corpCode) {
  69 + if (!isValidCorpCode(corpCode)) return "corpCode不能为空";
  70 + devicePullService.pullDevUtilAndSave(corpCode);
56 71 return "设备利用率数据同步完成";
57 72 }
58 73
59 74 @GetMapping("/device/syncOee")
60   - public String syncOee() {
61   - devicePullService.pullOeeAndSave();
  75 + public String syncOee(@RequestParam(required = false) String corpCode) {
  76 + if (!isValidCorpCode(corpCode)) return "corpCode不能为空";
  77 + devicePullService.pullOeeAndSave(corpCode);
62 78 return "OEE时序数据同步完成";
63 79 }
64 80
65 81 @GetMapping("/device/oeeStats")
66 82 public Map<String, Object> oeeStats(
  83 + @RequestParam(required = false) String corpCode,
67 84 @RequestParam String dtuSn,
68 85 @RequestParam(defaultValue = "day") String type,
69 86 @RequestParam(required = false) String startDate,
70 87 @RequestParam(required = false) String endDate) {
71   - return deviceSearchService.queryOeeStats(dtuSn, type, startDate, endDate);
  88 + if (!isValidCorpCode(corpCode)) return emptyResult();
  89 + return deviceSearchService.queryOeeStats(corpCode, dtuSn, type, startDate, endDate);
72 90 }
73 91
74 92 /**
75 93 * OEE时序图分页查询(含稼动率)
76   - * 查询所有设备在指定日期范围内的OEE时序数据,分页返回(每页最多20条)
77   - *
78   - * @param startDate 开始日期 yyyy-MM-dd
79   - * @param endDate 结束日期 yyyy-MM-dd
80   - * @param pageNo 页码,默认1
81   - * @param pageSize 每页条数,最大20,默认20
82 94 */
83 95 @GetMapping("/device/oeeTimeline")
84 96 public Map<String, Object> oeeTimeline(
  97 + @RequestParam(required = false) String corpCode,
85 98 @RequestParam String startDate,
86 99 @RequestParam String endDate,
87 100 @RequestParam(defaultValue = "1") Integer pageNo,
88 101 @RequestParam(defaultValue = "20") Integer pageSize) {
89   - return deviceSearchService.queryOeeTimeline(startDate, endDate, pageNo, pageSize);
  102 + if (!isValidCorpCode(corpCode)) return emptyResult();
  103 + return deviceSearchService.queryOeeTimeline(corpCode, startDate, endDate, pageNo, pageSize);
90 104 }
91 105
92 106 /**
93 107 * 智能灯统计稼动率查询(仪表盘)
94   - * 支持日/周/月查询,返回总时长、稼动率、当前机台状态、异常排行榜、每设备状态时长
95   - *
96   - * @param startDate 开始日期 yyyy-MM-dd (day模式必填)
97   - * @param endDate 结束日期 yyyy-MM-dd (day模式必填)
98 108 */
99 109 @GetMapping("/device/lampStatistics")
100 110 public Map<String, Object> lampStatistics(
  111 + @RequestParam(required = false) String corpCode,
101 112 @RequestParam(required = false) String startDate,
102 113 @RequestParam(required = false) String endDate) {
103   - return deviceSearchService.queryLampStatistics(startDate, endDate);
  114 + if (!isValidCorpCode(corpCode)) return emptyResult();
  115 + return deviceSearchService.queryLampStatistics(corpCode, startDate, endDate);
104 116 }
105 117
106 118 /**
107 119 * 开机率查询(基于 dev_util 表)
108   - * 灭灯(state=0)=未开机,其他灯=开机,每台设备单独计算,不分页
109   - *
110   - * @param startDate 开始日期 yyyy-MM-dd
111   - * @param endDate 结束日期 yyyy-MM-dd
112 120 */
113 121 @GetMapping("/device/bootRate")
114 122 public Map<String, Object> bootRate(
  123 + @RequestParam(required = false) String corpCode,
115 124 @RequestParam String startDate,
116 125 @RequestParam String endDate) {
117   - return deviceSearchService.queryBootRate(startDate, endDate);
  126 + if (!isValidCorpCode(corpCode)) return emptyResult();
  127 + return deviceSearchService.queryBootRate(corpCode, startDate, endDate);
118 128 }
119 129
120 130 @GetMapping("/energy/history")
121   - public void energyHistory() {
122   - energyPullService.pullEnergyHistoryAndSave();
  131 + public void energyHistory(@RequestParam(required = false) String corpCode) {
  132 + if (isValidCorpCode(corpCode)) energyPullService.pullEnergyHistoryAndSave(corpCode);
123 133 }
124 134
125 135 /**
126 136 * 分页查询能耗设备信息
127   - *
128   - * @param deviceName 设备名称(模糊匹配)
129   - * @param runStatus 状态(0:离线,1:停机,2:待机,3:运行)
130   - * @param pageNo 页码,默认1
131   - * @param pageSize 每页条数,默认10
132 137 */
133 138 @GetMapping("/energy/list")
134 139 public Map<String, Object> energyList(
  140 + @RequestParam(required = false) String corpCode,
135 141 @RequestParam(required = false) String deviceName,
136 142 @RequestParam(required = false) String runStatus,
137 143 @RequestParam(defaultValue = "1") Integer pageNo,
138 144 @RequestParam(defaultValue = "10") Integer pageSize) {
139   - return energySearchService.queryEnergyList(deviceName, runStatus, pageNo, pageSize);
  145 + if (!isValidCorpCode(corpCode)) return emptyResult();
  146 + return energySearchService.queryEnergyList(corpCode, deviceName, runStatus, pageNo, pageSize);
140 147 }
141 148
142 149 @GetMapping("/energy/stats")
143   - public Map<String, Object> energyStats() {
144   - return energySearchService.queryEnergyStats();
  150 + public Map<String, Object> energyStats(@RequestParam(required = false) String corpCode) {
  151 + if (!isValidCorpCode(corpCode)) return emptyResult();
  152 + return energySearchService.queryEnergyStats(corpCode);
145 153 }
146 154
147 155 /**
148 156 * 根据dtuSn查询指定日期的设备时用电量和OEE时序
149   - * 返回时用电量数组、总用电量、OEE时序、总时长、各状态运行时长和占比
150   - *
151   - * @param dtuSn 设备序列号
152   - * @param date 查询日期 yyyy-MM-dd
153 157 */
154 158 @GetMapping("/energy/detail")
155 159 public Map<String, Object> energyDetail(
  160 + @RequestParam(required = false) String corpCode,
156 161 @RequestParam String dtuSn,
157 162 @RequestParam String date) {
158   - return energySearchService.queryEnergyDetailByDate(dtuSn, date);
  163 + if (!isValidCorpCode(corpCode)) return emptyResult();
  164 + return energySearchService.queryEnergyDetailByDate(corpCode, dtuSn, date);
159 165 }
160 166
161 167 /**
162 168 * 根据dtuSn查询指定设备的运行时长明细
163   - * type=1(时): 传startDate,获取指定日期的运行时长明细
164   - * type=2(天): 传startDate和endDate,按日统计
165   - * type=3(月): 查本年年初到现在,按月统计
166   - *
167   - * @param dtuSn 设备序列号
168   - * @param type 类型:1-时,2-天,3-月
169   - * @param startDate 开始日期 yyyy-MM-dd (type=1,2必填)
170   - * @param endDate 结束日期 yyyy-MM-dd (type=2必填)
171 169 */
172 170 @GetMapping("/energy/runtimeDetail")
173 171 public Map<String, Object> energyRuntimeDetail(
  172 + @RequestParam(required = false) String corpCode,
174 173 @RequestParam String dtuSn,
175 174 @RequestParam(defaultValue = "1") String type,
176 175 @RequestParam(required = false) String startDate,
177 176 @RequestParam(required = false) String endDate) {
178   - return energySearchService.queryEnergyRuntimeDetail(dtuSn, type, startDate, endDate);
  177 + if (!isValidCorpCode(corpCode)) return emptyResult();
  178 + return energySearchService.queryEnergyRuntimeDetail(corpCode, dtuSn, type, startDate, endDate);
179 179 }
180 180
181 181 /**
182 182 * 查询能耗时序状态 - 分页查询
183   - * 获取指定日期所有设备的用电量数据,计算稼动率和总用电量
184   - *
185   - * @param date 查询日期 yyyy-MM-dd
186   - * @param pageNo 页码,默认1
187   - * @param pageSize 每页条数,默认12
188 183 */
189 184 @GetMapping("/energy/timelineStatus")
190 185 public Map<String, Object> energyTimelineStatus(
  186 + @RequestParam(required = false) String corpCode,
191 187 @RequestParam String date,
192 188 @RequestParam(defaultValue = "1") Integer pageNo,
193 189 @RequestParam(defaultValue = "12") Integer pageSize) {
194   - return energySearchService.queryEnergyTimelineStatus(date, pageNo, pageSize);
  190 + if (!isValidCorpCode(corpCode)) return emptyResult();
  191 + return energySearchService.queryEnergyTimelineStatus(corpCode, date, pageNo, pageSize);
195 192 }
196 193
197 194 /**
198 195 * eq_kwh综合统计查询
199   - * 查询指定日期范围内所有设备的稼动率、各状态时长、异常排名等
200   - *
201   - * @param startDate 开始日期 yyyy-MM-dd
202   - * @param endDate 结束日期 yyyy-MM-dd
203   - * @return 包含: 总稼动率、各状态时间(xxx.xx时)、当前运行状态、异常机台排名、每设备0/1/2/3状态时间
204 196 */
205 197 @GetMapping("/energy/eqKwhStatistics")
206 198 public Map<String, Object> eqKwhStatistics(
  199 + @RequestParam(required = false) String corpCode,
207 200 @RequestParam String startDate,
208 201 @RequestParam String endDate) {
209   - return energySearchService.queryEqKwhStatistics(startDate, endDate);
  202 + if (!isValidCorpCode(corpCode)) return emptyResult();
  203 + return energySearchService.queryEqKwhStatistics(corpCode, startDate, endDate);
210 204 }
211 205
212 206 /**
213 207 * 查询eq_kwh多设备能耗数据(按 时/日/月 聚合)
214   - * type=1 (时): 传startDate,返回该日所有设备的原始用电量明细
215   - * type=2 (日):
216   - * - 传startDate+endDate: 按此范围每日统计每台设备能耗
217   - * - 只传startDate: 自动取当月1号~月末,按日统计
218   - * type=3 (月): 自动取本年1月~当前月,按月统计每台能耗数据
219   - *
220   - * @param type 类型: 1-时, 2-日, 3-月
221   - * @param startDate 开始日期 yyyy-MM-dd
222   - * @param endDate 结束日期 yyyy-MM-dd (type=2时可选)
223 208 */
224 209 @GetMapping("/energy/eqKwhByType")
225 210 public Map<String, Object> eqKwhByType(
  211 + @RequestParam(required = false) String corpCode,
226 212 @RequestParam(defaultValue = "1") String type,
227 213 @RequestParam(required = false) String startDate,
228 214 @RequestParam(required = false) String endDate) {
229   - return energySearchService.queryEqKwhByType(type, startDate, endDate);
  215 + if (!isValidCorpCode(corpCode)) return emptyResult();
  216 + return energySearchService.queryEqKwhByType(corpCode, type, startDate, endDate);
230 217 }
231 218
232 219 /**
233 220 * 调试:查看eq_kwh表中的数据概况
234 221 */
235 222 @GetMapping("/energy/debug/eqKwhInfo")
236   - public Map<String, Object> debugEqKwhInfo() {
237   - return energySearchService.debugEqKwhInfo();
  223 + public Map<String, Object> debugEqKwhInfo(@RequestParam(required = false) String corpCode) {
  224 + if (!isValidCorpCode(corpCode)) return emptyResult();
  225 + return energySearchService.debugEqKwhInfo(corpCode);
  226 + }
  227 +
  228 + private boolean isValidCorpCode(String corpCode) {
  229 + return corpCode != null && !corpCode.trim().isEmpty();
238 230 }
239 231 }
... ...
  1 +package com.iot.scheduler.model;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 公司配置信息(来自 t_auto_ymk_ccfg_corp_conf 表)
  7 + */
  8 +@Data
  9 +public class CorpConf {
  10 +
  11 + /**
  12 + * 公司名称
  13 + */
  14 + private String corpName;
  15 + /**
  16 + * 数据源连接地址
  17 + */
  18 + private String datasourceUrl;
  19 + /**
  20 + * 数据源用户名
  21 + */
  22 + private String datasourceName;
  23 + /**
  24 + * 数据源密码
  25 + */
  26 + private String datasourcePassword;
  27 + /**
  28 + * 描述
  29 + */
  30 + private String description;
  31 + /**
  32 + * 公司编码(target_corp)
  33 + */
  34 + private String targetCorp;
  35 + /**
  36 + * IoT平台部门名称
  37 + */
  38 + private String iotOrg;
  39 + /**
  40 + * IoT平台密码(token.password)
  41 + */
  42 + private String iotPassword;
  43 + /**
  44 + * IoT平台用户名(token.userName)
  45 + */
  46 + private String iotUsername;
  47 +}
... ...
  1 +package com.iot.scheduler.service;
  2 +
  3 +import com.iot.scheduler.model.CorpConf;
  4 +import jakarta.annotation.PostConstruct;
  5 +import jakarta.annotation.Resource;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.apache.commons.lang3.StringUtils;
  8 +import org.springframework.beans.factory.annotation.Value;
  9 +import org.springframework.jdbc.core.JdbcTemplate;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import javax.sql.DataSource;
  13 +import java.sql.DriverManager;
  14 +import java.util.*;
  15 +import java.util.concurrent.ConcurrentHashMap;
  16 +
  17 +/**
  18 + * 公司配置服务
  19 + * - 根据 corpCode 查询 t_auto_ymk_ccfg_corp_conf 表获取公司配置
  20 + * - 数据源策略:公司配置了独立数据源则走独立库,否则使用 application.yml 默认库
  21 + * - 每个公司的表通过 t_auto_{corpCode}_iot_xxx 隔离,不同公司数据互不影响
  22 + */
  23 +@Slf4j
  24 +@Service
  25 +public class CorpConfigService {
  26 +
  27 + @Value("${corp.config.tableName}")
  28 + private String corpConfigTableName;
  29 +
  30 + @Value("${spring.datasource.url}")
  31 + private String defaultDatasourceUrl;
  32 + @Value("${spring.datasource.username}")
  33 + private String defaultDatasourceUsername;
  34 + @Value("${spring.datasource.password}")
  35 + private String defaultDatasourcePassword;
  36 +
  37 + @Resource
  38 + private JdbcTemplate masterJdbcTemplate;
  39 +
  40 + /**
  41 + * 配置缓存,避免频繁查数据库
  42 + */
  43 + private final Map<String, CorpConf> confCache = new ConcurrentHashMap<>();
  44 +
  45 + @PostConstruct
  46 + public void init() {
  47 + log.info("【公司配置服务】初始化完成, 配置表: {}, 默认数据库: {}", corpConfigTableName, defaultDatasourceUrl);
  48 + }
  49 +
  50 + /**
  51 + * 根据 corpCode 获取公司配置信息(带缓存)
  52 + *
  53 + * @param corpCode 公司编码
  54 + * @return 公司配置,未找到返回null
  55 + */
  56 + public CorpConf getCorpConf(String corpCode) {
  57 + if (corpCode == null || corpCode.isEmpty()) return null;
  58 + return confCache.computeIfAbsent(corpCode, this::loadCorpConfFromDb);
  59 + }
  60 +
  61 + /**
  62 + * 清除指定公司缓存
  63 + */
  64 + public void clearCache(String corpCode) {
  65 + if (corpCode != null) confCache.remove(corpCode);
  66 + }
  67 +
  68 + /**
  69 + * 获取所有已配置的公司编码列表
  70 + * 从配置表查询所有有效的 target_corp,用于定时任务遍历
  71 + *
  72 + * @return 公司编码列表(不为null)
  73 + */
  74 + public List<String> getAllCorpCodes() {
  75 + try {
  76 + String sql = "SELECT DISTINCT target_corp FROM " + corpConfigTableName +
  77 + " WHERE target_corp IS NOT NULL AND target_corp != '' ORDER BY target_corp";
  78 + List<Map<String, Object>> rows = masterJdbcTemplate.queryForList(sql);
  79 + List<String> result = new ArrayList<>(rows.size());
  80 + for (Map<String, Object> row : rows) {
  81 + String code = getStringVal(row.get("target_corp"));
  82 + if (code != null) result.add(code);
  83 + }
  84 + log.info("【公司配置】查询到 {} 个已配置公司: {}", result.size(), result);
  85 + return result;
  86 + } catch (Exception e) {
  87 + log.error("【公司配置】查询所有公司编码异常", e);
  88 + return Collections.emptyList();
  89 + }
  90 + }
  91 +
  92 + /**
  93 + * 清除全部缓存
  94 + */
  95 + public void clearAllCache() {
  96 + confCache.clear();
  97 + }
  98 +
  99 + /**
  100 + * 从数据库加载公司配置
  101 + */
  102 + private CorpConf loadCorpConfFromDb(String corpCode) {
  103 + try {
  104 + String sql = "SELECT corp_name, datasource_name, datasource_password, datasource_url, description, " +
  105 + "target_corp, iot_org, iot_password, iot_username FROM " + corpConfigTableName +
  106 + " WHERE target_corp = ? LIMIT 1";
  107 + Map<String, Object> row = masterJdbcTemplate.queryForMap(sql, corpCode);
  108 + CorpConf conf = new CorpConf();
  109 + conf.setCorpName(getStringVal(row.get("corp_name")));
  110 + conf.setDatasourceName(getStringVal(row.get("datasource_name")));
  111 + conf.setDatasourcePassword(getStringVal(row.get("datasource_password")));
  112 + conf.setDatasourceUrl(getStringVal(row.get("datasource_url")));
  113 + conf.setDescription(getStringVal(row.get("description")));
  114 + conf.setTargetCorp(getStringVal(row.get("target_corp")));
  115 + conf.setIotOrg(getStringVal(row.get("iot_org")));
  116 + conf.setIotPassword(getStringVal(row.get("iot_password")));
  117 + conf.setIotUsername(getStringVal(row.get("iot_username")));
  118 + log.info("【公司配置】加载成功, corpCode={}, corpName={}, hasCustomDs={}",
  119 + corpCode, conf.getCorpName(), hasCustomDatasource(conf));
  120 + return conf;
  121 + } catch (org.springframework.dao.EmptyResultDataAccessException e) {
  122 + // 查询无结果,属于正常情况(该corpCode未在配置表中注册)
  123 + log.warn("【公司配置】未找到corpCode={}的配置记录", corpCode);
  124 + return null;
  125 + } catch (Exception e) {
  126 + log.error("【公司配置】查询corpCode={}异常", corpCode, e);
  127 + return null;
  128 + }
  129 + }
  130 +
  131 + /**
  132 + * 获取公司的 JdbcTemplate(动态数据源)
  133 + * <p>
  134 + * 数据源选择策略:
  135 + * 1. 如果 t_auto_ymk_ccfg_corp_conf 中该公司配置了 datasource_url → 使用该独立数据源
  136 + * 2. 如果该公司未配置数据源 → 使用 application.yml 中的默认数据源
  137 + * </p>
  138 + *
  139 + * @param corpCode 公司编码
  140 + * @return JdbcTemplate
  141 + */
  142 + public JdbcTemplate getJdbcTemplate(String corpCode) {
  143 + CorpConf conf = getCorpConf(corpCode);
  144 + if (conf == null || !hasCustomDatasource(conf)) {
  145 + log.info("【公司数据源】corpCode={}, 未配置独立数据源, 使用application.yml默认数据源: {}", corpCode, defaultDatasourceUrl);
  146 + return masterJdbcTemplate;
  147 + }
  148 + try {
  149 + DataSource ds = DriverManager.getConnection(
  150 + conf.getDatasourceUrl(),
  151 + conf.getDatasourceName(),
  152 + conf.getDatasourcePassword()
  153 + ).unwrap(DataSource.class);
  154 + JdbcTemplate jt = new JdbcTemplate(ds);
  155 + log.info("【公司数据源】corpCode={}, 使用独立数据源: {}", corpCode, conf.getDatasourceUrl());
  156 + return jt;
  157 + } catch (Exception e) {
  158 + log.error("【公司数据源】corpCode={} 连接独立数据源失败! url={}, error={}", corpCode, conf.getDatasourceUrl(), e.getMessage());
  159 + throw new RuntimeException("公司 " + corpCode + " 的独立数据源连接失败: " + e.getMessage(), e);
  160 + }
  161 + }
  162 +
  163 + /**
  164 + * 获取IoT平台部门名称(groupName),无配置返回null
  165 + */
  166 + public String getIotOrg(String corpCode) {
  167 + CorpConf conf = getCorpConf(corpCode);
  168 + if (conf != null && conf.getIotOrg() != null && !conf.getIotOrg().isEmpty()) {
  169 + return conf.getIotOrg();
  170 + }
  171 + return null;
  172 + }
  173 +
  174 + /**
  175 + * 获取IoT平台用户名(token.userName),无配置返回null
  176 + */
  177 + public String getIotUsername(String corpCode) {
  178 + CorpConf conf = getCorpConf(corpCode);
  179 + if (conf != null && conf.getIotUsername() != null && !conf.getIotUsername().isEmpty()) {
  180 + return conf.getIotUsername();
  181 + }
  182 + return null;
  183 + }
  184 +
  185 + /**
  186 + * 获取IoT平台密码(token.password),无配置返回null
  187 + */
  188 + public String getIotPassword(String corpCode) {
  189 + CorpConf conf = getCorpConf(corpCode);
  190 + if (conf != null && conf.getIotPassword() != null && !conf.getIotPassword().isEmpty()) {
  191 + return conf.getIotPassword();
  192 + }
  193 + return null;
  194 + }
  195 +
  196 + /**
  197 + * 检查公司是否配置了有效的IoT平台凭证(org/username/password)
  198 + */
  199 + public boolean hasValidIotCredentials(String corpCode) {
  200 + String org = getIotOrg(corpCode);
  201 + String username = getIotUsername(corpCode);
  202 + String password = getIotPassword(corpCode);
  203 + boolean valid = StringUtils.isNotBlank(org)
  204 + && StringUtils.isNotBlank(username)
  205 + && StringUtils.isNotBlank(password);
  206 + if (!valid) {
  207 + log.warn("【IoT凭证】corpCode={} 的IoT配置不完整: org={}, username={}, password={}",
  208 + corpCode, org != null ? "***" : "null",
  209 + username != null ? "***" : "null",
  210 + password != null ? "***" : "null");
  211 + }
  212 + return valid;
  213 + }
  214 +
  215 + // ==================== 表名工具方法 ====================
  216 +
  217 + /**
  218 + * 生成设备主表名: t_auto_{corpCode}_iot_device
  219 + */
  220 + public String getDeviceTableName(String corpCode) {
  221 + return "t_auto_" + corpCode + "_iot_device";
  222 + }
  223 +
  224 + /**
  225 + * 生成设备利用率表名: t_auto_{corpCode}_iot_dev_util
  226 + */
  227 + public String getDevUtilTableName(String corpCode) {
  228 + return "t_auto_" + corpCode + "_iot_dev_util";
  229 + }
  230 +
  231 + /**
  232 + * 生成OEE表名: t_auto_{corpCode}_iot_dev_oee
  233 + */
  234 + public String getOeeTableName(String corpCode) {
  235 + return "t_auto_" + corpCode + "_iot_dev_oee";
  236 + }
  237 +
  238 + /**
  239 + * 生成能耗主表名: t_auto_{corpCode}_iot_energy
  240 + */
  241 + public String getEnergyTableName(String corpCode) {
  242 + return "t_auto_" + corpCode + "_iot_energy";
  243 + }
  244 +
  245 + /**
  246 + * 生成能耗用电量表名: t_auto_{corpCode}_iot_eq_kwh
  247 + */
  248 + public String getEqKwhTableName(String corpCode) {
  249 + return "t_auto_" + corpCode + "_iot_eq_kwh";
  250 + }
  251 +
  252 + /**
  253 + * 生成能耗运行状态明细表名: t_auto_{corpCode}_iot_e_run_dtl
  254 + */
  255 + public String getERunDtlTableName(String corpCode) {
  256 + return "t_auto_" + corpCode + "_iot_e_run_dtl";
  257 + }
  258 +
  259 + // ==================== 内部工具 ====================
  260 +
  261 + private boolean hasCustomDatasource(CorpConf conf) {
  262 + return conf.getDatasourceUrl() != null && !conf.getDatasourceUrl().isEmpty();
  263 + }
  264 +
  265 + private String getStringVal(Object val) {
  266 + return val != null ? String.valueOf(val) : null;
  267 + }
  268 +}
... ...
... ... @@ -40,13 +40,8 @@ import org.springframework.scheduling.annotation.Scheduled;
40 40 @Service
41 41 public class DevicePullService {
42 42
43   -
44 43 @Value("${device.token.url}")
45 44 private String deviceTokenUrl;
46   - @Value("${device.token.userName}")
47   - private String deviceUserName;
48   - @Value("${device.token.password}")
49   - private String devicePassword;
50 45 @Value("${device.info.url}")
51 46 private String deviceInfoUrl;
52 47 @Value("${device.detail.url}")
... ... @@ -55,14 +50,6 @@ public class DevicePullService {
55 50 private String deviceSnRateUrl;
56 51 @Value("${device.energyInfo.url}")
57 52 private String energyInfoUrl;
58   - @Value("${device.db.corpCode}")
59   - private String deviceCorpCode;
60   - @Value("${device.db.tableName}")
61   - private String deviceTableName;
62   - @Value("${device.db.devUtilTableName}")
63   - private String devUtilTableName;
64   - @Value("${device.db.oeeTableName}")
65   - private String oeeTableName;
66 53 @Value("${device.lamp.url}")
67 54 private String deviceLampUrl;
68 55
... ... @@ -70,11 +57,17 @@ public class DevicePullService {
70 57 private RedisTemplate<String, String> redisTemplate;
71 58 @Resource
72 59 private JdbcTemplate jdbcTemplate;
  60 + @Resource
  61 + private CorpConfigService corpConfigService;
73 62
74 63 final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
75 64
76   - public void pullDeviceAndPushToIot() throws ParseException {
77   - String deviceResult = getDeviceInfo();
  65 + public void pullDeviceAndPushToIot(String corpCode) throws ParseException {
  66 + if (!corpConfigService.hasValidIotCredentials(corpCode)) {
  67 + log.warn("【设备同步】IoT凭证为空, 跳过同步, corpCode={}", corpCode);
  68 + return;
  69 + }
  70 + String deviceResult = getDeviceInfo(corpCode);
78 71 Map<String, Object> deviceInfos = JSON.parseObject(deviceResult, new TypeReference<>() {
79 72 });
80 73
... ... @@ -89,21 +82,19 @@ public class DevicePullService {
89 82 String deviceId = deviceInfoJson.getString("deviceId");
90 83 String dtuSn = deviceInfoJson.getString("dtuSn");
91 84
92   - String deviceInfoDetails = getDeviceInfoDetail(dtuSn);
  85 + String deviceInfoDetails = getDeviceInfoDetail(corpCode, dtuSn);
93 86 if (StringUtils.isBlank(deviceInfoDetails)) {
94 87 return;
95 88 }
96 89
97 90 Map<String, Object> deviceInfoDetailMap = JSON.parseObject(deviceInfoDetails, new TypeReference<>() {
98 91 });
99   -
100 92 JSONArray deviceInfoDetailList = (JSONArray) deviceInfoDetailMap.get("data");
101 93 if (CollectionUtils.isEmpty(deviceInfoDetailList)) {
102 94 return;
103 95 }
104 96
105 97 JSONObject deviceInfoDetailJson = (JSONObject) deviceInfoDetailList.get(0);
106   -
107 98 String lampState = deviceInfoDetailJson.getString("lampState");
108 99 String startTime = deviceInfoDetailJson.getString("startTime");
109 100
... ... @@ -118,111 +109,74 @@ public class DevicePullService {
118 109 long minutes = (diffMillis % (1000 * 60 * 60)) / (1000 * 60);
119 110 long seconds = (diffMillis % (1000 * 60)) / 1000;
120 111 StringBuilder durationBuilder = new StringBuilder();
121   - if (hours > 0) {
122   - durationBuilder.append(hours).append("时");
123   - }
124   -
125   - if (minutes > 0) {
126   - durationBuilder.append(minutes).append("分");
127   - }
128   -
  112 + if (hours > 0) durationBuilder.append(hours).append("时");
  113 + if (minutes > 0) durationBuilder.append(minutes).append("分");
129 114 durationBuilder.append(seconds).append("秒");
130 115 String duration = durationBuilder.toString();
131 116
132   - // 获取稼动率
133   - String utilizationRate = getUtilizationRate(dtuSn);
  117 + String utilizationRate = getUtilizationRate(corpCode, dtuSn);
134 118
135   - log.info("设备数据汇总 - projectState:{}, projectType:{}, deviceName:{}, dtuId:{}, deviceId:{}, dtuSn:{}, " +
  119 + log.info("设备数据汇总 - corpCode:{}, projectState:{}, projectType:{}, deviceName:{}, dtuId:{}, deviceId:{}, dtuSn:{}, " +
136 120 "lampState:{}, duration:{}, utilizationRate:{}",
137   - projectState, projectType, deviceName, dtuId, deviceId, dtuSn,
  121 + corpCode, projectState, projectType, deviceName, dtuId, deviceId, dtuSn,
138 122 lampState, duration, utilizationRate);
139 123
140   - // 保存或更新数据库
141   - saveOrUpdateDevice(projectState, projectType, deviceName, dtuId, deviceId, dtuSn,
  124 + saveOrUpdateDevice(corpCode, projectState, projectType, deviceName, dtuId, deviceId, dtuSn,
142 125 lampState, startTime, duration, utilizationRate);
143 126 }
144   -
145   -
146 127 }
147 128
148   - public String getDeviceInfoDetail(String dtuSn) {
149   - String accessToken = getAccessToken();
  129 + public String getDeviceInfoDetail(String corpCode, String dtuSn) {
  130 + String accessToken = getAccessToken(corpCode);
150 131 Map<String, String> dtuSnOb = new HashMap<>(1);
151 132 dtuSnOb.put("dtuSn", dtuSn);
152 133 Map<String, String> headerMap = new HashMap<>(1);
153 134 headerMap.put("Authorization", "Bearer " + accessToken);
154 135 String deviceInfoDetail = sendRequestGet(deviceDetailUrl, dtuSnOb, headerMap);
155   - if (StringUtils.isBlank(deviceInfoDetail)) {
156   - return null;
157   - }
  136 + if (StringUtils.isBlank(deviceInfoDetail)) return null;
158 137
159   - Map<String, Object> deviceInfoDetailMap = JSON.parseObject(deviceInfoDetail,
160   - new TypeReference<>() {
161   - });
  138 + Map<String, Object> deviceInfoDetailMap = JSON.parseObject(deviceInfoDetail, new TypeReference<>() {
  139 + });
162 140 Integer deviceInfoDetailCode = (Integer) deviceInfoDetailMap.get("code");
163   - if (deviceInfoDetailCode != 200) {
164   - return null;
165   - }
  141 + if (deviceInfoDetailCode != 200) return null;
166 142
167 143 JSONArray deviceInfoDetailList = (JSONArray) deviceInfoDetailMap.get("data");
168   - if (CollectionUtils.isEmpty(deviceInfoDetailList)) {
169   - return null;
170   - }
  144 + if (CollectionUtils.isEmpty(deviceInfoDetailList)) return null;
171 145
172 146 JSONObject deviceInfoDetailJson = (JSONObject) deviceInfoDetailList.get(0);
173   - //灯详情数据
174   - //灯状态(0:灭灯,1:红,2:黄,3:绿,4:蓝)
175 147 Integer lampState = deviceInfoDetailJson.getInteger("lampState");
176   - if (lampState == null) {
177   - return null;
178   - }
179   -
  148 + if (lampState == null) return null;
180 149 return deviceInfoDetail;
181 150 }
182 151
183   - public String getDtuSnRateOfAction(String dtuSn, String startDate, String endDate) {
184   - String accessToken = getAccessToken();
  152 + public String getDtuSnRateOfAction(String corpCode, String dtuSn, String startDate, String endDate) {
  153 + String accessToken = getAccessToken(corpCode);
185 154 Map<String, String> headerMap = new HashMap<>(1);
186 155 headerMap.put("Authorization", "Bearer " + accessToken);
187   -
188 156 Map<String, String> paramsMap = new HashMap<>();
189 157 paramsMap.put("startDate", startDate);
190 158 paramsMap.put("endDate", endDate);
191 159 paramsMap.put("dtuSn", dtuSn);
192   -
193 160 String rateResult = sendRequestGet(deviceSnRateUrl, paramsMap, headerMap);
194   - if (StringUtils.isBlank(rateResult)) {
195   - return null;
196   - }
197   -
198   - Map<String, Object> rateMap = JSON.parseObject(rateResult, new TypeReference<>() {});
  161 + if (StringUtils.isBlank(rateResult)) return null;
  162 + Map<String, Object> rateMap = JSON.parseObject(rateResult, new TypeReference<>() {
  163 + });
199 164 Integer rateCode = (Integer) rateMap.get("code");
200   - if (rateCode != 200) {
201   - return null;
202   - }
203   -
  165 + if (rateCode != 200) return null;
204 166 return rateResult;
205 167 }
206 168
207   - public String getUtilizationRate(String dtuSn) {
  169 + public String getUtilizationRate(String corpCode, String dtuSn) {
208 170 String today = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
209   - String rateResult = getDtuSnRateOfAction(dtuSn, today, today);
210   - if (StringUtils.isBlank(rateResult)) {
211   - return "0%";
212   - }
213   -
214   - Map<String, Object> rateMap = JSON.parseObject(rateResult, new TypeReference<>() {});
  171 + String rateResult = getDtuSnRateOfAction(corpCode, dtuSn, today, today);
  172 + if (StringUtils.isBlank(rateResult)) return "0%";
  173 + Map<String, Object> rateMap = JSON.parseObject(rateResult, new TypeReference<>() {
  174 + });
215 175 JSONArray dataList = (JSONArray) rateMap.get("data");
216   - if (CollectionUtils.isEmpty(dataList)) {
217   - return "0%";
218   - }
219   -
  176 + if (CollectionUtils.isEmpty(dataList)) return "0%";
220 177 JSONObject todayData = (JSONObject) dataList.get(0);
221 178 JSONArray realRateList = todayData.getJSONArray("realRate");
222   - if (CollectionUtils.isEmpty(realRateList)) {
223   - return "0%";
224   - }
225   -
  179 + if (CollectionUtils.isEmpty(realRateList)) return "0%";
226 180 JSONObject rateObj = (JSONObject) realRateList.get(0);
227 181 long state0 = rateObj.getLongValue("0");
228 182 long state1 = rateObj.getLongValue("1");
... ... @@ -231,149 +185,132 @@ public class DevicePullService {
231 185 long state4 = rateObj.getLongValue("4");
232 186 long state5 = rateObj.getLongValue("5");
233 187 long totalTime = state0 + state1 + state2 + state3 + state4 + state5;
234   - if (totalTime == 0) {
235   - return "0%";
236   - }
237   -
  188 + if (totalTime == 0) return "0%";
238 189 double rate = (double) state3 / totalTime * 100;
239 190 return String.format("%.2f%%", rate);
240 191 }
241 192
242   - public String getLampData(String dtuSn, String date) {
243   - String accessToken = getAccessToken();
  193 + public String getLampData(String corpCode, String dtuSn, String date) {
  194 + String accessToken = getAccessToken(corpCode);
244 195 Map<String, String> headerMap = new HashMap<>(1);
245 196 headerMap.put("Authorization", "Bearer " + accessToken);
246   -
247 197 Map<String, String> paramsMap = new HashMap<>();
248 198 paramsMap.put("dtuSn", dtuSn);
249 199 paramsMap.put("date", date);
250   -
251 200 return sendRequestGet(deviceLampUrl, paramsMap, headerMap);
252 201 }
253 202
254   - public void saveOrUpdateDevice(String projectState, String projectType, String deviceName, String dtuId,
  203 + private void saveOrUpdateDevice(String corpCode, String projectState, String projectType, String deviceName, String dtuId,
255 204 String deviceId, String dtuSn, String lampState, String startTime,
256 205 String duration, String utilizationRate) {
257   - // 查询是否已存在(按公司+dtuSn判断)
258   - List<Map<String, Object>> existList = jdbcTemplate.queryForList(
259   - "SELECT id FROM " + deviceTableName + " WHERE corp_code = ? AND dtuSn = ?", deviceCorpCode, dtuSn);
  206 + String tableName = corpConfigService.getDeviceTableName(corpCode);
  207 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
260 208
  209 + List<Map<String, Object>> existList = jt.queryForList(
  210 + "SELECT id FROM " + tableName + " WHERE corp_code = ? AND dtuSn = ?", corpCode, dtuSn);
261 211 Date now = new Date();
262 212 if (!existList.isEmpty()) {
263   - // 更新
264   - jdbcTemplate.update("UPDATE " + deviceTableName + " SET projectState = ?, projectType = ?, deviceName = ?, " +
  213 + jt.update("UPDATE " + tableName + " SET projectState = ?, projectType = ?, deviceName = ?, " +
265 214 "dtuId = ?, deviceId = ?, lampState = ?, startTime = ?, duration = ?, utilizationRate = ?, updated_at = ? WHERE dtuSn = ?",
266   - projectState, projectType, deviceName, dtuId, deviceId, lampState,
267   - startTime, duration, utilizationRate, now, dtuSn);
268   - log.info("设备数据更新成功 - dtuSn:{}", dtuSn);
  215 + projectState, projectType, deviceName, dtuId, deviceId, lampState, startTime, duration, utilizationRate, now, dtuSn);
  216 + log.info("设备数据更新成功 - corpCode:{}, dtuSn:{}", corpCode, dtuSn);
269 217 } else {
270   - // 新增
271 218 String id = UUID.randomUUID().toString().replace("-", "");
272   - jdbcTemplate.update("INSERT INTO " + deviceTableName + " (id, corp_code, created_at, created_by, updated_at, updated_by, " +
  219 + jt.update("INSERT INTO " + tableName + " (id, corp_code, created_at, created_by, updated_at, updated_by, " +
273 220 "deviceName, projectType, projectState, dtuSn, dtuId, deviceId, lampState, startTime, duration, utilizationRate) " +
274   - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
275   - id, deviceCorpCode, now, "system", now, "system",
276   - deviceName, projectType, projectState, dtuSn, dtuId, deviceId, lampState,
277   - startTime, duration, utilizationRate);
278   - log.info("设备数据新增成功 - dtuSn:{}", dtuSn);
  221 + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
  222 + id, corpCode, now, "system", now, "system",
  223 + deviceName, projectType, projectState, dtuSn, dtuId, deviceId, lampState, startTime, duration, utilizationRate);
  224 + log.info("设备数据新增成功 - corpCode:{}, dtuSn:{}", corpCode, dtuSn);
279 225 }
280 226 }
281 227
282 228 /**
283   - * 首次全量同步:本年1月1日 ~ 昨天(手动触发一次)
  229 + * 首次全量同步
284 230 */
285   - public void pullDevUtilAndSave() {
  231 + public void pullDevUtilAndSave(String corpCode) {
  232 + if (!corpConfigService.hasValidIotCredentials(corpCode)) {
  233 + log.warn("【全量同步-利用率】IoT凭证为空, 跳过同步, corpCode={}", corpCode);
  234 + return;
  235 + }
286 236 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
287 237 Calendar cal = Calendar.getInstance();
288   - cal.add(Calendar.DAY_OF_MONTH, -1); // 昨天
  238 + cal.add(Calendar.DAY_OF_MONTH, -1);
289 239 String endDate = sdf.format(cal.getTime());
290 240 cal.set(cal.get(Calendar.YEAR), Calendar.JANUARY, 1);
291 241 String startDate = sdf.format(cal.getTime());
292   -
293   - log.info("【全量同步】设备利用率,日期范围: {} ~ {}", startDate, endDate);
294   - doSyncDevUtil(startDate, endDate);
  242 + log.info("【全量同步】设备利用率, corpCode:{}, 日期范围: {} ~ {}", corpCode, startDate, endDate);
  243 + doSyncDevUtil(corpCode, "2026-05-01", endDate);
295 244 }
296 245
297   - /**
298   - * 每日增量同步:仅同步昨天的数据(定时任务自动触发)
299   - */
300 246 @Scheduled(cron = "${scheduler.devUtil.cron:0 30 2 * * ?}")
301 247 public void pullDevUtilDaily() {
  248 + List<String> corpCodes = corpConfigService.getAllCorpCodes();
  249 + if (corpCodes.isEmpty()) {
  250 + log.warn("【每日增量】设备利用率: 未找到任何公司配置,跳过");
  251 + return;
  252 + }
302 253 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
303 254 Calendar cal = Calendar.getInstance();
304 255 cal.add(Calendar.DAY_OF_MONTH, -1);
305 256 String yesterday = sdf.format(cal.getTime());
306   -
307   - log.info("【每日增量】同步设备利用率,日期: {}", yesterday);
308   - doSyncDevUtil(yesterday, yesterday);
  257 + for (String corpCode : corpCodes) {
  258 + log.info("【每日增量】同步设备利用率, corpCode:{}, 日期: {}", corpCode, yesterday);
  259 + doSyncDevUtil(corpCode, yesterday, yesterday);
  260 + }
309 261 }
310 262
311   - /**
312   - * 核心同步逻辑:获取设备列表 → 按每31天分批调用 dtuSnRateOfAction → 写入/更新 t_auto_ymk_iot_dev_util
313   - */
314   - private void doSyncDevUtil(String startDate, String endDate) {
315   - String deviceResult = getDeviceInfo();
  263 + private void doSyncDevUtil(String corpCode, String startDate, String endDate) {
  264 + String deviceResult = getDeviceInfo(corpCode);
316 265 if (StringUtils.isBlank(deviceResult)) {
317 266 log.error("获取设备列表失败");
318 267 return;
319 268 }
320   - Map<String, Object> deviceInfos = JSON.parseObject(deviceResult, new TypeReference<>() {});
  269 + Map<String, Object> deviceInfos = JSON.parseObject(deviceResult, new TypeReference<>() {
  270 + });
321 271 JSONArray deviceInfoList = (JSONArray) deviceInfos.get("data");
322 272 if (CollectionUtils.isEmpty(deviceInfoList)) {
323 273 log.warn("设备列表为空");
324 274 return;
325 275 }
326 276
327   - // 按最多31天拆分为多个日期区间
328 277 List<DateRange> batches = splitDateRange(startDate, endDate);
329   - log.info("开始同步设备利用率数据,设备数: {}, 日期范围: {} ~ {}, 共分{}批次",
330   - deviceInfoList.size(), startDate, endDate, batches.size());
331   - int totalSaved = 0;
  278 + log.info("开始同步设备利用率数据, corpCode:{}, 设备数:{}, 日期范围: {} ~ {}, 共分{}批次",
  279 + corpCode, deviceInfoList.size(), startDate, endDate, batches.size());
332 280
333 281 for (Object o : deviceInfoList) {
334 282 JSONObject deviceInfoJson = (JSONObject) o;
335 283 String dtuSn = deviceInfoJson.getString("dtuSn");
336   -
337   - // 每个设备按31天一批次逐批调用
338 284 for (DateRange batch : batches) {
339 285 try {
340   - String rateResult = getDtuSnRateOfAction(dtuSn, batch.start, batch.end);
  286 + String rateResult = getDtuSnRateOfAction(corpCode, dtuSn, batch.start, batch.end);
341 287 if (StringUtils.isBlank(rateResult)) continue;
342   -
343   - Map<String, Object> rateMap = JSON.parseObject(rateResult, new TypeReference<>() {});
  288 + Map<String, Object> rateMap = JSON.parseObject(rateResult, new TypeReference<>() {
  289 + });
344 290 Integer code = (Integer) rateMap.get("code");
345 291 if (code == null || code != 200) continue;
346   -
347 292 JSONArray dataList = (JSONArray) rateMap.get("data");
348 293 if (CollectionUtils.isEmpty(dataList)) continue;
349   -
350 294 for (int i = 0; i < dataList.size(); i++) {
351 295 JSONObject dayData = (JSONObject) dataList.get(i);
352 296 String dateStr = dayData.getString("date");
353 297 JSONArray realRateList = dayData.getJSONArray("realRate");
354 298 if (CollectionUtils.isEmpty(realRateList)) continue;
355   -
356 299 JSONObject rateObj = (JSONObject) realRateList.get(0);
357   - double state0 = rateObj.getDoubleValue("0");
358   - double state1 = rateObj.getDoubleValue("1");
359   - double state2 = rateObj.getDoubleValue("2");
360   - double state3 = rateObj.getDoubleValue("3");
361   - double state4 = rateObj.getDoubleValue("4");
362   -
363   - saveOrUpdateDevUtil(dtuSn, dateStr, state0, state1, state2, state3, state4);
364   - totalSaved++;
  300 + double s0 = rateObj.getDoubleValue("0");
  301 + double s1 = rateObj.getDoubleValue("1");
  302 + double s2 = rateObj.getDoubleValue("2");
  303 + double s3 = rateObj.getDoubleValue("3");
  304 + double s4 = rateObj.getDoubleValue("4");
  305 + saveOrUpdateDevUtil(corpCode, dtuSn, dateStr, s0, s1, s2, s3, s4);
365 306 }
366 307 } catch (Exception e) {
367 308 log.error("处理设备 {} 数据异常 - 批次:{}~{}", dtuSn, batch.start, batch.end, e);
368 309 }
369 310 }
370 311 }
371   - log.info("设备利用率数据同步完成,共保存 {} 条记录", totalSaved);
372 312 }
373 313
374   - /**
375   - * 按最大31天将日期范围拆分为多段
376   - */
377 314 private static List<DateRange> splitDateRange(String startDate, String endDate) {
378 315 List<DateRange> ranges = new ArrayList<>();
379 316 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
... ... @@ -382,21 +319,13 @@ public class DevicePullService {
382 319 cur.setTime(sdf.parse(startDate));
383 320 Calendar endCal = Calendar.getInstance();
384 321 endCal.setTime(sdf.parse(endDate));
385   -
386 322 while (!cur.after(endCal)) {
387 323 DateRange range = new DateRange(sdf.format(cur.getTime()), null);
388   -
389   - // 向后推进30天(即当前日+30,共31天)
390 324 Calendar batchEnd = Calendar.getInstance();
391 325 batchEnd.setTime(cur.getTime());
392 326 batchEnd.add(Calendar.DAY_OF_MONTH, 30);
393   -
394   - if (!batchEnd.after(endCal)) {
395   - range.end = sdf.format(batchEnd.getTime());
396   - } else {
397   - range.end = sdf.format(endCal.getTime());
398   - }
399   -
  327 + if (!batchEnd.after(endCal)) range.end = sdf.format(batchEnd.getTime());
  328 + else range.end = sdf.format(endCal.getTime());
400 329 ranges.add(range);
401 330 cur.add(Calendar.DAY_OF_MONTH, 31);
402 331 }
... ... @@ -406,7 +335,7 @@ public class DevicePullService {
406 335 return ranges;
407 336 }
408 337
409   - private static class DateRange {
  338 + static class DateRange {
410 339 String start;
411 340 String end;
412 341
... ... @@ -416,67 +345,66 @@ public class DevicePullService {
416 345 }
417 346 }
418 347
419   - private void saveOrUpdateDevUtil(String dtuSn, String dateUtil,
420   - double s0, double s1, double s2, double s3, double s4) {
421   - List<Map<String, Object>> existList = jdbcTemplate.queryForList(
422   - "SELECT id FROM " + devUtilTableName + " WHERE corp_code = ? AND dtuSn = ? AND date_util = ?",
423   - deviceCorpCode, dtuSn, dateUtil);
  348 + private void saveOrUpdateDevUtil(String corpCode, String dtuSn, String dateUtil, double s0, double s1, double s2, double s3, double s4) {
  349 + String devUtilTableName = corpConfigService.getDevUtilTableName(corpCode);
  350 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
424 351
  352 + List<Map<String, Object>> existList = jt.queryForList(
  353 + "SELECT id FROM " + devUtilTableName + " WHERE corp_code = ? AND dtuSn = ? AND date_util = ?",
  354 + corpCode, dtuSn, dateUtil);
425 355 Date now = new Date();
426 356 if (!existList.isEmpty()) {
427   - jdbcTemplate.update(
428   - "UPDATE " + devUtilTableName + " SET `0`=?,`1`=?,`2`=?,`3`=?,`4`=?,updated_at=? WHERE dtuSn=? AND date_util=?",
  357 + jt.update("UPDATE " + devUtilTableName + " SET `0`=?,`1`=?,`2`=?,`3`=?,`4`=?,updated_at=? WHERE dtuSn=? AND date_util=?",
429 358 s0, s1, s2, s3, s4, now, dtuSn, dateUtil);
430 359 } else {
431 360 String id = UUID.randomUUID().toString().replace("-", "");
432   - jdbcTemplate.update(
433   - "INSERT INTO " + devUtilTableName + " (id,corp_code,created_at,created_by,updated_at,updated_by,date_util,dtuSn,`0`,`1`,`2`,`3`,`4`) " +
434   - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
435   - id, deviceCorpCode, now, "system", now, "system", dateUtil, dtuSn, s0, s1, s2, s3, s4);
  361 + jt.update("INSERT INTO " + devUtilTableName + " (id,corp_code,created_at,created_by,updated_at,updated_by,date_util,dtuSn,`0`,`1`,`2`,`3`,`4`) " +
  362 + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", id, corpCode, now, "system", now, "system", dateUtil, dtuSn, s0, s1, s2, s3, s4);
436 363 }
437 364 }
438 365
439 366 // ==================== OEE 时序数据同步 ====================
440 367
441   - /**
442   - * 首次全量同步 OEE:本年1月1日 ~ 昨天(手动触发一次)
443   - */
444   - public void pullOeeAndSave() {
  368 + public void pullOeeAndSave(String corpCode) {
  369 + if (!corpConfigService.hasValidIotCredentials(corpCode)) {
  370 + log.warn("【全量同步-OEE】IoT凭证为空, 跳过同步, corpCode={}", corpCode);
  371 + return;
  372 + }
445 373 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
446 374 Calendar cal = Calendar.getInstance();
447 375 cal.add(Calendar.DAY_OF_MONTH, -1);
448 376 String endDate = sdf.format(cal.getTime());
449 377 cal.set(cal.get(Calendar.YEAR), Calendar.JANUARY, 1);
450 378 String startDate = sdf.format(cal.getTime());
451   -
452   - log.info("【全量同步】OEE时序数据,日期范围: {} ~ {}", startDate, endDate);
453   - doSyncOee(startDate, endDate);
  379 + log.info("【全量同步】OEE时序数据, corpCode:{}, 日期范围: {} ~ {}", corpCode, startDate, endDate);
  380 + doSyncOee(corpCode, "2026-05-01", endDate);
454 381 }
455 382
456   - /**
457   - * 每日增量同步 OEE:仅同步昨天(定时任务自动触发)
458   - */
459 383 @Scheduled(cron = "${scheduler.oee.cron:0 35 2 * * ?}")
460 384 public void pullOeeDaily() {
  385 + List<String> corpCodes = corpConfigService.getAllCorpCodes();
  386 + if (corpCodes.isEmpty()) {
  387 + log.warn("OEE: no corp config found, skip");
  388 + return;
  389 + }
461 390 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
462 391 Calendar cal = Calendar.getInstance();
463 392 cal.add(Calendar.DAY_OF_MONTH, -1);
464 393 String yesterday = sdf.format(cal.getTime());
465   -
466   - log.info("【每日增量】同步OEE时序数据,日期: {}", yesterday);
467   - doSyncOee(yesterday, yesterday);
  394 + for (String corpCode : corpCodes) {
  395 + log.info("[Daily] sync OEE, corpCode:{}, date: {}", corpCode, yesterday);
  396 + doSyncOee(corpCode, yesterday, yesterday);
  397 + }
468 398 }
469 399
470   - /**
471   - * OEE 核心同步:获取设备列表 → 每个设备每天调用 /triColorLamp/dtuSn → lampData JSON 存入 description
472   - */
473   - private void doSyncOee(String startDate, String endDate) {
474   - String deviceResult = getDeviceInfo();
  400 + private void doSyncOee(String corpCode, String startDate, String endDate) {
  401 + String deviceResult = getDeviceInfo(corpCode);
475 402 if (StringUtils.isBlank(deviceResult)) {
476 403 log.error("获取设备列表失败");
477 404 return;
478 405 }
479   - Map<String, Object> deviceInfos = JSON.parseObject(deviceResult, new TypeReference<>() {});
  406 + Map<String, Object> deviceInfos = JSON.parseObject(deviceResult, new TypeReference<>() {
  407 + });
480 408 JSONArray deviceInfoList = (JSONArray) deviceInfos.get("data");
481 409 if (CollectionUtils.isEmpty(deviceInfoList)) {
482 410 log.warn("设备列表为空");
... ... @@ -499,30 +427,24 @@ public class DevicePullService {
499 427 return;
500 428 }
501 429
502   - log.info("开始同步OEE时序数据,设备数: {}, 天数: {}", deviceInfoList.size(), dateList.size());
503 430 int totalSaved = 0;
504   -
505 431 for (Object o : deviceInfoList) {
506 432 JSONObject deviceInfoJson = (JSONObject) o;
507 433 String dtuSn = deviceInfoJson.getString("dtuSn");
508   -
509 434 for (String dateStr : dateList) {
510 435 try {
511   - String lampResult = getLampData(dtuSn, dateStr);
  436 + String lampResult = getLampData(corpCode, dtuSn, dateStr);
512 437 if (StringUtils.isBlank(lampResult)) continue;
513   -
514   - Map<String, Object> lampMap = JSON.parseObject(lampResult, new TypeReference<>() {});
  438 + Map<String, Object> lampMap = JSON.parseObject(lampResult, new TypeReference<>() {
  439 + });
515 440 Integer code = (Integer) lampMap.get("code");
516 441 if (code == null || code != 200) continue;
517   -
518 442 JSONArray dataList = (JSONArray) lampMap.get("data");
519 443 if (CollectionUtils.isEmpty(dataList)) continue;
520   -
521 444 JSONObject dataObj = (JSONObject) dataList.get(0);
522 445 JSONArray lampDataArr = dataObj.getJSONArray("lampData");
523 446 String description = lampDataArr != null ? lampDataArr.toJSONString() : "[]";
524   -
525   - saveOrUpdateOee(dtuSn, dateStr, description);
  447 + saveOrUpdateOee(corpCode, dtuSn, dateStr, description);
526 448 totalSaved++;
527 449 } catch (Exception e) {
528 450 log.error("处理OEE设备 {} 数据异常 - date:{}", dtuSn, dateStr, e);
... ... @@ -532,11 +454,12 @@ public class DevicePullService {
532 454 log.info("OEE时序数据同步完成,共保存 {} 条记录", totalSaved);
533 455 }
534 456
535   - private void saveOrUpdateOee(String dtuSn, String oeeDate, String description) {
  457 + private void saveOrUpdateOee(String corpCode, String dtuSn, String oeeDate, String description) {
  458 + String oeeTableName = corpConfigService.getOeeTableName(corpCode);
  459 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
536 460 int maxLen = 60000;
537 461 String lamp1 = "";
538 462 String lamp2 = "";
539   -
540 463 if (description.length() > maxLen) {
541 464 lamp1 = description.substring(0, maxLen);
542 465 lamp2 = description.substring(maxLen);
... ... @@ -544,175 +467,107 @@ public class DevicePullService {
544 467 lamp1 = description;
545 468 }
546 469
547   - List<Map<String, Object>> existList = jdbcTemplate.queryForList(
  470 + List<Map<String, Object>> existList = jt.queryForList(
548 471 "SELECT id FROM " + oeeTableName + " WHERE corp_code = ? AND dtuSn = ? AND oee_date = ?",
549   - deviceCorpCode, dtuSn, oeeDate);
550   -
  472 + corpCode, dtuSn, oeeDate);
551 473 Date now = new Date();
552 474 if (!existList.isEmpty()) {
553   - jdbcTemplate.update(
554   - "UPDATE " + oeeTableName + " SET triColorLamp1=?, triColorLamp2=?, updated_at=? WHERE dtuSn=? AND oee_date=?",
555   - lamp1, lamp2, now, dtuSn, oeeDate);
  475 + jt.update("UPDATE " + oeeTableName + " SET triColorLamp1=?, triColorLamp2=?, updated_at=? WHERE dtuSn=? AND oee_date=?", lamp1, lamp2, now, dtuSn, oeeDate);
556 476 } else {
557 477 String id = UUID.randomUUID().toString().replace("-", "");
558   - jdbcTemplate.update(
559   - "INSERT INTO " + oeeTableName + " (id,corp_code,created_at,created_by,updated_at,updated_by,dtuSn,oee_date,triColorLamp1,triColorLamp2) " +
560   - "VALUES (?,?,?,?,?,?,?,?,?,?)",
561   - id, deviceCorpCode, now, "system", now, "system", dtuSn, oeeDate, lamp1, lamp2);
  478 + jt.update("INSERT INTO " + oeeTableName + " (id,corp_code,created_at,created_by,updated_at,updated_by,dtuSn,oee_date,triColorLamp1,triColorLamp2) " +
  479 + "VALUES (?,?,?,?,?,?,?,?,?,?)", id, corpCode, now, "system", now, "system", dtuSn, oeeDate, lamp1, lamp2);
562 480 }
563 481 }
564 482
565   - public String getEnergyInfo() {
566   - String accessToken = getAccessToken();
  483 + public String getEnergyInfo(String corpCode) {
  484 + String accessToken = getAccessToken(corpCode);
567 485 Map<String, String> headerMap = new HashMap<>(1);
568 486 headerMap.put("Authorization", "Bearer " + accessToken);
569   -
570 487 Map<String, String> paramsMap = new HashMap<>();
571   - paramsMap.put("groupName", "SHC");
572   -
  488 + paramsMap.put("groupName", corpConfigService.getIotOrg(corpCode));
573 489 String energyInfoResult = sendRequestGet(energyInfoUrl, paramsMap, headerMap);
574   - if (StringUtils.isBlank(energyInfoResult)) {
575   - return null;
576   - }
577   -
578   - // 解析设备信息
  490 + if (StringUtils.isBlank(energyInfoResult)) return null;
579 491 Map<String, Object> energyInfos = JSON.parseObject(energyInfoResult, new TypeReference<>() {
580 492 });
581 493 Integer energyInfoCode = (Integer) energyInfos.get("code");
582   -
583   - // 如果code不为200,可能是accessToken失效,重新获取token并重试
584 494 if (energyInfoCode != 200) {
585   - accessToken = getAccessToken();
586   - if (StringUtils.isEmpty(accessToken)) {
587   - return null;
588   - }
589   -
590   - // 更新headerMap中的Authorization
  495 + accessToken = getAccessToken(corpCode);
  496 + if (StringUtils.isEmpty(accessToken)) return null;
591 497 headerMap.put("Authorization", "Bearer " + accessToken);
592   -
593   - // 第二次请求设备信息
594 498 energyInfoResult = sendRequestGet(energyInfoUrl, paramsMap, headerMap);
595   - if (StringUtils.isBlank(energyInfoResult)) {
596   - return null;
597   - }
598   -
599   - // 重新解析设备信息
  499 + if (StringUtils.isBlank(energyInfoResult)) return null;
600 500 energyInfos = JSON.parseObject(energyInfoResult, new TypeReference<Map<String, Object>>() {
601 501 });
602 502 energyInfoCode = (Integer) energyInfos.get("code");
603   -
604   - // 如果第二次请求仍然失败,返回错误信息
605   - if (energyInfoCode != 200) {
606   - return null;
607   - }
  503 + if (energyInfoCode != 200) return null;
608 504 }
609   -
610   - // 返回成功的设备信息
611 505 return energyInfoResult;
612 506 }
613 507
614   - public String getDeviceInfo() {
615   - String accessToken = getAccessToken();
616   - // 初始化headerMap并设置Authorization
  508 + public String getDeviceInfo(String corpCode) {
  509 + String accessToken = getAccessToken(corpCode);
617 510 Map<String, String> headerMap = new HashMap<>(1);
618 511 headerMap.put("Authorization", "Bearer " + accessToken);
619   -
620 512 Map<String, String> paramsMap = new HashMap<>();
621   - paramsMap.put("groupName", "SHC");
622   -
623   - // 第一次请求设备信息
  513 + paramsMap.put("groupName", corpConfigService.getIotOrg(corpCode));
624 514 String deviceResult = sendRequestGet(deviceInfoUrl, paramsMap, headerMap);
625   -
626   - // 检查设备信息是否为空
627   - if (StringUtils.isBlank(deviceResult)) {
628   - return null;
629   - }
630   -
631   - // 解析设备信息
  515 + if (StringUtils.isBlank(deviceResult)) return null;
632 516 Map<String, Object> deviceInfos = JSON.parseObject(deviceResult, new TypeReference<Map<String, Object>>() {
633 517 });
634 518 Integer deviceInfoCode = (Integer) deviceInfos.get("code");
635   -
636   - // 如果code不为200,可能是accessToken失效,重新获取token并重试
637 519 if (deviceInfoCode != 200) {
638   - accessToken = getAccessToken();
639   - if (StringUtils.isEmpty(accessToken)) {
640   - return null;
641   - }
642   -
643   - // 更新headerMap中的Authorization
  520 + accessToken = getAccessToken(corpCode);
  521 + if (StringUtils.isEmpty(accessToken)) return null;
644 522 headerMap.put("Authorization", "Bearer " + accessToken);
645   -
646   - // 第二次请求设备信息
647 523 deviceResult = sendRequestGet(deviceInfoUrl, paramsMap, headerMap);
648   - if (StringUtils.isBlank(deviceResult)) {
649   - return null;
650   - }
651   -
652   - // 重新解析设备信息
  524 + if (StringUtils.isBlank(deviceResult)) return null;
653 525 deviceInfos = JSON.parseObject(deviceResult, new TypeReference<Map<String, Object>>() {
654 526 });
655 527 deviceInfoCode = (Integer) deviceInfos.get("code");
656   -
657   - // 如果第二次请求仍然失败,返回错误信息
658   - if (deviceInfoCode != 200) {
659   - return null;
660   - }
  528 + if (deviceInfoCode != 200) return null;
661 529 }
662   -
663   - // 返回成功的设备信息
664 530 return deviceResult;
665 531 }
666 532
667   - private String getAccessToken() {
  533 + private String getAccessToken(String corpCode) {
  534 + String redisKey = "hnyssl_device_token_" + corpCode;
668 535 String accessToken = "";
669   - String redisKey = "hnyssl_device_token";
670 536 if (StringUtils.isNotBlank(redisTemplate.opsForValue().get(redisKey)) && redisTemplate.getExpire(redisKey) > 0) {
671 537 return redisTemplate.opsForValue().get(redisKey);
672 538 }
673   -
  539 + // 使用公司配置中的用户名密码,如果没有则用默认值
  540 + String userName = corpConfigService.getIotUsername(corpCode);
  541 + String password = corpConfigService.getIotPassword(corpCode);
674 542 Map<String, String> param = new HashMap<>(2);
675   - param.put("username", deviceUserName);
676   - param.put("password", devicePassword);
  543 + param.put("username", userName);
  544 + param.put("password", password);
677 545 HttpPost httpPost = new HttpPost(deviceTokenUrl);
678 546 String result = sendPost(httpPost, JSON.toJSONString(param));
679   - if (StringUtils.isBlank(result)) {
680   - return accessToken;
681   - }
682   -
  547 + if (StringUtils.isBlank(result)) return accessToken;
683 548 Map<String, Object> res = JSON.parseObject(result, new TypeReference<>() {
684 549 });
685   -
686 550 Integer code = (Integer) res.get("code");
687 551 if (code == 200) {
688 552 JSONObject data = (JSONObject) res.get("data");
689 553 accessToken = (String) data.get("token");
690   - redisTemplate.opsForValue().set(redisKey, accessToken, 3600, TimeUnit.SECONDS); // 一小时过期
  554 + redisTemplate.opsForValue().set(redisKey, accessToken, 3600, TimeUnit.SECONDS);
691 555 }
692   -
693 556 return accessToken;
694 557 }
695 558
696 559 public static String sendRequestGet(String url, Map<String, String> params, Map<String, String> header) {
697   - //实例化httpclient
698 560 CloseableHttpClient httpclient = HttpClients.createDefault();
699 561 url = builderUrl(url, params);
700   - //请求结果
701 562 String content = "";
702   - //实例化get方法
703 563 HttpGet httpget = new HttpGet(url);
704 564 if (!CollectionUtils.isEmpty(header)) {
705   - for (Map.Entry<String, String> entry : header.entrySet()) {
  565 + for (Map.Entry<String, String> entry : header.entrySet())
706 566 httpget.setHeader(entry.getKey(), entry.getValue());
707   - }
708 567 }
709   -
710 568 try (CloseableHttpResponse response = httpclient.execute(httpget)) {
711   -
712   - //执行get方法
713   - if (response.getStatusLine().getStatusCode() == 200) {
  569 + if (response.getStatusLine().getStatusCode() == 200)
714 570 content = EntityUtils.toString(response.getEntity(), "UTF-8");
715   - }
716 571 } catch (IOException e) {
717 572 log.error("sendRequest---GET Error!", e);
718 573 }
... ... @@ -723,13 +578,9 @@ public class DevicePullService {
723 578 UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(url);
724 579 if (!CollectionUtils.isEmpty(params)) {
725 580 MultiValueMap<String, String> paramsValue = new LinkedMultiValueMap<>();
726   - for (Map.Entry<String, String> entry : params.entrySet()) {
727   - paramsValue.add(entry.getKey(), entry.getValue());
728   - }
729   -
  581 + for (Map.Entry<String, String> entry : params.entrySet()) paramsValue.add(entry.getKey(), entry.getValue());
730 582 uriBuilder = uriBuilder.queryParams(paramsValue);
731 583 }
732   -
733 584 return uriBuilder.toUriString();
734 585 }
735 586
... ... @@ -745,9 +596,7 @@ public class DevicePullService {
745 596 int len;
746 597 byte[] buf = new byte[128];
747 598 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
748   - while ((len = is.read(buf)) != -1) {
749   - byteArrayOutputStream.write(buf, 0, len);
750   - }
  599 + while ((len = is.read(buf)) != -1) byteArrayOutputStream.write(buf, 0, len);
751 600 result = byteArrayOutputStream.toString();
752 601 } catch (IOException e) {
753 602 e.printStackTrace();
... ...
... ... @@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSON;
4 4 import com.alibaba.fastjson.JSONArray;
5 5 import com.alibaba.fastjson.JSONObject;
6 6 import lombok.extern.slf4j.Slf4j;
7   -import org.springframework.beans.factory.annotation.Value;
8 7 import org.springframework.jdbc.core.JdbcTemplate;
9 8 import org.springframework.stereotype.Service;
10 9 import org.springframework.util.StringUtils;
... ... @@ -18,26 +17,22 @@ import java.util.*;
18 17 @Service
19 18 public class DeviceSearchService {
20 19
21   - @Value("${device.db.corpCode}")
22   - private String deviceCorpCode;
23   - @Value("${device.db.tableName}")
24   - private String deviceTableName;
25   - @Value("${device.db.oeeTableName}")
26   - private String oeeTableName;
27   - @Value("${device.db.devUtilTableName}")
28   - private String devUtilTableName;
29   -
30 20 @Resource
31 21 private JdbcTemplate jdbcTemplate;
32 22 @Resource
33 23 private DevicePullService devicePullService;
  24 + @Resource
  25 + private CorpConfigService corpConfigService;
34 26
35   - public Map<String, Object> queryDeviceList(String deviceName, String lampState, Integer pageNo, Integer pageSize) {
36   - StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM " + deviceTableName + " WHERE corp_code = ?");
  27 + public Map<String, Object> queryDeviceList(String corpCode, String deviceName, String lampState, Integer pageNo, Integer pageSize) {
  28 + String tableName = corpConfigService.getDeviceTableName(corpCode);
  29 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  30 +
  31 + StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM " + tableName + " WHERE corp_code = ?");
37 32 StringBuilder querySql = new StringBuilder("SELECT id, deviceName, projectType, projectState, dtuSn, dtuId, deviceId, " +
38   - "lampState, startTime, duration, utilizationRate FROM " + deviceTableName + " WHERE corp_code = ?");
  33 + "lampState, startTime, duration, utilizationRate FROM " + tableName + " WHERE corp_code = ?");
39 34 List<Object> params = new java.util.ArrayList<>();
40   - params.add(deviceCorpCode);
  35 + params.add(corpCode);
41 36
42 37 if (StringUtils.hasText(deviceName)) {
43 38 countSql.append(" AND deviceName LIKE ?");
... ... @@ -50,13 +45,13 @@ public class DeviceSearchService {
50 45 params.add(lampState);
51 46 }
52 47
53   - Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class, params.toArray());
  48 + Long total = jt.queryForObject(countSql.toString(), Long.class, params.toArray());
54 49 int offset = (pageNo - 1) * pageSize;
55 50 querySql.append(" ORDER BY created_at DESC LIMIT ?, ?");
56 51 params.add(offset);
57 52 params.add(pageSize);
58 53
59   - List<Map<String, Object>> list = jdbcTemplate.queryForList(querySql.toString(), params.toArray());
  54 + List<Map<String, Object>> list = jt.queryForList(querySql.toString(), params.toArray());
60 55
61 56 return Map.of(
62 57 "total", total != null ? total : 0,
... ... @@ -66,10 +61,13 @@ public class DeviceSearchService {
66 61 );
67 62 }
68 63
69   - public Map<String, Object> queryDeviceStats() {
70   - String sql = "SELECT lampState, COUNT(*) as cnt FROM " + deviceTableName +
  64 + public Map<String, Object> queryDeviceStats(String corpCode) {
  65 + String tableName = corpConfigService.getDeviceTableName(corpCode);
  66 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  67 +
  68 + String sql = "SELECT lampState, COUNT(*) as cnt FROM " + tableName +
71 69 " WHERE corp_code = ? GROUP BY lampState";
72   - List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, deviceCorpCode);
  70 + List<Map<String, Object>> rows = jt.queryForList(sql, corpCode);
73 71
74 72 long all = 0;
75 73 long red = 0, yellow = 0, green = 0, blue = 0, off = 0;
... ... @@ -96,9 +94,10 @@ public class DeviceSearchService {
96 94 );
97 95 }
98 96
99   - public Map<String, Object> queryLampData(String dtuSn, String date) {
100   - String result = devicePullService.getLampData(dtuSn, date);
101   - Map<String, Object> res = com.alibaba.fastjson.JSON.parseObject(result, new com.alibaba.fastjson.TypeReference<>() {});
  97 + public Map<String, Object> queryLampData(String corpCode, String dtuSn, String date) {
  98 + String result = devicePullService.getLampData(corpCode, dtuSn, date);
  99 + Map<String, Object> res = com.alibaba.fastjson.JSON.parseObject(result, new com.alibaba.fastjson.TypeReference<>() {
  100 + });
102 101 Integer code = (Integer) res.get("code");
103 102 if (code == null || code != 200) {
104 103 return Map.of("lampDurationStats", Map.of(), "list", List.of());
... ... @@ -159,28 +158,29 @@ public class DeviceSearchService {
159 158
160 159 /**
161 160 * 稼动率/OEE统计查询
162   - * @param dtuSn 设备序列号(可选,为空查全部)
163   - * @param type 查询类型:day-日(按日期段), week-周(今年第1周~本周), month-月(今年1月~本月)
  161 + *
  162 + * @param corpCode 公司编码
  163 + * @param dtuSn 设备序列号(可选,为空查全部)
  164 + * @param type 查询类型:day-日(按日期段), week-周(今年第1周~本周), month-月(今年1月~本月)
164 165 * @param startDate 日模式下的开始日期(yyyy-MM-dd)
165 166 * @param endDate 日模式下的结束日期(yyyy-MM-dd)
166 167 */
167   - public Map<String, Object> queryOeeStats(String dtuSn, String type, String startDate, String endDate) {
  168 + public Map<String, Object> queryOeeStats(String corpCode, String dtuSn, String type, String startDate, String endDate) {
168 169 // 1. 根据类型确定日期范围
169 170 List<String> dates = buildDateRange(type, startDate, endDate);
170   - log.info("OEE查询 - type:{}, dtuSn:{}, 日期范围:{}天, 起始:{}, 结束:{}", type, dtuSn, dates.size(), dates.get(0), dates.get(dates.size() - 1));
  171 + log.info("OEE查询 - corpCode:{}, type:{}, dtuSn:{}, 日期范围:{}天, 起始:{}, 结束:{}", corpCode, type, dtuSn, dates.size(), dates.get(0), dates.get(dates.size() - 1));
171 172
172 173 // 判断是否包含今天
173 174 String todayStr = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
174 175 boolean includeToday = dates.contains(todayStr);
175 176
176 177 // 2. 从数据库查询 oee 表数据
177   - List<Map<String, Object>> dbRecords = queryOeeFromDb(dtuSn, dates, includeToday ? todayStr : null);
  178 + List<Map<String, Object>> dbRecords = queryOeeFromDb(corpCode, dtuSn, dates, includeToday ? todayStr : null);
178 179 log.info("OEE查询 - DB返回记录数:{}, 排除today:{}", dbRecords.size(), includeToday ? todayStr : "无");
179   -
  180 +
180 181 Map<String, JSONObject> dbDataMap = new LinkedHashMap<>();
181 182 for (Map<String, Object> record : dbRecords) {
182 183 String oeeDate = String.valueOf(record.get("oee_date"));
183   - // 截断时间部分,只保留日期 yyyy-MM-dd
184 184 if (oeeDate.length() > 10) {
185 185 oeeDate = oeeDate.substring(0, 10);
186 186 }
... ... @@ -200,10 +200,11 @@ public class DeviceSearchService {
200 200 // 3. 如果包含今天且数据库没有今天的实时数据,则调用接口获取
201 201 if (includeToday && !dbDataMap.containsKey(todayStr)) {
202 202 if (StringUtils.hasText(dtuSn)) {
203   - String apiResult = devicePullService.getLampData(dtuSn, todayStr);
  203 + String apiResult = devicePullService.getLampData(corpCode, dtuSn, todayStr);
204 204 if (StringUtils.hasText(apiResult)) {
205 205 Map<String, Object> res = com.alibaba.fastjson.JSON.parseObject(apiResult,
206   - new com.alibaba.fastjson.TypeReference<>() {});
  206 + new com.alibaba.fastjson.TypeReference<>() {
  207 + });
207 208 Integer code = (Integer) res.get("code");
208 209 if (code != null && code == 200) {
209 210 JSONArray dataList = (JSONArray) res.get("data");
... ... @@ -241,9 +242,6 @@ public class DeviceSearchService {
241 242 );
242 243 }
243 244
244   - /**
245   - * 按天统计
246   - */
247 245 private Map<String, Object> aggregateByDay(List<String> dates, Map<String, JSONObject> dbDataMap) {
248 246 List<Map<String, Object>> dailyStats = new ArrayList<>();
249 247 long totalOffDur = 0, totalRedDur = 0, totalYellowDur = 0, totalGreenDur = 0, totalBlueDur = 0;
... ... @@ -252,19 +250,25 @@ public class DeviceSearchService {
252 250 for (String d : dates) {
253 251 long[] counts = countLampData(dbDataMap.get(d));
254 252
255   - totalOffDur += counts[0]; totalRedDur += counts[2]; totalYellowDur += counts[4];
256   - totalGreenDur += counts[6]; totalBlueDur += counts[8];
257   - totalOffCnt += (int)counts[1]; totalRedCnt += (int)counts[3]; totalYellowCnt += (int)counts[5];
258   - totalGreenCnt += (int)counts[7]; totalBlueCnt += (int)counts[9];
  253 + totalOffDur += counts[0];
  254 + totalRedDur += counts[2];
  255 + totalYellowDur += counts[4];
  256 + totalGreenDur += counts[6];
  257 + totalBlueDur += counts[8];
  258 + totalOffCnt += (int) counts[1];
  259 + totalRedCnt += (int) counts[3];
  260 + totalYellowCnt += (int) counts[5];
  261 + totalGreenCnt += (int) counts[7];
  262 + totalBlueCnt += (int) counts[9];
259 263
260 264 Map<String, Object> dayStat = new LinkedHashMap<>();
261 265 dayStat.put("label", d);
262 266 dayStat.put("date", d);
263   - dayStat.put("off", Map.of("duration", formatDuration(counts[0]), "seconds", counts[0], "count", (int)counts[1]));
264   - dayStat.put("red", Map.of("duration", formatDuration(counts[2]), "seconds", counts[2], "count", (int)counts[3]));
265   - dayStat.put("yellow", Map.of("duration", formatDuration(counts[4]), "seconds", counts[4], "count", (int)counts[5]));
266   - dayStat.put("green", Map.of("duration", formatDuration(counts[6]), "seconds", counts[6], "count", (int)counts[7]));
267   - dayStat.put("blue", Map.of("duration", formatDuration(counts[8]), "seconds", counts[8], "count", (int)counts[9]));
  267 + dayStat.put("off", Map.of("duration", formatDuration(counts[0]), "seconds", counts[0], "count", (int) counts[1]));
  268 + dayStat.put("red", Map.of("duration", formatDuration(counts[2]), "seconds", counts[2], "count", (int) counts[3]));
  269 + dayStat.put("yellow", Map.of("duration", formatDuration(counts[4]), "seconds", counts[4], "count", (int) counts[5]));
  270 + dayStat.put("green", Map.of("duration", formatDuration(counts[6]), "seconds", counts[6], "count", (int) counts[7]));
  271 + dayStat.put("blue", Map.of("duration", formatDuration(counts[8]), "seconds", counts[8], "count", (int) counts[9]));
268 272 dailyStats.add(dayStat);
269 273 }
270 274
... ... @@ -274,22 +278,16 @@ public class DeviceSearchService {
274 278 return Map.of("list", dailyStats, "summary", summary);
275 279 }
276 280
277   - /**
278   - * 按周统计
279   - */
280 281 private Map<String, Object> aggregateByWeek(List<String> dates, Map<String, JSONObject> dbDataMap) {
281   - // 按自然周(周一~周日)分组,第一周从1月1日开始(可能不足7天)
282 282 List<List<String>> weekBuckets = new ArrayList<>();
283 283 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
284 284
285   - // 确定年份和该年第一个周一的日期
286 285 String yearStr = dates.get(0).substring(0, 4);
287 286 int year = Integer.parseInt(yearStr);
288 287 Calendar jan1 = Calendar.getInstance();
289 288 jan1.set(year, Calendar.JANUARY, 1);
290 289 int jan1Dow = jan1.get(Calendar.DAY_OF_WEEK);
291 290
292   - // 找到第一个周一:如果1月1日就是周一,则firstMonday=1月1日;否则往后推到周一
293 291 Calendar firstMonday = Calendar.getInstance();
294 292 firstMonday.setTime(jan1.getTime());
295 293 int daysToAdd = jan1Dow == Calendar.MONDAY ? 0 : (Calendar.MONDAY - jan1Dow + 7) % 7;
... ... @@ -307,13 +305,11 @@ public class DeviceSearchService {
307 305
308 306 int bucketIndex;
309 307 if (!cal.after(firstMonday)) {
310   - // 在第一个周一之前(含当天),属于第1周(从1月1日开始)
311 308 bucketIndex = 0;
312 309 } else {
313   - // 第一个周一之后,按完整自然周计算
314 310 long diffMs = cal.getTimeInMillis() - firstMonday.getTimeInMillis();
315 311 long diffDays = diffMs / (1000 * 60 * 60 * 24);
316   - bucketIndex = 1 + (int)(diffDays / 7); // 第2周、第3周...
  312 + bucketIndex = 1 + (int) (diffDays / 7);
317 313 }
318 314
319 315 while (weekBuckets.size() <= bucketIndex) {
... ... @@ -326,23 +322,39 @@ public class DeviceSearchService {
326 322 }
327 323
328 324 List<Map<String, Object>> weeklyStats = new ArrayList<>();
329   - long tOffD=0, tRedD=0, tYelD=0, tGrnD=0, tBluD=0;
330   - int tOffC=0, tRedC=0, tYelC=0, tGrnC=0, tBluC=0;
  325 + long tOffD = 0, tRedD = 0, tYelD = 0, tGrnD = 0, tBluD = 0;
  326 + int tOffC = 0, tRedC = 0, tYelC = 0, tGrnC = 0, tBluC = 0;
331 327
332 328 for (int i = 0; i < weekBuckets.size(); i++) {
333 329 List<String> daysInWeek = weekBuckets.get(i);
334 330 if (daysInWeek.isEmpty()) continue;
335   - long wOffD=0, wRedD=0, wYelD=0, wGrnD=0, wBluD=0;
336   - int wOffC=0, wRedC=0, wYelC=0, wGrnC=0, wBluC=0;
  331 + long wOffD = 0, wRedD = 0, wYelD = 0, wGrnD = 0, wBluD = 0;
  332 + int wOffC = 0, wRedC = 0, wYelC = 0, wGrnC = 0, wBluC = 0;
337 333
338 334 for (String d : daysInWeek) {
339 335 long[] cnt = countLampData(dbDataMap.get(d));
340   - wOffD += cnt[0]; wRedD += cnt[2]; wYelD += cnt[4]; wGrnD += cnt[6]; wBluD += cnt[8];
341   - wOffC += (int)cnt[1]; wRedC += (int)cnt[3]; wYelC += (int)cnt[5]; wGrnC += (int)cnt[7]; wBluC += (int)cnt[9];
  336 + wOffD += cnt[0];
  337 + wRedD += cnt[2];
  338 + wYelD += cnt[4];
  339 + wGrnD += cnt[6];
  340 + wBluD += cnt[8];
  341 + wOffC += (int) cnt[1];
  342 + wRedC += (int) cnt[3];
  343 + wYelC += (int) cnt[5];
  344 + wGrnC += (int) cnt[7];
  345 + wBluC += (int) cnt[9];
342 346 }
343 347
344   - tOffD+=wOffD; tRedD+=wRedD; tYelD+=wYelD; tGrnD+=wGrnD; tBluD+=wBluD;
345   - tOffC+=wOffC; tRedC+=wRedC; tYelC+=wYelC; tGrnC+=wGrnC; tBluC+=wBluC;
  348 + tOffD += wOffD;
  349 + tRedD += wRedD;
  350 + tYelD += wYelD;
  351 + tGrnD += wGrnD;
  352 + tBluD += wBluD;
  353 + tOffC += wOffC;
  354 + tRedC += wRedC;
  355 + tYelC += wYelC;
  356 + tGrnC += wGrnC;
  357 + tBluC += wBluC;
346 358
347 359 Collections.sort(daysInWeek);
348 360 Map<String, Object> ws = new LinkedHashMap<>();
... ... @@ -363,39 +375,51 @@ public class DeviceSearchService {
363 375 return Map.of("list", weeklyStats, "summary", summary);
364 376 }
365 377
366   - /**
367   - * 按月统计
368   - */
369 378 private Map<String, Object> aggregateByMonth(List<String> dates, Map<String, JSONObject> dbDataMap) {
370   - // 按 年-月 分组
371 379 Map<String, List<String>> monthGroups = new LinkedHashMap<>();
372 380 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
373 381
374 382 for (String d : dates) {
375   - String monthKey = d.substring(0, 7); // yyyy-MM
  383 + String monthKey = d.substring(0, 7);
376 384 monthGroups.computeIfAbsent(monthKey, k -> new ArrayList<>()).add(d);
377 385 }
378 386
379 387 List<Map<String, Object>> monthlyStats = new ArrayList<>();
380   - long tOffD=0, tRedD=0, tYelD=0, tGrnD=0, tBluD=0;
381   - int tOffC=0, tRedC=0, tYelC=0, tGrnC=0, tBluC=0;
  388 + long tOffD = 0, tRedD = 0, tYelD = 0, tGrnD = 0, tBluD = 0;
  389 + int tOffC = 0, tRedC = 0, tYelC = 0, tGrnC = 0, tBluC = 0;
382 390
383 391 for (Map.Entry<String, List<String>> entry : monthGroups.entrySet()) {
384 392 String monthKey = entry.getKey();
385 393 List<String> daysInMonth = entry.getValue();
386   - long mOffD=0, mRedD=0, mYelD=0, mGrnD=0, mBluD=0;
387   - int mOffC=0, mRedC=0, mYelC=0, mGrnC=0, mBluC=0;
  394 + long mOffD = 0, mRedD = 0, mYelD = 0, mGrnD = 0, mBluD = 0;
  395 + int mOffC = 0, mRedC = 0, mYelC = 0, mGrnC = 0, mBluC = 0;
388 396
389 397 for (String d : daysInMonth) {
390 398 long[] cnt = countLampData(dbDataMap.get(d));
391   - mOffD += cnt[0]; mRedD += cnt[2]; mYelD += cnt[4]; mGrnD += cnt[6]; mBluD += cnt[8];
392   - mOffC += (int)cnt[1]; mRedC += (int)cnt[3]; mYelC += (int)cnt[5]; mGrnC += (int)cnt[7]; mBluC += (int)cnt[9];
  399 + mOffD += cnt[0];
  400 + mRedD += cnt[2];
  401 + mYelD += cnt[4];
  402 + mGrnD += cnt[6];
  403 + mBluD += cnt[8];
  404 + mOffC += (int) cnt[1];
  405 + mRedC += (int) cnt[3];
  406 + mYelC += (int) cnt[5];
  407 + mGrnC += (int) cnt[7];
  408 + mBluC += (int) cnt[9];
393 409 }
394 410
395   - tOffD+=mOffD; tRedD+=mRedD; tYelD+=mYelD; tGrnD+=mGrnD; tBluD+=mBluD;
396   - tOffC+=mOffC; tRedC+=mRedC; tYelC+=mYelC; tGrnC+=mGrnC; tBluC+=mBluC;
397   -
398   - String monthNum = monthKey.substring(5); // "01", "02" ...
  411 + tOffD += mOffD;
  412 + tRedD += mRedD;
  413 + tYelD += mYelD;
  414 + tGrnD += mGrnD;
  415 + tBluD += mBluD;
  416 + tOffC += mOffC;
  417 + tRedC += mRedC;
  418 + tYelC += mYelC;
  419 + tGrnC += mGrnC;
  420 + tBluC += mBluC;
  421 +
  422 + String monthNum = monthKey.substring(5);
399 423 Map<String, Object> ms = new LinkedHashMap<>();
400 424 ms.put("label", Integer.parseInt(monthNum) + "月");
401 425 ms.put("month", monthKey);
... ... @@ -412,10 +436,6 @@ public class DeviceSearchService {
412 436 return Map.of("list", monthlyStats, "summary", summary);
413 437 }
414 438
415   - /**
416   - * 统计单日 lampData 各状态时长和次数
417   - * 返回 [offDur, offCnt, redDur, redCnt, yelDur, yelCnt, grnDur, grnCnt, bluDur, bluCnt]
418   - */
419 439 private long[] countLampData(JSONObject dayData) {
420 440 long[] result = new long[10];
421 441 if (dayData != null) {
... ... @@ -423,14 +443,30 @@ public class DeviceSearchService {
423 443 if (lampArr != null) {
424 444 for (int i = 0; i < lampArr.size(); i++) {
425 445 JSONObject item = lampArr.getJSONObject(i);
  446 + if (item == null) continue;
426 447 int state = item.getIntValue("lampState");
427 448 long dur = item.getLongValue("duration");
428 449 switch (state) {
429   - case 0 -> { result[0] += dur; result[1]++; }
430   - case 1 -> { result[2] += dur; result[3]++; }
431   - case 2 -> { result[4] += dur; result[5]++; }
432   - case 3 -> { result[6] += dur; result[7]++; }
433   - case 4 -> { result[8] += dur; result[9]++; }
  450 + case 0 -> {
  451 + result[0] += dur;
  452 + result[1]++;
  453 + }
  454 + case 1 -> {
  455 + result[2] += dur;
  456 + result[3]++;
  457 + }
  458 + case 2 -> {
  459 + result[4] += dur;
  460 + result[5]++;
  461 + }
  462 + case 3 -> {
  463 + result[6] += dur;
  464 + result[7]++;
  465 + }
  466 + case 4 -> {
  467 + result[8] += dur;
  468 + result[9]++;
  469 + }
434 470 }
435 471 }
436 472 }
... ... @@ -438,11 +474,8 @@ public class DeviceSearchService {
438 474 return result;
439 475 }
440 476
441   - /**
442   - * 构建 summary 汇总对象
443   - */
444 477 private Map<String, Object> buildSummary(long offD, long redD, long yelD, long grnD, long bluD,
445   - int offC, int redC, int yelC, int grnC, int bluC) {
  478 + int offC, int redC, int yelC, int grnC, int bluC) {
446 479 Map<String, Object> summary = new LinkedHashMap<>();
447 480 summary.put("off", Map.of("duration", formatDuration(offD), "seconds", offD, "count", offC));
448 481 summary.put("red", Map.of("duration", formatDuration(redD), "seconds", redD, "count", redC));
... ... @@ -452,16 +485,12 @@ public class DeviceSearchService {
452 485 return summary;
453 486 }
454 487
455   - /**
456   - * 根据查询类型构建日期列表
457   - */
458 488 private List<String> buildDateRange(String type, String startDate, String endDate) {
459 489 List<String> result = new ArrayList<>();
460 490 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
461 491
462 492 switch (type) {
463 493 case "day": {
464   - // 指定日期范围
465 494 if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) {
466 495 try {
467 496 Date start = sdf.parse(startDate);
... ... @@ -479,19 +508,14 @@ public class DeviceSearchService {
479 508 break;
480 509 }
481 510 case "week": {
482   - // 今年1月1日 ~ 今天所在周的周日(自然周,不跨年)
483 511 Calendar now = Calendar.getInstance();
484 512 int currentYear = now.get(Calendar.YEAR);
485   -
486 513 Calendar startCal = Calendar.getInstance();
487 514 startCal.set(currentYear, Calendar.JANUARY, 1);
488   -
489   - // 本周结束(周日)
490 515 Calendar endCal = (Calendar) now.clone();
491 516 int todayDow = endCal.get(Calendar.DAY_OF_WEEK);
492 517 int toSunday = todayDow == Calendar.SUNDAY ? 0 : (7 - todayDow);
493 518 endCal.add(Calendar.DATE, toSunday);
494   -
495 519 Calendar cur = Calendar.getInstance();
496 520 cur.setTime(startCal.getTime());
497 521 while (!cur.after(endCal)) {
... ... @@ -501,11 +525,9 @@ public class DeviceSearchService {
501 525 break;
502 526 }
503 527 case "month": {
504   - // 今年1月 ~ 本月最后一天
505 528 Calendar now = Calendar.getInstance();
506 529 int year = now.get(Calendar.YEAR);
507   - int thisMonth = now.get(Calendar.MONTH); // 0-indexed
508   -
  530 + int thisMonth = now.get(Calendar.MONTH);
509 531 for (int m = 0; m <= thisMonth; m++) {
510 532 Calendar cal = Calendar.getInstance();
511 533 cal.set(year, m, 1);
... ... @@ -521,16 +543,16 @@ public class DeviceSearchService {
521 543 return result;
522 544 }
523 545
524   - /**
525   - * 从 oee 表查询指定日期范围的数据
526   - */
527   - private List<Map<String, Object>> queryOeeFromDb(String dtuSn, List<String> dates, String excludeToday) {
  546 + private List<Map<String, Object>> queryOeeFromDb(String corpCode, String dtuSn, List<String> dates, String excludeToday) {
528 547 if (dates.isEmpty()) return Collections.emptyList();
529 548
  549 + String oeeTableName = corpConfigService.getOeeTableName(corpCode);
  550 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  551 +
530 552 StringBuilder sql = new StringBuilder(
531 553 "SELECT oee_date, triColorLamp1, triColorLamp2 FROM " + oeeTableName + " WHERE corp_code = ?");
532 554 List<Object> params = new ArrayList<>();
533   - params.add(deviceCorpCode);
  555 + params.add(corpCode);
534 556
535 557 if (StringUtils.hasText(dtuSn)) {
536 558 sql.append(" AND dtuSn = ?");
... ... @@ -540,35 +562,24 @@ public class DeviceSearchService {
540 562 sql.append(" AND oee_date IN (");
541 563 List<Object> dateParams = new ArrayList<>();
542 564 for (String d : dates) {
543   - if (d.equals(excludeToday)) continue; // 排除今天,今天走接口
  565 + if (d.equals(excludeToday)) continue;
544 566 sql.append("?,");
545 567 dateParams.add(d);
546 568 }
547   - if (dateParams.isEmpty()) {
548   - return Collections.emptyList(); // 所有日期都是今天
549   - }
  569 + if (dateParams.isEmpty()) return Collections.emptyList();
550 570 sql.deleteCharAt(sql.length() - 1).append(")");
551 571 params.addAll(dateParams);
552 572 sql.append(" ORDER BY oee_date ASC");
553 573
554   - return jdbcTemplate.queryForList(sql.toString(), params.toArray());
  574 + return jt.queryForList(sql.toString(), params.toArray());
555 575 }
556 576
557 577 // ==================== OEE 时序图分页查询(含稼动率计算) ====================
558 578
559   - /**
560   - * 按设备分页查询所有设备的OEE时序数据,并计算稼动率
561   - *
562   - * @param startDate 开始日期 yyyy-MM-dd
563   - * @param endDate 结束日期 yyyy-MM-dd
564   - * @param pageNo 页码,从1开始
565   - * @param pageSize 每页设备数,最大20
566   - */
567   - public Map<String, Object> queryOeeTimeline(String startDate, String endDate, Integer pageNo, Integer pageSize) {
568   - log.info("========== [OEE时序图查询-按设备分页] startDate={}, endDate={}, pageNo={}, pageSize={} ==========",
569   - startDate, endDate, pageNo, pageSize);
  579 + public Map<String, Object> queryOeeTimeline(String corpCode, String startDate, String endDate, Integer pageNo, Integer pageSize) {
  580 + log.info("========== [OEE时序图查询-按设备分页] corpCode={}, startDate={}, endDate={}, pageNo={}, pageSize={} ==========",
  581 + corpCode, startDate, endDate, pageNo, pageSize);
570 582
571   - // 参数校验与默认值
572 583 if (!StringUtils.hasText(startDate) || !StringUtils.hasText(endDate)) {
573 584 return Map.of("total", 0, "pageNo", pageNo, "pageSize", pageSize, "list", List.of());
574 585 }
... ... @@ -581,7 +592,7 @@ public class DeviceSearchService {
581 592 log.info("包含今日({}): {}", todayStr, includeToday);
582 593
583 594 // 1. 从设备表查询所有 dtuSn 及设备名称
584   - Map<String, String> deviceNameMap = queryAllDtuSnWithName();
  595 + Map<String, String> deviceNameMap = queryAllDtuSnWithName(corpCode);
585 596 List<String> allDtuSns = new ArrayList<>(deviceNameMap.keySet());
586 597 int deviceTotal = allDtuSns.size();
587 598 log.info("设备总数: {}", deviceTotal);
... ... @@ -590,31 +601,25 @@ public class DeviceSearchService {
590 601 return buildPageResult(0, pn, ps, List.of());
591 602 }
592 603
593   - // 2. 构建日期范围列表
594 604 List<String> dateList = buildDayList(startDate, endDate);
595 605 log.info("日期范围共 {} 天: {} ~ {}", dateList.size(), dateList.get(0), dateList.get(dateList.size() - 1));
596 606
597   - // 3. 批量从数据库查询全部设备的 OEE 数据
598   - List<OeeRecord> allRecords = queryOeeBatch(allDtuSns, dateList, includeToday ? todayStr : null);
  607 + List<OeeRecord> allRecords = queryOeeBatch(corpCode, allDtuSns, dateList, includeToday ? todayStr : null);
599 608
600   - // 4. 如果包含今天,补充调用接口获取今天的实时数据
601 609 if (includeToday) {
602   - supplementTodayData(allDtuSns, todayStr, allRecords);
  610 + supplementTodayData(corpCode, allDtuSns, todayStr, allRecords);
603 611 }
604 612
605   - // 5. 按 dtuSn 分组,每个设备包含该日期范围内所有天的数据
606 613 Map<String, List<OeeRecord>> deviceMap = new LinkedHashMap<>();
607 614 for (OeeRecord r : allRecords) {
608 615 deviceMap.computeIfAbsent(r.dtuSn, k -> new ArrayList<>()).add(r);
609 616 }
610   - // 确保没有数据的设备也有空列表(保持顺序一致)
611 617 for (String sn : allDtuSns) {
612 618 if (!deviceMap.containsKey(sn)) {
613 619 deviceMap.put(sn, new ArrayList<>());
614 620 }
615 621 }
616 622
617   - // 6. 设备维度分页
618 623 List<Map<String, Object>> pageList;
619 624 int offset = (pn - 1) * ps;
620 625 List<String> pagedDevices = new ArrayList<>(deviceMap.keySet()).subList(
... ... @@ -624,10 +629,11 @@ public class DeviceSearchService {
624 629 return buildPageResult(deviceTotal, pn, ps, pageList);
625 630 }
626 631
627   - /** 查询设备表所有dtuSn及对应设备名称 */
628   - private Map<String, String> queryAllDtuSnWithName() {
629   - String sql = "SELECT dtuSn, deviceName FROM " + deviceTableName + " WHERE corp_code = ? ORDER BY dtuSn";
630   - List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, deviceCorpCode);
  632 + private Map<String, String> queryAllDtuSnWithName(String corpCode) {
  633 + String tableName = corpConfigService.getDeviceTableName(corpCode);
  634 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  635 + String sql = "SELECT dtuSn, deviceName FROM " + tableName + " WHERE corp_code = ? ORDER BY dtuSn";
  636 + List<Map<String, Object>> rows = jt.queryForList(sql, corpCode);
631 637 Map<String, String> result = new LinkedHashMap<>(rows.size());
632 638 for (Map<String, Object> row : rows) {
633 639 result.put((String) row.get("dtuSn"), (String) row.get("deviceName"));
... ... @@ -635,7 +641,6 @@ public class DeviceSearchService {
635 641 return result;
636 642 }
637 643
638   - /** 判断某日期是否在范围内 */
639 644 private boolean isDateInRange(String dateStr, String start, String end) {
640 645 try {
641 646 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
... ... @@ -648,7 +653,6 @@ public class DeviceSearchService {
648 653 }
649 654 }
650 655
651   - /** 构建连续日期列表 */
652 656 private List<String> buildDayList(String startDate, String endDate) {
653 657 List<String> result = new ArrayList<>();
654 658 try {
... ... @@ -667,12 +671,11 @@ public class DeviceSearchService {
667 671 return result;
668 672 }
669 673
670   - /** OEE内部记录结构 */
671   - private static class OeeRecord {
  674 + static class OeeRecord {
672 675 String dtuSn;
673 676 String oeeDate;
674   - JSONArray lampData; // 该日lampData数组
675   - double availabilityRate; // 稼动率 (%)
  677 + JSONArray lampData;
  678 + double availabilityRate;
676 679
677 680 OeeRecord(String dtuSn, String oeeDate) {
678 681 this.dtuSn = dtuSn;
... ... @@ -681,34 +684,37 @@ public class DeviceSearchService {
681 684 }
682 685 }
683 686
684   - /** 批量从数据库查询OEE数据 */
685   - private List<OeeRecord> queryOeeBatch(List<String> dtuSns, List<String> dateList, String excludeToday) {
  687 + private List<OeeRecord> queryOeeBatch(String corpCode, List<String> dtuSns, List<String> dateList, String excludeToday) {
686 688 List<OeeRecord> records = new ArrayList<>();
687   -
688 689 if (dateList.isEmpty()) return records;
689 690
  691 + String oeeTableName = corpConfigService.getOeeTableName(corpCode);
  692 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  693 +
690 694 StringBuilder sql = new StringBuilder(
691 695 "SELECT dtuSn, oee_date, triColorLamp1, triColorLamp2 FROM " + oeeTableName +
692 696 " WHERE corp_code = ? AND dtuSn IN (");
693 697 List<Object> params = new ArrayList<>();
694   - params.add(deviceCorpCode);
  698 + params.add(corpCode);
695 699
696   - // dtuSn IN 条件
697   - for (String sn : dtuSns) { sql.append("?,"); params.add(sn); }
  700 + for (String sn : dtuSns) {
  701 + sql.append("?,");
  702 + params.add(sn);
  703 + }
698 704 sql.deleteCharAt(sql.length() - 1).append(")");
699 705
700   - // 日期条件(排除今天)
701 706 sql.append(" AND oee_date IN (");
702 707 List<Object> dateParams = new ArrayList<>();
703 708 for (String d : dateList) {
704 709 if (d.equals(excludeToday)) continue;
705   - sql.append("?,"); dateParams.add(d);
  710 + sql.append("?,");
  711 + dateParams.add(d);
706 712 }
707   - if (dateParams.isEmpty()) return records; // 全是今天
  713 + if (dateParams.isEmpty()) return records;
708 714 sql.deleteCharAt(sql.length() - 1).append(")");
709 715 params.addAll(dateParams);
710 716
711   - List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql.toString(), params.toArray());
  717 + List<Map<String, Object>> rows = jt.queryForList(sql.toString(), params.toArray());
712 718 log.info("DB批量查询返回 {} 行OEE数据", rows.size());
713 719
714 720 for (Map<String, Object> row : rows) {
... ... @@ -734,59 +740,45 @@ public class DeviceSearchService {
734 740 record.availabilityRate = calcAvailabilityRate(record.lampData);
735 741 records.add(record);
736 742 }
737   -
738 743 return records;
739 744 }
740 745
741   - /** 补充今天的实时接口数据 */
742   - private void supplementTodayData(List<String> dtuSns, String todayStr, List<OeeRecord> records) {
  746 + private void supplementTodayData(String corpCode, List<String> dtuSns, String todayStr, List<OeeRecord> records) {
743 747 log.info("开始补充今日({})实时OEE数据...", todayStr);
744   - int apiCount = 0;
745 748 for (String dtuSn : dtuSns) {
746   - // 先检查是否已有该设备今天的数据
747 749 boolean exists = false;
748 750 for (OeeRecord r : records) {
749 751 if (r.dtuSn.equals(dtuSn) && r.oeeDate.equals(todayStr)) {
750   - exists = true; break;
  752 + exists = true;
  753 + break;
751 754 }
752 755 }
753 756 if (exists) continue;
754   -
755 757 try {
756   - String apiResult = devicePullService.getLampData(dtuSn, todayStr);
  758 + String apiResult = devicePullService.getLampData(corpCode, dtuSn, todayStr);
757 759 if (!StringUtils.hasText(apiResult)) continue;
758   -
759   - Map<String, Object> res = JSON.parseObject(apiResult, new com.alibaba.fastjson.TypeReference<>() {});
  760 + Map<String, Object> res = JSON.parseObject(apiResult, new com.alibaba.fastjson.TypeReference<>() {
  761 + });
760 762 Integer code = (Integer) res.get("code");
761 763 if (code == null || code != 200) continue;
762   -
763 764 JSONArray dataList = (JSONArray) res.get("data");
764 765 if (dataList == null || dataList.isEmpty()) continue;
765   -
766 766 JSONObject dataObj = (JSONObject) dataList.get(0);
767 767 OeeRecord record = new OeeRecord(dtuSn, todayStr);
768 768 record.lampData = dataObj.getJSONArray("lampData");
769 769 if (record.lampData == null) record.lampData = new JSONArray();
770 770 record.availabilityRate = calcAvailabilityRate(record.lampData);
771 771 records.add(record);
772   - apiCount++;
773 772 } catch (Exception e) {
774 773 log.error("获取今日OEE数据异常 - dtuSn:{}", dtuSn, e);
775 774 }
776 775 }
777   - log.info("今日实时数据补充完成, 新增 {} 条", apiCount);
778 776 }
779 777
780   - /**
781   - * 计算稼动率
782   - * 稼动率 = 绿灯(state=3)时长 / 总时长 * 100%
783   - */
784 778 private double calcAvailabilityRate(JSONArray lampData) {
785 779 if (lampData == null || lampData.isEmpty()) return 0.0;
786   -
787 780 long totalDuration = 0;
788 781 long greenDuration = 0;
789   -
790 782 for (int i = 0; i < lampData.size(); i++) {
791 783 JSONObject item = lampData.getJSONObject(i);
792 784 if (item == null) continue;
... ... @@ -795,36 +787,30 @@ public class DeviceSearchService {
795 787 totalDuration += dur;
796 788 if (state == 3) greenDuration += dur;
797 789 }
798   -
799 790 if (totalDuration == 0) return 0.0;
800   - return Math.round(greenDuration * 10000.0 / totalDuration) / 100.0; // 保留2位小数
  791 + return Math.round(greenDuration * 10000.0 / totalDuration) / 100.0;
801 792 }
802 793
803   - /** 按设备分组转换为响应格式:每个设备一条记录,包含lampData和设备名称 */
804 794 private List<Map<String, Object>> convertDeviceGroupToResponse(List<String> pagedDevices,
805   - Map<String, List<OeeRecord>> deviceMap,
806   - List<String> dateList,
807   - Map<String, String> deviceNameMap) {
  795 + Map<String, List<OeeRecord>> deviceMap,
  796 + List<String> dateList,
  797 + Map<String, String> deviceNameMap) {
808 798 List<Map<String, Object>> list = new ArrayList<>(pagedDevices.size());
809   -
810 799 for (String dtuSn : pagedDevices) {
811 800 Map<String, Object> item = new LinkedHashMap<>();
812 801 item.put("dtuSn", dtuSn);
813 802 item.put("deviceName", deviceNameMap.getOrDefault(dtuSn, ""));
814 803
815 804 List<OeeRecord> dayRecords = deviceMap.getOrDefault(dtuSn, Collections.emptyList());
816   - // 构建日期→record的快速查找map
817 805 Map<String, OeeRecord> recordByDate = new LinkedHashMap<>();
818 806 for (OeeRecord r : dayRecords) {
819 807 recordByDate.put(r.oeeDate, r);
820 808 }
821 809
822   - // 汇总计算综合稼动率
823 810 long totalDur = 0, greenDur = 0;
824 811 long offD = 0, redD = 0, yellowD = 0, greenD = 0, blueD = 0;
825 812 int offC = 0, redC = 0, yellowC = 0, greenC = 0, blueC = 0;
826 813
827   - // 拼接所有天的 lampData 为一个数组(按日期排序)
828 814 JSONArray allLampData = new JSONArray();
829 815 List<Map<String, Object>> dailyDetails = new ArrayList<>();
830 816
... ... @@ -837,11 +823,26 @@ public class DeviceSearchService {
837 823 int state = lamp.getIntValue("lampState");
838 824 long dur = lamp.getLongValue("duration");
839 825 switch (state) {
840   - case 0 -> { offD += dur; offC++; }
841   - case 1 -> { redD += dur; redC++; }
842   - case 2 -> { yellowD += dur; yellowC++; }
843   - case 3 -> { greenD += dur; greenC++; }
844   - case 4 -> { blueD += dur; blueC++; }
  826 + case 0 -> {
  827 + offD += dur;
  828 + offC++;
  829 + }
  830 + case 1 -> {
  831 + redD += dur;
  832 + redC++;
  833 + }
  834 + case 2 -> {
  835 + yellowD += dur;
  836 + yellowC++;
  837 + }
  838 + case 3 -> {
  839 + greenD += dur;
  840 + greenC++;
  841 + }
  842 + case 4 -> {
  843 + blueD += dur;
  844 + blueC++;
  845 + }
845 846 }
846 847 totalDur += dur;
847 848 if (state == 3) greenDur += dur;
... ... @@ -849,16 +850,11 @@ public class DeviceSearchService {
849 850 allLampData.addAll(rec.lampData);
850 851 }
851 852 double dayRate = (rec != null) ? rec.availabilityRate : 0.0;
852   - dailyDetails.add(Map.of(
853   - "oeeDate", d,
854   - "availabilityRate", dayRate,
855   - "hasData", rec != null
856   - ));
  853 + dailyDetails.add(Map.of("oeeDate", d, "availabilityRate", dayRate, "hasData", rec != null));
857 854 }
858 855
859 856 double overallRate = (totalDur > 0) ? Math.round(greenDur * 10000.0 / totalDur) / 100.0 : 0.0;
860 857
861   - // 扁平化字段,对齐截图格式
862 858 item.put("availabilityRatio", String.format("%.2f%%", overallRate));
863 859 item.put("offDuration", formatDuration(offD));
864 860 item.put("redDuration", formatDuration(redD));
... ... @@ -869,28 +865,21 @@ public class DeviceSearchService {
869 865 item.put("totalDays", dateList.size());
870 866 item.put("lampData", allLampData);
871 867 item.put("dailyDetails", dailyDetails);
872   -
873 868 list.add(item);
874 869 }
875   -
876 870 return list;
877 871 }
878 872
879   - /** 转换为响应格式 */
880 873 private List<Map<String, Object>> convertToResponse(List<OeeRecord> records) {
881 874 List<Map<String, Object>> list = new ArrayList<>(records.size());
882   -
883 875 for (OeeRecord r : records) {
884 876 Map<String, Object> item = new LinkedHashMap<>();
885 877 item.put("dtuSn", r.dtuSn);
886 878 item.put("oeeDate", r.oeeDate);
887   - item.put("availabilityRate", r.availabilityRate); // 稼动率
  879 + item.put("availabilityRate", r.availabilityRate);
888 880 item.put("availabilityRateStr", r.availabilityRate + "%");
889   -
890   - // 各状态时长统计
891 881 long offDur = 0, redDur = 0, yellowDur = 0, greenDur = 0, blueDur = 0;
892 882 int offCnt = 0, redCnt = 0, yellowCnt = 0, greenCnt = 0, blueCnt = 0;
893   -
894 883 if (r.lampData != null) {
895 884 for (int i = 0; i < r.lampData.size(); i++) {
896 885 JSONObject lamp = r.lampData.getJSONObject(i);
... ... @@ -898,52 +887,49 @@ public class DeviceSearchService {
898 887 int state = lamp.getIntValue("lampState");
899 888 long dur = lamp.getLongValue("duration");
900 889 switch (state) {
901   - case 0 -> { offDur += dur; offCnt++; }
902   - case 1 -> { redDur += dur; redCnt++; }
903   - case 2 -> { yellowDur += dur; yellowCnt++; }
904   - case 3 -> { greenDur += dur; greenCnt++; }
905   - case 4 -> { blueDur += dur; blueCnt++; }
  890 + case 0 -> {
  891 + offDur += dur;
  892 + offCnt++;
  893 + }
  894 + case 1 -> {
  895 + redDur += dur;
  896 + redCnt++;
  897 + }
  898 + case 2 -> {
  899 + yellowDur += dur;
  900 + yellowCnt++;
  901 + }
  902 + case 3 -> {
  903 + greenDur += dur;
  904 + greenCnt++;
  905 + }
  906 + case 4 -> {
  907 + blueDur += dur;
  908 + blueCnt++;
  909 + }
906 910 }
907 911 }
908 912 }
909   -
910 913 item.put("off", Map.of("duration", formatDuration(offDur), "seconds", offDur, "count", offCnt));
911 914 item.put("red", Map.of("duration", formatDuration(redDur), "seconds", redDur, "count", redCnt));
912 915 item.put("yellow", Map.of("duration", formatDuration(yellowDur), "seconds", yellowDur, "count", yellowCnt));
913 916 item.put("green", Map.of("duration", formatDuration(greenDur), "seconds", greenDur, "count", greenCnt));
914 917 item.put("blue", Map.of("duration", formatDuration(blueDur), "seconds", blueDur, "count", blueCnt));
915 918 item.put("lampData", r.lampData != null ? r.lampData : new JSONArray());
916   -
917 919 list.add(item);
918 920 }
919 921 return list;
920 922 }
921 923
922   - /** 构建分页结果(标准分页格式) */
923 924 private Map<String, Object> buildPageResult(long total, int pageNo, int pageSize, List<Map<String, Object>> list) {
924   - return Map.of(
925   - "data", Map.of(
926   - "total", total,
927   - "size", pageSize,
928   - "current", pageNo,
929   - "records", list
930   - )
931   - );
  925 + return Map.of("data", Map.of("total", total, "size", pageSize, "current", pageNo, "records", list));
932 926 }
933 927
934 928 // ==================== 智能灯统计稼动率查询(仪表盘) ====================
935 929
936   - /**
937   - * 智能灯统计稼动率综合查询(仪表盘数据)
938   - * 返回:总时长、稼动率、当前机台运行状态、异常排行榜、每设备状态时长
939   - *
940   - * @param startDate 开始日期 yyyy-MM-dd
941   - * @param endDate 结束日期 yyyy-MM-dd
942   - */
943   - public Map<String, Object> queryLampStatistics(String startDate, String endDate) {
944   - log.info("========== [智能灯统计查询] startDate={}, endDate={} ==========", startDate, endDate);
  930 + public Map<String, Object> queryLampStatistics(String corpCode, String startDate, String endDate) {
  931 + log.info("========== [智能灯统计查询] corpCode={}, startDate={}, endDate={} ==========", corpCode, startDate, endDate);
945 932
946   - // 1. 根据前端传入的起止日期构建日期范围
947 933 List<String> dateList = buildDayList(startDate, endDate);
948 934 if (dateList.isEmpty()) {
949 935 return buildEmptyLampStats();
... ... @@ -953,22 +939,17 @@ public class DeviceSearchService {
953 939 boolean includeToday = dateList.contains(todayStr);
954 940 log.info("日期范围共 {} 天, 包含今日({}): {}", dateList.size(), todayStr, includeToday);
955 941
956   - // 2. 获取所有设备及名称
957   - Map<String, String> deviceNameMap = queryAllDtuSnWithName();
  942 + Map<String, String> deviceNameMap = queryAllDtuSnWithName(corpCode);
958 943 List<String> allDtuSns = new ArrayList<>(deviceNameMap.keySet());
959 944 if (allDtuSns.isEmpty()) {
960 945 return buildEmptyLampStats();
961 946 }
962 947
963   - // 3. 批量从OEE表查询数据
964   - List<OeeRecord> allRecords = queryOeeBatch(allDtuSns, dateList, includeToday ? todayStr : null);
965   -
966   - // 4. 补充今天的实时数据
  948 + List<OeeRecord> allRecords = queryOeeBatch(corpCode, allDtuSns, dateList, includeToday ? todayStr : null);
967 949 if (includeToday) {
968   - supplementTodayData(allDtuSns, todayStr, allRecords);
  950 + supplementTodayData(corpCode, allDtuSns, todayStr, allRecords);
969 951 }
970 952
971   - // 5. 按 dtuSn 分组
972 953 Map<String, List<OeeRecord>> deviceMap = new LinkedHashMap<>();
973 954 for (OeeRecord r : allRecords) {
974 955 deviceMap.computeIfAbsent(r.dtuSn, k -> new ArrayList<>()).add(r);
... ... @@ -979,43 +960,27 @@ public class DeviceSearchService {
979 960 }
980 961 }
981 962
982   - // 6. 计算各项统计数据
983   - return buildLampStatisticsResult(allDtuSns, deviceMap, deviceNameMap, dateList);
  963 + return buildLampStatisticsResult(allDtuSns, deviceMap, deviceNameMap, dateList, corpCode);
984 964 }
985 965
986   - /** 构建空结果 */
987 966 private Map<String, Object> buildEmptyLampStats() {
988   - return Map.of(
989   - "totalDuration", Map.of(
990   - "off", Map.of("duration", "0时0分0秒", "seconds", 0),
991   - "red", Map.of("duration", "0时0分0秒", "seconds", 0),
992   - "yellow", Map.of("duration", "0时0分0秒", "seconds", 0),
993   - "green", Map.of("duration", "0时0分0秒", "seconds", 0),
994   - "blue", Map.of("duration", "0时0分0秒", "seconds", 0)
995   - ),
996   - "availabilityRate", "0.00%",
997   - "currentStatus", Map.of(
998   - "off", 0, "red", 0, "yellow", 0, "green", 0, "blue", 0
999   - ),
1000   - "abnormalRanking", List.of(),
1001   - "deviceList", List.of()
1002   - );
  967 + return Map.of("totalDuration", Map.of("off", Map.of("duration", "0时0分0秒", "seconds", 0),
  968 + "red", Map.of("duration", "0时0分0秒", "seconds", 0), "yellow", Map.of("duration", "0时0分0秒", "seconds", 0),
  969 + "green", Map.of("duration", "0时0分0秒", "seconds", 0), "blue", Map.of("duration", "0时0分0秒", "seconds", 0)),
  970 + "availabilityRate", "0.00%", "currentStatus", Map.of("off", 0, "red", 0, "yellow", 0, "green", 0, "blue", 0),
  971 + "abnormalRanking", List.of(), "deviceList", List.of());
1003 972 }
1004 973
1005   - /** 构建智能灯统计结果 */
1006 974 private Map<String, Object> buildLampStatisticsResult(List<String> allDtuSns,
1007   - Map<String, List<OeeRecord>> deviceMap,
1008   - Map<String, String> deviceNameMap,
1009   - List<String> dateList) {
1010   - // ---- ① 总时长:汇总所有设备各状态时长 ----
  975 + Map<String, List<OeeRecord>> deviceMap,
  976 + Map<String, String> deviceNameMap,
  977 + List<String> dateList,
  978 + String corpCode) {
1011 979 long totalOff = 0, totalRed = 0, totalYellow = 0, totalGreen = 0, totalBlue = 0;
1012   -
1013   - // ---- ② 设备维度统计:用于异常排名和每设备详情 ----
1014 980 List<Map<String, Object>> deviceStatList = new ArrayList<>();
1015 981
1016 982 for (String dtuSn : allDtuSns) {
1017 983 List<OeeRecord> dayRecords = deviceMap.getOrDefault(dtuSn, Collections.emptyList());
1018   -
1019 984 long devOff = 0, devRed = 0, devYellow = 0, devGreen = 0, devBlue = 0;
1020 985 long devTotalDur = 0, devGreenDur = 0;
1021 986
... ... @@ -1030,18 +995,19 @@ public class DeviceSearchService {
1030 995 case 0 -> devOff += dur;
1031 996 case 1 -> devRed += dur;
1032 997 case 2 -> devYellow += dur;
1033   - case 3 -> { devGreen += dur; devGreenDur += dur; }
  998 + case 3 -> {
  999 + devGreen += dur;
  1000 + devGreenDur += dur;
  1001 + }
1034 1002 case 4 -> devBlue += dur;
1035 1003 }
1036 1004 devTotalDur += dur;
1037 1005 }
1038 1006 }
1039 1007 }
1040   -
1041   - // 稼动率 = 绿 / (红 + 黄 + 绿) * 100%
1042 1008 long devRygDur = devRed + devYellow + devGreen;
1043 1009 double rate = (devRygDur > 0) ? Math.round(devGreenDur * 10000.0 / devRygDur) / 100.0 : 0.0;
1044   - long abnormalDur = devRed + devYellow; // 异常时长 = 红+黄
  1010 + long abnormalDur = devRed + devYellow;
1045 1011
1046 1012 Map<String, Object> devStat = new LinkedHashMap<>();
1047 1013 devStat.put("dtuSn", dtuSn);
... ... @@ -1057,12 +1023,10 @@ public class DeviceSearchService {
1057 1023 devStat.put("blueDuration", formatDuration(devBlue));
1058 1024 devStat.put("blueSeconds", devBlue);
1059 1025 devStat.put("availabilityRatio", String.format("%.2f%%", rate));
1060   - devStat.put("rygTotalDuration", formatDuration(devRygDur)); // 红+黄+绿 总时长
1061   - devStat.put("abnormalDuration", abnormalDur); // 用于排序
1062   -
  1026 + devStat.put("rygTotalDuration", formatDuration(devRygDur));
  1027 + devStat.put("abnormalDuration", abnormalDur);
1063 1028 deviceStatList.add(devStat);
1064 1029
1065   - // 累加到全局总计
1066 1030 totalOff += devOff;
1067 1031 totalRed += devRed;
1068 1032 totalYellow += devYellow;
... ... @@ -1070,45 +1034,29 @@ public class DeviceSearchService {
1070 1034 totalBlue += devBlue;
1071 1035 }
1072 1036
1073   - // ---- ③ 稼动率 = 绿 / (红 + 黄 + 绿) ----
1074 1037 long totalRygDur = totalRed + totalYellow + totalGreen;
1075 1038 double overallRate = (totalRygDur > 0) ? Math.round(totalGreen * 10000.0 / totalRygDur) / 100.0 : 0.0;
  1039 + Map<String, Integer> currentStatus = queryCurrentDeviceStatus(corpCode);
1076 1040
1077   - // ---- ④ 当前机台运行状态(从设备表实时查) ----
1078   - Map<String, Integer> currentStatus = queryCurrentDeviceStatus();
1079   -
1080   - // ---- ⑤ 异常排行榜(按红+黄时长降序) ----
1081   - deviceStatList.sort((a, b) -> Long.compare(
1082   - ((Number) b.get("abnormalDuration")).longValue(),
1083   - ((Number) a.get("abnormalDuration")).longValue()
1084   - ));
1085   - // 排序后移除辅助字段
  1041 + deviceStatList.sort((a, b) -> Long.compare(((Number) b.get("abnormalDuration")).longValue(), ((Number) a.get("abnormalDuration")).longValue()));
1086 1042 for (Map<String, Object> d : deviceStatList) {
1087 1043 d.remove("abnormalDuration");
1088 1044 }
1089 1045
1090   - return Map.of(
1091   - "totalDuration", Map.of(
1092   - "off", Map.of("duration", formatDuration(totalOff), "seconds", totalOff),
  1046 + return Map.of("totalDuration", Map.of("off", Map.of("duration", formatDuration(totalOff), "seconds", totalOff),
1093 1047 "red", Map.of("duration", formatDuration(totalRed), "seconds", totalRed),
1094 1048 "yellow", Map.of("duration", formatDuration(totalYellow), "seconds", totalYellow),
1095 1049 "green", Map.of("duration", formatDuration(totalGreen), "seconds", totalGreen),
1096   - "blue", Map.of("duration", formatDuration(totalBlue), "seconds", totalBlue)
1097   - ),
1098   - "availabilityRate", String.format("%.2f%%", overallRate),
1099   - "rygTotalDuration", formatDuration(totalRygDur), // 红黄绿总时长
1100   - "currentStatus", currentStatus,
1101   - "abnormalRanking", deviceStatList,
1102   - "deviceList", deviceStatList
1103   - );
  1050 + "blue", Map.of("duration", formatDuration(totalBlue), "seconds", totalBlue)),
  1051 + "availabilityRate", String.format("%.2f%%", overallRate), "rygTotalDuration", formatDuration(totalRygDur),
  1052 + "currentStatus", currentStatus, "abnormalRanking", deviceStatList, "deviceList", deviceStatList);
1104 1053 }
1105 1054
1106   - /** 查询当前设备的运行状态分布(从设备表) */
1107   - private Map<String, Integer> queryCurrentDeviceStatus() {
1108   - String sql = "SELECT lampState, COUNT(*) as cnt FROM " + deviceTableName +
1109   - " WHERE corp_code = ? GROUP BY lampState";
1110   - List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, deviceCorpCode);
1111   -
  1055 + private Map<String, Integer> queryCurrentDeviceStatus(String corpCode) {
  1056 + String tableName = corpConfigService.getDeviceTableName(corpCode);
  1057 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  1058 + String sql = "SELECT lampState, COUNT(*) as cnt FROM " + tableName + " WHERE corp_code = ? GROUP BY lampState";
  1059 + List<Map<String, Object>> rows = jt.queryForList(sql, corpCode);
1112 1060 int off = 0, red = 0, yellow = 0, green = 0, blue = 0;
1113 1061 for (Map<String, Object> row : rows) {
1114 1062 String state = String.valueOf(row.get("lampState"));
... ... @@ -1126,50 +1074,36 @@ public class DeviceSearchService {
1126 1074
1127 1075 // ==================== 开机率查询 ====================
1128 1076
1129   - /**
1130   - * 查询每台设备的开机率(基于 dev_util 表)
1131   - * 规则:灭灯(state=0)=未开机,其他灯(state=1/2/3/4)=开机
1132   - * 开机率 = 非灭灯时长 / 总时长 * 100%
1133   - *
1134   - * @param startDate 开始日期 yyyy-MM-dd
1135   - * @param endDate 结束日期 yyyy-MM-dd
1136   - */
1137   - public Map<String, Object> queryBootRate(String startDate, String endDate) {
1138   - log.info("========== [开机率查询] startDate={}, endDate={} ==========", startDate, endDate);
1139   -
  1077 + public Map<String, Object> queryBootRate(String corpCode, String startDate, String endDate) {
  1078 + log.info("========== [开机率查询] corpCode={}, startDate={}, endDate={} ==========", corpCode, startDate, endDate);
1140 1079 if (!StringUtils.hasText(startDate) || !StringUtils.hasText(endDate)) {
1141 1080 return Map.of("list", List.of(), "startDate", startDate, "endDate", endDate);
1142 1081 }
1143   -
1144 1082 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
1145 1083 String todayStr = sdf.format(new Date());
1146 1084 boolean includeToday = isDateInRange(todayStr, startDate, endDate);
1147   - log.info("设备总数: {}, 包含今日({}): {}", includeToday);
1148 1085
1149   - // 1. 获取所有设备列表
1150   - Map<String, String> deviceNameMap = queryAllDtuSnWithName();
  1086 + Map<String, String> deviceNameMap = queryAllDtuSnWithName(corpCode);
1151 1087 List<String> allDtuSns = new ArrayList<>(deviceNameMap.keySet());
1152 1088 if (allDtuSns.isEmpty()) {
1153 1089 return Map.of("list", List.of(), "startDate", startDate, "endDate", endDate);
1154 1090 }
1155 1091 log.info("设备总数: {}", allDtuSns.size());
1156 1092
1157   - // 2. 从 dev_util 表按 dtuSn 和日期范围汇总各状态时长(排除今天)
1158   - String dbStartDate = includeToday && startDate.equals(todayStr) ? getNextDay(todayStr) : startDate;
  1093 + String devUtilTableName = corpConfigService.getDevUtilTableName(corpCode);
  1094 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  1095 +
1159 1096 List<Map<String, Object>> rows;
1160 1097 if (includeToday) {
1161   - // 排除今天,今天走接口
1162   - rows = queryDevUtilBatch(deviceCorpCode, allDtuSns, startDate, endDate, todayStr);
  1098 + rows = queryDevUtilBatch(jt, devUtilTableName, corpCode, allDtuSns, startDate, endDate, todayStr);
1163 1099 } else {
1164 1100 String sql = "SELECT dtuSn, SUM(`0`) as s0, SUM(`1`) as s1, SUM(`2`) as s2, SUM(`3`) as s3, SUM(`4`) as s4, COUNT(*) as days " +
1165   - "FROM " + devUtilTableName +
1166   - " WHERE corp_code = ? AND date_util BETWEEN ? AND ? GROUP BY dtuSn";
1167   - rows = jdbcTemplate.queryForList(sql, deviceCorpCode, startDate, endDate);
  1101 + "FROM " + devUtilTableName + " WHERE corp_code = ? AND date_util BETWEEN ? AND ? GROUP BY dtuSn";
  1102 + rows = jt.queryForList(sql, corpCode, startDate, endDate);
1168 1103 }
1169 1104 log.info("dev_util 查询返回 {} 条记录", rows.size());
1170 1105
1171   - // 3. 按 dtuSn 构建查询结果 map
1172   - Map<String, double[]> rawMap = new LinkedHashMap<>(); // [s0, s1, s2, s3, s4, dataDays]
  1106 + Map<String, double[]> rawMap = new LinkedHashMap<>();
1173 1107 for (Map<String, Object> row : rows) {
1174 1108 String sn = (String) row.get("dtuSn");
1175 1109 double s0 = ((Number) row.get("s0")).doubleValue();
... ... @@ -1181,20 +1115,17 @@ public class DeviceSearchService {
1181 1115 rawMap.put(sn, new double[]{s0, s1, s2, s3, s4, dataDays});
1182 1116 }
1183 1117
1184   - // 4. 如果包含今天,补充调用稼动率接口获取今天的实时数据
1185 1118 if (includeToday) {
1186   - supplementTodayDevUtil(allDtuSns, todayStr, rawMap);
  1119 + supplementTodayDevUtil(corpCode, allDtuSns, todayStr, rawMap);
1187 1120 }
1188 1121
1189   - // 5. 构建最终结果
1190 1122 Map<String, Map<String, Object>> rateMap = new LinkedHashMap<>();
1191 1123 for (Map.Entry<String, double[]> entry : rawMap.entrySet()) {
1192 1124 String sn = entry.getKey();
1193 1125 double[] vals = entry.getValue();
1194 1126 double totalDur = vals[0] + vals[1] + vals[2] + vals[3] + vals[4];
1195   - double onDur = vals[1] + vals[2] + vals[3] + vals[4]; // 非灭灯时长 = 开机时长
  1127 + double onDur = vals[1] + vals[2] + vals[3] + vals[4];
1196 1128 double bootRate = totalDur > 0 ? Math.round(onDur * 10000.0 / totalDur) / 100.0 : 0.0;
1197   -
1198 1129 Map<String, Object> item = new LinkedHashMap<>();
1199 1130 item.put("dtuSn", sn);
1200 1131 item.put("deviceName", deviceNameMap.getOrDefault(sn, ""));
... ... @@ -1207,11 +1138,9 @@ public class DeviceSearchService {
1207 1138 item.put("bootRate", String.format("%.2f%%", bootRate));
1208 1139 item.put("bootRateValue", bootRate);
1209 1140 item.put("dataDays", (int) vals[5]);
1210   -
1211 1141 rateMap.put(sn, item);
1212 1142 }
1213 1143
1214   - // 6. 确保所有设备都在返回列表中(没有数据的设备开机率为0)
1215 1144 List<Map<String, Object>> resultList = new ArrayList<>(allDtuSns.size());
1216 1145 for (String sn : allDtuSns) {
1217 1146 if (rateMap.containsKey(sn)) {
... ... @@ -1233,7 +1162,6 @@ public class DeviceSearchService {
1233 1162 }
1234 1163 }
1235 1164
1236   - // 计算整体汇总
1237 1165 double sumTotal = 0, sumOn = 0, sumOff = 0;
1238 1166 for (Map<String, Object> r : rateMap.values()) {
1239 1167 sumTotal += ((Number) r.get("totalSeconds")).doubleValue();
... ... @@ -1242,105 +1170,82 @@ public class DeviceSearchService {
1242 1170 }
1243 1171 double overallBootRate = sumTotal > 0 ? Math.round(sumOn * 10000.0 / sumTotal) / 100.0 : 0.0;
1244 1172
1245   - return Map.of(
1246   - "summary", Map.of(
1247   - "totalDevices", allDtuSns.size(),
1248   - "totalDuration", formatDuration((long) sumTotal),
1249   - "totalSeconds", Math.round(sumTotal),
1250   - "onDuration", formatDuration((long) sumOn),
1251   - "onSeconds", Math.round(sumOn),
1252   - "offDuration", formatDuration((long) sumOff),
1253   - "offSeconds", Math.round(sumOff),
1254   - "overallBootRate", String.format("%.2f%%", overallBootRate),
1255   - "overallBootRateValue", overallBootRate
1256   - ),
1257   - "list", resultList,
1258   - "startDate", startDate,
1259   - "endDate", endDate
1260   - );
  1173 + return Map.of("summary", Map.of("totalDevices", allDtuSns.size(),
  1174 + "totalDuration", formatDuration((long) sumTotal), "totalSeconds", Math.round(sumTotal),
  1175 + "onDuration", formatDuration((long) sumOn), "onSeconds", Math.round(sumOn),
  1176 + "offDuration", formatDuration((long) sumOff), "offSeconds", Math.round(sumOff),
  1177 + "overallBootRate", String.format("%.2f%%", overallBootRate), "overallBootRateValue", overallBootRate),
  1178 + "list", resultList, "startDate", startDate, "endDate", endDate);
1261 1179 }
1262 1180
1263   - /** 批量从 dev_util 表查询(排除指定日期) */
1264   - private List<Map<String, Object>> queryDevUtilBatch(String corpCode, List<String> dtuSns,
1265   - String startDate, String endDate, String excludeToday) {
1266   - StringBuilder sql = new StringBuilder(
1267   - "SELECT dtuSn, SUM(`0`) as s0, SUM(`1`) as s1, SUM(`2`) as s2, SUM(`3`) as s3, SUM(`4`) as s4, COUNT(*) as days " +
1268   - "FROM " + devUtilTableName +
1269   - " WHERE corp_code = ? AND dtuSn IN (");
  1181 + private List<Map<String, Object>> queryDevUtilBatch(JdbcTemplate jt, String devUtilTableName, String corpCode,
  1182 + List<String> dtuSns, String startDate, String endDate, String excludeToday) {
  1183 + StringBuilder sql = new StringBuilder("SELECT dtuSn, SUM(`0`) as s0, SUM(`1`) as s1, SUM(`2`) as s2, SUM(`3`) as s3, SUM(`4`) as s4, COUNT(*) as days " +
  1184 + "FROM " + devUtilTableName + " WHERE corp_code = ? AND dtuSn IN (");
1270 1185 List<Object> params = new ArrayList<>();
1271 1186 params.add(corpCode);
1272   -
1273   - for (String sn : dtuSns) { sql.append("?,"); params.add(sn); }
  1187 + for (String sn : dtuSns) {
  1188 + sql.append("?,");
  1189 + params.add(sn);
  1190 + }
1274 1191 sql.deleteCharAt(sql.length() - 1).append(")");
1275   -
1276   - // 日期范围:如果开始日期=excludeToday,则从明天开始查
1277 1192 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
1278 1193 String actualStart = startDate.equals(excludeToday) ? getNextDay(excludeToday) : startDate;
1279   -
1280 1194 try {
1281 1195 Date startD = sdf.parse(actualStart);
1282 1196 Date endD = sdf.parse(endDate);
1283 1197 Date excD = sdf.parse(excludeToday);
1284   - if (!startD.before(excD) || endD.before(startD)) {
1285   - return Collections.emptyList(); // 只有今天一天或无效范围
1286   - }
  1198 + if (!startD.before(excD) || endD.before(startD)) return Collections.emptyList();
1287 1199 } catch (Exception e) {
1288 1200 log.error("日期解析失败: {} ~ {}, 排除: {}", actualStart, endDate, excludeToday, e);
1289 1201 return Collections.emptyList();
1290 1202 }
1291   -
1292 1203 sql.append(" AND date_util BETWEEN ? AND ?");
1293 1204 params.add(actualStart);
1294 1205 params.add(endDate);
1295 1206 sql.append(" GROUP BY dtuSn");
1296   -
1297   - return jdbcTemplate.queryForList(sql.toString(), params.toArray());
  1207 + return jt.queryForList(sql.toString(), params.toArray());
1298 1208 }
1299 1209
1300   - /** 补充今天的稼动率接口实时数据 */
1301   - private void supplementTodayDevUtil(List<String> dtuSns, String todayStr, Map<String, double[]> rawMap) {
  1210 + private void supplementTodayDevUtil(String corpCode, List<String> dtuSns, String todayStr, Map<String, double[]> rawMap) {
1302 1211 log.info("开机率查询 - 开始补充今日({})实时dev_util数据...", todayStr);
1303   - int apiCount = 0;
1304 1212 for (String dtuSn : dtuSns) {
1305 1213 try {
1306   - String rateResult = devicePullService.getDtuSnRateOfAction(dtuSn, todayStr, todayStr);
  1214 + String rateResult = devicePullService.getDtuSnRateOfAction(corpCode, dtuSn, todayStr, todayStr);
1307 1215 if (StringUtils.hasText(rateResult)) {
1308   - Map<String, Object> res = JSON.parseObject(rateResult, new com.alibaba.fastjson.TypeReference<>() {});
  1216 + Map<String, Object> res = JSON.parseObject(rateResult, new com.alibaba.fastjson.TypeReference<>() {
  1217 + });
1309 1218 Integer code = (Integer) res.get("code");
1310 1219 if (code == null || code != 200) continue;
1311   -
1312 1220 JSONArray dataList = (JSONArray) res.get("data");
1313 1221 if (dataList == null || dataList.isEmpty()) continue;
1314   -
1315 1222 JSONObject dayData = (JSONObject) dataList.get(0);
1316 1223 JSONArray realRateList = dayData.getJSONArray("realRate");
1317 1224 if (realRateList == null || realRateList.isEmpty()) continue;
1318   -
1319 1225 JSONObject rateObj = (JSONObject) realRateList.get(0);
1320 1226 double s0 = rateObj.getDoubleValue("0");
1321 1227 double s1 = rateObj.getDoubleValue("1");
1322 1228 double s2 = rateObj.getDoubleValue("2");
1323 1229 double s3 = rateObj.getDoubleValue("3");
1324 1230 double s4 = rateObj.getDoubleValue("4");
1325   -
1326   - // 累加到已有数据上(DB已有历史数据 + 今天实时数据)
1327 1231 double[] existing = rawMap.get(dtuSn);
1328 1232 if (existing != null) {
1329   - existing[0] += s0; existing[1] += s1; existing[2] += s2;
1330   - existing[3] += s3; existing[4] += s4; existing[5] += 1;
  1233 + existing[0] += s0;
  1234 + existing[1] += s1;
  1235 + existing[2] += s2;
  1236 + existing[3] += s3;
  1237 + existing[4] += s4;
  1238 + existing[5] += 1;
1331 1239 } else {
1332 1240 rawMap.put(dtuSn, new double[]{s0, s1, s2, s3, s4, 1});
1333 1241 }
1334   - apiCount++;
1335 1242 }
1336 1243 } catch (Exception e) {
1337 1244 log.error("获取今日稼动率数据异常 - dtuSn:{}", dtuSn, e);
1338 1245 }
1339 1246 }
1340   - log.info("开机率查询 - 今日实时数据补充完成, 新增 {} 条", apiCount);
1341 1247 }
1342 1248
1343   - /** 获取下一天的日期字符串 */
1344 1249 private static String getNextDay(String dateStr) {
1345 1250 try {
1346 1251 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
... ...
... ... @@ -40,22 +40,10 @@ public class EnergyPullService {
40 40
41 41 @Value("${energy.token.url}")
42 42 private String energyTokenUrl;
43   - @Value("${energy.token.userName}")
44   - private String energyUserName;
45   - @Value("${energy.token.password}")
46   - private String energyPassword;
47 43 @Value("${energy.deviceList.url}")
48 44 private String energyDeviceListUrl;
49 45 @Value("${energy.detail.url}")
50 46 private String energyDetailUrl;
51   - @Value("${energy.db.corpCode}")
52   - private String energyCorpCode;
53   - @Value("${energy.db.tableName}")
54   - private String energyTableName;
55   - @Value("${energy.db.eqKwhTableName}")
56   - private String eqKwhTableName;
57   - @Value("${energy.db.eRunDtlTableName}")
58   - private String eRunDtlTableName;
59 47 @Value("${energy.runStatus.url}")
60 48 private String energyRunStatusUrl;
61 49
... ... @@ -63,21 +51,39 @@ public class EnergyPullService {
63 51 private RedisTemplate<String, String> redisTemplate;
64 52 @Resource
65 53 private JdbcTemplate jdbcTemplate;
  54 + @Resource
  55 + private CorpConfigService corpConfigService;
66 56
67 57 /**
68   - * 定时任务:同步能耗设备数据
  58 + * 定时任务:同步能耗设备数据(遍历所有已配置公司)
69 59 */
70 60 @Scheduled(cron = "${scheduler.energy.cron:0 0/5 * * ?}")
71 61 public void pullEnergyDeviceAndSave() {
72   - log.info("【能耗数据同步】开始同步能耗设备数据");
  62 + List<String> corpCodes = corpConfigService.getAllCorpCodes();
  63 + if (corpCodes.isEmpty()) {
  64 + log.warn("[Scheduled] energy: no corp config found, skip");
  65 + return;
  66 + }
  67 + for (String corpCode : corpCodes) {
  68 + pullEnergyDeviceAndSave(corpCode);
  69 + }
  70 + }
  71 +
  72 + public void pullEnergyDeviceAndSave(String corpCode) {
  73 + if (!corpConfigService.hasValidIotCredentials(corpCode)) {
  74 + log.warn("【能耗数据同步】IoT凭证为空, 跳过同步, corpCode={}", corpCode);
  75 + return;
  76 + }
  77 + log.info("【能耗数据同步】开始同步能耗设备数据, corpCode={}", corpCode);
73 78 try {
74   - String energyResult = getEnergyDeviceList();
  79 + String energyResult = getEnergyDeviceList(corpCode);
75 80 if (StringUtils.isBlank(energyResult)) {
76 81 log.error("【能耗数据同步】获取能耗设备列表失败");
77 82 return;
78 83 }
79 84
80   - Map<String, Object> energyInfos = JSON.parseObject(energyResult, new TypeReference<>() {});
  85 + Map<String, Object> energyInfos = JSON.parseObject(energyResult, new TypeReference<>() {
  86 + });
81 87 Integer code = (Integer) energyInfos.get("code");
82 88 if (code == null || code != 200) {
83 89 log.error("【能耗数据同步】获取能耗设备列表返回code不为200, code:{}, response:{}", code, energyResult);
... ... @@ -93,97 +99,72 @@ public class EnergyPullService {
93 99 int successCount = 0;
94 100 for (Object o : dataList) {
95 101 JSONObject deviceJson = (JSONObject) o;
96   -
97 102 String projectState = deviceJson.getString("projectState");
98 103 String projectType = deviceJson.getString("projectType");
99 104 String deviceName = deviceJson.getString("deviceName");
100 105 String dtuId = deviceJson.getString("dtuId");
101 106 String deviceId = deviceJson.getString("deviceId");
102 107 String dtuSn = deviceJson.getString("dtuSn");
  108 + log.info("【能耗数据处理】corpCode:{}, deviceName:{}, dtuSn:{}, projectState:{}, projectType:{}",
  109 + corpCode, deviceName, dtuSn, projectState, projectType);
103 110
104   - log.info("【能耗数据处理】deviceName:{}, dtuSn:{}, projectState:{}, projectType:{}",
105   - deviceName, dtuSn, projectState, projectType);
106   -
107   - // 获取今日用电量
108   - String todayEvalue = getTodayEnergyValue(dtuSn);
109   -
110   - // 获取能耗OEE历史数据(最后一条的duration和runStatus)
111   - Map<String, String> runStatusMap = getEnergyRunStatus(dtuSn);
  111 + String todayEvalue = getTodayEnergyValue(corpCode, dtuSn);
  112 + Map<String, String> runStatusMap = getEnergyRunStatus(corpCode, dtuSn);
112 113 String duration = runStatusMap.get("duration");
113 114 String runStatus = runStatusMap.get("runStatus");
114 115
115   - saveOrUpdateEnergyDevice(projectState, projectType, deviceName, dtuId, deviceId, dtuSn,
  116 + saveOrUpdateEnergyDevice(corpCode, projectState, projectType, deviceName, dtuId, deviceId, dtuSn,
116 117 todayEvalue, duration, runStatus);
117 118 successCount++;
118 119 }
119   -
120 120 log.info("【能耗数据同步】同步完成,共处理 {} 条设备数据", successCount);
121 121 } catch (Exception e) {
122 122 log.error("【能耗数据同步】同步过程发生异常", e);
123 123 }
124 124 }
125 125
126   - /**
127   - * 获取能耗设备列表
128   - */
129   - public String getEnergyDeviceList() {
130   - String accessToken = getEnergyAccessToken();
  126 + public String getEnergyDeviceList(String corpCode) {
  127 + String accessToken = getEnergyAccessToken(corpCode);
131 128 Map<String, String> headerMap = new HashMap<>(1);
132 129 headerMap.put("Authorization", "Bearer " + accessToken);
133   -
134 130 Map<String, String> paramsMap = new HashMap<>();
135   - paramsMap.put("groupName", "SHC");
136   -
137   - // 第一次请求
  131 + paramsMap.put("groupName", corpConfigService.getIotOrg(corpCode));
138 132 String result = sendRequestGet(energyDeviceListUrl, paramsMap, headerMap);
139   - if (StringUtils.isBlank(result)) {
140   - return null;
141   - }
  133 + if (StringUtils.isBlank(result)) return null;
142 134
143   - Map<String, Object> resultMap = JSON.parseObject(result, new TypeReference<>() {});
  135 + Map<String, Object> resultMap = JSON.parseObject(result, new TypeReference<>() {
  136 + });
144 137 Integer resultCode = (Integer) resultMap.get("code");
145 138
146   - // Token失效,重试一次
147 139 if (resultCode == null || resultCode != 200) {
148 140 log.warn("【能耗设备列表】第一次请求失败,重新获取Token重试");
149   - accessToken = getEnergyAccessToken();
150   - if (StringUtils.isEmpty(accessToken)) {
151   - return null;
152   - }
  141 + accessToken = getEnergyAccessToken(corpCode);
  142 + if (StringUtils.isEmpty(accessToken)) return null;
153 143 headerMap.put("Authorization", "Bearer " + accessToken);
154   -
155 144 result = sendRequestGet(energyDeviceListUrl, paramsMap, headerMap);
156   - if (StringUtils.isBlank(result)) {
157   - return null;
158   - }
159   -
160   - resultMap = JSON.parseObject(result, new TypeReference<>() {});
  145 + if (StringUtils.isBlank(result)) return null;
  146 + resultMap = JSON.parseObject(result, new TypeReference<>() {
  147 + });
161 148 resultCode = (Integer) resultMap.get("code");
162 149 if (resultCode == null || resultCode != 200) {
163 150 log.error("【能耗设备列表】重试后仍然失败, code:{}", resultCode);
164 151 return null;
165 152 }
166 153 }
167   -
168 154 return result;
169 155 }
170 156
171   - /**
172   - * 获取能耗Token
173   - */
174   - private String getEnergyAccessToken() {
175   - String redisKey = "hnyssl_energy_token";
176   -
177   - // 检查Redis中是否有有效Token
  157 + private String getEnergyAccessToken(String corpCode) {
  158 + String redisKey = "hnyssl_energy_token_" + corpCode;
178 159 String cachedToken = redisTemplate.opsForValue().get(redisKey);
179   - if (StringUtils.isNotBlank(cachedToken) && redisTemplate.getExpire(redisKey) > 0) {
180   - return cachedToken;
181   - }
  160 + if (StringUtils.isNotBlank(cachedToken) && redisTemplate.getExpire(redisKey) > 0) return cachedToken;
182 161
183   - // 请求新Token
  162 + // 使用公司配置中的用户名密码
  163 + String userName = corpConfigService.getIotUsername(corpCode);
  164 + String password = corpConfigService.getIotPassword(corpCode);
184 165 Map<String, String> param = new HashMap<>(2);
185   - param.put("username", energyUserName);
186   - param.put("password", energyPassword);
  166 + param.put("username", userName);
  167 + param.put("password", password);
187 168
188 169 HttpPost httpPost = new HttpPost(energyTokenUrl);
189 170 String result = sendPost(httpPost, JSON.toJSONString(param));
... ... @@ -192,7 +173,8 @@ public class EnergyPullService {
192 173 return "";
193 174 }
194 175
195   - Map<String, Object> res = JSON.parseObject(result, new TypeReference<>() {});
  176 + Map<String, Object> res = JSON.parseObject(result, new TypeReference<>() {
  177 + });
196 178 Integer code = (Integer) res.get("code");
197 179 if (code == null || code != 200) {
198 180 log.error("【能耗Token】请求Token返回异常, code:{}", code);
... ... @@ -206,118 +188,85 @@ public class EnergyPullService {
206 188 return "";
207 189 }
208 190
209   - // 缓存Token,有效期1小时(Token实际有效期2小时)
210 191 redisTemplate.opsForValue().set(redisKey, token, 3600, TimeUnit.SECONDS);
211   - log.info("【能耗Token】获取Token成功并缓存");
212   -
  192 + log.info("【能耗Token】获取Token成功并缓存, corpCode={}", corpCode);
213 193 return token;
214 194 }
215 195
216   - /**
217   - * 保存或更新能耗设备数据(含用电量evalue、duration、runStatus)
218   - */
219   - private void saveOrUpdateEnergyDevice(String projectState, String projectType, String deviceName,
  196 + private void saveOrUpdateEnergyDevice(String corpCode, String projectState, String projectType, String deviceName,
220 197 String dtuId, String deviceId, String dtuSn,
221 198 String evalue, String duration, String runStatus) {
222   - // 查询是否已存在(按公司+dtuSn判断)
223   - List<Map<String, Object>> existList = jdbcTemplate.queryForList(
224   - "SELECT id FROM " + energyTableName + " WHERE corp_code = ? AND dtuSn = ?", energyCorpCode, dtuSn);
  199 + String energyTableName = corpConfigService.getEnergyTableName(corpCode);
  200 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
225 201
  202 + List<Map<String, Object>> existList = jt.queryForList(
  203 + "SELECT id FROM " + energyTableName + " WHERE corp_code = ? AND dtuSn = ?", corpCode, dtuSn);
226 204 Date now = new Date();
227 205 if (!existList.isEmpty()) {
228   - // 更新
229   - jdbcTemplate.update(
230   - "UPDATE " + energyTableName +
231   - " SET projectState = ?, projectType = ?, deviceName = ?, dtuId = ?, deviceId = ?, evalue = ?, duration = ?, runStatus = ?, updated_at = ?" +
232   - " WHERE corp_code = ? AND dtuSn = ?",
233   - projectState, projectType, deviceName, dtuId, deviceId, evalue, duration, runStatus, now, energyCorpCode, dtuSn);
234   - log.debug("【能耗数据】更新成功 - dtuSn:{}, evalue:{}, runStatus:{}", dtuSn, evalue, runStatus);
  206 + jt.update("UPDATE " + energyTableName +
  207 + " SET projectState = ?, projectType = ?, deviceName = ?, dtuId = ?, deviceId = ?, evalue = ?, duration = ?, runStatus = ?, updated_at = ?" +
  208 + " WHERE corp_code = ? AND dtuSn = ?",
  209 + projectState, projectType, deviceName, dtuId, deviceId, evalue, duration, runStatus, now, corpCode, dtuSn);
235 210 } else {
236   - // 新增
237 211 String id = UUID.randomUUID().toString().replace("-", "");
238   - jdbcTemplate.update(
239   - "INSERT INTO " + energyTableName +
240   - " (id, corp_code, created_at, created_by, updated_at, updated_by, deviceName, projectType, projectState, dtuSn, dtuId, deviceId, evalue, duration, runStatus)" +
241   - " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
242   - id, energyCorpCode, now, "system", now, "system",
  212 + jt.update("INSERT INTO " + energyTableName +
  213 + " (id, corp_code, created_at, created_by, updated_at, updated_by, deviceName, projectType, projectState, dtuSn, dtuId, deviceId, evalue, duration, runStatus)" +
  214 + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
  215 + id, corpCode, now, "system", now, "system",
243 216 deviceName, projectType, projectState, dtuSn, dtuId, deviceId, evalue, duration, runStatus);
244   - log.debug("【能耗数据】新增成功 - dtuSn:{}, evalue:{}", dtuSn, evalue);
245 217 }
246 218 }
247 219
248   - /**
249   - * 获取能耗OEE历史数据
250   - * 调用 /api/energy/runStatus?dtuSn=xxx&date=today
251   - * @return Map包含 lastDuration(最后一条的duration)和 lastRunStatus(最后一条的runStatus),失败返回空Map
252   - */
253   - private Map<String, String> getEnergyRunStatus(String dtuSn) {
  220 + private Map<String, String> getEnergyRunStatus(String corpCode, String dtuSn) {
254 221 Map<String, String> result = new HashMap<>();
255 222 result.put("duration", null);
256 223 result.put("runStatus", null);
257 224 try {
258   - String accessToken = getEnergyAccessToken();
  225 + String accessToken = getEnergyAccessToken(corpCode);
259 226 Map<String, String> headerMap = new HashMap<>(1);
260 227 headerMap.put("Authorization", "Bearer " + accessToken);
261   -
262 228 Map<String, String> paramsMap = new HashMap<>();
263 229 paramsMap.put("dtuSn", dtuSn);
264 230 paramsMap.put("date", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
265   -
266 231 String response = sendRequestGet(energyRunStatusUrl, paramsMap, headerMap);
267   - if (StringUtils.isBlank(response)) {
268   - return result;
269   - }
270   -
271   - Map<String, Object> resultMap = JSON.parseObject(response, new TypeReference<>() {});
  232 + if (StringUtils.isBlank(response)) return result;
  233 + Map<String, Object> resultMap = JSON.parseObject(response, new TypeReference<>() {
  234 + });
272 235 Integer resultCode = (Integer) resultMap.get("code");
273 236 if (resultCode == null || resultCode != 200) {
274   - // Token失效重试
275   - accessToken = getEnergyAccessToken();
276   - if (StringUtils.isEmpty(accessToken)) {
277   - return result;
278   - }
  237 + accessToken = getEnergyAccessToken(corpCode);
  238 + if (StringUtils.isEmpty(accessToken)) return result;
279 239 headerMap.put("Authorization", "Bearer " + accessToken);
280 240 response = sendRequestGet(energyRunStatusUrl, paramsMap, headerMap);
281   - if (StringUtils.isBlank(response)) {
282   - return result;
283   - }
284   - resultMap = JSON.parseObject(response, new TypeReference<>() {});
  241 + if (StringUtils.isBlank(response)) return result;
  242 + resultMap = JSON.parseObject(response, new TypeReference<>() {
  243 + });
285 244 resultCode = (Integer) resultMap.get("code");
286   - if (resultCode == null || resultCode != 200) {
287   - return result;
288   - }
  245 + if (resultCode == null || resultCode != 200) return result;
289 246 }
290   -
291 247 JSONArray dataList = (JSONArray) resultMap.get("data");
292   - // 完整data保存到 t_auto_ymk_iot_e_run_dtl 表
293 248 String description = dataList != null ? dataList.toJSONString() : "[]";
294   - saveOrUpdateERunDtl(dtuSn, description, new SimpleDateFormat("yyyy-MM-dd 00:00:00").format(new Date()));
  249 + saveOrUpdateERunDtl(corpCode, dtuSn, description, new SimpleDateFormat("yyyy-MM-dd 00:00:00").format(new Date()));
295 250
296   - // 取最后一条数据的 duration 和 runStatus
297 251 if (!CollectionUtils.isEmpty(dataList)) {
298 252 JSONObject lastItem = (JSONObject) dataList.get(dataList.size() - 1);
299 253 Long durVal = lastItem.getLong("duration");
300 254 Integer statusVal = lastItem.getInteger("runStatus");
301 255 result.put("duration", durVal != null ? String.valueOf(durVal) : null);
302 256 result.put("runStatus", statusVal != null ? String.valueOf(statusVal) : null);
303   - log.info("【能耗OEE】dtuSn:{}, 最后一条 - duration:{}, runStatus:{}",
304   - dtuSn, result.get("duration"), result.get("runStatus"));
305 257 }
306 258 } catch (Exception e) {
307   - log.error("【能耗OEE】获取异常 - dtuSn:{}", dtuSn, e);
  259 + log.error("【能耗OEE】获取异常 - corpCode:{}, dtuSn:{}", corpCode, dtuSn, e);
308 260 }
309 261 return result;
310 262 }
311 263
312   - /**
313   - * 保存或更新能耗OEE运行状态明细到 t_auto_ymk_iot_e_run_dtl 表
314   - * 超过60000字符拆分存入runStatus2
315   - */
316   - private void saveOrUpdateERunDtl(String dtuSn, String description, String useDate) {
  264 + private void saveOrUpdateERunDtl(String corpCode, String dtuSn, String description, String useDate) {
  265 + String eRunDtlTableName = corpConfigService.getERunDtlTableName(corpCode);
  266 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
317 267 int maxLen = 60000;
318 268 String rs1 = "";
319 269 String rs2 = "";
320   -
321 270 if (description.length() > maxLen) {
322 271 rs1 = description.substring(0, maxLen);
323 272 rs2 = description.substring(maxLen);
... ... @@ -325,67 +274,44 @@ public class EnergyPullService {
325 274 rs1 = description;
326 275 }
327 276
328   - List<Map<String, Object>> existList = jdbcTemplate.queryForList(
329   - "SELECT id FROM " + eRunDtlTableName + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?",
330   - energyCorpCode, dtuSn, useDate);
331   -
  277 + List<Map<String, Object>> existList = jt.queryForList(
  278 + "SELECT id FROM " + eRunDtlTableName + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?", corpCode, dtuSn, useDate);
332 279 Date now = new Date();
333 280 if (!existList.isEmpty()) {
334   - jdbcTemplate.update(
335   - "UPDATE " + eRunDtlTableName + " SET runStatus1=?, runStatus2=?, updated_at=? WHERE corp_code=? AND dtuSn=? AND use_date=?",
336   - rs1, rs2, now, energyCorpCode, dtuSn, useDate);
  281 + jt.update("UPDATE " + eRunDtlTableName + " SET runStatus1=?, runStatus2=?, updated_at=? WHERE corp_code=? AND dtuSn=? AND use_date=?", rs1, rs2, now, corpCode, dtuSn, useDate);
337 282 } else {
338 283 String id = UUID.randomUUID().toString().replace("-", "");
339   - jdbcTemplate.update(
340   - "INSERT INTO " + eRunDtlTableName +
341   - " (id,corp_code,created_at,created_by,updated_at,updated_by,dtuSn,use_date,runStatus1,runStatus2)" +
342   - " VALUES (?,?,?,?,?,?,?,?,?,?)",
343   - id, energyCorpCode, now, "system", now, "system", dtuSn, useDate, rs1, rs2);
  284 + jt.update("INSERT INTO " + eRunDtlTableName + " (id,corp_code,created_at,created_by,updated_at,updated_by,dtuSn,use_date,runStatus1,runStatus2)" +
  285 + " VALUES (?,?,?,?,?,?,?,?,?,?)", id, corpCode, now, "system", now, "system", dtuSn, useDate, rs1, rs2);
344 286 }
345 287 }
346 288
347 289 // ==================== 能耗历史数据全量/增量同步 ====================
348 290
349   - /**
350   - * 首次全量同步:本年1月1日 ~ 昨天(手动触发一次)
351   - */
352   - public void pullEnergyHistoryAndSave() {
  291 + public void pullEnergyHistoryAndSave(String corpCode) {
  292 + if (!corpConfigService.hasValidIotCredentials(corpCode)) {
  293 + log.warn("【能耗历史-全量】IoT凭证为空, 跳过同步, corpCode={}", corpCode);
  294 + return;
  295 + }
353 296 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
354 297 Calendar cal = Calendar.getInstance();
355   - cal.add(Calendar.DAY_OF_MONTH, -1); // 昨天
  298 + cal.add(Calendar.DAY_OF_MONTH, -1);
356 299 String endDate = sdf.format(cal.getTime());
357 300 cal.set(cal.get(Calendar.YEAR), Calendar.JANUARY, 1);
358 301 String startDate = sdf.format(cal.getTime());
359   -
360   - log.info("【能耗历史-全量】开始同步,日期范围: {} ~ {}", startDate, endDate);
361   - doSyncEnergyHistory(startDate, endDate);
  302 + log.info("【能耗历史-全量】开始同步, corpCode:{}, 日期范围: {} ~ {}", corpCode, startDate, endDate);
  303 + doSyncEnergyHistory(corpCode, startDate, endDate);
362 304 }
363   -//
364   -// /**
365   -// * 每日增量同步:仅同步昨天的数据(定时任务自动触发)
366   -// */
367   -// @Scheduled(cron = "${scheduler.energyHistory.cron:0 40 2 * * ?}")
368   -// public void pullEnergyHistoryDaily() {
369   -// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
370   -// Calendar cal = Calendar.getInstance();
371   -// cal.add(Calendar.DAY_OF_MONTH, -1);
372   -// String yesterday = sdf.format(cal.getTime());
373   -//
374   -// log.info("【能耗历史-每日增量】同步,日期: {}", yesterday);
375   -// doSyncEnergyHistory(yesterday, yesterday);
376   -// }
377 305
378   - /**
379   - * 核心同步逻辑:获取能耗设备列表 → 每个设备每天调用用电量+OEE接口 → 写入 eq_kwh / e_run_dtl 表
380   - */
381   - private void doSyncEnergyHistory(String startDate, String endDate) {
382   - String energyResult = getEnergyDeviceList();
  306 + private void doSyncEnergyHistory(String corpCode, String startDate, String endDate) {
  307 + String energyResult = getEnergyDeviceList(corpCode);
383 308 if (StringUtils.isBlank(energyResult)) {
384 309 log.error("【能耗历史】获取设备列表失败");
385 310 return;
386 311 }
387 312
388   - Map<String, Object> energyInfos = JSON.parseObject(energyResult, new TypeReference<>() {});
  313 + Map<String, Object> energyInfos = JSON.parseObject(energyResult, new TypeReference<>() {
  314 + });
389 315 Integer code = (Integer) energyInfos.get("code");
390 316 if (code == null || code != 200) {
391 317 log.error("【能耗历史】获取设备列表返回code异常, code:{}", code);
... ... @@ -414,161 +340,109 @@ public class EnergyPullService {
414 340 return;
415 341 }
416 342
417   - log.info("【能耗历史】开始同步,设备数:{}, 天数:{}, 总计 {} 条记录待处理",
418   - deviceList.size(), dateList.size(), deviceList.size() * dateList.size());
419   -
  343 + log.info("【能耗历史】开始同步, corpCode:{}, 设备数:{}, 天数:{}, 总计{}条记录待处理", corpCode, deviceList.size(), dateList.size(), deviceList.size() * dateList.size());
420 344 int totalSaved = 0;
421 345 for (Object o : deviceList) {
422 346 JSONObject deviceJson = (JSONObject) o;
423 347 String dtuSn = deviceJson.getString("dtuSn");
424   -
425 348 for (String dateStr : dateList) {
426 349 try {
427   - // 1. 同步当日用电量数据到 t_auto_ymk_iot_eq_kwh
428   - syncEnergyValueForDate(dtuSn, dateStr);
429   -
430   - // 2. 同步当日OEE时序数据到 t_auto_ymk_iot_e_run_dtl
431   - syncRunStatusForDate(dtuSn, dateStr);
432   -
  350 + syncEnergyValueForDate(corpCode, dtuSn, dateStr);
  351 + syncRunStatusForDate(corpCode, dtuSn, dateStr);
433 352 totalSaved++;
434 353 } catch (Exception e) {
435   - log.error("【能耗历史】处理异常 - dtuSn:{}, date:{}", dtuSn, dateStr, e);
  354 + log.error("【能耗历史】处理异常 - corpCode:{}, dtuSn:{}, date:{}", corpCode, dtuSn, dateStr, e);
436 355 }
437 356 }
438 357 }
439   -
440 358 log.info("【能耗历史】同步完成,共保存 {} 条记录", totalSaved);
441 359 }
442 360
443   - /**
444   - * 同步指定日期的设备用电量数据到 t_auto_ymk_iot_eq_kwh 表
445   - */
446   - private void syncEnergyValueForDate(String dtuSn, String dateStr) {
447   - String accessToken = getEnergyAccessToken();
  361 + private void syncEnergyValueForDate(String corpCode, String dtuSn, String dateStr) {
  362 + String accessToken = getEnergyAccessToken(corpCode);
448 363 Map<String, String> headerMap = new HashMap<>(1);
449 364 headerMap.put("Authorization", "Bearer " + accessToken);
450   -
451 365 Map<String, String> paramsMap = new HashMap<>();
452 366 paramsMap.put("dtuSn", dtuSn);
453 367 paramsMap.put("startDate", dateStr);
454 368 paramsMap.put("type", "0");
455   -
456 369 String result = sendRequestGet(energyDetailUrl, paramsMap, headerMap);
457   - if (StringUtils.isBlank(result)) {
458   - return;
459   - }
460   -
461   - Map<String, Object> resultMap = JSON.parseObject(result, new TypeReference<>() {});
  370 + if (StringUtils.isBlank(result)) return;
  371 + Map<String, Object> resultMap = JSON.parseObject(result, new TypeReference<>() {
  372 + });
462 373 Integer resultCode = (Integer) resultMap.get("code");
463 374 if (resultCode == null || resultCode != 200) {
464   - accessToken = getEnergyAccessToken();
465   - if (StringUtils.isEmpty(accessToken)) {
466   - return;
467   - }
  375 + accessToken = getEnergyAccessToken(corpCode);
  376 + if (StringUtils.isEmpty(accessToken)) return;
468 377 headerMap.put("Authorization", "Bearer " + accessToken);
469 378 result = sendRequestGet(energyDetailUrl, paramsMap, headerMap);
470   - if (StringUtils.isBlank(result)) {
471   - return;
472   - }
473   - resultMap = JSON.parseObject(result, new TypeReference<>() {});
  379 + if (StringUtils.isBlank(result)) return;
  380 + resultMap = JSON.parseObject(result, new TypeReference<>() {
  381 + });
474 382 resultCode = (Integer) resultMap.get("code");
475   - if (resultCode == null || resultCode != 200) {
476   - return;
477   - }
  383 + if (resultCode == null || resultCode != 200) return;
478 384 }
479   -
480 385 JSONArray dataList = (JSONArray) resultMap.get("data");
481 386 String description = dataList != null ? dataList.toJSONString() : "[]";
482   - saveOrUpdateEqKwh(dtuSn, description, dateStr + " 00:00:00");
  387 + saveOrUpdateEqKwh(corpCode, dtuSn, description, dateStr + " 00:00:00");
483 388 }
484 389
485   - /**
486   - * 同步指定日期的设备OEE时序数据到 t_auto_ymk_iot_e_run_dtl 表
487   - */
488   - private void syncRunStatusForDate(String dtuSn, String dateStr) {
489   - String accessToken = getEnergyAccessToken();
  390 + private void syncRunStatusForDate(String corpCode, String dtuSn, String dateStr) {
  391 + String accessToken = getEnergyAccessToken(corpCode);
490 392 Map<String, String> headerMap = new HashMap<>(1);
491 393 headerMap.put("Authorization", "Bearer " + accessToken);
492   -
493 394 Map<String, String> paramsMap = new HashMap<>();
494 395 paramsMap.put("dtuSn", dtuSn);
495 396 paramsMap.put("date", dateStr);
496   -
497 397 String response = sendRequestGet(energyRunStatusUrl, paramsMap, headerMap);
498   - if (StringUtils.isBlank(response)) {
499   - return;
500   - }
501   -
502   - Map<String, Object> resultMap = JSON.parseObject(response, new TypeReference<>() {});
  398 + if (StringUtils.isBlank(response)) return;
  399 + Map<String, Object> resultMap = JSON.parseObject(response, new TypeReference<>() {
  400 + });
503 401 Integer resultCode = (Integer) resultMap.get("code");
504 402 if (resultCode == null || resultCode != 200) {
505   - accessToken = getEnergyAccessToken();
506   - if (StringUtils.isEmpty(accessToken)) {
507   - return;
508   - }
  403 + accessToken = getEnergyAccessToken(corpCode);
  404 + if (StringUtils.isEmpty(accessToken)) return;
509 405 headerMap.put("Authorization", "Bearer " + accessToken);
510 406 response = sendRequestGet(energyRunStatusUrl, paramsMap, headerMap);
511   - if (StringUtils.isBlank(response)) {
512   - return;
513   - }
514   - resultMap = JSON.parseObject(response, new TypeReference<>() {});
  407 + if (StringUtils.isBlank(response)) return;
  408 + resultMap = JSON.parseObject(response, new TypeReference<>() {
  409 + });
515 410 resultCode = (Integer) resultMap.get("code");
516   - if (resultCode == null || resultCode != 200) {
517   - return;
518   - }
  411 + if (resultCode == null || resultCode != 200) return;
519 412 }
520   -
521 413 JSONArray dataList = (JSONArray) resultMap.get("data");
522 414 String description = dataList != null ? dataList.toJSONString() : "[]";
523   - saveOrUpdateERunDtl(dtuSn, description, dateStr + " 00:00:00");
  415 + saveOrUpdateERunDtl(corpCode, dtuSn, description, dateStr + " 00:00:00");
524 416 }
525 417
526   - /**
527   - * 获取今日设备用电量 (type=0 按小时查询)
528   - * 同时将完整响应data保存到 t_auto_ymk_iot_eq_kwh 表
529   - * @return 今日用电量value,失败返回null
530   - */
531   - private String getTodayEnergyValue(String dtuSn) {
  418 + private String getTodayEnergyValue(String corpCode, String dtuSn) {
532 419 try {
533   - String accessToken = getEnergyAccessToken();
  420 + String accessToken = getEnergyAccessToken(corpCode);
534 421 Map<String, String> headerMap = new HashMap<>(1);
535 422 headerMap.put("Authorization", "Bearer " + accessToken);
536   -
537 423 Map<String, String> paramsMap = new HashMap<>();
538 424 paramsMap.put("dtuSn", dtuSn);
539 425 paramsMap.put("startDate", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
540 426 paramsMap.put("type", "0");
541   -
542 427 String result = sendRequestGet(energyDetailUrl, paramsMap, headerMap);
543   - if (StringUtils.isBlank(result)) {
544   - return null;
545   - }
546   -
547   - Map<String, Object> resultMap = JSON.parseObject(result, new TypeReference<>() {});
  428 + if (StringUtils.isBlank(result)) return null;
  429 + Map<String, Object> resultMap = JSON.parseObject(result, new TypeReference<>() {
  430 + });
548 431 Integer resultCode = (Integer) resultMap.get("code");
549 432 if (resultCode == null || resultCode != 200) {
550   - accessToken = getEnergyAccessToken();
551   - if (StringUtils.isEmpty(accessToken)) {
552   - return null;
553   - }
  433 + accessToken = getEnergyAccessToken(corpCode);
  434 + if (StringUtils.isEmpty(accessToken)) return null;
554 435 headerMap.put("Authorization", "Bearer " + accessToken);
555 436 result = sendRequestGet(energyDetailUrl, paramsMap, headerMap);
556   - if (StringUtils.isBlank(result)) {
557   - return null;
558   - }
559   - resultMap = JSON.parseObject(result, new TypeReference<>() {});
  437 + if (StringUtils.isBlank(result)) return null;
  438 + resultMap = JSON.parseObject(result, new TypeReference<>() {
  439 + });
560 440 resultCode = (Integer) resultMap.get("code");
561   - if (resultCode == null || resultCode != 200) {
562   - return null;
563   - }
  441 + if (resultCode == null || resultCode != 200) return null;
564 442 }
565   -
566 443 JSONArray dataList = (JSONArray) resultMap.get("data");
567   - // 将完整data保存到eq_kwh表
568 444 String description = dataList != null ? dataList.toJSONString() : "[]";
569   - saveOrUpdateEqKwh(dtuSn, description, new SimpleDateFormat("yyyy-MM-dd 00:00:00").format(new Date()));
570   -
571   - // 累加所有小时的value作为今日总用电量
  445 + saveOrUpdateEqKwh(corpCode, dtuSn, description, new SimpleDateFormat("yyyy-MM-dd 00:00:00").format(new Date()));
572 446 double totalValue = 0;
573 447 if (!CollectionUtils.isEmpty(dataList)) {
574 448 for (Object o : dataList) {
... ... @@ -576,82 +450,58 @@ public class EnergyPullService {
576 450 totalValue += item.getDoubleValue("value");
577 451 }
578 452 }
579   -
580 453 String evalueStr = String.valueOf(totalValue);
581   - log.info("【能耗用电量】dtuSn:{}, 今日用电量:{}", dtuSn, evalueStr);
  454 + log.info("【能耗用电量】corpCode:{}, dtuSn:{}, 今日用电量:{}", corpCode, dtuSn, evalueStr);
582 455 return evalueStr;
583   -
584 456 } catch (Exception e) {
585   - log.error("【能耗用电量】获取异常 - dtuSn:{}", dtuSn, e);
  457 + log.error("【能耗用电量】获取异常 - corpCode:{}, dtuSn:{}", corpCode, dtuSn, e);
586 458 return null;
587 459 }
588 460 }
589 461
590   - /**
591   - * 保存或更新用电量数据到 t_auto_ymk_iot_eq_kwh 表
592   - */
593   - private void saveOrUpdateEqKwh(String dtuSn, String description, String useDate) {
594   - List<Map<String, Object>> existList = jdbcTemplate.queryForList(
595   - "SELECT id FROM " + eqKwhTableName + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?",
596   - energyCorpCode, dtuSn, useDate);
597   -
  462 + private void saveOrUpdateEqKwh(String corpCode, String dtuSn, String description, String useDate) {
  463 + String eqKwhTableName = corpConfigService.getEqKwhTableName(corpCode);
  464 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  465 + List<Map<String, Object>> existList = jt.queryForList(
  466 + "SELECT id FROM " + eqKwhTableName + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?", corpCode, dtuSn, useDate);
598 467 Date now = new Date();
599 468 if (!existList.isEmpty()) {
600   - jdbcTemplate.update(
601   - "UPDATE " + eqKwhTableName + " SET description = ?, updated_at = ? WHERE corp_code = ? AND dtuSn = ? AND use_date = ?",
602   - description, now, energyCorpCode, dtuSn, useDate);
  469 + jt.update("UPDATE " + eqKwhTableName + " SET description = ?, updated_at = ? WHERE corp_code = ? AND dtuSn = ? AND use_date = ?", description, now, corpCode, dtuSn, useDate);
603 470 } else {
604 471 String id = UUID.randomUUID().toString().replace("-", "");
605   - jdbcTemplate.update(
606   - "INSERT INTO " + eqKwhTableName +
607   - " (id, corp_code, created_at, created_by, updated_at, updated_by, dtuSn, use_date, description)" +
608   - " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
609   - id, energyCorpCode, now, "system", now, "system", dtuSn, useDate, description);
  472 + jt.update("INSERT INTO " + eqKwhTableName + " (id, corp_code, created_at, created_by, updated_at, updated_by, dtuSn, use_date, description)" +
  473 + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", id, corpCode, now, "system", now, "system", dtuSn, useDate, description);
610 474 }
611 475 }
612 476
613   - /**
614   - * 发送GET请求
615   - */
616 477 public static String sendRequestGet(String url, Map<String, String> params, Map<String, String> header) {
617 478 CloseableHttpClient httpclient = HttpClients.createDefault();
618 479 url = builderUrl(url, params);
619 480 String content = "";
620 481 HttpGet httpget = new HttpGet(url);
621 482 if (!CollectionUtils.isEmpty(header)) {
622   - for (Map.Entry<String, String> entry : header.entrySet()) {
  483 + for (Map.Entry<String, String> entry : header.entrySet())
623 484 httpget.setHeader(entry.getKey(), entry.getValue());
624   - }
625 485 }
626   -
627 486 try (CloseableHttpResponse response = httpclient.execute(httpget)) {
628   - if (response.getStatusLine().getStatusCode() == 200) {
  487 + if (response.getStatusLine().getStatusCode() == 200)
629 488 content = EntityUtils.toString(response.getEntity(), "UTF-8");
630   - }
631 489 } catch (IOException e) {
632 490 log.error("sendRequest---GET Error!", e);
633 491 }
634 492 return content;
635 493 }
636 494
637   - /**
638   - * 构建带参数的URL
639   - */
640 495 private static String builderUrl(String url, Map<String, String> params) {
641 496 UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(url);
642 497 if (!CollectionUtils.isEmpty(params)) {
643 498 MultiValueMap<String, String> paramsValue = new LinkedMultiValueMap<>();
644   - for (Map.Entry<String, String> entry : params.entrySet()) {
645   - paramsValue.add(entry.getKey(), entry.getValue());
646   - }
  499 + for (Map.Entry<String, String> entry : params.entrySet()) paramsValue.add(entry.getKey(), entry.getValue());
647 500 uriBuilder = uriBuilder.queryParams(paramsValue);
648 501 }
649 502 return uriBuilder.toUriString();
650 503 }
651 504
652   - /**
653   - * 发送POST请求
654   - */
655 505 private String sendPost(HttpPost httpPost, String jsonData) {
656 506 CloseableHttpClient httpClient = HttpClients.createDefault();
657 507 StringEntity entity = new StringEntity(jsonData, ContentType.create("application/json", Consts.UTF_8));
... ... @@ -664,9 +514,7 @@ public class EnergyPullService {
664 514 int len;
665 515 byte[] buf = new byte[128];
666 516 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
667   - while ((len = is.read(buf)) != -1) {
668   - byteArrayOutputStream.write(buf, 0, len);
669   - }
  517 + while ((len = is.read(buf)) != -1) byteArrayOutputStream.write(buf, 0, len);
670 518 result = byteArrayOutputStream.toString();
671 519 } catch (IOException e) {
672 520 log.error("sendPost error!", e);
... ...
... ... @@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSON;
4 4 import com.alibaba.fastjson.JSONArray;
5 5 import com.alibaba.fastjson.JSONObject;
6 6 import lombok.extern.slf4j.Slf4j;
7   -import org.springframework.beans.factory.annotation.Value;
8 7 import org.springframework.jdbc.core.JdbcTemplate;
9 8 import org.springframework.stereotype.Service;
10 9 import org.springframework.util.StringUtils;
... ... @@ -20,59 +19,45 @@ import java.util.*;
20 19 @Service
21 20 public class EnergySearchService {
22 21
23   - @Value("${energy.db.corpCode}")
24   - private String energyCorpCode;
25   - @Value("${energy.db.tableName}")
26   - private String energyTableName;
27   - @Value("${energy.db.eqKwhTableName}")
28   - private String eqKwhTableName;
29   - @Value("${energy.db.eRunDtlTableName}")
30   - private String eRunDtlTableName;
31   -
32 22 @Resource
33 23 private JdbcTemplate jdbcTemplate;
  24 + @Resource
  25 + private CorpConfigService corpConfigService;
34 26
35 27 /**
36 28 * 分页查询能耗设备信息
37   - * 查询表 t_auto_ymk_iot_energy,按设备名称排序
38   - *
39   - * @param deviceName 设备名称(模糊匹配)
40   - * @param runStatus 状态(0:离线,1:停机,2:待机,3:运行)
41   - * @param pageNo 页码,默认1
42   - * @param pageSize 每页条数,默认10
43 29 */
44   - public Map<String, Object> queryEnergyList(String deviceName, String runStatus,
  30 + public Map<String, Object> queryEnergyList(String corpCode, String deviceName, String runStatus,
45 31 Integer pageNo, Integer pageSize) {
  32 + String energyTableName = corpConfigService.getEnergyTableName(corpCode);
  33 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  34 +
46 35 StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM " + energyTableName + " WHERE corp_code = ?");
47 36 StringBuilder querySql = new StringBuilder(
48 37 "SELECT id, deviceName, projectType, projectState, dtuSn, dtuId, deviceId, " +
49 38 "evalue, duration, runStatus, created_at, updated_at " +
50 39 "FROM " + energyTableName + " WHERE corp_code = ?");
51 40 List<Object> params = new ArrayList<>();
52   - params.add(energyCorpCode);
  41 + params.add(corpCode);
53 42
54   - // 设备名称模糊查询
55 43 if (StringUtils.hasText(deviceName)) {
56 44 countSql.append(" AND deviceName LIKE ?");
57 45 querySql.append(" AND deviceName LIKE ?");
58 46 params.add("%" + deviceName + "%");
59 47 }
60   -
61   - // 设备状态精确匹配
62 48 if (StringUtils.hasText(runStatus)) {
63 49 countSql.append(" AND runStatus = ?");
64 50 querySql.append(" AND runStatus = ?");
65 51 params.add(runStatus);
66 52 }
67 53
68   - Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class, params.toArray());
  54 + Long total = jt.queryForObject(countSql.toString(), Long.class, params.toArray());
69 55 int offset = (pageNo - 1) * pageSize;
70   -
71 56 querySql.append(" ORDER BY deviceName ASC LIMIT ?, ?");
72 57 params.add(offset);
73 58 params.add(pageSize);
74 59
75   - List<Map<String, Object>> list = jdbcTemplate.queryForList(querySql.toString(), params.toArray());
  60 + List<Map<String, Object>> list = jt.queryForList(querySql.toString(), params.toArray());
76 61 list.forEach(row -> {
77 62 if (row.get("duration") != null) {
78 63 long seconds = Long.parseLong(String.valueOf(row.get("duration")));
... ... @@ -86,111 +71,75 @@ public class EnergySearchService {
86 71 row.put("duration", sb.toString());
87 72 }
88 73 });
89   -
90   - return Map.of(
91   - "code", 200,
92   - "msg", "请求成功",
93   - "total", total != null ? total : 0,
94   - "pageNo", pageNo,
95   - "pageSize", pageSize,
96   - "list", list
97   - );
  74 + return Map.of("code", 200, "msg", "请求成功", "total", total != null ? total : 0, "pageNo", pageNo,
  75 + "pageSize", pageSize, "list", list);
98 76 }
99 77
100   - /**
101   - * 统计能耗设备各runStatus数量及总数量
102   - * runStatus: 0-离线, 1-停机, 2-待机, 3-运行
103   - */
104   - public Map<String, Object> queryEnergyStats() {
105   - String sql = "SELECT runStatus, COUNT(*) AS cnt FROM " + energyTableName
106   - + " WHERE corp_code = ? GROUP BY runStatus";
107   - List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, energyCorpCode);
108   -
  78 + public Map<String, Object> queryEnergyStats(String corpCode) {
  79 + String energyTableName = corpConfigService.getEnergyTableName(corpCode);
  80 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  81 + String sql = "SELECT runStatus, COUNT(*) AS cnt FROM " + energyTableName + " WHERE corp_code = ? GROUP BY runStatus";
  82 + List<Map<String, Object>> rows = jt.queryForList(sql, corpCode);
109 83 int total = 0;
110 84 Map<String, Integer> statusMap = new LinkedHashMap<>();
111 85 statusMap.put("0", 0);
112 86 statusMap.put("1", 0);
113 87 statusMap.put("2", 0);
114 88 statusMap.put("3", 0);
115   -
116 89 for (Map<String, Object> row : rows) {
117 90 String key = String.valueOf(row.get("runStatus"));
118 91 int cnt = ((Number) row.get("cnt")).intValue();
119 92 total += cnt;
120 93 statusMap.merge(key, cnt, Integer::sum);
121 94 }
122   -
123   - return Map.of(
124   - "code", 200,
125   - "msg", "请求成功",
126   - "total", total,
127   - "0", statusMap.get("0"),
128   - "1", statusMap.get("1"),
129   - "2", statusMap.get("2"),
130   - "3", statusMap.get("3")
131   - );
  95 + return Map.of("code", 200, "msg", "请求成功", "total", total, "0", statusMap.get("0"),
  96 + "1", statusMap.get("1"), "2", statusMap.get("2"), "3", statusMap.get("3"));
132 97 }
133 98
134   - /**
135   - * 根据dtuSn查询指定日期的设备时用电量和OEE时序
136   - * 1. 时用电量:从t_auto_ymk_iot_eq_kwh表获取,并计算当日总用电量
137   - * 2. OEE时序:从t_auto_ymk_iot_e_run_dtl表获取,统计总时长、各状态运行时长和占比
138   - *
139   - * @param dtuSn 设备序列号
140   - * @param date 查询日期 yyyy-MM-dd
141   - */
142   - public Map<String, Object> queryEnergyDetailByDate(String dtuSn, String date) {
143   - // 1. 查询时用电量数据 - 原始数据直接返回
  99 + public Map<String, Object> queryEnergyDetailByDate(String corpCode, String dtuSn, String date) {
  100 + String eqKwhTableName = corpConfigService.getEqKwhTableName(corpCode);
  101 + String eRunDtlTableName = corpConfigService.getERunDtlTableName(corpCode);
  102 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  103 +
144 104 Object kwhRawData = Collections.emptyList();
145 105 BigDecimal totalKwh = BigDecimal.ZERO;
146 106 try {
147   - String kwhSql = "SELECT description FROM " + eqKwhTableName
148   - + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?";
149   - Map<String, Object> kwhRow = jdbcTemplate.queryForMap(kwhSql, energyCorpCode, dtuSn, date + " 00:00:00");
  107 + String kwhSql = "SELECT description FROM " + eqKwhTableName + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?";
  108 + Map<String, Object> kwhRow = jt.queryForMap(kwhSql, corpCode, dtuSn, date + " 00:00:00");
150 109 if (kwhRow != null && kwhRow.get("description") != null) {
151 110 String description = String.valueOf(kwhRow.get("description"));
152 111 kwhRawData = JSON.parseArray(description);
153   - // 计算总用电量
154 112 JSONArray dataArray = JSON.parseArray(description);
155 113 for (int i = 0; i < dataArray.size(); i++) {
156 114 JSONObject item = dataArray.getJSONObject(i);
157 115 Double value = item.getDouble("value");
158   - if (value != null) {
159   - totalKwh = totalKwh.add(BigDecimal.valueOf(value));
160   - }
  116 + if (value != null) totalKwh = totalKwh.add(BigDecimal.valueOf(value));
161 117 }
162 118 }
163 119 } catch (Exception e) {
164   - log.warn("【能耗明细】查询时用电量数据为空或异常 - dtuSn:{}, date:{}", dtuSn, date);
  120 + log.warn("【能耗明细】查询时用电量数据为空或异常 - corpCode:{}, dtuSn:{}, date:{}", corpCode, dtuSn, date, e);
165 121 }
166 122
167   - // 2. 查询OEE时序数据 - 原始数据直接返回
168 123 Object oeeRawData = Collections.emptyList();
169 124 long totalDuration = 0;
170   - // runStatus状态: 0-离线, 1-停机, 2-待机, 3-运行
171 125 Map<Integer, Long> statusDurationMap = new LinkedHashMap<>();
172   - statusDurationMap.put(0, 0L); // 离线
173   - statusDurationMap.put(1, 0L); // 停机
174   - statusDurationMap.put(2, 0L); // 待机
175   - statusDurationMap.put(3, 0L); // 运行
176   -
  126 + statusDurationMap.put(0, 0L);
  127 + statusDurationMap.put(1, 0L);
  128 + statusDurationMap.put(2, 0L);
  129 + statusDurationMap.put(3, 0L);
177 130 try {
178   - String oeeSql = "SELECT runStatus1, runStatus2 FROM " + eRunDtlTableName
179   - + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?";
180   - Map<String, Object> oeeRow = jdbcTemplate.queryForMap(oeeSql, energyCorpCode, dtuSn, date + " 00:00:00");
  131 + String oeeSql = "SELECT runStatus1, runStatus2 FROM " + eRunDtlTableName + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?";
  132 + Map<String, Object> oeeRow = jt.queryForMap(oeeSql, corpCode, dtuSn, date + " 00:00:00");
181 133 if (oeeRow != null) {
182 134 String rs1 = oeeRow.get("runStatus1") != null ? String.valueOf(oeeRow.get("runStatus1")) : "";
183 135 String rs2 = oeeRow.get("runStatus2") != null ? String.valueOf(oeeRow.get("runStatus2")) : "";
184 136 String jsonStr = rs1 + rs2;
185 137 oeeRawData = JSON.parseArray(jsonStr);
186   -
187   - // 统计各状态时长
188 138 JSONArray dataArray = JSON.parseArray(jsonStr);
189 139 for (int i = 0; i < dataArray.size(); i++) {
190 140 JSONObject item = dataArray.getJSONObject(i);
191 141 Long duration = item.getLong("duration");
192 142 Integer runStatus = item.getInteger("runStatus");
193   -
194 143 if (duration != null && duration > 0) {
195 144 totalDuration += duration;
196 145 int statusKey = runStatus != null ? runStatus : 0;
... ... @@ -199,16 +148,13 @@ public class EnergySearchService {
199 148 }
200 149 }
201 150 } catch (Exception e) {
202   - log.warn("【能耗明细】查询OEE时序数据为空或异常 - dtuSn:{}, date:{}", dtuSn, date);
  151 + log.warn("【能耗明细】查询OEE时序数据为空或异常 - corpCode:{}, dtuSn:{}, date:{}", corpCode, dtuSn, date, e);
203 152 }
204 153
205   - // 3. 构建OEE统计结果(含格式化时长和占比)
206 154 List<Map<String, Object>> statusStats = new ArrayList<>();
207 155 for (Map.Entry<Integer, Long> entry : statusDurationMap.entrySet()) {
208 156 long dur = entry.getValue();
209   - double percent = totalDuration > 0 ? BigDecimal.valueOf(dur * 100.0 / totalDuration)
210   - .setScale(2, RoundingMode.HALF_UP).doubleValue() : 0.0;
211   -
  157 + double percent = totalDuration > 0 ? BigDecimal.valueOf(dur * 100.0 / totalDuration).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0.0;
212 158 Map<String, Object> stat = new LinkedHashMap<>();
213 159 stat.put("status", entry.getKey());
214 160 stat.put("durationSeconds", dur);
... ... @@ -216,43 +162,13 @@ public class EnergySearchService {
216 162 stat.put("percent", percent);
217 163 statusStats.add(stat);
218 164 }
219   -
220   - return Map.of(
221   - "code", 200,
222   - "msg", "请求成功",
223   - "dtuSn", dtuSn,
224   - "date", date,
225   - "kwhData", Map.of(
226   - "list", kwhRawData,
227   - "totalKwh", totalKwh.setScale(2, RoundingMode.HALF_UP)
228   - ),
229   - "oeeData", Map.of(
230   - "list", oeeRawData,
231   - "totalDurationFormatted", formatDuration(totalDuration),
232   - "totalDurationSeconds", totalDuration,
233   - "statusStats", statusStats
234   - )
235   - );
  165 + return Map.of("code", 200, "msg", "请求成功", "dtuSn", dtuSn, "date", date, "kwhData", Map.of("list", kwhRawData, "totalKwh", totalKwh.setScale(2, RoundingMode.HALF_UP)),
  166 + "oeeData", Map.of("list", oeeRawData, "totalDurationFormatted", formatDuration(totalDuration), "totalDurationSeconds", totalDuration, "statusStats", statusStats));
236 167 }
237 168
238   - /**
239   - * 根据dtuSn查询指定设备的运行时长明细
240   - * 核心数据为设备时用电量(eq_kwh),其中包含每个状态的运行时长,需要统计
241   - * type=1(时): 传startDate,获取指定日期的时用电量明细+各状态时长统计
242   - * type=2(天): 传startDate和endDate,按日统计
243   - * type=3(月): 查本年年初到现在,按月统计
244   - *
245   - * @param dtuSn 设备序列号
246   - * @param type 类型:1-时,2-天,3-月
247   - * @param startDate 开始日期 yyyy-MM-dd (type=1,2必填)
248   - * @param endDate 结束日期 yyyy-MM-dd (type=2必填)
249   - */
250   - public Map<String, Object> queryEnergyRuntimeDetail(String dtuSn, String type,
251   - String startDate, String endDate) {
  169 + public Map<String, Object> queryEnergyRuntimeDetail(String corpCode, String dtuSn, String type, String startDate, String endDate) {
252 170 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
253 171 List<String> dateList = new ArrayList<>();
254   -
255   - // 根据type构建日期列表
256 172 if ("1".equals(type)) {
257 173 dateList.add(startDate);
258 174 } else if ("2".equals(type)) {
... ... @@ -278,41 +194,32 @@ public class EnergySearchService {
278 194 startCal.add(Calendar.DAY_OF_MONTH, 1);
279 195 }
280 196 }
  197 + String eqKwhTableName = corpConfigService.getEqKwhTableName(corpCode);
  198 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
281 199
282   - // 统计汇总数据
283 200 long totalDurationAll = 0;
284 201 BigDecimal totalKwhAll = BigDecimal.ZERO;
285 202 Map<Integer, Long> statusDurationAllMap = initStatusMap();
286   -
287   - // type=1: 直接返回每日明细; type=2/3: 按日期聚合后返回
288 203 List<Map<String, Object>> detailList = new ArrayList<>();
289   - // type=3 按月聚合用
290 204 Map<String, Map<String, Object>> monthAggMap = "3".equals(type) ? new LinkedHashMap<>() : null;
291 205
292 206 for (String dateStr : dateList) {
293   - // 从eq_kwh表查询时用电量数据
294 207 Object kwhRawData = Collections.emptyList();
295 208 BigDecimal dayKwh = BigDecimal.ZERO;
296 209 long dayTotalDuration = 0;
297 210 Map<Integer, Long> dayStatusDuration = initStatusMap();
298   -
299 211 try {
300   - String kwhSql = "SELECT description FROM " + eqKwhTableName
301   - + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?";
302   - Map<String, Object> kwhRow = jdbcTemplate.queryForMap(kwhSql, energyCorpCode, dtuSn, dateStr + " 00:00:00");
  212 + String kwhSql = "SELECT description FROM " + eqKwhTableName + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?";
  213 + Map<String, Object> kwhRow = jt.queryForMap(kwhSql, corpCode, dtuSn, dateStr + " 00:00:00");
303 214 if (kwhRow != null && kwhRow.get("description") != null) {
304 215 String desc = String.valueOf(kwhRow.get("description"));
305 216 JSONArray dataArray = JSON.parseArray(desc);
306   -
307 217 for (int i = 0; i < dataArray.size(); i++) {
308 218 JSONObject item = dataArray.getJSONObject(i);
309 219 Double value = item.getDouble("value");
310 220 if (value != null) dayKwh = dayKwh.add(BigDecimal.valueOf(value));
311   -
312   - // 从字段 0/1/2/3 统计各状态运行时长 + 格式化新字段
313 221 for (int statusKey = 0; statusKey <= 3; statusKey++) {
314 222 Long dur = item.getLong(String.valueOf(statusKey));
315   - item.put(statusKey + "Formatted", formatDuration(dur != null ? dur : 0L));
316 223 if (dur != null && dur > 0) {
317 224 dayTotalDuration += dur;
318 225 totalDurationAll += dur;
... ... @@ -326,11 +233,9 @@ public class EnergySearchService {
326 233 } catch (Exception ignored) {
327 234 }
328 235 totalKwhAll = totalKwhAll.add(dayKwh);
329   -
330 236 String periodKey = "3".equals(type) ? dateStr.substring(0, 7) : dateStr;
331 237
332 238 if ("1".equals(type)) {
333   - // type=1(时): 返回原始kwhList + 当日统计
334 239 Map<String, Object> entry = new LinkedHashMap<>();
335 240 entry.put("date", periodKey);
336 241 entry.put("kwhList", kwhRawData);
... ... @@ -340,7 +245,6 @@ public class EnergySearchService {
340 245 entry.put("statusStats", buildStatusStats(dayStatusDuration, dayTotalDuration));
341 246 detailList.add(entry);
342 247 } else if ("2".equals(type)) {
343   - // type=2(天): 返回每日汇总统计,不含kwhList
344 248 Map<String, Object> entry = new LinkedHashMap<>();
345 249 entry.put("date", periodKey);
346 250 entry.put("totalKwh", dayKwh.setScale(2, RoundingMode.HALF_UP));
... ... @@ -349,7 +253,6 @@ public class EnergySearchService {
349 253 entry.put("statusStats", buildStatusStats(dayStatusDuration, dayTotalDuration));
350 254 detailList.add(entry);
351 255 } else if ("3".equals(type)) {
352   - // type=3(月): 按月聚合
353 256 monthAggMap.computeIfAbsent(periodKey, k -> {
354 257 Map<String, Object> m = new LinkedHashMap<>();
355 258 m.put("date", k);
... ... @@ -361,24 +264,17 @@ public class EnergySearchService {
361 264 Map<String, Object> monthEntry = monthAggMap.get(periodKey);
362 265 monthEntry.put("totalKwh", ((BigDecimal) monthEntry.get("totalKwh")).add(dayKwh));
363 266 monthEntry.put("totalDurationSeconds", (Long) monthEntry.get("totalDurationSeconds") + dayTotalDuration);
364   - @SuppressWarnings("unchecked")
365   - Map<Integer, Long> mStatusMap = (Map<Integer, Long>) monthEntry.get("statusDurationMap");
  267 + @SuppressWarnings("unchecked") Map<Integer, Long> mStatusMap = (Map<Integer, Long>) monthEntry.get("statusDurationMap");
366 268 for (int sk = 0; sk <= 3; sk++) {
367   - if (dayStatusDuration.get(sk) > 0) {
368   - mStatusMap.merge(sk, dayStatusDuration.get(sk), Long::sum);
369   - }
  269 + if (dayStatusDuration.get(sk) > 0) mStatusMap.merge(sk, dayStatusDuration.get(sk), Long::sum);
370 270 }
371 271 }
372 272 }
373   -
374   - // type=3 构建月度聚合结果
375 273 if ("3".equals(type) && monthAggMap != null) {
376 274 for (Map.Entry<String, Map<String, Object>> me : monthAggMap.entrySet()) {
377 275 Map<String, Object> m = me.getValue();
378 276 long mDur = (Long) m.get("totalDurationSeconds");
379   - @SuppressWarnings("unchecked")
380   - Map<Integer, Long> mStatusMap = (Map<Integer, Long>) m.get("statusDurationMap");
381   -
  277 + @SuppressWarnings("unchecked") Map<Integer, Long> mStatusMap = (Map<Integer, Long>) m.get("statusDurationMap");
382 278 Map<String, Object> entry = new LinkedHashMap<>();
383 279 entry.put("date", m.get("date"));
384 280 entry.put("totalKwh", ((BigDecimal) m.get("totalKwh")).setScale(2, RoundingMode.HALF_UP));
... ... @@ -388,58 +284,36 @@ public class EnergySearchService {
388 284 detailList.add(entry);
389 285 }
390 286 }
391   -
392 287 List<Map<String, Object>> allStatusStats = buildStatusStats(statusDurationAllMap, totalDurationAll);
393   -
394   - return Map.of(
395   - "code", 200,
396   - "msg", "请求成功",
397   - "dtuSn", dtuSn,
398   - "type", type,
399   - "detailList", detailList,
400   - "summary", Map.of(
401   - "totalDurationFormatted", formatDuration(totalDurationAll),
402   - "totalDurationSeconds", totalDurationAll,
403   - "totalKwh", totalKwhAll.setScale(2, RoundingMode.HALF_UP),
404   - "statusStats", allStatusStats
405   - )
406   - );
  288 + return Map.of("code", 200, "msg", "请求成功", "dtuSn", dtuSn, "type", type, "detailList", detailList,
  289 + "summary", Map.of("totalDurationFormatted", formatDuration(totalDurationAll), "totalDurationSeconds", totalDurationAll,
  290 + "totalKwh", totalKwhAll.setScale(2, RoundingMode.HALF_UP), "statusStats", allStatusStats));
407 291 }
408 292
409   - /**
410   - * 查询能耗时序状态 - 分页查询
411   - * 从e_run_dtl(OEE时序)表获取指定日期的设备时序数据,每页12条
412   - * 同时计算每个设备的稼动率和总用电量(从eq_kwh表)
413   - *
414   - * @param date 查询日期 yyyy-MM-dd
415   - * @param pageNo 页码,默认1
416   - * @param pageSize 每页条数,默认12
417   - */
418   - public Map<String, Object> queryEnergyTimelineStatus(String date, Integer pageNo, Integer pageSize) {
419   - // 先查OEE时序表获取有数据的设备列表
420   - String countSql = "SELECT COUNT(DISTINCT r.dtuSn) FROM " + eRunDtlTableName + " r "
421   - + " INNER JOIN " + energyTableName + " e ON e.dtuSn = r.dtuSn AND e.corp_code = r.corp_code "
422   - + " WHERE r.corp_code = ? AND r.use_date = ?";
423   - Long total = jdbcTemplate.queryForObject(countSql, Long.class, energyCorpCode, date + " 00:00:00");
  293 + public Map<String, Object> queryEnergyTimelineStatus(String corpCode, String date, Integer pageNo, Integer pageSize) {
  294 + String eRunDtlTableName = corpConfigService.getERunDtlTableName(corpCode);
  295 + String energyTableName = corpConfigService.getEnergyTableName(corpCode);
  296 + String eqKwhTableName = corpConfigService.getEqKwhTableName(corpCode);
  297 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  298 +
  299 + String countSql = "SELECT COUNT(DISTINCT r.dtuSn) FROM " + eRunDtlTableName + " r " +
  300 + "INNER JOIN " + energyTableName + " e ON e.dtuSn = r.dtuSn AND e.corp_code = r.corp_code WHERE r.corp_code = ? AND r.use_date = ?";
  301 + Long total = jt.queryForObject(countSql, Long.class, corpCode, date + " 00:00:00");
424 302
425 303 int offset = (pageNo - 1) * pageSize;
426   - String querySql = "SELECT r.dtuSn, e.deviceName, r.runStatus1, r.runStatus2 FROM " + eRunDtlTableName + " r "
427   - + " INNER JOIN " + energyTableName + " e ON e.dtuSn = r.dtuSn AND e.corp_code = r.corp_code "
428   - + " WHERE r.corp_code = ? AND r.use_date = ? "
429   - + " ORDER BY e.deviceName ASC LIMIT ?, ?";
430   - List<Map<String, Object>> rows = jdbcTemplate.queryForList(querySql, energyCorpCode, date + " 00:00:00", offset, pageSize);
  304 + String querySql = "SELECT r.dtuSn, e.deviceName, r.runStatus1, r.runStatus2 FROM " + eRunDtlTableName + " r " +
  305 + "INNER JOIN " + energyTableName + " e ON e.dtuSn = r.dtuSn AND e.corp_code = r.corp_code WHERE r.corp_code = ? AND r.use_date = ? " +
  306 + "ORDER BY e.deviceName ASC LIMIT ?, ?";
  307 + List<Map<String, Object>> rows = jt.queryForList(querySql, corpCode, date + " 00:00:00", offset, pageSize);
431 308
432 309 List<Map<String, Object>> list = new ArrayList<>();
433 310 for (Map<String, Object> row : rows) {
434 311 Map<String, Object> item = new LinkedHashMap<>();
435 312 item.put("dtuSn", row.get("dtuSn"));
436 313 item.put("deviceName", row.get("deviceName"));
437   -
438   - // OEE时序数据(原始返回)
439 314 Object oeeRawData = Collections.emptyList();
440 315 long totalDuration = 0;
441 316 long runDuration = 0;
442   -
443 317 String rs1 = row.get("runStatus1") != null ? String.valueOf(row.get("runStatus1")) : "";
444 318 String rs2 = row.get("runStatus2") != null ? String.valueOf(row.get("runStatus2")) : "";
445 319 if (StringUtils.hasText(rs1) || StringUtils.hasText(rs2)) {
... ... @@ -453,23 +327,20 @@ public class EnergySearchService {
453 327 Integer runStatus = jo.getInteger("runStatus");
454 328 if (duration != null && duration > 0 && runStatus != null) {
455 329 totalDuration += duration;
456   - if (runStatus == 3) runDuration += duration; // 运行状态
  330 + if (runStatus == 3) runDuration += duration;
457 331 }
458 332 }
459 333 } catch (Exception ignored) {
460 334 }
461 335 }
462   -
463 336 item.put("timelineList", oeeRawData);
464 337 item.put("totalDurationFormatted", formatDuration(totalDuration));
465 338 item.put("totalDurationSeconds", totalDuration);
466 339
467   - // 从eq_kwh表获取用电量
468 340 BigDecimal totalKwh = BigDecimal.ZERO;
469 341 try {
470   - String kwhSql = "SELECT description FROM " + eqKwhTableName
471   - + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?";
472   - Map<String, Object> kwhRow = jdbcTemplate.queryForMap(kwhSql, energyCorpCode, row.get("dtuSn"), date + " 00:00:00");
  342 + String kwhSql = "SELECT description FROM " + eqKwhTableName + " WHERE corp_code = ? AND dtuSn = ? AND use_date = ?";
  343 + Map<String, Object> kwhRow = jt.queryForMap(kwhSql, corpCode, row.get("dtuSn"), date + " 00:00:00");
473 344 if (kwhRow != null && kwhRow.get("description") != null) {
474 345 JSONArray dataArray = JSON.parseArray(String.valueOf(kwhRow.get("description")));
475 346 for (int i = 0; i < dataArray.size(); i++) {
... ... @@ -479,25 +350,12 @@ public class EnergySearchService {
479 350 }
480 351 } catch (Exception ignored) {
481 352 }
482   -
483 353 item.put("totalKwh", totalKwh.setScale(2, RoundingMode.HALF_UP));
484   - // 稼动率 = 运行时长 / 总时长
485   - double utilizationRate = totalDuration > 0
486   - ? BigDecimal.valueOf(runDuration * 100.0 / totalDuration).setScale(2, RoundingMode.HALF_UP).doubleValue()
487   - : 0.0;
  354 + double utilizationRate = totalDuration > 0 ? BigDecimal.valueOf(runDuration * 100.0 / totalDuration).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0.0;
488 355 item.put("utilizationRate", utilizationRate);
489   -
490 356 list.add(item);
491 357 }
492   -
493   - return Map.of(
494   - "code", 200,
495   - "msg", "请求成功",
496   - "total", total != null ? total : 0,
497   - "pageNo", pageNo,
498   - "pageSize", pageSize,
499   - "list", list
500   - );
  358 + return Map.of("code", 200, "msg", "请求成功", "total", total != null ? total : 0, "pageNo", pageNo, "pageSize", pageSize, "list", list);
501 359 }
502 360
503 361 private static Map<Integer, Long> initStatusMap() {
... ... @@ -513,8 +371,7 @@ public class EnergySearchService {
513 371 List<Map<String, Object>> list = new ArrayList<>();
514 372 for (Map.Entry<Integer, Long> entry : statusMap.entrySet()) {
515 373 long dur = entry.getValue();
516   - double percent = totalDur > 0 ? BigDecimal.valueOf(dur * 100.0 / totalDur)
517   - .setScale(2, RoundingMode.HALF_UP).doubleValue() : 0.0;
  374 + double percent = totalDur > 0 ? BigDecimal.valueOf(dur * 100.0 / totalDur).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0.0;
518 375 Map<String, Object> stat = new LinkedHashMap<>();
519 376 stat.put("status", entry.getKey());
520 377 stat.put("durationSeconds", dur);
... ... @@ -527,17 +384,10 @@ public class EnergySearchService {
527 384
528 385 private static Object itemToMap(JSONArray dataArray) {
529 386 List<Object> list = new ArrayList<>();
530   - for (int i = 0; i < dataArray.size(); i++) {
531   - list.add(dataArray.getJSONObject(i));
532   - }
  387 + for (int i = 0; i < dataArray.size(); i++) list.add(dataArray.getJSONObject(i));
533 388 return list;
534 389 }
535 390
536   - /**
537   - * 格式化时长为 xx时xx分xx秒 格式
538   - * 时不为0则展示xx时xx分xx秒
539   - * 时和分都为0则只展示xx秒
540   - */
541 391 private static String formatDuration(long totalSeconds) {
542 392 long h = totalSeconds / 3600;
543 393 long m = (totalSeconds % 3600) / 60;
... ... @@ -549,143 +399,79 @@ public class EnergySearchService {
549 399 return sb.toString();
550 400 }
551 401
552   - /**
553   - * 格式化时长为 xxx.xx 时
554   - */
555 402 private static String formatDurationToHours(long totalSeconds) {
556 403 double hours = totalSeconds / 3600.0;
557 404 return String.format("%.2f时", hours);
558 405 }
559 406
560   - // ==================== eq_kwh 综合统计查询 ====================
  407 + // ==================== eq_kwh 综合统计 ====================
561 408
562   - /**
563   - * 查询 eq_kwh 表的综合统计数据
564   - * 返回:
565   - * 1. 所有设备的总的稼动率 + 每个状态的时间(xxx.xx时)
566   - * 2. 当前设备的运行状态
567   - * 3. 异常机台排名(待机+停机时间最长往下排, xx时xx分xx秒)
568   - * 4. 每个设备统计时间内的总的0/1/2/3的时间(xx时xx分xx秒)
569   - *
570   - * @param startDate 开始日期 yyyy-MM-dd
571   - * @param endDate 结束日期 yyyy-MM-dd
572   - */
573   - public Map<String, Object> queryEqKwhStatistics(String startDate, String endDate) {
574   - log.info("========== [eq_kwh综合统计] startDate={}, endDate={} ==========", startDate, endDate);
575   -
576   - if (!StringUtils.hasText(startDate) || !StringUtils.hasText(endDate)) {
577   - return Map.of(
578   - "code", 400, "msg", "参数错误: startDate和endDate必填",
579   - "data", Map.of()
580   - );
581   - }
  409 + public Map<String, Object> queryEqKwhStatistics(String corpCode, String startDate, String endDate) {
  410 + log.info("========== [eq_kwh综合统计] corpCode={}, startDate={}, endDate={} ==========", corpCode, startDate, endDate);
  411 + if (!StringUtils.hasText(startDate) || !StringUtils.hasText(endDate))
  412 + return Map.of("code", 400, "msg", "参数错误: startDate和endDate必填", "data", Map.of());
582 413
583   - // 1. 构建日期列表
584 414 List<String> dateList = buildDateList(startDate, endDate);
585   - if (dateList.isEmpty()) {
586   - return buildEmptyEqKwhStats();
587   - }
  415 + if (dateList.isEmpty()) return buildEmptyEqKwhStats();
588 416 log.info("日期范围共 {} 天: {} ~ {}", dateList.size(), dateList.get(0), dateList.get(dateList.size() - 1));
589 417
590   - // 2. 获取所有设备列表及名称
591   - Map<String, String> deviceNameMap = queryAllEnergyDeviceNames();
592   -
593   - // 3. 从 eq_kwh 表批量查询所有设备在指定日期范围内的数据
594   - List<Map<String, Object>> allRawData = queryEqKwhBatch(dateList);
  418 + Map<String, String> deviceNameMap = queryAllEnergyDeviceNames(corpCode);
  419 + List<Map<String, Object>> allRawData = queryEqKwhBatch(corpCode, dateList);
595 420 log.info("eq_kwh 批量查询返回 {} 条原始记录", allRawData.size());
596 421
597   - // 4. 按 dtuSn 分组聚合数据,计算每个设备的各状态时长
598 422 Map<String, DeviceStatResult> deviceStatMap = aggregateByDevice(allRawData, deviceNameMap);
599   -
600   - // 确保所有设备都在结果中(无数据的设为0)
601 423 for (String sn : deviceNameMap.keySet()) {
602   - if (!deviceStatMap.containsKey(sn)) {
  424 + if (!deviceStatMap.containsKey(sn))
603 425 deviceStatMap.put(sn, new DeviceStatResult(sn, deviceNameMap.getOrDefault(sn, "")));
604   - }
605 426 }
606   -
607   - // 5. 计算全局汇总
608   - return buildEqKwhStatisticsResult(deviceStatMap, deviceNameMap);
  427 + return buildEqKwhStatisticsResult(corpCode, deviceStatMap, deviceNameMap);
609 428 }
610 429
611   - /**
612   - * 设备统计内部结构
613   - */
614 430 static class DeviceStatResult {
615 431 String dtuSn;
616 432 String deviceName;
617   - long status0; // 离线时长(秒)
618   - long status1; // 停机时长(秒)
619   - long status2; // 待机时长(秒)
620   - long status3; // 运行时长(秒)
621   - double totalKwh; // 总用电量
  433 + long status0, status1, status2, status3;
  434 + double totalKwh;
622 435
623 436 DeviceStatResult(String dtuSn, String deviceName) {
624 437 this.dtuSn = dtuSn;
625 438 this.deviceName = deviceName;
626 439 }
627 440
628   - /**
629   - * 总时长(秒)
630   - */
631 441 long totalDuration() {
632 442 return status0 + status1 + status2 + status3;
633 443 }
634 444
635   - /**
636   - * 异常时长 = 停机 + 待机
637   - */
638 445 long abnormalDuration() {
639 446 return status1 + status2;
640 447 }
641 448
642   - /**
643   - * 稼动分母 = 停机 + 待机 + 运行 (排除离线)
644   - */
645 449 long activeDuration() {
646 450 return status1 + status2 + status3;
647 451 }
648 452 }
649 453
650   - /**
651   - * 构建空结果
652   - */
653 454 private Map<String, Object> buildEmptyEqKwhStats() {
654   - return Map.of(
655   - "code", 200, "msg", "请求成功", "data",
656   - Map.of(
657   - "summary", Map.of(
658   - "availabilityRate", "0.00%",
659   - "totalStatusDuration", Map.of(
660   - "status0", Map.of("durationFormatted", "0时0分0秒", "durationSeconds", 0L, "durationHours", "0.00时"),
661   - "status1", Map.of("durationFormatted", "0时0分0秒", "durationSeconds", 0L, "durationHours", "0.00时"),
662   - "status2", Map.of("durationFormatted", "0时0分0秒", "durationSeconds", 0L, "durationHours", "0.00时"),
663   - "status3", Map.of("durationFormatted", "0时0分0秒", "durationSeconds", 0L, "durationHours", "0.00时")
664   - )
665   - ),
  455 + return Map.of("code", 200, "msg", "请求成功", "data", Map.of(
  456 + "summary", Map.of("availabilityRate", "0.00%", "totalStatusDuration",
  457 + Map.of("status0", Map.of("durationFormatted", "0时0分0秒", "durationSeconds", 0L, "durationHours", "0.00时"),
  458 + "status1", Map.of("durationFormatted", "0时0分0秒", "durationSeconds", 0L, "durationHours", "0.00时"),
  459 + "status2", Map.of("durationFormatted", "0时0分0秒", "durationSeconds", 0L, "durationHours", "0.00时"),
  460 + "status3", Map.of("durationFormatted", "0时0分0秒", "durationSeconds", 0L, "durationHours", "0.00时")),
666 461 "currentStatus", Map.of("0", 0, "1", 0, "2", 0, "3", 0),
667   - "abnormalRanking", List.of(),
668   - "deviceList", List.of()
669   - )
670   - );
  462 + "abnormalRanking", List.of(), "deviceList", List.of())));
671 463 }
672 464
673   - /**
674   - * 获取能耗设备表所有dtuSn及名称
675   - */
676   - private Map<String, String> queryAllEnergyDeviceNames() {
  465 + private Map<String, String> queryAllEnergyDeviceNames(String corpCode) {
  466 + String energyTableName = corpConfigService.getEnergyTableName(corpCode);
  467 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
677 468 String sql = "SELECT dtuSn, deviceName FROM " + energyTableName + " WHERE corp_code = ? ORDER BY dtuSn";
678   - List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, energyCorpCode);
  469 + List<Map<String, Object>> rows = jt.queryForList(sql, corpCode);
679 470 Map<String, String> result = new LinkedHashMap<>(rows.size());
680   - for (Map<String, Object> row : rows) {
681   - result.put((String) row.get("dtuSn"), (String) row.get("deviceName"));
682   - }
  471 + for (Map<String, Object> row : rows) result.put((String) row.get("dtuSn"), (String) row.get("deviceName"));
683 472 return result;
684 473 }
685 474
686   - /**
687   - * 构建连续日期列表
688   - */
689 475 private List<String> buildDateList(String startDate, String endDate) {
690 476 List<String> result = new ArrayList<>();
691 477 try {
... ... @@ -704,66 +490,43 @@ public class EnergySearchService {
704 490 return result;
705 491 }
706 492
707   - /**
708   - * 从 eq_kwh 表批量查询指定日期范围的所有记录
709   - */
710   - private List<Map<String, Object>> queryEqKwhBatch(List<String> dateList) {
  493 + private List<Map<String, Object>> queryEqKwhBatch(String corpCode, List<String> dateList) {
711 494 if (dateList.isEmpty()) return Collections.emptyList();
712   -
713   - StringBuilder sql = new StringBuilder(
714   - "SELECT dtuSn, use_date, description FROM " + eqKwhTableName +
715   - " WHERE corp_code = ? AND use_date IN (");
  495 + String eqKwhTableName = corpConfigService.getEqKwhTableName(corpCode);
  496 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  497 + StringBuilder sql = new StringBuilder("SELECT dtuSn, use_date, description FROM " + eqKwhTableName + " WHERE corp_code = ? AND use_date IN (");
716 498 List<Object> params = new ArrayList<>();
717   - params.add(energyCorpCode);
718   -
  499 + params.add(corpCode);
719 500 for (String d : dateList) {
720 501 sql.append("?,");
721 502 params.add(d + "T00:00:00");
722 503 }
723   - sql.deleteCharAt(sql.length() - 1).append(")");
724   - sql.append(" ORDER BY dtuSn, use_date");
725   -
726   - return jdbcTemplate.queryForList(sql.toString(), params.toArray());
  504 + sql.deleteCharAt(sql.length() - 1).append(")").append(" ORDER BY dtuSn, use_date");
  505 + return jt.queryForList(sql.toString(), params.toArray());
727 506 }
728 507
729   - /**
730   - * 按 dtuSn 分组聚合各状态时长
731   - */
732   - private Map<String, DeviceStatResult> aggregateByDevice(List<Map<String, Object>> rawData,
733   - Map<String, String> deviceNameMap) {
  508 + private Map<String, DeviceStatResult> aggregateByDevice(List<Map<String, Object>> rawData, Map<String, String> deviceNameMap) {
734 509 Map<String, DeviceStatResult> resultMap = new LinkedHashMap<>();
735   -
736 510 for (Map<String, Object> row : rawData) {
737 511 String sn = (String) row.get("dtuSn");
738 512 String desc = row.get("description") != null ? String.valueOf(row.get("description")) : "";
739   -
740   - DeviceStatResult dsr = resultMap.computeIfAbsent(sn,
741   - k -> new DeviceStatResult(k, deviceNameMap.getOrDefault(k, "")));
742   -
  513 + DeviceStatResult dsr = resultMap.computeIfAbsent(sn, k -> new DeviceStatResult(k, deviceNameMap.getOrDefault(k, "")));
743 514 if (!StringUtils.hasText(desc)) continue;
744   -
745 515 try {
746 516 JSONArray dataArray = JSON.parseArray(desc);
747 517 if (dataArray == null) continue;
748   -
749 518 for (int i = 0; i < dataArray.size(); i++) {
750 519 JSONObject item = dataArray.getJSONObject(i);
751 520 if (item == null) continue;
752   -
753   - // 用电量
754 521 Double value = item.getDouble("value");
755 522 if (value != null) dsr.totalKwh += value;
756   -
757   - // 各状态时长: 0-离线, 1-停机, 2-待机, 3-运行
758 523 for (int sk = 0; sk <= 3; sk++) {
759 524 Long dur = item.getLong(String.valueOf(sk));
760   - if (dur != null && dur > 0) {
761   - switch (sk) {
762   - case 0 -> dsr.status0 += dur;
763   - case 1 -> dsr.status1 += dur;
764   - case 2 -> dsr.status2 += dur;
765   - case 3 -> dsr.status3 += dur;
766   - }
  525 + if (dur != null && dur > 0) switch (sk) {
  526 + case 0 -> dsr.status0 += dur;
  527 + case 1 -> dsr.status1 += dur;
  528 + case 2 -> dsr.status2 += dur;
  529 + case 3 -> dsr.status3 += dur;
767 530 }
768 531 }
769 532 }
... ... @@ -771,135 +534,57 @@ public class EnergySearchService {
771 534 log.warn("解析eq_kwh description异常 - dtuSn:{}", sn, e);
772 535 }
773 536 }
774   -
775 537 return resultMap;
776 538 }
777 539
778   - /**
779   - * 构建最终统计结果
780   - */
781   - private Map<String, Object> buildEqKwhStatisticsResult(Map<String, DeviceStatResult> deviceStatMap,
782   - Map<String, String> deviceNameMap) {
783   - // ---- 全局汇总 ----
  540 + private Map<String, Object> buildEqKwhStatisticsResult(String corpCode, Map<String, DeviceStatResult> deviceStatMap, Map<String, String> deviceNameMap) {
784 541 long totalS0 = 0, totalS1 = 0, totalS2 = 0, totalS3 = 0;
785 542 double totalKwhSum = 0;
786   -
787   - // ---- 设备详情列表 ----
788 543 List<Map<String, Object>> deviceList = new ArrayList<>(deviceStatMap.size());
789   -
790 544 for (Map.Entry<String, DeviceStatResult> entry : deviceStatMap.entrySet()) {
791 545 DeviceStatResult dsr = entry.getValue();
792   -
793 546 totalS0 += dsr.status0;
794 547 totalS1 += dsr.status1;
795 548 totalS2 += dsr.status2;
796 549 totalS3 += dsr.status3;
797 550 totalKwhSum += dsr.totalKwh;
798   -
799   - // 单设备稼动率 = 运行 / (停机+待机+运行)
800 551 long active = dsr.activeDuration();
801 552 double devRate = active > 0 ? Math.round(dsr.status3 * 10000.0 / active) / 100.0 : 0.0;
802   -
803 553 Map<String, Object> devItem = new LinkedHashMap<>();
804 554 devItem.put("dtuSn", dsr.dtuSn);
805 555 devItem.put("deviceName", dsr.deviceName);
806   - // 各状态时长 - 格式化为 xx时xx分xx秒
807   - devItem.put("status0", Map.of(
808   - "durationFormatted", formatDuration(dsr.status0),
809   - "durationSeconds", dsr.status0,
810   - "durationHours", formatDurationToHours(dsr.status0)
811   - ));
812   - devItem.put("status1", Map.of(
813   - "durationFormatted", formatDuration(dsr.status1),
814   - "durationSeconds", dsr.status1,
815   - "durationHours", formatDurationToHours(dsr.status1)
816   - ));
817   - devItem.put("status2", Map.of(
818   - "durationFormatted", formatDuration(dsr.status2),
819   - "durationSeconds", dsr.status2,
820   - "durationHours", formatDurationToHours(dsr.status2)
821   - ));
822   - devItem.put("status3", Map.of(
823   - "durationFormatted", formatDuration(dsr.status3),
824   - "durationSeconds", dsr.status3,
825   - "durationHours", formatDurationToHours(dsr.status3)
826   - ));
  556 + devItem.put("status0", Map.of("durationFormatted", formatDuration(dsr.status0), "durationSeconds", dsr.status0, "durationHours", formatDurationToHours(dsr.status0)));
  557 + devItem.put("status1", Map.of("durationFormatted", formatDuration(dsr.status1), "durationSeconds", dsr.status1, "durationHours", formatDurationToHours(dsr.status1)));
  558 + devItem.put("status2", Map.of("durationFormatted", formatDuration(dsr.status2), "durationSeconds", dsr.status2, "durationHours", formatDurationToHours(dsr.status2)));
  559 + devItem.put("status3", Map.of("durationFormatted", formatDuration(dsr.status3), "durationSeconds", dsr.status3, "durationHours", formatDurationToHours(dsr.status3)));
827 560 devItem.put("totalDurationFormatted", formatDuration(dsr.totalDuration()));
828 561 devItem.put("totalDurationSeconds", dsr.totalDuration());
829 562 devItem.put("totalDurationHours", formatDurationToHours(dsr.totalDuration()));
830 563 devItem.put("totalKwh", Math.round(dsr.totalKwh * 100.0) / 100.0);
831 564 devItem.put("availabilityRate", String.format("%.2f%%", devRate));
832   - devItem.put("availabilityRateValue", devRate);
833   - // 异常时长用于排序(内部字段,后面移除)
834 565 devItem.put("_abnormalDur", dsr.abnormalDuration());
835   -
836 566 deviceList.add(devItem);
837 567 }
838   -
839   - // ---- ① 总稼动率 ----
840 568 long totalActive = totalS1 + totalS2 + totalS3;
841   - double overallAvailabilityRate = totalActive > 0
842   - ? Math.round(totalS3 * 10000.0 / totalActive) / 100.0 : 0.0;
843   -
844   - // ---- ② 当前设备运行状态(从energy表查) ----
845   - Map<String, Integer> currentStatus = queryCurrentRunStatus();
846   -
847   - // ---- ③ 异常机台排名(按 停机+待机 降序) ----
848   - deviceList.sort((a, b) -> Long.compare(
849   - ((Number) b.get("_abnormalDur")).longValue(),
850   - ((Number) a.get("_abnormalDur")).longValue()
851   - ));
852   - // 移除辅助字段
853   - for (Map<String, Object> d : deviceList) {
854   - d.remove("_abnormalDur");
855   - }
856   -
857   - return Map.of(
858   - "code", 200, "msg", "请求成功", "data",
859   - Map.of(
860   - "summary", Map.of(
861   - "availabilityRate", String.format("%.2f%%", overallAvailabilityRate),
862   - "availabilityRateValue", overallAvailabilityRate,
863   - "totalDevices", deviceNameMap.size(),
864   - "totalKwh", Math.round(totalKwhSum * 100.0) / 100.0,
865   - "totalStatusDuration", Map.of(
866   - "status0", Map.of(
867   - "durationFormatted", formatDuration(totalS0),
868   - "durationSeconds", totalS0,
869   - "durationHours", formatDurationToHours(totalS0)
870   - ),
871   - "status1", Map.of(
872   - "durationFormatted", formatDuration(totalS1),
873   - "durationSeconds", totalS1,
874   - "durationHours", formatDurationToHours(totalS1)
875   - ),
876   - "status2", Map.of(
877   - "durationFormatted", formatDuration(totalS2),
878   - "durationSeconds", totalS2,
879   - "durationHours", formatDurationToHours(totalS2)
880   - ),
881   - "status3", Map.of(
882   - "durationFormatted", formatDuration(totalS3),
883   - "durationSeconds", totalS3,
884   - "durationHours", formatDurationToHours(totalS3)
885   - )
886   - )
887   - ),
888   - "currentStatus", currentStatus,
889   - "abnormalRanking", deviceList,
890   - "deviceList", deviceList
891   - )
892   - );
  569 + double overallAvailabilityRate = totalActive > 0 ? Math.round(totalS3 * 10000.0 / totalActive) / 100.0 : 0.0;
  570 + Map<String, Integer> currentStatus = queryCurrentRunStatus(corpCode);
  571 + deviceList.sort((a, b) -> Long.compare(((Number) b.get("_abnormalDur")).longValue(), ((Number) a.get("_abnormalDur")).longValue()));
  572 + for (Map<String, Object> d : deviceList) d.remove("_abnormalDur");
  573 + return Map.of("code", 200, "msg", "请求成功", "data", Map.of(
  574 + "summary", Map.of("availabilityRate", String.format("%.2f%%", overallAvailabilityRate), "availabilityRateValue", overallAvailabilityRate,
  575 + "totalDevices", deviceNameMap.size(), "totalKwh", Math.round(totalKwhSum * 100.0) / 100.0,
  576 + "totalStatusDuration", Map.of("status0", Map.of("durationFormatted", formatDuration(totalS0), "durationSeconds", totalS0, "durationHours", formatDurationToHours(totalS0)),
  577 + "status1", Map.of("durationFormatted", formatDuration(totalS1), "durationSeconds", totalS1, "durationHours", formatDurationToHours(totalS1)),
  578 + "status2", Map.of("durationFormatted", formatDuration(totalS2), "durationSeconds", totalS2, "durationHours", formatDurationToHours(totalS2)),
  579 + "status3", Map.of("durationFormatted", formatDuration(totalS3), "durationSeconds", totalS3, "durationHours", formatDurationToHours(totalS3))),
  580 + "currentStatus", currentStatus, "abnormalRanking", deviceList, "deviceList", deviceList)));
893 581 }
894 582
895   - /**
896   - * 查询当前设备运行状态分布(从energy表) runStatus: 0-离线, 1-停机, 2-待机, 3-运行
897   - */
898   - private Map<String, Integer> queryCurrentRunStatus() {
899   - String sql = "SELECT runStatus, COUNT(*) as cnt FROM " + energyTableName +
900   - " WHERE corp_code = ? GROUP BY runStatus";
901   - List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql, energyCorpCode);
902   -
  583 + private Map<String, Integer> queryCurrentRunStatus(String corpCode) {
  584 + String energyTableName = corpConfigService.getEnergyTableName(corpCode);
  585 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  586 + String sql = "SELECT runStatus, COUNT(*) as cnt FROM " + energyTableName + " WHERE corp_code = ? GROUP BY runStatus";
  587 + List<Map<String, Object>> rows = jt.queryForList(sql, corpCode);
903 588 int s0 = 0, s1 = 0, s2 = 0, s3 = 0;
904 589 for (Map<String, Object> row : rows) {
905 590 String key = String.valueOf(row.get("runStatus"));
... ... @@ -914,46 +599,22 @@ public class EnergySearchService {
914 599 return Map.of("0", s0, "1", s1, "2", s2, "3", s3);
915 600 }
916 601
917   - // ==================== eq_kwh 多设备时/日/月查询 ====================
918   -
919   - /**
920   - * 查询 eq_kwh 表的多设备能耗数据(按 时/日/月 聚合)
921   - * <p>
922   - * type=1 (时): 只需传 startDate,返回该日所有设备的原始用电量明细(kwhList)
923   - * type=2 (日):
924   - * - 若传了 startDate+endDate: 按此范围每日统计每台设备能耗
925   - * - 若只传 startDate: 自动补全为当月的第1天~最后一天
926   - * type=3 (月): 自动取本年1月~当前月,按月统计每台设备能耗
927   - *
928   - * @param type 查询类型: 1-时, 2-日, 3-月
929   - * @param startDate 开始日期 yyyy-MM-dd (type=1,2必填; type=3可选)
930   - * @param endDate 结束日期 yyyy-MM-dd (type=2可选)
931   - */
932   - public Map<String, Object> queryEqKwhByType(String type, String startDate, String endDate) {
933   - log.info("========== [eq_kwh多设备查询] type={}, startDate={}, endDate={} ==========", type, startDate, endDate);
  602 + // ==================== eq_kwh 多设备查询 ====================
934 603
  604 + public Map<String, Object> queryEqKwhByType(String corpCode, String type, String startDate, String endDate) {
  605 + log.info("========== [eq_kwh多设备查询] corpCode={}, type={}, startDate={}, endDate={} ==========", corpCode, type, startDate, endDate);
935 606 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
936 607 List<String> dateList = new ArrayList<>();
937   -
938   - // 根据type构建日期列表 + 实际起止日期
939 608 String actualStart;
940 609 String actualEnd;
941   -
942 610 if ("1".equals(type)) {
943   - // type=1: 单日查询,直接返回原始kwh数据
944   - if (!StringUtils.hasText(startDate)) {
945   - return Map.of("code", 400, "msg", "参数错误: type=1时startDate必填");
946   - }
  611 + if (!StringUtils.hasText(startDate)) return Map.of("code", 400, "msg", "参数错误: type=1时startDate必填");
947 612 dateList.add(startDate);
948 613 actualStart = startDate;
949 614 actualEnd = startDate;
950 615 } else if ("2".equals(type)) {
951   - // type=2: 按日统计
952   - if (!StringUtils.hasText(startDate)) {
953   - return Map.of("code", 400, "msg", "参数错误: type=2时startDate必填");
954   - }
  616 + if (!StringUtils.hasText(startDate)) return Map.of("code", 400, "msg", "参数错误: type=2时startDate必填");
955 617 if (StringUtils.hasText(endDate)) {
956   - // 有结束日期,直接用传入范围
957 618 try {
958 619 Date start = sdf.parse(startDate);
959 620 Date end = sdf.parse(endDate);
... ... @@ -969,19 +630,17 @@ public class EnergySearchService {
969 630 return Map.of("code", 400, "msg", "日期格式错误: " + startDate + " ~ " + endDate);
970 631 }
971 632 } else {
972   - // 只有开始日期 -> 自动取所在月的1号~最后一天
973 633 try {
974 634 Date startD = sdf.parse(startDate);
975 635 Calendar cal = Calendar.getInstance();
976 636 cal.setTime(startD);
977   - cal.set(Calendar.DAY_OF_MONTH, 1); // 月初
  637 + cal.set(Calendar.DAY_OF_MONTH, 1);
978 638 actualStart = sdf.format(cal.getTime());
979   - cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); // 月末
  639 + cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
980 640 actualEnd = sdf.format(cal.getTime());
981   -
982 641 Calendar cur = Calendar.getInstance();
983 642 cur.setTime(sdf.parse(actualStart));
984   - while (!cur.after(cal)) { // cur <= actualEnd
  643 + while (!cur.after(cal)) {
985 644 dateList.add(sdf.format(cur.getTime()));
986 645 cur.add(Calendar.DAY_OF_MONTH, 1);
987 646 }
... ... @@ -990,9 +649,7 @@ public class EnergySearchService {
990 649 }
991 650 }
992 651 } else if ("3".equals(type)) {
993   - // type=3: 按月统计
994 652 if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) {
995   - // 有起止日期:按传入范围按月聚合
996 653 try {
997 654 Date start = sdf.parse(startDate);
998 655 Date end = sdf.parse(endDate);
... ... @@ -1008,14 +665,12 @@ public class EnergySearchService {
1008 665 return Map.of("code", 400, "msg", "日期格式错误: " + startDate + " ~ " + endDate);
1009 666 }
1010 667 } else {
1011   - // 无日期:默认本年1月1日 ~ 今天
1012 668 Calendar now = Calendar.getInstance();
1013 669 int year = now.get(Calendar.YEAR);
1014 670 Calendar startCal = Calendar.getInstance();
1015 671 startCal.set(year, Calendar.JANUARY, 1);
1016 672 actualStart = sdf.format(startCal.getTime());
1017 673 actualEnd = sdf.format(now.getTime());
1018   -
1019 674 Calendar cur = Calendar.getInstance();
1020 675 cur.setTime(startCal.getTime());
1021 676 while (!cur.getTime().after(now.getTime())) {
... ... @@ -1026,28 +681,13 @@ public class EnergySearchService {
1026 681 } else {
1027 682 return Map.of("code", 400, "msg", "参数错误: type只支持1/2/3");
1028 683 }
1029   -
1030 684 log.info("实际日期范围: {} ~ {}, 共{}天", actualStart, actualEnd, dateList.size());
1031   -
1032   - // 获取所有设备列表及名称
1033   - Map<String, String> deviceNameMap = queryAllEnergyDeviceNames();
1034   -
1035   - // 从 eq_kwh 批量查询所有记录
1036   - List<Map<String, Object>> allRawData = queryEqKwhBatch(dateList);
1037   - log.info("eq_kwh 批量查询返回 {} 条原始记录", allRawData.size());
1038   -
1039   - // 构建结果
1040   - return buildEqKwhByTypeResult(type, dateList, allRawData, deviceNameMap, actualStart, actualEnd);
  685 + Map<String, String> deviceNameMap = queryAllEnergyDeviceNames(corpCode);
  686 + List<Map<String, Object>> allRawData = queryEqKwhBatch(corpCode, dateList);
  687 + return buildEqKwhByTypeResult(corpCode, type, dateList, allRawData, deviceNameMap, actualStart, actualEnd);
1041 688 }
1042 689
1043   - /**
1044   - * 构建 type=1/2/3 的多设备查询结果
1045   - */
1046   - private Map<String, Object> buildEqKwhByTypeResult(String type, List<String> dateList,
1047   - List<Map<String, Object>> allRawData,
1048   - Map<String, String> deviceNameMap,
1049   - String actualStart, String actualEnd) {
1050   - // 按 dtuSn+日期 建立快速查找Map: key="dtuSn|yyyy-MM-dd" -> JSONArray(kwh原始数据)
  690 + private Map<String, Object> buildEqKwhByTypeResult(String corpCode, String type, List<String> dateList, List<Map<String, Object>> allRawData, Map<String, String> deviceNameMap, String actualStart, String actualEnd) {
1051 691 Map<String, Object> rawDataMap = new LinkedHashMap<>();
1052 692 for (Map<String, Object> row : allRawData) {
1053 693 String sn = (String) row.get("dtuSn");
... ... @@ -1055,56 +695,32 @@ public class EnergySearchService {
1055 695 String desc = row.get("description") != null ? String.valueOf(row.get("description")) : "";
1056 696 if (StringUtils.hasText(desc)) {
1057 697 try {
1058   - // use_date 可能是 "2026-05-21T00:00:00" 或 "2026-05-21 00:00:00",统一截取前10位作为纯日期key
1059 698 String dateKey = ud.length() > 10 ? ud.substring(0, 10) : ud;
1060 699 rawDataMap.put(sn + "|" + dateKey, JSON.parseArray(desc));
1061 700 } catch (Exception ignored) {
1062 701 }
1063 702 }
1064 703 }
1065   -
1066   - // ---- type=1: 返回所有设备的原始kwh明细 ----
1067   - if ("1".equals(type)) {
1068   - return buildType1Result(dateList.get(0), rawDataMap, deviceNameMap);
1069   - }
1070   -
1071   - // ---- type=2: 按日统计 ----
1072   - if ("2".equals(type)) {
1073   - return buildType2Result(dateList, rawDataMap, deviceNameMap, actualStart, actualEnd);
1074   - }
1075   -
1076   - // ---- type=3: 按月统计 ----
1077   - if ("3".equals(type)) {
1078   - return buildType3Result(dateList, rawDataMap, deviceNameMap, actualStart, actualEnd);
1079   - }
1080   -
  704 + if ("1".equals(type)) return buildType1Result(dateList.get(0), rawDataMap, deviceNameMap);
  705 + if ("2".equals(type)) return buildType2Result(dateList, rawDataMap, deviceNameMap, actualStart, actualEnd);
  706 + if ("3".equals(type)) return buildType3Result(dateList, rawDataMap, deviceNameMap, actualStart, actualEnd);
1081 707 return Map.of("code", 500, "msg", "未知type");
1082 708 }
1083 709
1084   - /**
1085   - * type=1 (时): 单日查询 - 返回每个设备的 kwh 原始数据 + 当日汇总
1086   - */
1087   - private Map<String, Object> buildType1Result(String dateStr, Map<String, Object> rawDataMap,
1088   - Map<String, String> deviceNameMap) {
  710 + private Map<String, Object> buildType1Result(String dateStr, Map<String, Object> rawDataMap, Map<String, String> deviceNameMap) {
1089 711 List<Map<String, Object>> list = new ArrayList<>();
1090 712 BigDecimal totalKwhAll = BigDecimal.ZERO;
1091   -
1092 713 for (String sn : deviceNameMap.keySet()) {
1093 714 String mapKey = sn + "|" + dateStr;
1094 715 Object rawObj = rawDataMap.getOrDefault(mapKey, Collections.emptyList());
1095   - @SuppressWarnings("unchecked")
1096   - JSONArray dataArray = rawObj instanceof JSONArray ? (JSONArray) rawObj : new JSONArray();
1097   -
1098   - // 统计当日总用电量
  716 + @SuppressWarnings("unchecked") JSONArray dataArray = rawObj instanceof JSONArray ? (JSONArray) rawObj : new JSONArray();
1099 717 BigDecimal dayKwh = BigDecimal.ZERO;
1100 718 for (int i = 0; i < dataArray.size(); i++) {
1101 719 JSONObject item = dataArray.getJSONObject(i);
1102   - if (item != null && item.getDouble("value") != null) {
  720 + if (item != null && item.getDouble("value") != null)
1103 721 dayKwh = dayKwh.add(BigDecimal.valueOf(item.getDouble("value")));
1104   - }
1105 722 }
1106 723 totalKwhAll = totalKwhAll.add(dayKwh);
1107   -
1108 724 Map<String, Object> item = new LinkedHashMap<>();
1109 725 item.put("dtuSn", sn);
1110 726 item.put("deviceName", deviceNameMap.getOrDefault(sn, ""));
... ... @@ -1113,67 +729,44 @@ public class EnergySearchService {
1113 729 item.put("totalKwh", dayKwh.setScale(2, RoundingMode.HALF_UP));
1114 730 list.add(item);
1115 731 }
1116   -
1117   - return Map.of(
1118   - "code", 200, "msg", "请求成功",
1119   - "type", "1",
1120   - "date", dateStr,
1121   - "totalDevices", deviceNameMap.size(),
1122   - "totalKwh", totalKwhAll.setScale(2, RoundingMode.HALF_UP),
1123   - "list", list
1124   - );
  732 + return Map.of("code", 200, "msg", "请求成功", "type", "1", "date", dateStr, "totalDevices", deviceNameMap.size(), "totalKwh", totalKwhAll.setScale(2, RoundingMode.HALF_UP), "list", list);
1125 733 }
1126 734
1127   - /**
1128   - * type=2 (日): 按设备统计 - 每个设备一行,包含该设备每天的能耗明细
1129   - */
1130   - private Map<String, Object> buildType2Result(List<String> dateList, Map<String, Object> rawDataMap,
1131   - Map<String, String> deviceNameMap,
1132   - String actualStart, String actualEnd) {
  735 + private Map<String, Object> buildType2Result(List<String> dateList, Map<String, Object> rawDataMap, Map<String, String> deviceNameMap, String actualStart, String actualEnd) {
1133 736 BigDecimal grandTotalKwh = BigDecimal.ZERO;
1134 737 List<Map<String, Object>> deviceList = new ArrayList<>(deviceNameMap.size());
1135   -
1136 738 for (String sn : deviceNameMap.keySet()) {
1137 739 Map<String, Object> devItem = new LinkedHashMap<>();
1138 740 devItem.put("dtuSn", sn);
1139 741 devItem.put("deviceName", deviceNameMap.getOrDefault(sn, ""));
1140   -
1141   - // 该设备的每日数据列表
1142 742 List<Map<String, Object>> dailyDataList = new ArrayList<>(dateList.size());
1143 743 BigDecimal devTotalKwh = BigDecimal.ZERO;
1144 744 long devTotalDur = 0;
1145 745 Map<Integer, Long> devStatusAll = initStatusMap();
1146   -
1147 746 for (String d : dateList) {
1148 747 String mapKey = sn + "|" + d;
1149 748 Object rawObj = rawDataMap.getOrDefault(mapKey, Collections.emptyList());
1150   - @SuppressWarnings("unchecked")
1151   - JSONArray dataArray = rawObj instanceof JSONArray ? (JSONArray) rawObj : new JSONArray();
1152   -
  749 + @SuppressWarnings("unchecked") JSONArray dataArray = rawObj instanceof JSONArray ? (JSONArray) rawObj : new JSONArray();
1153 750 BigDecimal dayKwh = BigDecimal.ZERO;
1154 751 long dayDur = 0;
1155 752 Map<Integer, Long> dayStatus = initStatusMap();
1156   -
1157 753 for (int i = 0; i < dataArray.size(); i++) {
1158 754 JSONObject item = dataArray.getJSONObject(i);
1159 755 if (item == null) continue;
1160   -
1161 756 Double value = item.getDouble("value");
1162 757 if (value != null) dayKwh = dayKwh.add(BigDecimal.valueOf(value));
1163   -
1164 758 for (int sk = 0; sk <= 3; sk++) {
1165 759 Long dur = item.getLong(String.valueOf(sk));
1166 760 if (dur != null && dur > 0) {
1167 761 dayDur += dur;
1168   - dayStatus.merge(sk, dur, Long::sum);
1169 762 devTotalDur += dur;
  763 + dayStatus.merge(sk, dur, Long::sum);
1170 764 devStatusAll.merge(sk, dur, Long::sum);
1171 765 }
1172 766 }
1173 767 }
1174   -
1175 768 devTotalKwh = devTotalKwh.add(dayKwh);
1176   -
  769 + grandTotalKwh = grandTotalKwh.add(devTotalKwh);
1177 770 Map<String, Object> dayEntry = new LinkedHashMap<>();
1178 771 dayEntry.put("date", d);
1179 772 dayEntry.put("totalKwh", dayKwh.setScale(2, RoundingMode.HALF_UP));
... ... @@ -1182,50 +775,27 @@ public class EnergySearchService {
1182 775 dayEntry.put("statusStats", buildStatusStats(dayStatus, dayDur));
1183 776 dailyDataList.add(dayEntry);
1184 777 }
1185   -
1186   - grandTotalKwh = grandTotalKwh.add(devTotalKwh);
1187   -
1188 778 devItem.put("totalKwh", devTotalKwh.setScale(2, RoundingMode.HALF_UP));
1189 779 devItem.put("totalDurationFormatted", formatDuration(devTotalDur));
1190 780 devItem.put("totalDurationSeconds", devTotalDur);
1191 781 devItem.put("statusStats", buildStatusStats(devStatusAll, devTotalDur));
1192 782 devItem.put("dailyData", dailyDataList);
1193   -
1194 783 deviceList.add(devItem);
1195 784 }
1196   -
1197   - return Map.of(
1198   - "code", 200, "msg", "请求成功",
1199   - "type", "2",
1200   - "actualStartDate", actualStart,
1201   - "actualEndDate", actualEnd,
1202   - "totalDays", dateList.size(),
1203   - "totalDevices", deviceList.size(),
1204   - "grandTotalKwh", grandTotalKwh.setScale(2, RoundingMode.HALF_UP),
1205   - "list", deviceList
1206   - );
  785 + return Map.of("code", 200, "msg", "请求成功", "type", "2", "actualStartDate", actualStart, "actualEndDate", actualEnd,
  786 + "totalDays", dateList.size(), "totalDevices", deviceList.size(), "grandTotalKwh", grandTotalKwh.setScale(2, RoundingMode.HALF_UP), "list", deviceList);
1207 787 }
1208 788
1209   - /**
1210   - * type=3 (月): 按设备统计 - 每个设备一行,包含该设备每月的能耗明细
1211   - */
1212   - private Map<String, Object> buildType3Result(List<String> dateList, Map<String, Object> rawDataMap,
1213   - Map<String, String> deviceNameMap,
1214   - String actualStart, String actualEnd) {
  789 + private Map<String, Object> buildType3Result(List<String> dateList, Map<String, Object> rawDataMap, Map<String, String> deviceNameMap, String actualStart, String actualEnd) {
1215 790 BigDecimal grandTotalKwh = BigDecimal.ZERO;
1216 791 List<Map<String, Object>> deviceList = new ArrayList<>(deviceNameMap.size());
1217   -
1218 792 for (String sn : deviceNameMap.keySet()) {
1219 793 Map<String, Object> devItem = new LinkedHashMap<>();
1220 794 devItem.put("dtuSn", sn);
1221 795 devItem.put("deviceName", deviceNameMap.getOrDefault(sn, ""));
1222   -
1223   - // 该设备的按月聚合数据
1224 796 Map<String, Map<String, Object>> monthAgg = new LinkedHashMap<>();
1225   -
1226 797 for (String d : dateList) {
1227 798 String monthKey = d.substring(0, 7);
1228   -
1229 799 monthAgg.computeIfAbsent(monthKey, k -> {
1230 800 Map<String, Object> m = new LinkedHashMap<>();
1231 801 m.put("month", k);
... ... @@ -1235,18 +805,15 @@ public class EnergySearchService {
1235 805 m.put("statusDurationMap", initStatusMap());
1236 806 return m;
1237 807 });
1238   -
1239 808 String mapKey = sn + "|" + d;
1240 809 Object rawObj = rawDataMap.getOrDefault(mapKey, Collections.emptyList());
1241 810 @SuppressWarnings("unchecked")
1242 811 JSONArray dataArray = rawObj instanceof JSONArray ? (JSONArray) rawObj : new JSONArray();
1243 812
1244 813 Map<String, Object> mEntry = monthAgg.get(monthKey);
1245   -
1246 814 for (int i = 0; i < dataArray.size(); i++) {
1247 815 JSONObject item = dataArray.getJSONObject(i);
1248 816 if (item == null) continue;
1249   -
1250 817 Double value = item.getDouble("value");
1251 818 if (value != null) {
1252 819 BigDecimal valBd = BigDecimal.valueOf(value);
... ... @@ -1270,21 +837,17 @@ public class EnergySearchService {
1270 837 BigDecimal devTotalKwh = BigDecimal.ZERO;
1271 838 long devTotalDur = 0;
1272 839 Map<Integer, Long> devStatusAll = initStatusMap();
1273   -
1274 840 for (Map.Entry<String, Map<String, Object>> me : monthAgg.entrySet()) {
1275 841 Map<String, Object> m = me.getValue();
1276 842 long mDur = (Long) m.get("totalDurationSeconds");
1277 843 BigDecimal mKwh = (BigDecimal) m.get("totalKwh");
1278   - @SuppressWarnings("unchecked")
1279   - Map<Integer, Long> mStatus = (Map<Integer, Long>) m.get("statusDurationMap");
1280   -
  844 + @SuppressWarnings("unchecked") Map<Integer, Long> mStatus = (Map<Integer, Long>) m.get("statusDurationMap");
1281 845 devTotalKwh = devTotalKwh.add(mKwh);
1282 846 devTotalDur += mDur;
1283 847 for (int sk = 0; sk <= 3; sk++) {
1284 848 long sd = mStatus.getOrDefault(sk, 0L);
1285 849 if (sd > 0) devStatusAll.merge(sk, sd, Long::sum);
1286 850 }
1287   -
1288 851 Map<String, Object> monthEntry = new LinkedHashMap<>();
1289 852 monthEntry.put("month", m.get("month"));
1290 853 monthEntry.put("label", m.get("label"));
... ... @@ -1294,71 +857,37 @@ public class EnergySearchService {
1294 857 monthEntry.put("statusStats", buildStatusStats(mStatus, mDur));
1295 858 monthlyDataList.add(monthEntry);
1296 859 }
1297   -
1298 860 grandTotalKwh = grandTotalKwh.add(devTotalKwh);
1299   -
1300 861 devItem.put("totalKwh", devTotalKwh.setScale(2, RoundingMode.HALF_UP));
1301 862 devItem.put("totalDurationFormatted", formatDuration(devTotalDur));
1302 863 devItem.put("totalDurationSeconds", devTotalDur);
1303 864 devItem.put("statusStats", buildStatusStats(devStatusAll, devTotalDur));
1304 865 devItem.put("monthlyData", monthlyDataList);
1305   -
1306 866 deviceList.add(devItem);
1307 867 }
1308   -
1309   - return Map.of(
1310   - "code", 200, "msg", "请求成功",
1311   - "type", "3",
1312   - "year", actualStart.substring(0, 4),
1313   - "actualStartDate", actualStart,
1314   - "actualEndDate", actualEnd,
1315   - "totalMonths", dateList.isEmpty() ? 0 :
1316   - (dateList.stream().map(d -> d.substring(0, 7)).distinct().toList()).size(),
1317   - "totalDevices", deviceList.size(),
1318   - "grandTotalKwh", grandTotalKwh.setScale(2, RoundingMode.HALF_UP),
1319   - "list", deviceList
1320   - );
  868 + return Map.of("code", 200, "msg", "请求成功", "type", "3", "year", actualStart.substring(0, 4), "actualStartDate", actualStart,
  869 + "actualEndDate", actualEnd, "totalMonths", dateList.isEmpty() ? 0 : (dateList.stream().map(d -> d.substring(0, 7)).distinct().toList()).size(),
  870 + "totalDevices", deviceList.size(), "grandTotalKwh", grandTotalKwh.setScale(2, RoundingMode.HALF_UP), "list", deviceList);
1321 871 }
1322 872
1323   - /** 调试接口: 查看eq_kwh表中的数据概况 */
1324   - public Map<String, Object> debugEqKwhInfo() {
1325   - // 总记录数
1326   - Long totalCount = jdbcTemplate.queryForObject(
1327   - "SELECT COUNT(*) FROM " + eqKwhTableName + " WHERE corp_code = ?", Long.class, energyCorpCode);
1328   -
1329   - // 按日期分组统计
1330   - List<Map<String, Object>> dateStats = jdbcTemplate.queryForList(
1331   - "SELECT use_date, COUNT(*) as cnt FROM " + eqKwhTableName +
1332   - " WHERE corp_code = ? GROUP BY use_date ORDER BY use_date DESC LIMIT 20",
1333   - energyCorpCode);
1334   -
1335   - // 按设备分组统计
1336   - List<Map<String, Object>> deviceStats = jdbcTemplate.queryForList(
1337   - "SELECT dtuSn, COUNT(*) as cnt FROM " + eqKwhTableName +
1338   - " WHERE corp_code = ? GROUP BY dtuSn ORDER BY dtuSn",
1339   - energyCorpCode);
1340   -
1341   - // 最新一条记录的 description 预览
  873 + /**
  874 + * 调试接口
  875 + */
  876 + public Map<String, Object> debugEqKwhInfo(String corpCode) {
  877 + String eqKwhTableName = corpConfigService.getEqKwhTableName(corpCode);
  878 + String energyTableName = corpConfigService.getEnergyTableName(corpCode);
  879 + JdbcTemplate jt = corpConfigService.getJdbcTemplate(corpCode);
  880 + Long totalCount = jt.queryForObject("SELECT COUNT(*) FROM " + eqKwhTableName + " WHERE corp_code = ?", Long.class, corpCode);
  881 + List<Map<String, Object>> dateStats = jt.queryForList("SELECT use_date, COUNT(*) as cnt FROM " + eqKwhTableName + " WHERE corp_code = ? GROUP BY use_date ORDER BY use_date DESC LIMIT 20", corpCode);
  882 + List<Map<String, Object>> deviceStats = jt.queryForList("SELECT dtuSn, COUNT(*) as cnt FROM " + eqKwhTableName + " WHERE corp_code = ? GROUP BY dtuSn ORDER BY dtuSn", corpCode);
1342 883 Map<String, Object> lastRecord = null;
1343 884 try {
1344   - String sql = "SELECT dtuSn, use_date, LEFT(description, 500) AS desc_preview, LENGTH(description) as desc_len " +
1345   - "FROM " + eqKwhTableName + " WHERE corp_code = ? ORDER BY use_date DESC LIMIT 1";
1346   - lastRecord = jdbcTemplate.queryForMap(sql, energyCorpCode);
1347   - } catch (Exception ignored) {}
1348   -
1349   - // energy 表设备数
1350   - Long deviceCount = jdbcTemplate.queryForObject(
1351   - "SELECT COUNT(*) FROM " + energyTableName + " WHERE corp_code = ?", Long.class, energyCorpCode);
1352   -
1353   - return Map.of(
1354   - "code", 200, "msg", "请求成功",
1355   - "tableName", eqKwhTableName,
1356   - "corpCode", energyCorpCode,
1357   - "totalRecords", totalCount != null ? totalCount : 0,
1358   - "deviceCountInEnergyTable", deviceCount != null ? deviceCount : 0,
1359   - "latestDateRecords", dateStats,
1360   - "perDeviceRecordCount", deviceStats,
1361   - "lastRecordPreview", lastRecord
1362   - );
  885 + String sql = "SELECT dtuSn, use_date, LEFT(description, 500) AS desc_preview, LENGTH(description) as desc_len FROM " + eqKwhTableName + " WHERE corp_code = ? ORDER BY use_date DESC LIMIT 1";
  886 + lastRecord = jt.queryForMap(sql, corpCode);
  887 + } catch (Exception ignored) {
  888 + }
  889 + Long deviceCount = jt.queryForObject("SELECT COUNT(*) FROM " + energyTableName + " WHERE corp_code = ?", Long.class, corpCode);
  890 + return Map.of("code", 200, "msg", "请求成功", "tableName", eqKwhTableName, "corpCode", corpCode, "totalRecords", totalCount != null ? totalCount : 0,
  891 + "deviceCountInEnergyTable", deviceCount != null ? deviceCount : 0, "latestDateRecords", dateStats, "perDeviceRecordCount", deviceStats, "lastRecordPreview", lastRecord);
1363 892 }
1364 893 }
... ...
... ... @@ -89,3 +89,8 @@ energy:
89 89 eRunDtlTableName: "t_auto_ymk_iot_e_run_dtl"
90 90 runStatus:
91 91 url: "https://iotgc.cniot.vip/api/energy/runStatus"
  92 +
  93 +# 公司配置表(主数据库中存储各公司的数据源和IoT配置)
  94 +corp:
  95 + config:
  96 + tableName: "t_auto_ymk_ccfg_corp_conf"
... ...