优化进度管理页面接口

This commit is contained in:
cjh
2026-06-24 15:39:34 +08:00
parent 2f9be5d98d
commit 98d6ded07e
3 changed files with 162 additions and 85 deletions

View File

@@ -0,0 +1,17 @@
package com.bim.api.config;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer longToStringCustomizer() {
return builder -> builder
.serializerByType(Long.class, ToStringSerializer.instance)
.serializerByType(Long.TYPE, ToStringSerializer.instance);
}
}

View File

@@ -47,13 +47,15 @@ public class BimController {
}
@GetMapping("/progressData")
public Map<String, Object> getProgressData(@RequestParam String date) {
return progressDataService.getOrCreateTask(date);
public Map<String, Object> getProgressData(@RequestParam String date,
@RequestParam(required = false) Long modelId) {
return progressDataService.getOrCreateTask(date, modelId);
}
@GetMapping("/progressData/result")
public Map<String, Object> getProgressDataResult(@RequestParam String date) {
return progressDataService.getTaskResultByDate(date);
public Map<String, Object> getProgressDataResult(@RequestParam String date,
@RequestParam(required = false) Long modelId) {
return progressDataService.getTaskResultByDate(date, modelId);
}
@PostMapping("/getPjDayListByPositionId")

View File

@@ -1,5 +1,6 @@
package com.bim.api.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bim.api.entity.ActAmtResponse;
import com.bim.api.entity.PartCodeRelation;
@@ -15,6 +16,7 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -42,36 +44,38 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
this.redisTemplate = redisTemplate;
}
public Map<String, Object> getOrCreateTask(String date) {
public Map<String, Object> getOrCreateTask(String date, Long modelId) {
String normalizedDate = normalizeDate(date);
String taskScope = buildTaskScope(normalizedDate, modelId);
Map<String, Object> result = new HashMap<>();
result.put("date", normalizedDate);
result.put("modelId", modelId == null ? null : String.valueOf(modelId));
String status = getTaskStatusByDate(normalizedDate);
String status = getTaskStatusByDate(taskScope);
if ("COMPLETED".equals(status)) {
Object cachedData = redisTemplate.opsForValue().get(buildTaskKey(normalizedDate, SUFFIX_RESULT));
Object cachedData = redisTemplate.opsForValue().get(buildTaskKey(taskScope, SUFFIX_RESULT));
if (cachedData != null) {
Integer cachedTotal = (Integer) redisTemplate.opsForValue().get(buildTaskKey(normalizedDate, SUFFIX_TOTAL));
int dbTotal = list().size();
if (cachedTotal != null && cachedTotal == dbTotal) {
Integer cachedTotal = (Integer) redisTemplate.opsForValue().get(buildTaskKey(taskScope, SUFFIX_TOTAL));
int dbTotal = listRelationsByModel(modelId).size();
if (cachedTotal != null && cachedTotal == dbTotal && hasCurrentResultShape(cachedData)) {
result.put("status", "COMPLETED");
return result;
}
clearTaskCache(normalizedDate);
clearTaskCache(taskScope);
} else {
result.put("status", "COMPLETED");
return result;
}
}
String processing = (String) redisTemplate.opsForValue().get(buildProcessingKey(normalizedDate));
String processing = (String) redisTemplate.opsForValue().get(buildProcessingKey(taskScope));
if (processing != null) {
result.put("status", "PROCESSING");
return result;
}
Boolean lockResult = redisTemplate.opsForValue().setIfAbsent(
buildProcessingKey(normalizedDate),
buildProcessingKey(taskScope),
"1",
TASK_TTL_MINUTES,
TimeUnit.MINUTES
@@ -81,29 +85,31 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
return result;
}
initTask(normalizedDate);
calculateProgressAsync(normalizedDate);
initTask(taskScope);
calculateProgressAsync(normalizedDate, taskScope, modelId);
result.put("status", "STARTED");
return result;
}
public Map<String, Object> getTaskResultByDate(String date) {
public Map<String, Object> getTaskResultByDate(String date, Long modelId) {
String normalizedDate = normalizeDate(date);
String taskScope = buildTaskScope(normalizedDate, modelId);
Map<String, Object> result = new HashMap<>();
result.put("date", normalizedDate);
result.put("modelId", modelId == null ? null : String.valueOf(modelId));
String status = getTaskStatusByDate(normalizedDate);
String status = getTaskStatusByDate(taskScope);
result.put("status", status);
if (status == null) {
String processing = (String) redisTemplate.opsForValue().get(buildProcessingKey(normalizedDate));
String processing = (String) redisTemplate.opsForValue().get(buildProcessingKey(taskScope));
result.put("status", processing != null ? "PROCESSING" : "NOT_FOUND");
return result;
}
Integer total = (Integer) redisTemplate.opsForValue().get(buildTaskKey(normalizedDate, SUFFIX_TOTAL));
Integer current = (Integer) redisTemplate.opsForValue().get(buildTaskKey(normalizedDate, SUFFIX_CURRENT));
Integer total = (Integer) redisTemplate.opsForValue().get(buildTaskKey(taskScope, SUFFIX_TOTAL));
Integer current = (Integer) redisTemplate.opsForValue().get(buildTaskKey(taskScope, SUFFIX_CURRENT));
result.put("total", total != null ? total : 0);
result.put("current", current != null ? current : 0);
@@ -115,12 +121,12 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
result.put("progress", progress);
if ("COMPLETED".equals(status)) {
Object data = redisTemplate.opsForValue().get(buildTaskKey(normalizedDate, SUFFIX_RESULT));
Object data = redisTemplate.opsForValue().get(buildTaskKey(taskScope, SUFFIX_RESULT));
result.put("data", data);
}
if ("FAILED".equals(status)) {
Object error = redisTemplate.opsForValue().get(buildTaskKey(normalizedDate, SUFFIX_ERROR));
Object error = redisTemplate.opsForValue().get(buildTaskKey(taskScope, SUFFIX_ERROR));
if (error != null) {
result.put("error", error);
}
@@ -140,16 +146,20 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
}
}
private String buildTaskKey(String date, String suffix) {
return KEY_TASK_PREFIX + date + suffix;
private String buildTaskScope(String date, Long modelId) {
return modelId == null ? date : date + ":model:" + modelId;
}
private String buildProcessingKey(String date) {
return KEY_PROCESSING_PREFIX + date;
private String buildTaskKey(String taskScope, String suffix) {
return KEY_TASK_PREFIX + taskScope + suffix;
}
private String getTaskStatusByDate(String date) {
return (String) redisTemplate.opsForValue().get(buildTaskKey(date, SUFFIX_STATUS));
private String buildProcessingKey(String taskScope) {
return KEY_PROCESSING_PREFIX + taskScope;
}
private String getTaskStatusByDate(String taskScope) {
return (String) redisTemplate.opsForValue().get(buildTaskKey(taskScope, SUFFIX_STATUS));
}
private Map<String, Object> buildActAmtRequest(String projectId, String positionId, String period) {
@@ -175,25 +185,28 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
setTaskValue(date, SUFFIX_CURRENT, 0);
}
private void calculateProgressAsync(String date) {
setTaskValue(date, SUFFIX_STATUS, "PROCESSING");
private void calculateProgressAsync(String date, String taskScope, Long modelId) {
setTaskValue(taskScope, SUFFIX_STATUS, "PROCESSING");
new Thread(() -> {
try {
List<PartCodeRelation> relations = list();
List<PartCodeRelation> relations = listRelationsByModel(modelId);
int total = relations.size();
setTaskValue(date, SUFFIX_TOTAL, total);
setTaskValue(date, SUFFIX_CURRENT, 0);
setTaskValue(taskScope, SUFFIX_TOTAL, total);
setTaskValue(taskScope, SUFFIX_CURRENT, 0);
List<Map<String, Object>> results = new ArrayList<>();
List<Map<String, Object>> rawActAmtResults = new ArrayList<>();
int current = 0;
for (PartCodeRelation relation : relations) {
Map<String, List<PartCodeRelation>> groupedRelations = groupRelationsByPart(relations);
for (List<PartCodeRelation> partRelations : groupedRelations.values()) {
PartCodeRelation relation = partRelations.get(0);
Map<String, Object> item = new HashMap<>();
item.put("partId", relation.getPartId());
item.put("codeData", relation.getCodeData());
item.put("codeList", buildCodeList(partRelations));
item.put("isLeaf", relation.getIsLeaf());
try {
@@ -213,7 +226,7 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
rawEntry.put("request", requestParams);
rawEntry.put("isLeaf", relation.getIsLeaf());
double progressData;
ProgressAmount progressAmount;
if (Boolean.TRUE.equals(relation.getIsLeaf())) {
ProjectProgressParams params = new ProjectProgressParams();
params.setProjectId(PROJECT_ID);
@@ -223,7 +236,7 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
PjDayListResponse pjDayResponse = thirdPartyAuthUtil.findPjDayListByPositionId(params);
rawEntry.put("api", "findPjDayListByPositionId");
rawEntry.put("response", pjDayResponse);
progressData = calculateAverageByPjDay(pjDayResponse);
progressAmount = calculateProgressAmountByPjDay(pjDayResponse);
} else {
ActAmtResponse actAmtResponse = thirdPartyAuthUtil.findActAmtByPositionId(
PROJECT_ID,
@@ -232,15 +245,18 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
);
rawEntry.put("api", "findActAmtByPositionId");
rawEntry.put("response", actAmtResponse);
progressData = calculateAverageByActAmt(actAmtResponse);
progressAmount = calculateProgressAmountByActAmt(actAmtResponse);
}
rawActAmtResults.add(rawEntry);
double progressData = progressAmount.ratio();
if (Double.isNaN(progressData) || Double.isInfinite(progressData)) {
progressData = 0;
}
item.put("progressData", progressData);
item.put("progressNumerator", progressAmount.numerator());
item.put("progressDenominator", progressAmount.denominator());
} catch (Exception e) {
item.put("progressData", 0);
item.put("error", e.getMessage());
@@ -257,84 +273,126 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
}
results.add(item);
current++;
setTaskValue(date, SUFFIX_CURRENT, current);
current += partRelations.size();
setTaskValue(taskScope, SUFFIX_CURRENT, current);
}
setTaskValue(date, SUFFIX_RESULT, results);
setTaskValue(date, SUFFIX_RAW_ACT_AMT, rawActAmtResults);
setTaskValue(date, SUFFIX_CURRENT, total);
setTaskValue(date, SUFFIX_STATUS, "COMPLETED");
redisTemplate.delete(buildProcessingKey(date));
setTaskValue(taskScope, SUFFIX_RESULT, results);
setTaskValue(taskScope, SUFFIX_RAW_ACT_AMT, rawActAmtResults);
setTaskValue(taskScope, SUFFIX_CURRENT, total);
setTaskValue(taskScope, SUFFIX_STATUS, "COMPLETED");
redisTemplate.delete(buildProcessingKey(taskScope));
} catch (Exception e) {
setTaskValue(date, SUFFIX_STATUS, "FAILED");
setTaskValue(date, SUFFIX_ERROR, e.getMessage());
redisTemplate.delete(buildProcessingKey(date));
setTaskValue(taskScope, SUFFIX_STATUS, "FAILED");
setTaskValue(taskScope, SUFFIX_ERROR, e.getMessage());
redisTemplate.delete(buildProcessingKey(taskScope));
}
}).start();
}
private void setTaskValue(String date, String suffix, Object value) {
private void setTaskValue(String taskScope, String suffix, Object value) {
redisTemplate.opsForValue().set(
buildTaskKey(date, suffix),
buildTaskKey(taskScope, suffix),
value,
TASK_TTL_MINUTES,
TimeUnit.MINUTES
);
}
private void clearTaskCache(String date) {
redisTemplate.delete(buildTaskKey(date, SUFFIX_STATUS));
redisTemplate.delete(buildTaskKey(date, SUFFIX_TOTAL));
redisTemplate.delete(buildTaskKey(date, SUFFIX_CURRENT));
redisTemplate.delete(buildTaskKey(date, SUFFIX_RESULT));
redisTemplate.delete(buildTaskKey(date, SUFFIX_ERROR));
redisTemplate.delete(buildTaskKey(date, SUFFIX_RAW_ACT_AMT));
redisTemplate.delete(buildProcessingKey(date));
private void clearTaskCache(String taskScope) {
redisTemplate.delete(buildTaskKey(taskScope, SUFFIX_STATUS));
redisTemplate.delete(buildTaskKey(taskScope, SUFFIX_TOTAL));
redisTemplate.delete(buildTaskKey(taskScope, SUFFIX_CURRENT));
redisTemplate.delete(buildTaskKey(taskScope, SUFFIX_RESULT));
redisTemplate.delete(buildTaskKey(taskScope, SUFFIX_ERROR));
redisTemplate.delete(buildTaskKey(taskScope, SUFFIX_RAW_ACT_AMT));
redisTemplate.delete(buildProcessingKey(taskScope));
}
private double calculateAverageByActAmt(ActAmtResponse response) {
if (response == null || response.getData() == null || response.getData().isEmpty()) {
return 0;
private List<PartCodeRelation> listRelationsByModel(Long modelId) {
return list(new LambdaQueryWrapper<PartCodeRelation>()
.eq(modelId != null, PartCodeRelation::getModelId, modelId));
}
private boolean hasCurrentResultShape(Object cachedData) {
if (!(cachedData instanceof List<?> cachedList)) {
return false;
}
double sum = 0;
int count = 0;
if (cachedList.isEmpty()) {
return true;
}
Object first = cachedList.get(0);
return first instanceof Map<?, ?> cachedItem
&& cachedItem.containsKey("codeList")
&& cachedItem.containsKey("progressNumerator")
&& cachedItem.containsKey("progressDenominator");
}
private Map<String, List<PartCodeRelation>> groupRelationsByPart(List<PartCodeRelation> relations) {
Map<String, List<PartCodeRelation>> grouped = new LinkedHashMap<>();
for (PartCodeRelation relation : relations) {
if (relation == null || relation.getPartId() == null || relation.getPartId().isBlank()) {
continue;
}
String key = relation.getPartId() + "|" + relation.getIsLeaf();
grouped.computeIfAbsent(key, ignored -> new ArrayList<>()).add(relation);
}
return grouped;
}
private List<Map<String, Object>> buildCodeList(List<PartCodeRelation> relations) {
List<Map<String, Object>> codeList = new ArrayList<>();
for (PartCodeRelation relation : relations) {
Map<String, Object> codeItem = new HashMap<>();
codeItem.put("codeId", relation.getCodeId());
codeItem.put("code", relation.getCodeId());
codeItem.put("codeData", relation.getCodeData());
codeItem.put("modelId", relation.getModelId() == null ? null : String.valueOf(relation.getModelId()));
codeList.add(codeItem);
}
return codeList;
}
private ProgressAmount calculateProgressAmountByActAmt(ActAmtResponse response) {
if (response == null || response.getData() == null || response.getData().isEmpty()) {
return new ProgressAmount(0, 0);
}
double numeratorSum = 0;
double denominatorSum = 0;
for (ActAmtResponse.ActAmtData data : response.getData()) {
if (data == null) {
continue;
}
Double numerator = data.getActAmt();
Double denominator = data.getMeteringAmt();
double ratio = 0;
if (numerator != null && denominator != null && denominator != 0) {
ratio = numerator / denominator;
}
sum += ratio;
count++;
}
return count == 0 ? 0 : sum / count;
numeratorSum += safeDouble(data.getActAmt());
denominatorSum += safeDouble(data.getMeteringAmt());
}
return new ProgressAmount(numeratorSum, denominatorSum);
}
private double calculateAverageByPjDay(PjDayListResponse response) {
private ProgressAmount calculateProgressAmountByPjDay(PjDayListResponse response) {
if (response == null || response.getData() == null || response.getData().isEmpty()) {
return 0;
return new ProgressAmount(0, 0);
}
double sum = 0;
int count = 0;
double numeratorSum = 0;
double denominatorSum = 0;
for (PjDayListResponse.PjDayData data : response.getData()) {
if (data == null) {
continue;
}
Double numerator = data.getTotalAmt();
Double denominator = data.getMeteringAmt();
double ratio = 0;
if (numerator != null && denominator != null && denominator != 0) {
ratio = numerator / denominator;
}
sum += ratio;
count++;
numeratorSum += safeDouble(data.getTotalAmt());
denominatorSum += safeDouble(data.getMeteringAmt());
}
return new ProgressAmount(numeratorSum, denominatorSum);
}
private double safeDouble(Double value) {
return value == null || Double.isNaN(value) || Double.isInfinite(value) ? 0 : value;
}
private record ProgressAmount(double numerator, double denominator) {
double ratio() {
return denominator == 0 ? 0 : numerator / denominator;
}
return count == 0 ? 0 : sum / count;
}
}