|
|
@@ -1,11 +1,771 @@
|
|
|
package com.usky.vpp.service.impl;
|
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.usky.common.core.bean.CommonPage;
|
|
|
+import com.usky.common.core.exception.BusinessException;
|
|
|
+import com.usky.vpp.config.VppReportProperties;
|
|
|
+import com.usky.vpp.domain.VppCustomer;
|
|
|
+import com.usky.vpp.domain.VppDrEvaluation;
|
|
|
+import com.usky.vpp.domain.VppDrEvent;
|
|
|
+import com.usky.vpp.domain.VppEnergySummaryDaily;
|
|
|
+import com.usky.vpp.domain.VppReportAuditLog;
|
|
|
+import com.usky.vpp.domain.VppReportData;
|
|
|
+import com.usky.vpp.domain.VppReportLog;
|
|
|
+import com.usky.vpp.domain.VppReportRecord;
|
|
|
+import com.usky.vpp.domain.VppReportTask;
|
|
|
+import com.usky.vpp.domain.VppResourcePoint;
|
|
|
+import com.usky.vpp.domain.VppSite;
|
|
|
+import com.usky.vpp.mapper.VppCustomerMapper;
|
|
|
+import com.usky.vpp.mapper.VppDrEvaluationMapper;
|
|
|
+import com.usky.vpp.mapper.VppDrEventMapper;
|
|
|
+import com.usky.vpp.mapper.VppEnergySummaryDailyMapper;
|
|
|
+import com.usky.vpp.mapper.VppReportAuditLogMapper;
|
|
|
+import com.usky.vpp.mapper.VppReportDataMapper;
|
|
|
+import com.usky.vpp.mapper.VppReportLogMapper;
|
|
|
+import com.usky.vpp.mapper.VppReportRecordMapper;
|
|
|
+import com.usky.vpp.mapper.VppReportTaskMapper;
|
|
|
+import com.usky.vpp.mapper.VppResourcePointMapper;
|
|
|
+import com.usky.vpp.mapper.VppSiteMapper;
|
|
|
import com.usky.vpp.service.VppReportInfoService;
|
|
|
+import com.usky.vpp.service.vo.ReportAuditLogVO;
|
|
|
+import com.usky.vpp.service.vo.ReportAuditRequest;
|
|
|
+import com.usky.vpp.service.vo.ReportDataSaveRequest;
|
|
|
+import com.usky.vpp.service.vo.ReportPrefillVO;
|
|
|
+import com.usky.vpp.service.vo.ReportRecordVO;
|
|
|
+import com.usky.vpp.service.vo.ReportTaskCreateRequest;
|
|
|
+import com.usky.vpp.service.vo.ReportTaskCreateResultVO;
|
|
|
+import com.usky.vpp.service.vo.ReportTaskVO;
|
|
|
+import com.usky.vpp.service.vo.ReportValidateResultVO;
|
|
|
+import com.usky.vpp.util.VppAuditHelper;
|
|
|
+import com.usky.vpp.util.VppPageHelper;
|
|
|
+import com.usky.vpp.util.VppReportHelper;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.time.format.DateTimeParseException;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
-/**
|
|
|
- * VppReportInfoService 默认实现(待按业务完善)
|
|
|
- */
|
|
|
@Service
|
|
|
public class VppReportInfoServiceImpl implements VppReportInfoService {
|
|
|
+
|
|
|
+ private static final DateTimeFormatter DEADLINE_FMT = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private VppReportTaskMapper taskMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppReportRecordMapper recordMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppReportDataMapper dataMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppReportAuditLogMapper auditLogMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppReportLogMapper reportLogMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppCustomerMapper customerMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppResourcePointMapper resourcePointMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppSiteMapper siteMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppEnergySummaryDailyMapper energySummaryDailyMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppDrEventMapper drEventMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppDrEvaluationMapper drEvaluationMapper;
|
|
|
+ @Autowired
|
|
|
+ private VppReportProperties reportProperties;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CommonPage<ReportTaskVO> pageTask(Map<String, Object> params) {
|
|
|
+ Page<VppReportTask> page = VppPageHelper.of(params);
|
|
|
+ LambdaQueryWrapper<VppReportTask> wrapper = activeTaskWrapper();
|
|
|
+ applyTaskFilters(wrapper, params);
|
|
|
+ wrapper.orderByAsc(VppReportTask::getDeadline);
|
|
|
+ Page<VppReportTask> result = taskMapper.selectPage(page, wrapper);
|
|
|
+ List<ReportTaskVO> records = result.getRecords().stream()
|
|
|
+ .map(this::toTaskVo)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ refreshOverdueTasks(result.getRecords());
|
|
|
+ return new CommonPage<>(records, result.getTotal(), result.getCurrent(), result.getSize());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public ReportTaskCreateResultVO createTask(ReportTaskCreateRequest request) {
|
|
|
+ validateCreateTaskRequest(request);
|
|
|
+
|
|
|
+ String taskNo = VppReportHelper.nextTaskNo();
|
|
|
+ ensureTaskNoUnique(taskNo);
|
|
|
+
|
|
|
+ VppReportTask task = new VppReportTask();
|
|
|
+ task.setTaskNo(taskNo);
|
|
|
+ task.setTaskName(request.getTaskName().trim());
|
|
|
+ task.setReportOrg(request.getReportOrg());
|
|
|
+ task.setReportCycle(request.getReportCycle());
|
|
|
+ task.setReportType(request.getReportType().trim());
|
|
|
+ task.setDeadline(request.getDeadline());
|
|
|
+ task.setTaskStatus(VppReportHelper.TASK_STATUS_PENDING_FILL);
|
|
|
+ task.setAssigneeId(request.getAssigneeId());
|
|
|
+ task.setRemindDays(request.getRemindDays() != null ? request.getRemindDays() : 3);
|
|
|
+ VppAuditHelper.fillCreate(task);
|
|
|
+ taskMapper.insert(task);
|
|
|
+
|
|
|
+ String reportPeriod = resolveReportPeriod(request);
|
|
|
+ String recordNo = VppReportHelper.nextRecordNo(reportPeriod);
|
|
|
+ ensureRecordNoUnique(recordNo);
|
|
|
+
|
|
|
+ VppReportRecord record = new VppReportRecord();
|
|
|
+ record.setTaskId(task.getId());
|
|
|
+ record.setRecordNo(recordNo);
|
|
|
+ record.setReportPeriod(reportPeriod);
|
|
|
+ record.setSubmitMode(request.getSubmitMode() != null
|
|
|
+ ? request.getSubmitMode() : VppReportHelper.SUBMIT_MODE_API);
|
|
|
+ record.setRecordStatus(VppReportHelper.RECORD_STATUS_DRAFT);
|
|
|
+ record.setRetryCount(0);
|
|
|
+ VppAuditHelper.fillCreate(record);
|
|
|
+ recordMapper.insert(record);
|
|
|
+
|
|
|
+ ReportTaskCreateResultVO result = new ReportTaskCreateResultVO();
|
|
|
+ result.setTaskId(task.getId());
|
|
|
+ result.setTaskNo(task.getTaskNo());
|
|
|
+ result.setRecordId(record.getId());
|
|
|
+ result.setRecordNo(record.getRecordNo());
|
|
|
+ result.setReportPeriod(record.getReportPeriod());
|
|
|
+ result.setTaskStatus(task.getTaskStatus());
|
|
|
+ result.setRecordStatus(record.getRecordStatus());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ReportRecordVO getRecord(Long id) {
|
|
|
+ VppReportRecord record = requireRecord(id);
|
|
|
+ VppReportTask task = requireTask(record.getTaskId());
|
|
|
+ VppReportData data = loadReportData(record.getId());
|
|
|
+ return toRecordVo(record, task, data, loadAuditLogs(record.getId()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ReportPrefillVO prefill(Long id) {
|
|
|
+ VppReportRecord record = requireRecord(id);
|
|
|
+ VppReportTask task = requireTask(record.getTaskId());
|
|
|
+ return buildPrefillData(record, task);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Map<String, Object> saveData(Long id, ReportDataSaveRequest request) {
|
|
|
+ if (request == null || request.getFormData() == null || request.getFormData().isEmpty()) {
|
|
|
+ throw new BusinessException("填报数据不能为空");
|
|
|
+ }
|
|
|
+ if (request.getIsDraft() == null) {
|
|
|
+ throw new BusinessException("isDraft 不能为空");
|
|
|
+ }
|
|
|
+ VppReportRecord record = requireRecord(id);
|
|
|
+ if (record.getRecordStatus() != VppReportHelper.RECORD_STATUS_DRAFT
|
|
|
+ && record.getRecordStatus() != VppReportHelper.RECORD_STATUS_FAILED) {
|
|
|
+ throw new BusinessException("当前状态不允许修改填报数据");
|
|
|
+ }
|
|
|
+
|
|
|
+ VppReportData data = loadReportData(record.getId());
|
|
|
+ String formJson = VppReportHelper.toJson(request.getFormData());
|
|
|
+ if (data == null) {
|
|
|
+ data = new VppReportData();
|
|
|
+ data.setRecordId(record.getId());
|
|
|
+ data.setFormData(formJson);
|
|
|
+ data.setIsDraft(Boolean.TRUE.equals(request.getIsDraft()) ? 1 : 0);
|
|
|
+ VppAuditHelper.fillCreate(data);
|
|
|
+ dataMapper.insert(data);
|
|
|
+ } else {
|
|
|
+ data.setFormData(formJson);
|
|
|
+ data.setIsDraft(Boolean.TRUE.equals(request.getIsDraft()) ? 1 : 0);
|
|
|
+ data.setValidationErrors(null);
|
|
|
+ VppAuditHelper.fillUpdate(data);
|
|
|
+ dataMapper.updateById(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (record.getRecordStatus() == VppReportHelper.RECORD_STATUS_FAILED) {
|
|
|
+ record.setRecordStatus(VppReportHelper.RECORD_STATUS_DRAFT);
|
|
|
+ VppAuditHelper.fillUpdate(record);
|
|
|
+ recordMapper.updateById(record);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>(2);
|
|
|
+ result.put("recordId", record.getId());
|
|
|
+ result.put("isDraft", request.getIsDraft());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ReportValidateResultVO validateData(Long id) {
|
|
|
+ VppReportRecord record = requireRecord(id);
|
|
|
+ VppReportData data = requireReportData(record.getId());
|
|
|
+ Map<String, Object> formData = VppReportHelper.parseFormData(data.getFormData());
|
|
|
+ int systemPvCount = countResourceType("PV");
|
|
|
+ ReportValidateResultVO result = VppReportHelper.validateFormData(formData, systemPvCount, true);
|
|
|
+ data.setValidationErrors(VppReportHelper.toJson(result.getErrors()));
|
|
|
+ VppAuditHelper.fillUpdate(data);
|
|
|
+ dataMapper.updateById(data);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Map<String, Object> submitAudit(Long id) {
|
|
|
+ VppReportRecord record = requireRecord(id);
|
|
|
+ if (record.getRecordStatus() != VppReportHelper.RECORD_STATUS_DRAFT
|
|
|
+ && record.getRecordStatus() != VppReportHelper.RECORD_STATUS_FAILED) {
|
|
|
+ throw new BusinessException("仅草稿或失败状态可提交审核");
|
|
|
+ }
|
|
|
+ VppReportData data = requireReportData(record.getId());
|
|
|
+ if (data.getIsDraft() != null && data.getIsDraft() == 1) {
|
|
|
+ throw new BusinessException("请先定稿后再提交审核");
|
|
|
+ }
|
|
|
+ ReportValidateResultVO validation = validateData(id);
|
|
|
+ if (!validation.isValid()) {
|
|
|
+ throw new BusinessException("填报数据校验未通过,请先修正");
|
|
|
+ }
|
|
|
+
|
|
|
+ record.setRecordStatus(VppReportHelper.RECORD_STATUS_PENDING_AUDIT);
|
|
|
+ VppAuditHelper.fillUpdate(record);
|
|
|
+ recordMapper.updateById(record);
|
|
|
+
|
|
|
+ VppReportTask task = requireTask(record.getTaskId());
|
|
|
+ if (task.getTaskStatus() == VppReportHelper.TASK_STATUS_PENDING_FILL) {
|
|
|
+ task.setTaskStatus(VppReportHelper.TASK_STATUS_PENDING_AUDIT);
|
|
|
+ VppAuditHelper.fillUpdate(task);
|
|
|
+ taskMapper.updateById(task);
|
|
|
+ }
|
|
|
+
|
|
|
+ saveAuditLog(record.getId(), 1, VppReportHelper.AUDIT_ACTION_SUBMIT, "提交审核");
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>(2);
|
|
|
+ result.put("recordId", record.getId());
|
|
|
+ result.put("recordStatus", record.getRecordStatus());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void audit(Long id, ReportAuditRequest request) {
|
|
|
+ if (request == null || request.getPassed() == null) {
|
|
|
+ throw new BusinessException("审核结果不能为空");
|
|
|
+ }
|
|
|
+ if (request.getAuditLevel() == null) {
|
|
|
+ throw new BusinessException("auditLevel 不能为空");
|
|
|
+ }
|
|
|
+ VppReportRecord record = requireRecord(id);
|
|
|
+ if (record.getRecordStatus() != VppReportHelper.RECORD_STATUS_PENDING_AUDIT) {
|
|
|
+ throw new BusinessException("仅待审核状态可审核");
|
|
|
+ }
|
|
|
+
|
|
|
+ int action = Boolean.TRUE.equals(request.getPassed())
|
|
|
+ ? VppReportHelper.AUDIT_ACTION_PASS
|
|
|
+ : VppReportHelper.AUDIT_ACTION_REJECT;
|
|
|
+ saveAuditLog(record.getId(), request.getAuditLevel(), action, request.getOpinion());
|
|
|
+
|
|
|
+ if (Boolean.TRUE.equals(request.getPassed())) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ record.setRecordStatus(VppReportHelper.RECORD_STATUS_DRAFT);
|
|
|
+ VppAuditHelper.fillUpdate(record);
|
|
|
+ recordMapper.updateById(record);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public ReportRecordVO submit(Long id) {
|
|
|
+ VppReportRecord record = requireRecord(id);
|
|
|
+ if (record.getRecordStatus() != VppReportHelper.RECORD_STATUS_PENDING_AUDIT) {
|
|
|
+ throw new BusinessException("仅待审核且已通过审批的记录可执行报送");
|
|
|
+ }
|
|
|
+ if (!hasAuditPass(record.getId())) {
|
|
|
+ throw new BusinessException("请先完成审核通过后再报送");
|
|
|
+ }
|
|
|
+ requireReportData(record.getId());
|
|
|
+
|
|
|
+ int retryMax = reportProperties.getRetryMax() != null ? reportProperties.getRetryMax() : 3;
|
|
|
+ int retryCount = record.getRetryCount() != null ? record.getRetryCount() : 0;
|
|
|
+ if (retryCount >= retryMax) {
|
|
|
+ throw new BusinessException("已达最大重试次数");
|
|
|
+ }
|
|
|
+
|
|
|
+ record.setRecordStatus(VppReportHelper.RECORD_STATUS_SUBMITTING);
|
|
|
+ VppAuditHelper.fillUpdate(record);
|
|
|
+ recordMapper.updateById(record);
|
|
|
+
|
|
|
+ VppReportTask task = requireTask(record.getTaskId());
|
|
|
+ task.setTaskStatus(VppReportHelper.TASK_STATUS_SUBMITTING);
|
|
|
+ VppAuditHelper.fillUpdate(task);
|
|
|
+ taskMapper.updateById(task);
|
|
|
+
|
|
|
+ boolean success = executeExternalSubmit(record, task);
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ if (success) {
|
|
|
+ record.setRecordStatus(VppReportHelper.RECORD_STATUS_SUCCESS);
|
|
|
+ record.setSubmittedAt(now);
|
|
|
+ record.setReportFileUrl("/report/files/" + record.getId() + ".xlsx");
|
|
|
+ record.setReceiptFileUrl("/report/receipts/" + record.getId() + ".pdf");
|
|
|
+ record.setArchivedAt(now);
|
|
|
+ task.setTaskStatus(VppReportHelper.TASK_STATUS_COMPLETED);
|
|
|
+ } else {
|
|
|
+ record.setRecordStatus(VppReportHelper.RECORD_STATUS_FAILED);
|
|
|
+ record.setRetryCount(retryCount + 1);
|
|
|
+ }
|
|
|
+ VppAuditHelper.fillUpdate(record);
|
|
|
+ recordMapper.updateById(record);
|
|
|
+ VppAuditHelper.fillUpdate(task);
|
|
|
+ taskMapper.updateById(task);
|
|
|
+
|
|
|
+ return toRecordVo(record, task, loadReportData(record.getId()), loadAuditLogs(record.getId()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CommonPage<ReportRecordVO> pageHistory(Map<String, Object> params) {
|
|
|
+ Page<VppReportRecord> page = VppPageHelper.of(params);
|
|
|
+ LambdaQueryWrapper<VppReportRecord> wrapper = activeRecordWrapper();
|
|
|
+ applyHistoryFilters(wrapper, params);
|
|
|
+ wrapper.orderByDesc(VppReportRecord::getSubmittedAt, VppReportRecord::getCreateTime);
|
|
|
+ Page<VppReportRecord> result = recordMapper.selectPage(page, wrapper);
|
|
|
+
|
|
|
+ Map<Long, VppReportTask> taskMap = loadTaskMap(result.getRecords());
|
|
|
+ List<ReportRecordVO> records = result.getRecords().stream()
|
|
|
+ .map(record -> toHistoryVo(record, taskMap.get(record.getTaskId())))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ return new CommonPage<>(records, result.getTotal(), result.getCurrent(), result.getSize());
|
|
|
+ }
|
|
|
+
|
|
|
+ private ReportPrefillVO buildPrefillData(VppReportRecord record, VppReportTask task) {
|
|
|
+ VppReportHelper.PeriodRange period = VppReportHelper.parseReportPeriod(record.getReportPeriod());
|
|
|
+ List<VppCustomer> customers = listActiveCustomers();
|
|
|
+ List<VppResourcePoint> resources = listActiveResources();
|
|
|
+ List<VppSite> sites = listActiveSites();
|
|
|
+ List<VppEnergySummaryDaily> energyRows = listEnergyRows(period);
|
|
|
+ List<VppDrEvent> drEvents = listDrEvents(period);
|
|
|
+ List<VppDrEvaluation> evaluations = listDrEvaluations(drEvents);
|
|
|
+ return VppReportHelper.buildPrefill(record, task, customers, resources, sites,
|
|
|
+ energyRows, drEvents, evaluations);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean executeExternalSubmit(VppReportRecord record, VppReportTask task) {
|
|
|
+ VppReportLog log = new VppReportLog();
|
|
|
+ log.setReportType(task.getReportType());
|
|
|
+ log.setRequestId(record.getRecordNo());
|
|
|
+ log.setPayloadSize(0);
|
|
|
+ log.setReportedAt(LocalDateTime.now());
|
|
|
+ VppAuditHelper.fillCreate(log);
|
|
|
+ try {
|
|
|
+ log.setReportStatus(VppReportHelper.RECORD_STATUS_SUCCESS);
|
|
|
+ log.setResponseCode(200);
|
|
|
+ reportLogMapper.insert(log);
|
|
|
+ return true;
|
|
|
+ } catch (Exception ex) {
|
|
|
+ log.setReportStatus(VppReportHelper.RECORD_STATUS_FAILED);
|
|
|
+ log.setResponseCode(500);
|
|
|
+ log.setErrorMessage(ex.getMessage());
|
|
|
+ reportLogMapper.insert(log);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean hasAuditPass(Long recordId) {
|
|
|
+ LambdaQueryWrapper<VppReportAuditLog> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppReportAuditLog::getRecordId, recordId)
|
|
|
+ .eq(VppReportAuditLog::getAction, VppReportHelper.AUDIT_ACTION_PASS)
|
|
|
+ .orderByDesc(VppReportAuditLog::getCreateTime)
|
|
|
+ .last("LIMIT 1");
|
|
|
+ return auditLogMapper.selectOne(wrapper) != null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void saveAuditLog(Long recordId, int auditLevel, int action, String opinion) {
|
|
|
+ VppReportAuditLog log = new VppReportAuditLog();
|
|
|
+ log.setRecordId(recordId);
|
|
|
+ log.setAuditLevel(auditLevel);
|
|
|
+ log.setAction(action);
|
|
|
+ log.setOpinion(opinion);
|
|
|
+ VppAuditHelper.fillCreate(log);
|
|
|
+ auditLogMapper.insert(log);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void refreshOverdueTasks(List<VppReportTask> tasks) {
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ for (VppReportTask task : tasks) {
|
|
|
+ if (task.getDeadline() != null
|
|
|
+ && task.getDeadline().isBefore(now)
|
|
|
+ && task.getTaskStatus() != null
|
|
|
+ && task.getTaskStatus() < VppReportHelper.TASK_STATUS_COMPLETED) {
|
|
|
+ task.setTaskStatus(VppReportHelper.TASK_STATUS_OVERDUE);
|
|
|
+ VppAuditHelper.fillUpdate(task);
|
|
|
+ taskMapper.updateById(task);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private ReportTaskVO toTaskVo(VppReportTask task) {
|
|
|
+ ReportTaskVO vo = new ReportTaskVO();
|
|
|
+ vo.setId(task.getId());
|
|
|
+ vo.setTaskNo(task.getTaskNo());
|
|
|
+ vo.setTaskName(task.getTaskName());
|
|
|
+ vo.setReportOrg(task.getReportOrg());
|
|
|
+ vo.setReportOrgName(VppReportHelper.resolveReportOrgName(task.getReportOrg()));
|
|
|
+ vo.setReportCycle(task.getReportCycle());
|
|
|
+ vo.setReportType(task.getReportType());
|
|
|
+ vo.setDeadline(task.getDeadline());
|
|
|
+ vo.setTaskStatus(task.getTaskStatus());
|
|
|
+ vo.setAssigneeId(task.getAssigneeId());
|
|
|
+ vo.setAssigneeName(task.getCreatedBy());
|
|
|
+ vo.setRemindDays(task.getRemindDays());
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private ReportRecordVO toRecordVo(VppReportRecord record,
|
|
|
+ VppReportTask task,
|
|
|
+ VppReportData data,
|
|
|
+ List<VppReportAuditLog> auditLogs) {
|
|
|
+ ReportRecordVO vo = new ReportRecordVO();
|
|
|
+ vo.setId(record.getId());
|
|
|
+ vo.setTaskId(record.getTaskId());
|
|
|
+ vo.setTaskName(task.getTaskName());
|
|
|
+ vo.setRecordNo(record.getRecordNo());
|
|
|
+ vo.setReportPeriod(record.getReportPeriod());
|
|
|
+ vo.setReportOrg(task.getReportOrg());
|
|
|
+ vo.setReportOrgName(VppReportHelper.resolveReportOrgName(task.getReportOrg()));
|
|
|
+ vo.setSubmitMode(record.getSubmitMode());
|
|
|
+ vo.setRecordStatus(record.getRecordStatus());
|
|
|
+ vo.setReportFileUrl(record.getReportFileUrl());
|
|
|
+ vo.setReceiptFileUrl(record.getReceiptFileUrl());
|
|
|
+ vo.setRetryCount(record.getRetryCount());
|
|
|
+ vo.setSubmittedAt(record.getSubmittedAt());
|
|
|
+ if (data != null) {
|
|
|
+ vo.setFormData(VppReportHelper.parseFormData(data.getFormData()));
|
|
|
+ vo.setIsDraft(data.getIsDraft() != null && data.getIsDraft() == 1);
|
|
|
+ } else {
|
|
|
+ vo.setIsDraft(true);
|
|
|
+ }
|
|
|
+ vo.setAuditLogs(auditLogs.stream().map(this::toAuditLogVo).collect(Collectors.toList()));
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private ReportRecordVO toHistoryVo(VppReportRecord record, VppReportTask task) {
|
|
|
+ ReportRecordVO vo = new ReportRecordVO();
|
|
|
+ vo.setId(record.getId());
|
|
|
+ vo.setRecordNo(record.getRecordNo());
|
|
|
+ vo.setReportPeriod(record.getReportPeriod());
|
|
|
+ vo.setRecordStatus(record.getRecordStatus());
|
|
|
+ vo.setSubmittedAt(record.getSubmittedAt());
|
|
|
+ vo.setReceiptFileUrl(record.getReceiptFileUrl());
|
|
|
+ if (task != null) {
|
|
|
+ vo.setTaskName(task.getTaskName());
|
|
|
+ vo.setReportOrg(task.getReportOrg());
|
|
|
+ vo.setReportOrgName(VppReportHelper.resolveReportOrgName(task.getReportOrg()));
|
|
|
+ }
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private ReportAuditLogVO toAuditLogVo(VppReportAuditLog log) {
|
|
|
+ ReportAuditLogVO vo = new ReportAuditLogVO();
|
|
|
+ vo.setId(log.getId());
|
|
|
+ vo.setAuditLevel(log.getAuditLevel());
|
|
|
+ vo.setAction(log.getAction());
|
|
|
+ vo.setOpinion(log.getOpinion());
|
|
|
+ vo.setOperatorName(log.getCreatedBy());
|
|
|
+ vo.setOperatedAt(log.getCreateTime());
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<Long, VppReportTask> loadTaskMap(List<VppReportRecord> records) {
|
|
|
+ if (records == null || records.isEmpty()) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+ List<Long> taskIds = records.stream()
|
|
|
+ .map(VppReportRecord::getTaskId)
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .distinct()
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ if (taskIds.isEmpty()) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+ LambdaQueryWrapper<VppReportTask> wrapper = activeTaskWrapper();
|
|
|
+ wrapper.in(VppReportTask::getId, taskIds);
|
|
|
+ return taskMapper.selectList(wrapper).stream()
|
|
|
+ .collect(Collectors.toMap(VppReportTask::getId, task -> task, (a, b) -> a));
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VppReportAuditLog> loadAuditLogs(Long recordId) {
|
|
|
+ LambdaQueryWrapper<VppReportAuditLog> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppReportAuditLog::getRecordId, recordId)
|
|
|
+ .orderByAsc(VppReportAuditLog::getCreateTime);
|
|
|
+ return auditLogMapper.selectList(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ private VppReportData loadReportData(Long recordId) {
|
|
|
+ LambdaQueryWrapper<VppReportData> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppReportData::getRecordId, recordId)
|
|
|
+ .eq(VppReportData::getDeleteFlag, VppAuditHelper.NOT_DELETED)
|
|
|
+ .last("LIMIT 1");
|
|
|
+ return dataMapper.selectOne(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ private VppReportData requireReportData(Long recordId) {
|
|
|
+ VppReportData data = loadReportData(recordId);
|
|
|
+ if (data == null) {
|
|
|
+ throw new BusinessException("请先保存填报数据");
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ private VppReportRecord requireRecord(Long id) {
|
|
|
+ if (id == null) {
|
|
|
+ throw new BusinessException("报送记录ID不能为空");
|
|
|
+ }
|
|
|
+ LambdaQueryWrapper<VppReportRecord> wrapper = activeRecordWrapper();
|
|
|
+ wrapper.eq(VppReportRecord::getId, id);
|
|
|
+ VppReportRecord record = recordMapper.selectOne(wrapper);
|
|
|
+ if (record == null) {
|
|
|
+ throw new BusinessException("报送记录不存在");
|
|
|
+ }
|
|
|
+ return record;
|
|
|
+ }
|
|
|
+
|
|
|
+ private VppReportTask requireTask(Long taskId) {
|
|
|
+ if (taskId == null) {
|
|
|
+ throw new BusinessException("报送任务不存在");
|
|
|
+ }
|
|
|
+ LambdaQueryWrapper<VppReportTask> wrapper = activeTaskWrapper();
|
|
|
+ wrapper.eq(VppReportTask::getId, taskId);
|
|
|
+ VppReportTask task = taskMapper.selectOne(wrapper);
|
|
|
+ if (task == null) {
|
|
|
+ throw new BusinessException("报送任务不存在");
|
|
|
+ }
|
|
|
+ return task;
|
|
|
+ }
|
|
|
+
|
|
|
+ private LambdaQueryWrapper<VppReportTask> activeTaskWrapper() {
|
|
|
+ LambdaQueryWrapper<VppReportTask> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppReportTask::getDeleteFlag, VppAuditHelper.NOT_DELETED);
|
|
|
+ return wrapper;
|
|
|
+ }
|
|
|
+
|
|
|
+ private LambdaQueryWrapper<VppReportRecord> activeRecordWrapper() {
|
|
|
+ LambdaQueryWrapper<VppReportRecord> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppReportRecord::getDeleteFlag, VppAuditHelper.NOT_DELETED);
|
|
|
+ return wrapper;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void applyTaskFilters(LambdaQueryWrapper<VppReportTask> wrapper, Map<String, Object> params) {
|
|
|
+ if (params == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Integer taskStatus = parseInt(params.get("taskStatus"));
|
|
|
+ if (taskStatus != null) {
|
|
|
+ wrapper.eq(VppReportTask::getTaskStatus, taskStatus);
|
|
|
+ }
|
|
|
+ Integer reportOrg = parseInt(params.get("reportOrg"));
|
|
|
+ if (reportOrg != null) {
|
|
|
+ wrapper.eq(VppReportTask::getReportOrg, reportOrg);
|
|
|
+ }
|
|
|
+ Integer reportCycle = parseInt(params.get("reportCycle"));
|
|
|
+ if (reportCycle != null) {
|
|
|
+ wrapper.eq(VppReportTask::getReportCycle, reportCycle);
|
|
|
+ }
|
|
|
+ LocalDateTime deadlineStart = parseDateTime(params.get("deadlineStart"));
|
|
|
+ if (deadlineStart != null) {
|
|
|
+ wrapper.ge(VppReportTask::getDeadline, deadlineStart);
|
|
|
+ }
|
|
|
+ LocalDateTime deadlineEnd = parseDateTime(params.get("deadlineEnd"));
|
|
|
+ if (deadlineEnd != null) {
|
|
|
+ wrapper.le(VppReportTask::getDeadline, deadlineEnd);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void applyHistoryFilters(LambdaQueryWrapper<VppReportRecord> wrapper, Map<String, Object> params) {
|
|
|
+ if (params == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Integer recordStatus = parseInt(params.get("recordStatus"));
|
|
|
+ if (recordStatus != null) {
|
|
|
+ wrapper.eq(VppReportRecord::getRecordStatus, recordStatus);
|
|
|
+ }
|
|
|
+ String reportPeriod = parseString(params.get("reportPeriod"));
|
|
|
+ if (StringUtils.hasText(reportPeriod)) {
|
|
|
+ wrapper.eq(VppReportRecord::getReportPeriod, reportPeriod);
|
|
|
+ }
|
|
|
+ Integer reportOrg = parseInt(params.get("reportOrg"));
|
|
|
+ if (reportOrg != null) {
|
|
|
+ List<Long> taskIds = listTaskIdsByOrg(reportOrg);
|
|
|
+ if (taskIds.isEmpty()) {
|
|
|
+ wrapper.eq(VppReportRecord::getTaskId, -1L);
|
|
|
+ } else {
|
|
|
+ wrapper.in(VppReportRecord::getTaskId, taskIds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Long> listTaskIdsByOrg(Integer reportOrg) {
|
|
|
+ LambdaQueryWrapper<VppReportTask> wrapper = activeTaskWrapper();
|
|
|
+ wrapper.eq(VppReportTask::getReportOrg, reportOrg)
|
|
|
+ .select(VppReportTask::getId);
|
|
|
+ return taskMapper.selectList(wrapper).stream()
|
|
|
+ .map(VppReportTask::getId)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VppCustomer> listActiveCustomers() {
|
|
|
+ LambdaQueryWrapper<VppCustomer> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppCustomer::getDeleteFlag, VppAuditHelper.NOT_DELETED);
|
|
|
+ return customerMapper.selectList(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VppResourcePoint> listActiveResources() {
|
|
|
+ LambdaQueryWrapper<VppResourcePoint> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppResourcePoint::getDeleteFlag, VppAuditHelper.NOT_DELETED);
|
|
|
+ return resourcePointMapper.selectList(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VppSite> listActiveSites() {
|
|
|
+ LambdaQueryWrapper<VppSite> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppSite::getDeleteFlag, VppAuditHelper.NOT_DELETED);
|
|
|
+ return siteMapper.selectList(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ private int countResourceType(String resourceType) {
|
|
|
+ LambdaQueryWrapper<VppResourcePoint> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppResourcePoint::getDeleteFlag, VppAuditHelper.NOT_DELETED)
|
|
|
+ .eq(VppResourcePoint::getResourceType, resourceType);
|
|
|
+ return resourcePointMapper.selectCount(wrapper).intValue();
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VppEnergySummaryDaily> listEnergyRows(VppReportHelper.PeriodRange period) {
|
|
|
+ if (period == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ LambdaQueryWrapper<VppEnergySummaryDaily> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppEnergySummaryDaily::getDeleteFlag, VppAuditHelper.NOT_DELETED)
|
|
|
+ .ge(VppEnergySummaryDaily::getSummaryDate, period.getStart())
|
|
|
+ .le(VppEnergySummaryDaily::getSummaryDate, period.getEnd());
|
|
|
+ return energySummaryDailyMapper.selectList(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VppDrEvent> listDrEvents(VppReportHelper.PeriodRange period) {
|
|
|
+ if (period == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ LocalDateTime start = period.getStart().atStartOfDay();
|
|
|
+ LocalDateTime end = period.getEnd().atTime(23, 59, 59);
|
|
|
+ LambdaQueryWrapper<VppDrEvent> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppDrEvent::getDeleteFlag, VppAuditHelper.NOT_DELETED)
|
|
|
+ .ge(VppDrEvent::getStartTime, start)
|
|
|
+ .le(VppDrEvent::getStartTime, end);
|
|
|
+ return drEventMapper.selectList(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<VppDrEvaluation> listDrEvaluations(List<VppDrEvent> events) {
|
|
|
+ if (events == null || events.isEmpty()) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ List<Long> eventIds = events.stream().map(VppDrEvent::getId).collect(Collectors.toList());
|
|
|
+ LambdaQueryWrapper<VppDrEvaluation> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(VppDrEvaluation::getDeleteFlag, VppAuditHelper.NOT_DELETED)
|
|
|
+ .in(VppDrEvaluation::getEventId, eventIds);
|
|
|
+ return drEvaluationMapper.selectList(wrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Integer parseInt(Object value) {
|
|
|
+ if (value == null || !StringUtils.hasText(value.toString())) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return Integer.parseInt(value.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ private String parseString(Object value) {
|
|
|
+ return value == null ? null : value.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private LocalDateTime parseDateTime(Object value) {
|
|
|
+ if (value == null || !StringUtils.hasText(value.toString())) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String text = value.toString();
|
|
|
+ try {
|
|
|
+ return LocalDateTime.parse(text, DEADLINE_FMT);
|
|
|
+ } catch (DateTimeParseException ex) {
|
|
|
+ try {
|
|
|
+ return LocalDate.parse(text).atStartOfDay();
|
|
|
+ } catch (DateTimeParseException ignored) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void validateCreateTaskRequest(ReportTaskCreateRequest request) {
|
|
|
+ if (request == null) {
|
|
|
+ throw new BusinessException("请求体不能为空");
|
|
|
+ }
|
|
|
+ if (!StringUtils.hasText(request.getTaskName())) {
|
|
|
+ throw new BusinessException("任务名称不能为空");
|
|
|
+ }
|
|
|
+ if (request.getReportOrg() == null || request.getReportOrg() < 1 || request.getReportOrg() > 4) {
|
|
|
+ throw new BusinessException("报送机构无效,取值 1~4");
|
|
|
+ }
|
|
|
+ if (request.getReportCycle() == null || request.getReportCycle() < 1 || request.getReportCycle() > 3) {
|
|
|
+ throw new BusinessException("报送周期无效,取值 1~3");
|
|
|
+ }
|
|
|
+ if (!StringUtils.hasText(request.getReportType())) {
|
|
|
+ throw new BusinessException("报表类型编码不能为空");
|
|
|
+ }
|
|
|
+ if (request.getDeadline() == null) {
|
|
|
+ throw new BusinessException("截止日期不能为空");
|
|
|
+ }
|
|
|
+ if (request.getSubmitMode() != null
|
|
|
+ && request.getSubmitMode() != VppReportHelper.SUBMIT_MODE_API
|
|
|
+ && request.getSubmitMode() != VppReportHelper.SUBMIT_MODE_MANUAL) {
|
|
|
+ throw new BusinessException("报送方式无效,取值 1 或 2");
|
|
|
+ }
|
|
|
+ String reportPeriod = resolveReportPeriod(request);
|
|
|
+ if (request.getReportCycle() == 1 && !reportPeriod.matches("\\d{4}-\\d{2}")) {
|
|
|
+ throw new BusinessException("月报报送周期须为 YYYY-MM 格式");
|
|
|
+ }
|
|
|
+ if (request.getReportCycle() == 2 && !reportPeriod.matches("\\d{4}")) {
|
|
|
+ throw new BusinessException("年报报送周期须为 YYYY 格式");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveReportPeriod(ReportTaskCreateRequest request) {
|
|
|
+ if (StringUtils.hasText(request.getReportPeriod())) {
|
|
|
+ return request.getReportPeriod().trim();
|
|
|
+ }
|
|
|
+ if (request.getDeadline() != null) {
|
|
|
+ if (request.getReportCycle() != null && request.getReportCycle() == 2) {
|
|
|
+ return String.valueOf(request.getDeadline().getYear());
|
|
|
+ }
|
|
|
+ return String.format("%04d-%02d", request.getDeadline().getYear(), request.getDeadline().getMonthValue());
|
|
|
+ }
|
|
|
+ throw new BusinessException("报送周期标识不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ensureTaskNoUnique(String taskNo) {
|
|
|
+ LambdaQueryWrapper<VppReportTask> wrapper = activeTaskWrapper();
|
|
|
+ wrapper.eq(VppReportTask::getTaskNo, taskNo);
|
|
|
+ if (taskMapper.selectCount(wrapper) > 0) {
|
|
|
+ throw new BusinessException("任务编号冲突,请重试");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ensureRecordNoUnique(String recordNo) {
|
|
|
+ LambdaQueryWrapper<VppReportRecord> wrapper = activeRecordWrapper();
|
|
|
+ wrapper.eq(VppReportRecord::getRecordNo, recordNo);
|
|
|
+ if (recordMapper.selectCount(wrapper) > 0) {
|
|
|
+ throw new BusinessException("报送记录编号冲突,请重试");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|