package com.doumee.service.business.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.doumee.core.conditoner.ConditionerUtil; import com.doumee.core.conditoner.model.ConditionerConstant; import com.doumee.core.conditoner.model.request.*; import com.doumee.core.conditoner.model.response.*; import com.doumee.core.constants.ResponseStatus; import com.doumee.core.exception.BusinessException; import com.doumee.core.model.LoginUserInfo; import com.doumee.core.utils.Constants; import com.doumee.dao.business.*; import com.doumee.dao.business.dto.*; import com.doumee.dao.business.model.*; import com.doumee.service.business.ConditionerBizService; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @Service public class ConditionerBizServiceImpl implements ConditionerBizService { public static final int ACTION_PWR = 1; public static final int ACTION_MODE = 2; public static final int ACTION_FAN = 3; public static final int ACTION_TEMP = 4; public static final int ACTION_LOCK = 5; public static final int ACTION_QUERY_DL = 6; public static final int ACTION_QUERY_POWER = 7; private static final String PID_DLJ = "dlj"; private static final String LOCK_SET_TYPE = "lock_many"; private static final long ADJUST_OPERATE_INTERVAL_MS = 2000L; private static volatile boolean syncing = false; /** 同一内机:温度/风速/模式操作最小间隔(毫秒) */ private static final Map LAST_ADJUST_OPERATE_MS = new ConcurrentHashMap<>(); @Autowired private YwConditionerGatewayMapper gatewayMapper; @Autowired private YwConditionerGatewayLogMapper gatewayLogMapper; @Autowired private YwConditionerMeterMapper meterMapper; @Autowired private YwConditionerBillingMapper billingMapper; @Autowired private YwConditionerMapper conditionerMapper; @Autowired private YwConditionerActionsMapper actionsMapper; @Autowired private YwConditionerUsageMapper usageMapper; @Override public void ensureLogin() { if (StringUtils.isNotBlank(ConditionerConstant.kt_token)) { return; } ConditionerBaseResponse resp = ConditionerUtil.login(); if (resp == null || !resp.isSuccess()) { throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), resp != null ? resp.getMessage() : "智精灵平台登录失败"); } } @Override @Transactional(rollbackFor = Exception.class) public String syncGateways(String source) { ensureLogin(); ConditionerSessionRequest req = new ConditionerSessionRequest(); ConditionerBaseResponse> resp = ConditionerUtil.getWg(req); if (resp == null || !resp.isSuccess()) { throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "同步网关失败")); } List list = resp.getData() != null ? resp.getData() : Collections.emptyList(); Date now = new Date(); int add = 0, upd = 0, logs = 0; for (GatewayInfoResponse item : list) { if (StringUtils.isBlank(item.getWg_mac())) { continue; } String newStatus = ConditionerConstant.normalizeGatewayOnline(item.getWg_status()); YwConditionerGateway local = gatewayMapper.selectOne(new QueryWrapper().lambda() .eq(YwConditionerGateway::getIsdeleted, Constants.ZERO) .eq(YwConditionerGateway::getWgMac, item.getWg_mac()) .last(" limit 1 ")); if (local == null) { local = new YwConditionerGateway(); local.setCreator(0); local.setCreateDate(now); local.setIsdeleted(Constants.ZERO); local.setPlatformWgId(item.getWg_id()); local.setWgMac(item.getWg_mac()); local.setWgBz(item.getWg_bz()); local.setOnlineStatus(newStatus); local.setLastSyncDate(now); gatewayMapper.insert(local); add++; } else { String oldStatus = local.getOnlineStatus(); local.setPlatformWgId(item.getWg_id()); local.setWgBz(item.getWg_bz()); local.setOnlineStatus(newStatus); local.setLastSyncDate(now); local.setEditDate(now); gatewayMapper.updateById(local); upd++; if (!Objects.equals(oldStatus, newStatus)) { writeGatewayLog(local.getId(), item.getWg_mac(), oldStatus, newStatus, source, now); logs++; } } } return "同步网关:新增【" + add + "】更新【" + upd + "】状态变更【" + logs + "】"; } @Override public String syncGatewayStatus() { return syncGateways("schedule"); } @Override @Transactional(rollbackFor = Exception.class) public String syncMeters() { ensureLogin(); MeterDbManageRequest req = new MeterDbManageRequest(); ConditionerBaseResponse> resp = ConditionerUtil.getDb(req); if (resp == null || !resp.isSuccess()) { throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "同步电表失败")); } List list = resp.getData() != null ? resp.getData() : Collections.emptyList(); Date now = new Date(); int add = 0, upd = 0; for (MeterDbInfoResponse item : list) { if (item.getDb_id() == null) { continue; } YwConditionerMeter local = meterMapper.selectOne(new QueryWrapper().lambda() .eq(YwConditionerMeter::getIsdeleted, Constants.ZERO) .eq(YwConditionerMeter::getPlatformDbId, item.getDb_id()) .last(" limit 1 ")); if (local == null) { local = new YwConditionerMeter(); local.setCreator(0); local.setCreateDate(now); local.setIsdeleted(Constants.ZERO); add++; } else { upd++; } fillMeter(local, item, now); if (local.getId() == null) { meterMapper.insert(local); } else { meterMapper.updateById(local); } } return "同步电表:新增【" + add + "】更新【" + upd + "】"; } @Override @Transactional(rollbackFor = Exception.class) public String syncBilling() { ensureLogin(); ConditionerSessionRequest req = new ConditionerSessionRequest(); ConditionerBaseResponse> resp = ConditionerUtil.getDlSjXs(req); if (resp == null || !resp.isSuccess()) { throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "同步计费系数失败")); } List list = resp.getData() != null ? resp.getData() : Collections.emptyList(); Date now = new Date(); int add = 0, upd = 0; for (DlSjXsResponse item : list) { if (item.getDev_id() == null) { continue; } YwConditionerBilling local = billingMapper.selectOne(new QueryWrapper().lambda() .eq(YwConditionerBilling::getIsdeleted, Constants.ZERO) .eq(YwConditionerBilling::getPlatformDevId, item.getDev_id()) .last(" limit 1 ")); if (local == null) { local = new YwConditionerBilling(); local.setCreator(0); local.setCreateDate(now); local.setIsdeleted(Constants.ZERO); add++; } else { upd++; } local.setPlatformDevId(item.getDev_id()); local.setKwType(item.getKw_type()); local.setFanArg(item.getFan_arg()); local.setFanKw(item.getFan_kw()); local.setHigKw(item.getHig_kw()); local.setMidKw(item.getMid_kw()); local.setLowKw(item.getLow_kw()); local.setKtDj(item.getKt_dj()); local.setLastSyncDate(now); local.setEditDate(now); YwConditioner dev = findConditionerByPlatformDevId(item.getDev_id()); if (dev != null) { local.setDevName(dev.getName()); local.setWgMac(dev.getWgMac()); } if (local.getId() == null) { billingMapper.insert(local); } else { billingMapper.updateById(local); } } return "同步计费系数:新增【" + add + "】更新【" + upd + "】"; } @Override @Transactional(rollbackFor = Exception.class) public String syncIndoorUnits() { ensureLogin(); ConditionerSessionRequest session = new ConditionerSessionRequest(); ConditionerBaseResponse> statusResp = ConditionerUtil.getDevList(session); if (statusResp == null || !statusResp.isSuccess()) { throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(statusResp, "同步内机状态失败")); } ConditionerBaseResponse> archiveResp = ConditionerUtil.getDev(session); Map archiveMap = new HashMap<>(); if (archiveResp != null && archiveResp.getData() != null) { for (DeviceArchiveResponse a : archiveResp.getData()) { if (a.getDev_id() != null) { archiveMap.put(a.getDev_id(), a); } } } List list = statusResp.getData() != null ? statusResp.getData() : Collections.emptyList(); Date now = new Date(); int add = 0, upd = 0; for (DeviceStatusResponse item : list) { if (item.getDev_id() == null) { continue; } YwConditioner local = findConditionerByPlatformDevId(item.getDev_id()); if (local == null) { local = new YwConditioner(); local.setCreator(0); local.setCreateDate(now); local.setIsdeleted(Constants.ZERO); local.setCode(String.valueOf(item.getDev_id())); local.setPlatformDevId(item.getDev_id()); add++; } else { upd++; } fillConditionerFromStatus(local, item, archiveMap.get(item.getDev_id()), now); if (local.getId() == null) { conditionerMapper.insert(local); } else { conditionerMapper.updateById(local); } } return "同步设备与状态:新增【" + add + "】更新【" + upd + "】"; } @Override @Transactional(rollbackFor = Exception.class) public String syncUsage(YwConditionerReportQueryDTO query) { YwConditionerReportQueryDTO q = query != null ? query : new YwConditionerReportQueryDTO(); ReportDateRange range = resolveReportDateRange(q); ensureLogin(); DayDlQueryRequest req = new DayDlQueryRequest(); req.setStart_time(range.start.toString()); req.setEnd_time(range.end.toString()); Integer gsId = q.getGsId(); if (gsId != null) { req.setGs_id(gsId); } req.setPage(1); req.setPageSize(5000); ConditionerBaseResponse> resp = ConditionerUtil.getDayDl(req); if (resp == null || !resp.isSuccess()) { throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "同步用量失败")); } List list = resp.getData() != null ? resp.getData() : Collections.emptyList(); Date now = new Date(); int upsert = 0; for (Object row : list) { JSONObject o = (JSONObject) JSON.toJSON(row); Integer devId = o.getInteger("dev_id"); String uptime = o.getString("uptime"); if (devId == null || StringUtils.isBlank(uptime)) { continue; } Date usageDate = parseDate(uptime); if (usageDate == null) { continue; } YwConditionerUsage usage = usageMapper.selectOne(new QueryWrapper().lambda() .eq(YwConditionerUsage::getIsdeleted, Constants.ZERO) .eq(YwConditionerUsage::getPlatformDevId, devId) .eq(YwConditionerUsage::getUsageDate, usageDate) .eq(gsId != null, YwConditionerUsage::getGsId, gsId) .isNull(gsId == null, YwConditionerUsage::getGsId) .last(" limit 1 ")); if (usage == null) { usage = new YwConditionerUsage(); usage.setCreator(0); usage.setCreateDate(now); usage.setIsdeleted(Constants.ZERO); usage.setPlatformDevId(devId); usage.setGsId(gsId); } usage.setDevName(o.getString("dev_name")); usage.setUsageDate(usageDate); usage.setSumTime(o.getBigDecimal("sum_time")); usage.setSumDl(o.getBigDecimal("sum_dl")); usage.setSumDf(o.getBigDecimal("sum_df")); usage.setSyncDate(now); usage.setEditDate(now); if (usage.getId() == null) { usageMapper.insert(usage); } else { usageMapper.updateById(usage); } upsert++; } return "同步用量:处理【" + upsert + "】条"; } @Override public String syncUsagePreviousDay() { LocalDate yesterday = LocalDate.now().minusDays(1); YwConditionerReportQueryDTO query = new YwConditionerReportQueryDTO(); query.setReportType("day"); query.setStartTime(yesterday.toString()); query.setEndTime(yesterday.toString()); return syncUsage(query); } @Override @Transactional(rollbackFor = Exception.class) public String operate(YwConditionerOperateDTO dto, LoginUserInfo user) { if (dto == null || dto.getId() == null || dto.getActionType() == null) { throw new BusinessException(ResponseStatus.BAD_REQUEST); } YwConditioner dev = requireConditioner(dto.getId()); ensureDeviceOnline(dev); ensureLogin(); String setType; Object setVal = dto.getSetVal(); int actionType = dto.getActionType(); reserveAdjustOperateInterval(dev.getId(), actionType); switch (actionType) { case ACTION_PWR: setType = "pwr"; break; case ACTION_MODE: setType = "mode"; break; case ACTION_FAN: setType = "fan"; break; case ACTION_TEMP: setType = "temp"; if (setVal != null) { try { setVal = Integer.parseInt(String.valueOf(setVal)) * 10; } catch (NumberFormatException ignored) { } } break; default: throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "不支持的操作类型"); } DevControlRequest req = buildDevControl(dev, setType, setVal); String reqJson = JSON.toJSONString(req); ConditionerBaseResponse resp = ConditionerUtil.devCtr(req); boolean ok = resp != null && resp.isSuccess(); String actionDesc = formatOperateDescription(actionType, dto.getSetVal()); saveActionRecord(dev, actionType, actionDesc, ok, resp, reqJson, dto.getSource(), user); if (!ok) { throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "控制失败")); } // 温度/风速/模式:控制成功即按设定值更新本地,不再拉取三方设备状态 if (actionType == ACTION_PWR) { refreshDevStatus(dev); } applyOperateResultToDevice(dev, actionType, dto.getSetVal()); dev.setEditDate(new Date()); dev.setLastSyncDate(new Date()); conditionerMapper.updateById(dev); return "操作成功"; } @Override @Transactional(rollbackFor = Exception.class) public String lock(YwConditionerLockDTO dto, LoginUserInfo user) { if (dto == null || dto.getId() == null) { throw new BusinessException(ResponseStatus.BAD_REQUEST); } YwConditioner dev = requireConditioner(dto.getId()); ensureDeviceOnline(dev); ensureLogin(); JSONObject lockVal = buildLockSetVal(dto); DevLockControlRequest req = buildLockManyControlRequest(dev, lockVal); String reqJson = JSON.toJSONString(req); ConditionerBaseResponse resp = ConditionerUtil.devLockManyCtr(req); boolean ok = resp != null && resp.isSuccess(); saveActionRecord(dev, ACTION_LOCK, lockVal.toJSONString(), ok, resp, reqJson, dto.getSource(), user); if (!ok) { throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "锁定失败")); } applyLockResultToDevice(dev, dto); refreshDevStatus(dev); applyLockResultToDevice(dev, dto); Date now = new Date(); dev.setEditDate(now); dev.setLastSyncDate(now); conditionerMapper.updateById(dev); return Objects.equals(dto.getLockPwr(), 0) ? "解锁成功" : "锁定成功"; } private JSONObject buildLockSetVal(YwConditionerLockDTO dto) { JSONObject lockVal = new JSONObject(); lockVal.put("lock_pwr", dto.getLockPwr() != null ? dto.getLockPwr() : -1); lockVal.put("pwr", dto.getPwr() != null ? dto.getPwr() : -1); lockVal.put("mode", dto.getMode() != null ? dto.getMode() : -1); lockVal.put("fan", dto.getFan() != null ? dto.getFan() : -1); lockVal.put("min_temp", dto.getMinTemp() != null ? dto.getMinTemp() : -1); lockVal.put("max_temp", dto.getMaxTemp() != null ? dto.getMaxTemp() : -1); return lockVal; } private void applyLockResultToDevice(YwConditioner dev, YwConditionerLockDTO dto) { dev.setLockPwr(dto.getLockPwr()); dev.setLockMode(dto.getMode()); dev.setLockFan(dto.getFan()); dev.setLockMinTemp(dto.getMinTemp()); dev.setLockMaxTemp(dto.getMaxTemp()); dev.setKtLock(hasActiveLock(dto) ? 1 : 0); } private DevLockControlRequest buildLockManyControlRequest(YwConditioner dev, JSONObject lockVal) { DevLockControlRequest req = new DevLockControlRequest(); req.fillSessionDefaults(); req.setCtr_type("set_many"); DevLockManyControlItem item = new DevLockManyControlItem(); item.setWg_mac(dev.getWgMac()); item.setWg_qid(dev.getWgQid()); item.setPid(StringUtils.defaultIfBlank(dev.getPid(), PID_DLJ)); item.setSet_type(LOCK_SET_TYPE); item.setSet_val(lockVal); req.setLi_data(Collections.singletonList(item)); return req; } private boolean hasActiveLock(YwConditionerLockDTO dto) { return Objects.equals(dto.getLockPwr(), 1) || (dto.getMode() != null && dto.getMode() >= 0) || (dto.getFan() != null && dto.getFan() >= 0) || (dto.getMinTemp() != null && dto.getMinTemp() >= 0) || (dto.getMaxTemp() != null && dto.getMaxTemp() >= 0); } /** * 同一台内机:设定温度/风速/模式操作间隔不低于 2 秒 */ private void reserveAdjustOperateInterval(Integer deviceId, int actionType) { if (deviceId == null || (actionType != ACTION_MODE && actionType != ACTION_FAN && actionType != ACTION_TEMP)) { return; } long now = System.currentTimeMillis(); Long last = LAST_ADJUST_OPERATE_MS.get(deviceId); if (last != null && now - last < ADJUST_OPERATE_INTERVAL_MS) { long waitSec = (ADJUST_OPERATE_INTERVAL_MS - (now - last) + 999) / 1000; throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "操作过于频繁,请" + waitSec + "秒后再试"); } LAST_ADJUST_OPERATE_MS.put(deviceId, now); } @Override @Transactional(rollbackFor = Exception.class) public String queryMeterEnergy(Integer meterId, LoginUserInfo user) { return queryMeter(meterId, "find_dn", ACTION_QUERY_DL, user); } @Override @Transactional(rollbackFor = Exception.class) public String queryMeterPower(Integer meterId, LoginUserInfo user) { return queryMeter(meterId, "find_power", ACTION_QUERY_POWER, user); } private String queryMeter(Integer meterId, String setType, int actionType, LoginUserInfo user) { YwConditionerMeter meter = meterMapper.selectById(meterId); if (meter == null || Objects.equals(meter.getIsdeleted(), Constants.ONE)) { throw new BusinessException(ResponseStatus.DATA_EMPTY); } ensureLogin(); DevControlRequest req = buildMeterQueryRequest(meter, setType); String reqJson = JSON.toJSONString(req); ConditionerBaseResponse resp = ConditionerUtil.devCtr(req); boolean ok = resp != null && resp.isSuccess(); YwConditionerActions action = new YwConditionerActions(); action.setActionType(actionType); action.setWgMac(meter.getWgMac()); action.setDevName(meter.getDbName()); action.setActionContent(setType); action.setRequestBody(reqJson); action.setResponseBody(resp != null ? JSON.toJSONString(resp) : null); action.setResultStatus(ok ? Constants.ONE : Constants.ZERO); action.setResultMsg(ok ? "成功" : apiMsg(resp, "失败")); action.setSource("admin"); saveAction(action, user); if (!ok) { throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), apiMsg(resp, "查询失败")); } String syncMsg = syncMeters(); return "查询成功;" + syncMsg; } @Override public void saveAction(YwConditionerActions action, LoginUserInfo user) { Date now = new Date(); if (action.getCreateDate() == null) { action.setCreateDate(now); } if (action.getCreator() == null && user != null) { action.setCreator(user.getId()); } action.setIsdeleted(Constants.ZERO); actionsMapper.insert(action); } @Override public YwConditionerUsageReportPageDTO queryUsageReport(YwConditionerReportQueryDTO query) { YwConditionerReportQueryDTO q = query != null ? query : new YwConditionerReportQueryDTO(); ReportDateRange range = resolveReportDateRange(q); LocalDate start = range.start; LocalDate end = range.end; List dateColumns = new ArrayList<>(); DateTimeFormatter colFmt = range.dayMode ? DateTimeFormatter.ofPattern("yyyy-MM-dd") : DateTimeFormatter.ofPattern("M.d"); for (LocalDate d = start; !d.isAfter(end); d = d.plusDays(1)) { dateColumns.add(d.format(colFmt)); } QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda() .eq(YwConditionerUsage::getIsdeleted, Constants.ZERO) .ge(YwConditionerUsage::getUsageDate, java.sql.Date.valueOf(start)) .le(YwConditionerUsage::getUsageDate, java.sql.Date.valueOf(end)); if (q.getGsId() != null) { wrapper.lambda().eq(YwConditionerUsage::getGsId, q.getGsId()); } if (StringUtils.isNotBlank(q.getDevKeyword())) { wrapper.lambda().and(w -> w.like(YwConditionerUsage::getDevName, q.getDevKeyword()) .or().eq(YwConditionerUsage::getPlatformDevId, parseIntOrNull(q.getDevKeyword()))); } List rows = usageMapper.selectList(wrapper); Map grouped = new LinkedHashMap<>(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); for (YwConditionerUsage row : rows) { YwConditionerUsageReportVO vo = grouped.computeIfAbsent(row.getPlatformDevId(), k -> { YwConditionerUsageReportVO n = new YwConditionerUsageReportVO(); n.setDevId(row.getPlatformDevId()); n.setDevName(row.getDevName()); n.setTotalTime(BigDecimal.ZERO); n.setTotalDl(BigDecimal.ZERO); n.setTotalDf(BigDecimal.ZERO); return n; }); if (StringUtils.isBlank(vo.getDevName()) && StringUtils.isNotBlank(row.getDevName())) { vo.setDevName(row.getDevName()); } String dayKey = sdf.format(row.getUsageDate()); YwConditionerUsageReportVO.YwConditionerUsageDailyVO daily = new YwConditionerUsageReportVO.YwConditionerUsageDailyVO(); daily.setTime(row.getSumTime()); daily.setDl(row.getSumDl()); daily.setDf(row.getSumDf()); vo.getDaily().put(dayKey, daily); vo.setTotalTime(add(vo.getTotalTime(), row.getSumTime())); vo.setTotalDl(add(vo.getTotalDl(), row.getSumDl())); vo.setTotalDf(add(vo.getTotalDf(), row.getSumDf())); } List all = new ArrayList<>(grouped.values()); enrichReportDeviceInfo(all); int page = q.getPage() != null && q.getPage() > 0 ? q.getPage() : 1; int capacity = q.getCapacity() != null && q.getCapacity() > 0 ? q.getCapacity() : 20; int from = (page - 1) * capacity; int to = Math.min(from + capacity, all.size()); List pageRecords = from >= all.size() ? Collections.emptyList() : all.subList(from, to); YwConditionerUsageReportPageDTO result = new YwConditionerUsageReportPageDTO(); result.setDateColumns(dateColumns); result.setRecords(pageRecords); result.setTotal(all.size()); result.setPage(page); result.setCapacity(capacity); return result; } @Override public List> gatewayOptions() { List list = gatewayMapper.selectList(new QueryWrapper().lambda() .eq(YwConditionerGateway::getIsdeleted, Constants.ZERO) .orderByAsc(YwConditionerGateway::getWgMac)); return list.stream().map(g -> { Map m = new LinkedHashMap<>(); m.put("id", g.getId()); m.put("wgMac", g.getWgMac()); m.put("label", g.getWgMac() + (StringUtils.isNotBlank(g.getWgBz()) ? " (" + g.getWgBz() + ")" : "")); return m; }).collect(Collectors.toList()); } @Override public String syncAll() { if (syncing) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "同步任务正在执行,请稍后"); } syncing = true; try { StringBuilder sb = new StringBuilder(); sb.append(syncGateways("manual")).append(";"); sb.append(syncMeters()).append(";"); sb.append(syncBilling()).append(";"); sb.append(syncIndoorUnits()); return sb.toString(); } finally { syncing = false; } } private void refreshDevStatus(YwConditioner dev) { GetDevOneRequest oneReq = new GetDevOneRequest(); oneReq.setWg_mac(dev.getWgMac()); oneReq.setWg_qid(dev.getWgQid()); oneReq.fillSessionDefaults(); ConditionerBaseResponse resp = ConditionerUtil.getDevOne(oneReq); if (resp != null && resp.isSuccess() && resp.getData() != null) { fillConditionerFromStatus(dev, resp.getData(), null, new Date()); } } private void applyOperateResultToDevice(YwConditioner dev, int actionType, Object setVal) { if (setVal == null) { return; } Integer val = parseIntOrNull(String.valueOf(setVal)); if (val == null) { return; } switch (actionType) { case ACTION_PWR: dev.setPwr(val); break; case ACTION_MODE: dev.setMode(val); break; case ACTION_FAN: dev.setFanSet(val); dev.setFan(val); break; case ACTION_TEMP: dev.setTempSet(val <= 50 ? val * 10 : val); break; default: break; } } /** 内机控制操作可读描述,写入控制历史 actionContent */ private String formatOperateDescription(int actionType, Object setVal) { Integer val = setVal == null ? null : parseIntOrNull(String.valueOf(setVal)); switch (actionType) { case ACTION_PWR: if (val == null) { return "设置开关"; } return String.format("设置开关为【%s】", val == 1 ? "开机" : "关机"); case ACTION_MODE: if (val == null) { return "设置模式"; } return String.format("设置模式为【%s】", modeLabel(val)); case ACTION_FAN: if (val == null) { return "设置风速"; } return String.format("设置风速为【%s】", fanLabel(val)); case ACTION_TEMP: if (val == null) { return "设置温度"; } return String.format("设置温度为【%s】", formatTempDisplay(val)); default: return setVal == null ? "设备控制" : String.valueOf(setVal); } } private String modeLabel(int mode) { switch (mode) { case 1: return "制热"; case 2: return "制冷"; case 3: return "送风"; case 4: return "除湿"; default: return String.valueOf(mode); } } private String fanLabel(int fan) { switch (fan) { case 1: return "低速"; case 2: return "中速"; case 3: return "高速"; case 4: return "自动"; default: return String.valueOf(fan); } } private String formatTempDisplay(int val) { double celsius = val > 100 ? val / 10.0 : val; if (Math.abs(celsius - Math.rint(celsius)) < 0.05) { return String.format("%.0f℃", celsius); } return String.format("%.1f℃", celsius); } private DevControlRequest buildMeterQueryRequest(YwConditionerMeter meter, String setType) { if (StringUtils.isBlank(meter.getWgMac())) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "电表未绑定网关,请先同步电表"); } if (StringUtils.isBlank(meter.getDbAdr())) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "电表地址为空,请先同步电表"); } if (StringUtils.isBlank(meter.getXyName())) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "电表协议为空,请先同步电表"); } if (StringUtils.isBlank(meter.getDljMac())) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "多联机MAC为空,请先同步电表"); } DevControlRequest req = new DevControlRequest(); req.fillSessionDefaults(); req.setWg_mac(meter.getWgMac()); req.setWg_qid(meter.getDbAdr()); req.setCtr_type("set_one"); req.setSet_type(setType); JSONObject setVal = new JSONObject(); setVal.put("db_bb", meter.getDbBb() != null ? meter.getDbBb() : 1); setVal.put("xy_name", meter.getXyName()); setVal.put("dlj_mac", meter.getDljMac()); setVal.put("btl", meter.getBtl() != null ? meter.getBtl() : 4); setVal.put("jy", meter.getJy() != null ? meter.getJy() : 0); req.setSet_val(setVal); return req; } private DevControlRequest buildDevControl(YwConditioner dev, String setType, Object setVal) { DevControlRequest req = new DevControlRequest(); req.fillSessionDefaults(); req.setPid(StringUtils.defaultIfBlank(dev.getPid(), PID_DLJ)); req.setWg_mac(dev.getWgMac()); req.setWg_qid(dev.getWgQid()); req.setCtr_type("set_one"); req.setSet_type(setType); req.setSet_val(setVal); return req; } private void saveActionRecord(YwConditioner dev, int actionType, String content, boolean ok, ConditionerBaseResponse resp, String reqJson, String source, LoginUserInfo user) { YwConditionerActions action = new YwConditionerActions(); action.setConditionerId(dev.getId()); action.setPlatformDevId(dev.getPlatformDevId()); action.setDevName(dev.getName()); action.setWgMac(dev.getWgMac()); action.setActionType(actionType); action.setActionContent(content); action.setRequestBody(reqJson); action.setResponseBody(resp != null ? JSON.toJSONString(resp) : null); action.setResultStatus(ok ? Constants.ONE : Constants.ZERO); action.setResultMsg(ok ? "成功" : apiMsg(resp, "失败")); action.setSource(StringUtils.defaultIfBlank(source, "admin")); saveAction(action, user); } private void writeGatewayLog(Integer gatewayId, String wgMac, String oldStatus, String newStatus, String source, Date now) { YwConditionerGatewayLog log = new YwConditionerGatewayLog(); log.setCreator(0); log.setCreateDate(now); log.setIsdeleted(Constants.ZERO); log.setGatewayId(gatewayId); log.setWgMac(wgMac); log.setOldStatus(oldStatus); log.setNewStatus(newStatus); log.setLogTime(now); log.setSource(StringUtils.defaultIfBlank(source, "manual")); gatewayLogMapper.insert(log); } private void fillMeter(YwConditionerMeter local, MeterDbInfoResponse item, Date now) { local.setPlatformDbId(item.getDb_id()); local.setDbName(item.getDb_name()); local.setDbAdr(item.getDb_adr()); local.setWgMac(item.getWg_mac()); local.setWgId(item.getWg_id()); local.setXyId(item.getXy_id()); local.setXyName(item.getXy_name()); local.setDljMac(item.getDlj_mac()); local.setBtl(item.getBtl()); local.setJy(item.getJy()); local.setDbBb(item.getDb_bb()); local.setOutdoorLoop(item.getDb_rhd()); local.setDbUptime(item.getDb_uptime()); if (item.getDb_data() != null) { local.setDbData(JSON.toJSONString(item.getDb_data())); BigDecimal energy = parseDbDataEnergy(item.getDb_data()); if (energy != null) { local.setTotalDl(energy); } } local.setLastSyncDate(now); local.setEditDate(now); } private BigDecimal parseDbDataEnergy(Object dbData) { if (dbData == null) { return null; } if (dbData instanceof Number) { return new BigDecimal(dbData.toString()); } if (dbData instanceof String) { String text = ((String) dbData).trim(); if (StringUtils.isBlank(text)) { return null; } try { return new BigDecimal(text); } catch (Exception ignored) { try { return parseDbDataEnergy(JSON.parse(text)); } catch (Exception ignored2) { return null; } } } if (dbData instanceof JSONObject) { JSONObject obj = (JSONObject) dbData; for (String key : Arrays.asList("dl", "dn", "total_dl", "totalDl", "db_data", "energy")) { if (obj.containsKey(key) && obj.get(key) != null) { return parseDbDataEnergy(obj.get(key)); } } return null; } if (dbData instanceof Map) { return parseDbDataEnergy(new JSONObject((Map) dbData)); } return null; } private void fillConditionerFromStatus(YwConditioner local, DeviceStatusResponse item, DeviceArchiveResponse archive, Date now) { local.setPlatformDevId(item.getDev_id()); local.setWgId(item.getWg_id()); local.setWgMac(item.getWg_mac()); local.setWgQid(item.getWg_qid()); local.setPid(StringUtils.defaultIfBlank(item.getPid(), PID_DLJ)); local.setOnline(ConditionerConstant.normalizeDeviceOnline(item.getOnline())); local.setPwr(item.getPwr()); local.setMode(item.getMode()); local.setFan(item.getFan()); local.setFanSet(item.getFan_set()); local.setTemp(item.getTemp()); local.setTempSet(item.getTemp_set()); local.setKtLock(item.getKt_lock()); local.setStopLogo(item.getStop_logo()); local.setUptime(item.getUptime()); local.setFloorId(item.getFloor_id()); local.setFloorName(item.getFloor_name()); local.setRoomId(item.getRoom_id()); local.setRoomName(item.getRoom_name()); local.setDevTypeId(item.getDev_type_id()); local.setDevTypeName(item.getDev_type_name()); if (StringUtils.isNotBlank(item.getDev_name())) { local.setName(item.getDev_name()); } if (archive != null) { if (StringUtils.isNotBlank(archive.getDev_name())) { local.setName(archive.getDev_name()); } if (archive.getFloor_id() != null) { local.setFloorId(archive.getFloor_id()); } if (StringUtils.isNotBlank(archive.getFloor_name())) { local.setFloorName(archive.getFloor_name()); } if (archive.getRoom_id() != null) { local.setRoomId(archive.getRoom_id()); } if (StringUtils.isNotBlank(archive.getRoom_name())) { local.setRoomName(archive.getRoom_name()); } } local.setLastSyncDate(now); local.setEditDate(now); } private YwConditioner findConditionerByPlatformDevId(Integer platformDevId) { return conditionerMapper.selectOne(new QueryWrapper().lambda() .eq(YwConditioner::getIsdeleted, Constants.ZERO) .eq(YwConditioner::getPlatformDevId, platformDevId) .last(" limit 1 ")); } private YwConditioner requireConditioner(Integer id) { YwConditioner dev = conditionerMapper.selectById(id); if (dev == null || Objects.equals(dev.getIsdeleted(), Constants.ONE)) { throw new BusinessException(ResponseStatus.DATA_EMPTY); } if (StringUtils.isBlank(dev.getWgMac()) || StringUtils.isBlank(dev.getWgQid())) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "设备未绑定平台网关信息,请先同步内机"); } return dev; } private void ensureDeviceOnline(YwConditioner dev) { if (!"在线".equals(ConditionerConstant.normalizeDeviceOnline(dev.getOnline()))) { throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "设备离线了,请检查对应设备网络"); } } private YearMonth parseMonth(String month) { if (StringUtils.isBlank(month)) { return YearMonth.now().minusMonths(1); } try { return YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM")); } catch (Exception e) { return YearMonth.now().minusMonths(1); } } private void enrichReportDeviceInfo(List records) { if (records == null || records.isEmpty()) { return; } Set devIds = records.stream() .map(YwConditionerUsageReportVO::getDevId) .filter(Objects::nonNull) .collect(Collectors.toSet()); if (devIds.isEmpty()) { return; } List devs = conditionerMapper.selectList(new QueryWrapper().lambda() .eq(YwConditioner::getIsdeleted, Constants.ZERO) .in(YwConditioner::getPlatformDevId, devIds)); Map devMap = devs.stream() .filter(d -> d.getPlatformDevId() != null) .collect(Collectors.toMap(YwConditioner::getPlatformDevId, d -> d, (a, b) -> a)); for (YwConditionerUsageReportVO vo : records) { YwConditioner dev = devMap.get(vo.getDevId()); if (dev == null) { continue; } vo.setFloorName(dev.getFloorName()); vo.setRoomName(dev.getRoomName()); if (StringUtils.isNotBlank(dev.getName())) { vo.setDevName(dev.getName()); } } } private static class ReportDateRange { LocalDate start; LocalDate end; boolean dayMode; } private ReportDateRange resolveReportDateRange(YwConditionerReportQueryDTO q) { String reportType = StringUtils.defaultIfBlank(q.getReportType(), "month"); ReportDateRange range = new ReportDateRange(); if ("day".equalsIgnoreCase(reportType)) { if (StringUtils.isBlank(q.getStartTime()) || StringUtils.isBlank(q.getEndTime())) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "请选择时间段"); } LocalDate start; LocalDate end; try { start = LocalDate.parse(q.getStartTime()); end = LocalDate.parse(q.getEndTime()); } catch (Exception e) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "日期格式不正确"); } if (end.isBefore(start)) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "结束日期不能早于开始日期"); } long days = ChronoUnit.DAYS.between(start, end) + 1; if (days > 31) { throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "时间段不能超过31天"); } range.start = start; range.end = end; range.dayMode = true; return range; } YearMonth ym = parseMonth(q.getMonth()); range.start = ym.atDay(1); range.end = ym.atEndOfMonth(); range.dayMode = false; return range; } private Date parseDate(String text) { try { if (text.length() > 10) { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(text); } return new SimpleDateFormat("yyyy-MM-dd").parse(text); } catch (Exception e) { return null; } } private Integer parseIntOrNull(String text) { try { return Integer.parseInt(text.trim()); } catch (Exception e) { return null; } } private BigDecimal add(BigDecimal a, BigDecimal b) { if (a == null) { a = BigDecimal.ZERO; } return b != null ? a.add(b) : a; } private String apiMsg(ConditionerBaseResponse resp, String def) { return resp != null && StringUtils.isNotBlank(resp.getMessage()) ? resp.getMessage() : def; } }