package com.doumee.service.business.impl;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONArray;
|
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.constants.ResponseStatus;
|
import com.doumee.core.device.ElectronicToolUtil;
|
import com.doumee.core.device.model.ElectronicConstant;
|
import com.doumee.core.device.model.ElectronicNotifyStatus;
|
import com.doumee.core.device.model.request.*;
|
import com.doumee.core.device.model.response.ElectronicBaseResponse;
|
import com.doumee.core.device.model.response.ElectronicDataResponse;
|
import com.doumee.core.device.model.response.QueryDataInfoResponse;
|
import com.doumee.core.device.model.response.QueryDataV2Response;
|
import com.doumee.core.exception.BusinessException;
|
import com.doumee.core.model.LoginUserInfo;
|
import com.doumee.core.utils.Constants;
|
import com.doumee.core.utils.DateUtil;
|
import com.doumee.dao.business.*;
|
import com.doumee.dao.business.dto.YwElectricalEditDTO;
|
import com.doumee.dao.business.dto.YwElectricalOperateDTO;
|
import com.doumee.dao.business.model.*;
|
import com.doumee.service.business.YwElectricalBizService;
|
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
import org.apache.commons.lang3.StringUtils;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.CollectionUtils;
|
|
import java.lang.reflect.Field;
|
import java.math.BigDecimal;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
@Service
|
public class YwElectricalBizServiceImpl implements YwElectricalBizService {
|
|
private static final Logger log = LoggerFactory.getLogger(YwElectricalBizServiceImpl.class);
|
|
public static final int ACTION_RESET_PREPAY = 1;
|
public static final int ACTION_RESET_POSTPAY = 2;
|
public static final int ACTION_REMOTE_CLOSE = 3;
|
public static final int ACTION_TRIP = 4;
|
public static final int ACTION_CLOSE = 5;
|
public static final int ACTION_OPEN = 6;
|
public static final int ACTION_RECHARGE = 7;
|
public static final int ACTION_READ = 8;
|
|
/** ele_read 抄电表数据操作类型(与 queryData functionids=253 不同) */
|
private static final int ELE_READ_TYPE_METER = 3;
|
/** queryData 电表状态详情功能 ID */
|
private static final String QUERY_DATA_FUNCTION_METER_STATUS = "253";
|
|
@Autowired
|
private YwElectricalMapper ywElectricalMapper;
|
@Autowired
|
private YwElectricalRoomMapper ywElectricalRoomMapper;
|
@Autowired
|
private YwElectricalActionsMapper ywElectricalActionsMapper;
|
@Autowired
|
private YwElectricalLogMapper ywElectricalLogMapper;
|
@Autowired
|
private YwElectricalDataMapper ywElectricalDataMapper;
|
@Autowired
|
private YwElectricalChargeMapper ywElectricalChargeMapper;
|
|
@Override
|
public YwElectricalEditDTO getDetail(Integer id) {
|
YwElectrical e = ywElectricalMapper.selectById(id);
|
if (e == null || Objects.equals(e.getIsdeleted(), Constants.ONE)) {
|
throw new BusinessException(ResponseStatus.DATA_EMPTY);
|
}
|
YwElectricalEditDTO dto = new YwElectricalEditDTO();
|
BeanUtils.copyProperties(e, dto);
|
List<YwElectricalRoom> rooms = ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
|
.eq(YwElectricalRoom::getIsdeleted, Constants.ZERO)
|
.eq(YwElectricalRoom::getType, Constants.ZERO)
|
.eq(YwElectricalRoom::getObjId, id));
|
dto.setRoomIds(rooms.stream().map(YwElectricalRoom::getRoomId).filter(Objects::nonNull).collect(Collectors.toList()));
|
fillRoomNames(Collections.singletonList(dto));
|
return dto;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void updateDetail(YwElectricalEditDTO dto, LoginUserInfo user) {
|
if (dto == null || dto.getId() == null) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST);
|
}
|
ywElectricalMapper.update(null, new UpdateWrapper<YwElectrical>().lambda()
|
.eq(YwElectrical::getId, dto.getId())
|
.set(YwElectrical::getRate, dto.getRate() != null ? dto.getRate() : BigDecimal.ONE)
|
.set(YwElectrical::getElectricalParamId, dto.getElectricalParamId())
|
.set(YwElectrical::getEdirot, user.getId())
|
.set(YwElectrical::getEditDate, new Date()));
|
saveRooms(dto.getId(), dto.getRoomIds(), user);
|
}
|
|
private void saveRooms(Integer electricalId, List<Integer> roomIds, LoginUserInfo user) {
|
ywElectricalRoomMapper.update(null, new UpdateWrapper<YwElectricalRoom>().lambda()
|
.set(YwElectricalRoom::getIsdeleted, Constants.ONE)
|
.set(YwElectricalRoom::getEditDate, new Date())
|
.set(YwElectricalRoom::getEditor, user.getId())
|
.eq(YwElectricalRoom::getObjId, electricalId)
|
.eq(YwElectricalRoom::getType, Constants.ZERO));
|
if (CollectionUtils.isEmpty(roomIds)) {
|
return;
|
}
|
int sort = 0;
|
for (Integer roomId : roomIds) {
|
if (roomId == null) continue;
|
YwElectricalRoom r = new YwElectricalRoom();
|
r.setCreator(user.getId());
|
r.setCreateDate(new Date());
|
r.setEditor(user.getId());
|
r.setEditDate(new Date());
|
r.setIsdeleted(Constants.ZERO);
|
r.setType(Constants.ZERO);
|
r.setObjId(electricalId);
|
r.setRoomId(roomId);
|
r.setSortnum(++sort);
|
ywElectricalRoomMapper.insert(r);
|
}
|
}
|
|
@Override
|
public Map<String, Object> getRemoteInfo(Integer electricalId) {
|
YwElectrical e = requireElectrical(electricalId);
|
fillRoomNames(Collections.singletonList(e));
|
Map<String, Object> map = new LinkedHashMap<>();
|
map.put("electrical", e);
|
YwElectricalData latest = findLatestData(e.getId(), e.getAddress());
|
map.put("latestData", latest);
|
map.put("purchaseCount", latest != null && latest.getCountnum() != null ? latest.getCountnum() : "0");
|
return map;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public String operate(YwElectricalOperateDTO dto, LoginUserInfo user) {
|
YwElectrical e = requireElectrical(dto.getElectricalId());
|
String action = dto.getAction();
|
if (StringUtils.isBlank(action)) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST);
|
}
|
switch (action) {
|
case "resetPrepay":
|
return doSecurityReset(e, "default", ACTION_RESET_PREPAY, user);
|
case "resetPostpay":
|
return doSecurityReset(e, "noprepay", ACTION_RESET_POSTPAY, user);
|
case "trip":
|
return doEleControl(e, 10, ACTION_TRIP, user);
|
case "close":
|
return doEleControl(e, 11, ACTION_CLOSE, user);
|
case "openAccount":
|
return doOpenAccount(e, dto, user);
|
case "recharge":
|
return doRecharge(e, dto, user);
|
case "readMeter":
|
return doReadMeter(e, user);
|
default:
|
throw new BusinessException(ResponseStatus.BAD_REQUEST);
|
}
|
}
|
|
private String doSecurityReset(YwElectrical e, String paymentMode, int actionType, LoginUserInfo user) {
|
String oprId = newOprId();
|
SecurityResetRequest req = new SecurityResetRequest();
|
req.setOpr_id(oprId);
|
req.setCid(e.getCollectorId());
|
req.setAddress(e.getAddress());
|
req.setTime_out(86400);
|
req.setMust_online("false");
|
req.setRetry_times(1);
|
SecurityResetParamRequest p = new SecurityResetParamRequest();
|
p.setPaymentmode(paymentMode);
|
p.setAccount_id(StringUtils.defaultIfBlank(e.getAccountId(), "1"));
|
req.setParams(p);
|
String reqJson = JSON.toJSONString(Collections.singletonList(req));
|
ElectronicBaseResponse resp = ElectronicToolUtil.eleSecurityReset(Collections.singletonList(req));
|
return finishAsync(e, actionType, oprId, "/Api_v2/ele_security/reset", reqJson, resp, user);
|
}
|
|
private String doEleControl(YwElectrical e, int type, int actionType, LoginUserInfo user) {
|
String oprId = newOprId();
|
EleControlApiRequest req = new EleControlApiRequest();
|
req.setOpr_id(oprId);
|
req.setCid(e.getCollectorId());
|
req.setAddress(e.getAddress());
|
req.setTime_out(86400);
|
req.setMust_online("false");
|
req.setRetry_times(1);
|
req.setType(type);
|
String reqJson = JSON.toJSONString(Collections.singletonList(req));
|
List<OpenAccountRequest> list = new ArrayList<>();
|
list.add(req);
|
ElectronicBaseResponse resp = ElectronicToolUtil.eleControl(list);
|
return finishAsync(e, actionType, oprId, "/Api_v2/ele_security/ele_control", reqJson, resp, user);
|
}
|
|
private String doOpenAccount(YwElectrical e, YwElectricalOperateDTO dto, LoginUserInfo user) {
|
String oprId = newOprId();
|
OpenAccountRequest req = buildOpenAccountRequest(e, oprId, dto.getMoney(), dto.getRemark());
|
String reqJson = JSON.toJSONString(Collections.singletonList(req));
|
ElectronicBaseResponse resp = ElectronicToolUtil.openAcount(Collections.singletonList(req));
|
return finishAsync(e, ACTION_OPEN, oprId, "/Api_v2/ele_security/open_acount", reqJson, resp, user);
|
}
|
|
private String doRecharge(YwElectrical e, YwElectricalOperateDTO dto, LoginUserInfo user) {
|
if (!Objects.equals(e.getAccountStatus(), Constants.ONE)) {
|
throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "电表未开户,请先开户");
|
}
|
String oprId = newOprId();
|
OpenAccountRequest req = buildOpenAccountRequest(e, oprId, dto.getMoney(), dto.getRemark());
|
String reqJson = JSON.toJSONString(Collections.singletonList(req));
|
ElectronicBaseResponse resp = ElectronicToolUtil.recharger(Collections.singletonList(req));
|
String msg = finishAsync(e, ACTION_RECHARGE, oprId, "/Api_v2/ele_security/recharge", reqJson, resp, user);
|
if (isSuccess(resp)) {
|
saveChargeRecord(e, oprId, dto, user);
|
}
|
return msg;
|
}
|
|
private String doReadMeter(YwElectrical e, LoginUserInfo user) {
|
String oprId = newOprId();
|
EleReadRequest req = new EleReadRequest();
|
req.setOpr_id(oprId);
|
req.setCid(e.getCollectorId());
|
req.setAddress(e.getAddress());
|
req.setTime_out(86400);
|
req.setMust_online("false");
|
req.setType(ELE_READ_TYPE_METER);
|
req.setRetry_times(1);
|
String reqJson = JSON.toJSONString(Collections.singletonList(req));
|
ElectronicBaseResponse resp = ElectronicToolUtil.eleRead(Collections.singletonList(req));
|
String msg = finishAsync(e, ACTION_READ, oprId, "/Api_v2/ele_read", reqJson, resp, user);
|
if (!isSuccess(resp)) {
|
throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), msg);
|
}
|
boolean synced = syncMeterDataForElectrical(e);
|
refreshBalanceFromData(e);
|
return synced ? "抄表成功,用量余额已更新" : "抄表请求已提交,暂无新抄表数据,请稍后刷新查看";
|
}
|
|
private OpenAccountRequest buildOpenAccountRequest(YwElectrical e, String oprId, BigDecimal money, String remark) {
|
OpenAccountRequest req = new OpenAccountRequest();
|
req.setOpr_id(oprId);
|
req.setCid(e.getCollectorId());
|
req.setAddress(e.getAddress());
|
req.setTime_out(86400);
|
req.setMust_online("false");
|
req.setRetry_times(1);
|
OpenAccountParamRequest p = new OpenAccountParamRequest();
|
p.setMoney(money != null ? money.toPlainString() : "0");
|
p.setAccount_id(StringUtils.defaultIfBlank(e.getAccountId(), "1"));
|
p.setCount("1");
|
p.setRate(e.getRate() != null ? e.getRate().toPlainString() : "1");
|
req.setParams(p);
|
return req;
|
}
|
|
private String finishAsync(YwElectrical e, int actionType, String oprId, String apiName, String reqJson,
|
ElectronicBaseResponse resp, LoginUserInfo user) {
|
saveLog(e, apiName, reqJson, resp, user);
|
YwElectricalActions act = new YwElectricalActions();
|
act.setCreator(user.getId());
|
act.setCreateDate(new Date());
|
act.setEditor(user.getId());
|
act.setEditDate(new Date());
|
act.setIsdeleted(Constants.ZERO);
|
act.setElectricalId(e.getId());
|
act.setActionType(actionType);
|
act.setOprId(oprId);
|
act.setRequestBody(reqJson);
|
act.setResponseBody(resp != null ? JSON.toJSONString(resp) : null);
|
if (isSuccess(resp)) {
|
act.setStatus(Constants.ZERO);
|
act.setResultMsg("已提交,等待平台执行");
|
} else {
|
act.setStatus(Constants.TWO);
|
act.setResultMsg(resp != null ? resp.getError_msg() : "调用失败");
|
}
|
ywElectricalActionsMapper.insert(act);
|
return isSuccess(resp) ? "提交成功,请稍后在操作记录或充值记录中查看结果" : act.getResultMsg();
|
}
|
|
private void saveLog(YwElectrical e, String apiName, String reqJson, ElectronicBaseResponse resp, LoginUserInfo user) {
|
YwElectricalLog logRow = new YwElectricalLog();
|
logRow.setCreator(user.getId());
|
logRow.setCreateDate(new Date());
|
logRow.setEditor(user.getId());
|
logRow.setEditDate(new Date());
|
logRow.setIsdeleted(Constants.ZERO);
|
logRow.setDeviceType(Constants.ZERO);
|
logRow.setType(Constants.ZERO);
|
logRow.setName(apiName);
|
logRow.setUrl(apiName);
|
logRow.setRequest(reqJson);
|
logRow.setRepose(resp != null ? JSON.toJSONString(resp) : null);
|
logRow.setSuccess(isSuccess(resp) ? Constants.ZERO : Constants.ONE);
|
logRow.setObjId(String.valueOf(e.getId()));
|
ywElectricalLogMapper.insert(logRow);
|
}
|
|
private boolean isSuccess(ElectronicBaseResponse resp) {
|
return resp != null && "SUCCESS".equalsIgnoreCase(resp.getStatus());
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public boolean handleElectricalNotify(String responseContent, String timestamp, String sign) {
|
if (!ElectronicToolUtil.verifyNotifySign(responseContent, timestamp, sign)) {
|
log.warn("electricalNotify sign check failed, timestamp={}", timestamp);
|
return false;
|
}
|
JSONArray items;
|
try {
|
items = JSON.parseArray(responseContent);
|
} catch (Exception e) {
|
log.warn("electricalNotify invalid response_content", e);
|
return false;
|
}
|
if (items == null || items.isEmpty()) {
|
return true;
|
}
|
for (int i = 0; i < items.size(); i++) {
|
applyNotifyItem(items.getJSONObject(i));
|
}
|
return true;
|
}
|
|
private void applyNotifyItem(JSONObject item) {
|
if (item == null) return;
|
String oprId = item.getString("opr_id");
|
if (StringUtils.isBlank(oprId)) return;
|
YwElectricalActions act = ywElectricalActionsMapper.selectOne(new QueryWrapper<YwElectricalActions>().lambda()
|
.eq(YwElectricalActions::getOprId, oprId)
|
.eq(YwElectricalActions::getIsdeleted, Constants.ZERO)
|
.orderByDesc(YwElectricalActions::getCreateDate)
|
.last("limit 1"));
|
if (act == null) {
|
log.info("electricalNotify no action for opr_id={}", oprId);
|
return;
|
}
|
ElectronicNotifyStatus ns = ElectronicNotifyStatus.fromCode(item.getString("status"));
|
String errMsg = item.get("error_msg") != null ? String.valueOf(item.get("error_msg")) : null;
|
String resultMsg = ns.getLabel() + (StringUtils.isNotBlank(errMsg) ? ":" + errMsg : "");
|
|
YwElectricalActions upd = new YwElectricalActions();
|
upd.setId(act.getId());
|
upd.setEditDate(new Date());
|
upd.setResponseBody(item.toJSONString());
|
upd.setResultMsg(resultMsg);
|
if (ns.isTerminalSuccess()) {
|
upd.setStatus(Constants.ONE);
|
} else if (ns.isTerminalFail()) {
|
upd.setStatus(Constants.TWO);
|
} else if (ns.isInProgress()) {
|
upd.setStatus(Constants.ZERO);
|
}
|
ywElectricalActionsMapper.updateById(upd);
|
|
YwElectrical e = ywElectricalMapper.selectById(act.getElectricalId());
|
if (e == null) return;
|
saveNotifyLog(e, item, ns);
|
if (ns.isTerminalSuccess()) {
|
applyNotifySideEffect(e, act.getActionType());
|
if (Objects.equals(act.getActionType(), ACTION_RECHARGE)) {
|
updateChargeByNotify(oprId, Constants.ONE, resultMsg);
|
}
|
} else if (ns.isTerminalFail() && Objects.equals(act.getActionType(), ACTION_RECHARGE)) {
|
updateChargeByNotify(oprId, Constants.TWO, resultMsg);
|
}
|
}
|
|
private void saveChargeRecord(YwElectrical e, String oprId, YwElectricalOperateDTO dto, LoginUserInfo user) {
|
fillRoomNames(Collections.singletonList(e));
|
YwElectricalCharge charge = new YwElectricalCharge();
|
charge.setCreator(user.getId());
|
charge.setCreateDate(new Date());
|
charge.setEditor(user.getId());
|
charge.setEditDate(new Date());
|
charge.setIsdeleted(Constants.ZERO);
|
charge.setType(Constants.ZERO);
|
charge.setObjId(e.getId());
|
charge.setAddress(e.getAddress());
|
charge.setName(e.getName());
|
charge.setCId(e.getCollectorId());
|
charge.setMoney(dto.getMoney() != null ? dto.getMoney() : BigDecimal.ZERO);
|
charge.setRemark(dto.getRemark());
|
charge.setOprId(oprId);
|
charge.setStatus(Constants.ZERO);
|
charge.setStatusTime(new Date());
|
charge.setStatusInfo("充值中");
|
charge.setBanlance(e.getBalance());
|
charge.setRoomNames(e.getRoomNames());
|
if (StringUtils.isNotBlank(e.getParamId())) {
|
try {
|
charge.setParamId(Integer.parseInt(e.getParamId()));
|
} catch (NumberFormatException ignored) {
|
}
|
}
|
ywElectricalChargeMapper.insert(charge);
|
}
|
|
private void updateChargeByNotify(String oprId, int status, String statusInfo) {
|
ywElectricalChargeMapper.update(null, new UpdateWrapper<YwElectricalCharge>().lambda()
|
.set(YwElectricalCharge::getStatus, status)
|
.set(YwElectricalCharge::getStatusTime, new Date())
|
.set(YwElectricalCharge::getStatusInfo, statusInfo)
|
.set(YwElectricalCharge::getEditDate, new Date())
|
.eq(YwElectricalCharge::getOprId, oprId)
|
.eq(YwElectricalCharge::getIsdeleted, Constants.ZERO));
|
}
|
|
private void saveNotifyLog(YwElectrical e, JSONObject item, ElectronicNotifyStatus ns) {
|
YwElectricalLog logRow = new YwElectricalLog();
|
logRow.setCreateDate(new Date());
|
logRow.setEditDate(new Date());
|
logRow.setIsdeleted(Constants.ZERO);
|
logRow.setDeviceType(Constants.ZERO);
|
logRow.setType(Constants.ONE);
|
logRow.setName("electricalNotify");
|
logRow.setUrl("/electronic/electricalNotify");
|
logRow.setRequest(null);
|
logRow.setRepose(item.toJSONString());
|
logRow.setSuccess(ns.isTerminalFail() ? Constants.ONE : Constants.ZERO);
|
logRow.setObjId(String.valueOf(e.getId()));
|
ywElectricalLogMapper.insert(logRow);
|
}
|
|
private void applyNotifySideEffect(YwElectrical e, Integer actionType) {
|
if (actionType == null) return;
|
UpdateWrapper<YwElectrical> uw = new UpdateWrapper<>();
|
uw.lambda().eq(YwElectrical::getId, e.getId()).set(YwElectrical::getEditDate, new Date());
|
switch (actionType) {
|
case ACTION_TRIP:
|
uw.lambda().set(YwElectrical::getRelayStatus, "0");
|
ywElectricalMapper.update(null, uw);
|
break;
|
case ACTION_CLOSE:
|
uw.lambda().set(YwElectrical::getRelayStatus, "1");
|
ywElectricalMapper.update(null, uw);
|
break;
|
case ACTION_OPEN:
|
uw.lambda().set(YwElectrical::getAccountStatus, Constants.ONE)
|
.set(YwElectrical::getLastOpenDate, new Date());
|
ywElectricalMapper.update(null, uw);
|
break;
|
case ACTION_RECHARGE:
|
case ACTION_READ:
|
syncMeterDataForElectrical(e);
|
refreshBalanceFromData(e);
|
break;
|
default:
|
break;
|
}
|
}
|
|
private void refreshBalanceFromData(YwElectrical e) {
|
YwElectricalData data = findLatestData(e.getId(), e.getAddress());
|
if (data == null) return;
|
UpdateWrapper<YwElectrical> uw = new UpdateWrapper<>();
|
uw.lambda().eq(YwElectrical::getId, e.getId());
|
if (StringUtils.isNotBlank(data.getZhygzdl())) {
|
uw.lambda().set(YwElectrical::getBalanceBattery, data.getZhygzdl());
|
}
|
if (StringUtils.isNotBlank(data.getYe())) {
|
try {
|
uw.lambda().set(YwElectrical::getBalance, new BigDecimal(data.getYe()));
|
} catch (Exception ignored) {
|
}
|
}
|
uw.lambda().set(YwElectrical::getBalanceTime, new Date());
|
uw.lambda().set(YwElectrical::getEditDate, new Date());
|
ywElectricalMapper.update(null, uw);
|
}
|
|
private YwElectricalData findLatestData(Integer electricalId, String address) {
|
QueryWrapper<YwElectricalData> q = new QueryWrapper<>();
|
q.lambda().eq(YwElectricalData::getIsdeleted, Constants.ZERO)
|
.and(w -> w.eq(YwElectricalData::getDeviceId, String.valueOf(electricalId))
|
.or().eq(StringUtils.isNotBlank(address), YwElectricalData::getAddress, address))
|
.orderByDesc(YwElectricalData::getCreateDate).last("limit 1");
|
return ywElectricalDataMapper.selectOne(q);
|
}
|
|
@Override
|
public void syncMeterDataScheduled() {
|
try {
|
syncMeterDataInternal();
|
} catch (Exception e) {
|
log.warn("syncMeterDataScheduled failed", e);
|
}
|
}
|
|
@Override
|
public String syncMeterDataFromPlatform() {
|
MeterDataSyncStats stats = syncMeterDataInternal();
|
return "抄表同步完成:新增【" + stats.addCount + "】条,跳过重复【" + stats.skipCount + "】条";
|
}
|
|
private static class MeterDataSyncStats {
|
private int addCount;
|
private int skipCount;
|
}
|
|
private MeterDataSyncStats syncMeterDataInternal() {
|
MeterDataSyncStats stats = new MeterDataSyncStats();
|
String startTime = resolveSyncStartTime();
|
QueryDataRequest param = buildQueryDataRequest(startTime, DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"));
|
log.info("sync meter data, start_time={}, end_time={}", startTime, param.getEnd_time());
|
List<QueryDataInfoResponse> list = fetchQueryDataList(param);
|
if (CollectionUtils.isEmpty(list)) {
|
return stats;
|
}
|
for (QueryDataInfoResponse item : list) {
|
if (item == null || StringUtils.isBlank(item.getAddress())) {
|
continue;
|
}
|
YwElectrical meter = ywElectricalMapper.selectOne(new QueryWrapper<YwElectrical>().lambda()
|
.eq(YwElectrical::getIsdeleted, Constants.ZERO)
|
.eq(YwElectrical::getAddress, item.getAddress())
|
.last("limit 1"));
|
if (saveDataRow(item, meter != null ? meter.getId() : null)) {
|
stats.addCount++;
|
if (meter != null) {
|
refreshBalanceFromData(meter);
|
}
|
} else {
|
stats.skipCount++;
|
}
|
}
|
return stats;
|
}
|
|
/** 单表抄表后从第三方拉取最新数据入库,返回是否有新记录 */
|
private boolean syncMeterDataForElectrical(YwElectrical e) {
|
if (e == null || StringUtils.isBlank(e.getAddress())) {
|
return false;
|
}
|
Calendar cal = Calendar.getInstance();
|
cal.add(Calendar.HOUR_OF_DAY, -24);
|
QueryDataRequest param = buildQueryDataRequest(
|
DateUtil.formatDate(cal.getTime(), "yyyy-MM-dd HH:mm:ss"),
|
DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"));
|
List<QueryDataInfoResponse> list;
|
try {
|
list = fetchQueryDataList(param);
|
} catch (BusinessException ex) {
|
log.warn("sync meter data for electricalId={} failed: {}", e.getId(), ex.getMessage());
|
return false;
|
}
|
if (CollectionUtils.isEmpty(list)) {
|
return false;
|
}
|
String address = e.getAddress().trim();
|
boolean updated = false;
|
for (QueryDataInfoResponse item : list) {
|
if (item == null || StringUtils.isBlank(item.getAddress())) {
|
continue;
|
}
|
if (!address.equalsIgnoreCase(item.getAddress().trim())) {
|
continue;
|
}
|
if (saveDataRow(item, e.getId())) {
|
updated = true;
|
}
|
}
|
return updated;
|
}
|
|
private QueryDataRequest buildQueryDataRequest(String startTime, String endTime) {
|
QueryDataRequest param = new QueryDataRequest();
|
param.setType("json");
|
param.setFunctionids(QUERY_DATA_FUNCTION_METER_STATUS);
|
param.setStart_time(startTime);
|
param.setEnd_time(endTime);
|
param.setOffset(0);
|
param.setLimit(500);
|
return param;
|
}
|
|
private List<QueryDataInfoResponse> fetchQueryDataList(QueryDataRequest param) {
|
ElectronicDataResponse response = ElectronicToolUtil.queryDataRequest(param);
|
if (!ElectronicToolUtil.isDataApiSuccess(response)) {
|
throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(),
|
ElectronicToolUtil.dataApiErrorMessage(response, "抄表数据同步失败"));
|
}
|
return parseQueryDataList(response);
|
}
|
|
private List<QueryDataInfoResponse> parseQueryDataList(ElectronicDataResponse response) {
|
if (response == null || response.getData() == null) {
|
return Collections.emptyList();
|
}
|
String json = JSON.toJSONString(response.getData());
|
if (StringUtils.isBlank(json) || "null".equals(json)) {
|
return Collections.emptyList();
|
}
|
try {
|
String trimmed = json.trim();
|
if (trimmed.startsWith("[")) {
|
return JSON.parseArray(trimmed, QueryDataInfoResponse.class);
|
}
|
if (trimmed.startsWith("{")) {
|
QueryDataInfoResponse one = JSON.parseObject(trimmed, QueryDataInfoResponse.class);
|
return one != null ? Collections.singletonList(one) : Collections.emptyList();
|
}
|
} catch (Exception e) {
|
log.warn("parse queryDataRequest failed", e);
|
throw new BusinessException(ResponseStatus.SERVER_ERROR.getCode(), "抄表数据解析失败");
|
}
|
return Collections.emptyList();
|
}
|
|
/** 取本地已同步的最大抄表时间作为下次 start_time;无记录时默认近24小时 */
|
private String resolveSyncStartTime() {
|
YwElectricalData latest = ywElectricalDataMapper.selectOne(new QueryWrapper<YwElectricalData>().lambda()
|
.eq(YwElectricalData::getIsdeleted, Constants.ZERO)
|
.isNotNull(YwElectricalData::getAddTime)
|
.ne(YwElectricalData::getAddTime, "")
|
.orderByDesc(YwElectricalData::getAddTime)
|
.last("limit 1"));
|
if (latest != null && StringUtils.isNotBlank(latest.getAddTime())) {
|
String addTime = latest.getAddTime().trim();
|
if (addTime.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")) {
|
return addTime;
|
}
|
log.warn("invalid addTime in yw_electrical_data, fallback to 24h: {}", addTime);
|
}
|
Calendar cal = Calendar.getInstance();
|
cal.add(Calendar.HOUR_OF_DAY, -24);
|
return DateUtil.formatDate(cal.getTime(), "yyyy-MM-dd HH:mm:ss");
|
}
|
|
private boolean existsByDataId(String dataId) {
|
if (StringUtils.isBlank(dataId)) {
|
return false;
|
}
|
return ywElectricalDataMapper.selectCount(new QueryWrapper<YwElectricalData>().lambda()
|
.eq(YwElectricalData::getIsdeleted, Constants.ZERO)
|
.eq(YwElectricalData::getDataId, dataId.trim())) > 0;
|
}
|
|
/** 保存抄表记录;dataId 已存在则跳过,返回 false */
|
private boolean saveDataRow(QueryDataInfoResponse item, Integer electricalId) {
|
String dataId = item.getId();
|
if (StringUtils.isNotBlank(dataId) && existsByDataId(dataId)) {
|
return false;
|
}
|
YwElectricalData row = new YwElectricalData();
|
row.setCreateDate(new Date());
|
row.setEditDate(new Date());
|
row.setIsdeleted(Constants.ZERO);
|
row.setDeviceId(electricalId != null ? String.valueOf(electricalId) : null);
|
applyItemToRow(row, item);
|
ywElectricalDataMapper.insert(row);
|
return true;
|
}
|
|
/** 第三方 item / data_v2 字段名与本地实体差异映射 */
|
private static final Map<String, String> SYNC_FIELD_ALIASES;
|
private static final Set<String> ITEM_SKIP_KEYS;
|
|
static {
|
Map<String, String> aliases = new HashMap<>();
|
aliases.put("id", "dataId");
|
aliases.put("add_time", "addTime");
|
aliases.put("count", "countnum");
|
SYNC_FIELD_ALIASES = Collections.unmodifiableMap(aliases);
|
ITEM_SKIP_KEYS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("data_v2", "idnum")));
|
}
|
|
/** 解析 data 数组中的 item 属性映射到 yw_electrical_data */
|
private void applyItemToRow(YwElectricalData row, QueryDataInfoResponse item) {
|
JSONObject itemJson = (JSONObject) JSON.toJSON(item);
|
for (String key : itemJson.keySet()) {
|
if (ITEM_SKIP_KEYS.contains(key)) {
|
continue;
|
}
|
Object val = itemJson.get(key);
|
if (val == null) {
|
continue;
|
}
|
String fieldName = SYNC_FIELD_ALIASES.getOrDefault(key, key);
|
setElectricalDataField(row, fieldName, val);
|
}
|
JSONObject dataV2 = extractDataV2Json(item, itemJson);
|
if (dataV2 != null && !dataV2.isEmpty()) {
|
applyDataV2Json(row, dataV2);
|
}
|
applyDspFallback(row, item.getDsp());
|
}
|
|
private JSONObject extractDataV2Json(QueryDataInfoResponse item, JSONObject itemJson) {
|
if (!CollectionUtils.isEmpty(item.getData_v2())) {
|
return mergeDataV2Json(item.getData_v2());
|
}
|
Object dataV2 = itemJson.get("data_v2");
|
if (dataV2 == null) {
|
return null;
|
}
|
if (dataV2 instanceof JSONObject) {
|
return (JSONObject) dataV2;
|
}
|
if (dataV2 instanceof JSONArray) {
|
JSONArray arr = (JSONArray) dataV2;
|
JSONObject merged = new JSONObject();
|
for (int i = 0; i < arr.size(); i++) {
|
Object element = arr.get(i);
|
if (element instanceof JSONObject) {
|
mergeJsonObject(merged, (JSONObject) element);
|
} else if (element != null) {
|
QueryDataV2Response part = arr.getObject(i, QueryDataV2Response.class);
|
if (part != null) {
|
mergeJsonObject(merged, (JSONObject) JSON.toJSON(part));
|
}
|
}
|
}
|
return merged;
|
}
|
return null;
|
}
|
|
private void mergeJsonObject(JSONObject target, JSONObject source) {
|
for (String key : source.keySet()) {
|
Object val = source.get(key);
|
if (val != null) {
|
target.put(key, val);
|
}
|
}
|
}
|
|
private void applyDspFallback(YwElectricalData row, String dsp) {
|
if (StringUtils.isNotBlank(row.getZhygzdl()) || StringUtils.isBlank(dsp)) {
|
return;
|
}
|
if (dsp.contains("kWh") || dsp.contains("kwh")) {
|
String num = dsp.replaceAll("[^0-9.]", " ").trim().split("\\s+")[0];
|
if (StringUtils.isNotBlank(num)) {
|
row.setZhygzdl(num);
|
}
|
}
|
}
|
|
/** 合并 data_v2 列表中各条记录的属性(后值覆盖前值) */
|
private JSONObject mergeDataV2Json(List<QueryDataV2Response> dataV2List) {
|
JSONObject merged = new JSONObject();
|
for (QueryDataV2Response part : dataV2List) {
|
if (part == null) {
|
continue;
|
}
|
JSONObject obj = (JSONObject) JSON.toJSON(part);
|
for (String key : obj.keySet()) {
|
Object val = obj.get(key);
|
if (val != null) {
|
merged.put(key, val);
|
}
|
}
|
}
|
return merged;
|
}
|
|
/** 按属性名将 data_v2 解析写入 yw_electrical_data 同名字段(覆盖 item 层同名字段) */
|
private void applyDataV2Json(YwElectricalData row, JSONObject dataV2) {
|
if (dataV2 == null || dataV2.isEmpty()) {
|
return;
|
}
|
for (String key : dataV2.keySet()) {
|
Object val = dataV2.get(key);
|
if (val == null) {
|
continue;
|
}
|
String fieldName = SYNC_FIELD_ALIASES.getOrDefault(key, key);
|
setElectricalDataField(row, fieldName, val);
|
}
|
}
|
|
private void setElectricalDataField(YwElectricalData row, String fieldName, Object val) {
|
if (val == null || StringUtils.isBlank(fieldName)) {
|
return;
|
}
|
try {
|
Field field = YwElectricalData.class.getDeclaredField(fieldName);
|
field.setAccessible(true);
|
Class<?> type = field.getType();
|
if (String.class.equals(type)) {
|
String strVal = convertFieldString(val);
|
if (StringUtils.isNotBlank(strVal)) {
|
field.set(row, strVal);
|
}
|
} else if (Integer.class.equals(type) || int.class.equals(type)) {
|
if (val instanceof Number) {
|
field.set(row, ((Number) val).intValue());
|
} else {
|
String text = String.valueOf(val).trim();
|
if (StringUtils.isNotBlank(text)) {
|
field.set(row, Integer.parseInt(text));
|
}
|
}
|
} else if (BigDecimal.class.equals(type)) {
|
String text = String.valueOf(val).trim();
|
if (StringUtils.isNotBlank(text)) {
|
field.set(row, new BigDecimal(text));
|
}
|
}
|
} catch (NoSuchFieldException | IllegalAccessException | NumberFormatException ignored) {
|
// 忽略第三方扩展字段或无法转换的值
|
}
|
}
|
|
private String convertFieldString(Object val) {
|
if (val instanceof Boolean) {
|
return String.valueOf(val);
|
}
|
if (val instanceof JSONArray || val instanceof JSONObject || val instanceof Collection) {
|
return JSON.toJSONString(val);
|
}
|
return String.valueOf(val).trim();
|
}
|
|
@Override
|
public void cleanLogBeforeThreeMonths() {
|
Calendar cal = Calendar.getInstance();
|
cal.add(Calendar.MONTH, -3);
|
ywElectricalLogMapper.delete(new QueryWrapper<YwElectricalLog>().lambda()
|
.lt(YwElectricalLog::getCreateDate, cal.getTime()));
|
}
|
|
@Override
|
public void enrichList(List<YwElectrical> list) {
|
fillRoomNames(list);
|
if (CollectionUtils.isEmpty(list)) return;
|
for (YwElectrical row : list) {
|
if (StringUtils.isNotBlank(row.getWarnType())) {
|
row.setWarnTypeName(resolveWarnTypeNames(row.getWarnType()));
|
}
|
}
|
}
|
|
private String resolveWarnTypeNames(String warnType) {
|
String[] codes = warnType.split(",");
|
List<String> names = new ArrayList<>();
|
for (String codeStr : codes) {
|
if (StringUtils.isBlank(codeStr)) {
|
continue;
|
}
|
String trimmed = codeStr.trim();
|
try {
|
int code = Integer.parseInt(trimmed);
|
ElectronicConstant.warningDefId def = ElectronicConstant.warningDefId.getByKey(code);
|
names.add(def != null ? def.getName() : trimmed);
|
} catch (NumberFormatException ex) {
|
names.add(trimmed);
|
}
|
}
|
return names.isEmpty() ? warnType : String.join(",", names);
|
}
|
|
@Override
|
public void enrichDataList(List<YwElectricalData> list) {
|
if (CollectionUtils.isEmpty(list)) {
|
return;
|
}
|
List<YwElectrical> temp = new ArrayList<>();
|
for (YwElectricalData row : list) {
|
if (StringUtils.isBlank(row.getElectricalName()) && StringUtils.isNotBlank(row.getName())) {
|
row.setElectricalName(row.getName());
|
}
|
if (row.getElectricalId() == null) {
|
continue;
|
}
|
YwElectrical e = new YwElectrical();
|
e.setId(row.getElectricalId());
|
temp.add(e);
|
}
|
fillRoomNames(temp);
|
Map<Integer, String> roomMap = temp.stream()
|
.filter(e -> StringUtils.isNotBlank(e.getRoomNames()))
|
.collect(Collectors.toMap(YwElectrical::getId, YwElectrical::getRoomNames, (a, b) -> a));
|
for (YwElectricalData row : list) {
|
if (row.getElectricalId() != null) {
|
row.setRoomNames(roomMap.get(row.getElectricalId()));
|
}
|
}
|
}
|
|
private void fillRoomNames(List<? extends YwElectrical> list) {
|
if (CollectionUtils.isEmpty(list)) return;
|
List<Integer> ids = list.stream().map(YwElectrical::getId).filter(Objects::nonNull).collect(Collectors.toList());
|
if (ids.isEmpty()) return;
|
MPJLambdaWrapper<YwElectricalRoom> w = new MPJLambdaWrapper<>();
|
w.selectAll(YwElectricalRoom.class)
|
.selectAs(YwRoom::getRoomNum, YwElectricalRoom::getRoomName)
|
.selectAs(YwBuilding::getName, YwElectricalRoom::getBuildingName)
|
.selectAs(YwFloor::getName, YwElectricalRoom::getFloorName)
|
.leftJoin(YwRoom.class, YwRoom::getId, YwElectricalRoom::getRoomId)
|
.leftJoin(YwFloor.class, YwFloor::getId, YwRoom::getFloor)
|
.leftJoin(YwBuilding.class, YwBuilding::getId, YwRoom::getBuildingId)
|
.eq(YwElectricalRoom::getIsdeleted, Constants.ZERO)
|
.eq(YwElectricalRoom::getType, Constants.ZERO)
|
.in(YwElectricalRoom::getObjId, ids);
|
List<YwElectricalRoom> rooms = ywElectricalRoomMapper.selectJoinList(YwElectricalRoom.class, w);
|
Map<Integer, List<YwElectricalRoom>> grouped = rooms.stream().collect(Collectors.groupingBy(YwElectricalRoom::getObjId));
|
for (YwElectrical row : list) {
|
List<YwElectricalRoom> rs = grouped.get(row.getId());
|
if (CollectionUtils.isEmpty(rs)) continue;
|
row.setRoomNames(rs.stream().map(this::formatRoomPath).filter(StringUtils::isNotBlank)
|
.collect(Collectors.joining("、")));
|
}
|
}
|
|
private String formatRoomPath(YwElectricalRoom r) {
|
List<String> parts = new ArrayList<>();
|
if (StringUtils.isNotBlank(r.getBuildingName())) parts.add(r.getBuildingName());
|
if (StringUtils.isNotBlank(r.getFloorName())) parts.add(r.getFloorName());
|
if (StringUtils.isNotBlank(r.getRoomName())) parts.add(r.getRoomName());
|
return String.join("/", parts);
|
}
|
|
private YwElectrical requireElectrical(Integer id) {
|
YwElectrical e = ywElectricalMapper.selectById(id);
|
if (e == null || Objects.equals(e.getIsdeleted(), Constants.ONE)) {
|
throw new BusinessException(ResponseStatus.DATA_EMPTY);
|
}
|
return e;
|
}
|
|
private String newOprId() {
|
return UUID.randomUUID().toString().replace("-", "");
|
}
|
}
|