This commit is contained in:
2026-06-08 10:28:35 +08:00
parent cb180a8771
commit f33bf970d7
6 changed files with 722 additions and 83 deletions

View File

@@ -38,7 +38,7 @@ public class PartCodeController {
public Map<String, Object> getByCodeId(@PathVariable String codeId) {
Map<String, Object> result = new HashMap<>();
try {
Map<String, Object> list = service.findByCodeId(codeId);
Map<String, Object> list = service.findByCodeId(codeId);
result.put("code", 200);
result.put("data", list);
} catch (Exception e) {
@@ -69,38 +69,28 @@ public class PartCodeController {
try {
Object partIdsObj = params.get("partIds");
Object codeIdsObj = params.get("codeIds");
if (partIdsObj == null || codeIdsObj == null) {
result.put("code", 500);
result.put("message", "参数不能为空");
return result;
}
List<Map<String, Object>> partInfoList;
List<Map<String, Object>> codeInfoList;
if (partIdsObj instanceof List) {
partInfoList = (List<Map<String, Object>>) partIdsObj;
} else {
partInfoList = List.of((Map<String, Object>) partIdsObj);
}
if (codeIdsObj instanceof List) {
codeInfoList = (List<Map<String, Object>>) codeIdsObj;
} else {
codeInfoList = List.of((Map<String, Object>) codeIdsObj);
}
// 从partInfos中提取id和createDate
String[] partIds = new String[partInfoList.size()];
String[] createDates = new String[partInfoList.size()];
for (int i = 0; i < partInfoList.size(); i++) {
Map<String, Object> partInfo = partInfoList.get(i);
partIds[i] = (String) partInfo.get("id");
createDates[i] = (String) partInfo.get("createDate");
}
// 从codeInfos中提取code和data
String[] codeIds = new String[codeInfoList.size()];
String[] codeDatas = new String[codeInfoList.size()];
for (int i = 0; i < codeInfoList.size(); i++) {
@@ -109,12 +99,11 @@ public class PartCodeController {
Map<String, Object> data = (Map<String, Object>) codeInfo.get("data");
codeDatas[i] = data != null ? data.toString() : "";
}
List<PartCodeRelation> list = service.saveBatchWithData(partIds, codeIds, codeDatas, createDates);
List<PartCodeRelation> list = service.saveBatchWithData(partInfoList, codeIds, codeDatas);
result.put("code", 200);
result.put("data", list);
} catch (Exception e) {
e.printStackTrace();
result.put("code", 500);
result.put("message", e.getMessage());
}
@@ -131,7 +120,7 @@ public class PartCodeController {
if (partIdsObj == null || codeIdsObj == null) {
result.put("code", 500);
result.put("message", "鍙傛暟涓嶈兘涓虹┖");
result.put("message", "参数不能为空");
return result;
}
@@ -150,14 +139,6 @@ public class PartCodeController {
codeInfoList = List.of((Map<String, Object>) codeIdsObj);
}
String[] partIds = new String[partInfoList.size()];
String[] createDates = new String[partInfoList.size()];
for (int i = 0; i < partInfoList.size(); i++) {
Map<String, Object> partInfo = partInfoList.get(i);
partIds[i] = (String) partInfo.get("id");
createDates[i] = (String) partInfo.get("createDate");
}
String[] codeIds = new String[codeInfoList.size()];
String[] codeDatas = new String[codeInfoList.size()];
for (int i = 0; i < codeInfoList.size(); i++) {
@@ -167,7 +148,7 @@ public class PartCodeController {
codeDatas[i] = data != null ? data.toString() : "";
}
List<PartCodeRelation> list = service.deleteBatch(partIds, codeIds, codeDatas, createDates);
List<PartCodeRelation> list = service.deleteBatch(partInfoList, codeIds, codeDatas);
result.put("code", 200);
result.put("data", list);
} catch (Exception e) {

View File

@@ -18,6 +18,7 @@ public class PartCodeRelation {
private String codeData;
private String createDate;
private Boolean isLeaf;
private LocalDateTime createTime;
}
}

View File

@@ -12,7 +12,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -183,17 +182,44 @@ public class PartCodeRelationService extends ServiceImpl<PartCodeRelationMapper,
@Transactional
public List<PartCodeRelation> saveBatchWithData(String[] partIds, String[] codeIds, String[] codeDatas) {
return saveBatchWithData(partIds, codeIds, codeDatas, null);
List<Map<String, Object>> partInfoList = new ArrayList<>();
for (String partId : partIds) {
Map<String, Object> partInfo = new HashMap<>();
partInfo.put("id", partId);
partInfoList.add(partInfo);
}
return saveBatchWithData(partInfoList, codeIds, codeDatas);
}
@Transactional
public List<PartCodeRelation> saveBatchWithData(String[] partIds, String[] codeIds, String[] codeDatas, String[] createDates) {
List<Map<String, Object>> partInfoList = new ArrayList<>();
for (int i = 0; i < partIds.length; i++) {
Map<String, Object> partInfo = new HashMap<>();
partInfo.put("id", partIds[i]);
if (createDates != null && i < createDates.length) {
partInfo.put("createDate", createDates[i]);
}
partInfoList.add(partInfo);
}
return saveBatchWithData(partInfoList, codeIds, codeDatas);
}
@Transactional
public List<PartCodeRelation> saveBatchWithData(List<Map<String, Object>> partInfoList, String[] codeIds, String[] codeDatas) {
List<PartCodeRelation> result = new ArrayList<>();
if (partInfoList == null || partInfoList.isEmpty()) {
return result;
}
for (int i = 0; i < codeIds.length; i++) {
String codeId = codeIds[i];
String codeData = codeDatas[i];
for (int j = 0; j < partIds.length; j++) {
String partId = partIds[j];
for (Map<String, Object> partInfo : partInfoList) {
String partId = safeString(partInfo.get("id"));
if (partId == null || partId.isBlank()) {
continue;
}
long cnt = count(new LambdaQueryWrapper<PartCodeRelation>()
.eq(PartCodeRelation::getPartId, partId)
.eq(PartCodeRelation::getCodeId, codeId));
@@ -203,11 +229,13 @@ public class PartCodeRelationService extends ServiceImpl<PartCodeRelationMapper,
relation.setCodeId(codeId);
relation.setCodeData(codeData);
relation.setCreateTime(LocalDateTime.now());
if (createDates != null && createDates[j] != null && !createDates[j].isEmpty()) {
relation.setCreateDate(createDates[j].replaceAll("^(\\d{4}-\\d{2}-\\d{2}).*", "$1"));
String createDate = safeString(partInfo.get("createDate"));
if (createDate != null && !createDate.isEmpty()) {
relation.setCreateDate(createDate.replaceAll("^(\\d{4}-\\d{2}-\\d{2}).*", "$1"));
} else {
relation.setCreateDate("");
}
relation.setIsLeaf(parseBooleanValue(partInfo.get("isLeaf")));
save(relation);
result.add(relation);
}
@@ -217,14 +245,55 @@ public class PartCodeRelationService extends ServiceImpl<PartCodeRelationMapper,
}
@Transactional
public List<PartCodeRelation> deleteBatch(String[] partIds, String[] codeIds, String[] codeDatas, String[] createDates) {
if (partIds == null || partIds.length == 0) {
public List<PartCodeRelation> deleteBatch(List<Map<String, Object>> partInfoList, String[] codeIds, String[] codeDatas) {
if (partInfoList == null || partInfoList.isEmpty()) {
return List.of();
}
List<String> partIds = new ArrayList<>();
for (Map<String, Object> partInfo : partInfoList) {
String partId = safeString(partInfo.get("id"));
if (partId != null && !partId.isBlank()) {
partIds.add(partId);
}
}
if (partIds.isEmpty()) {
return List.of();
}
remove(new LambdaQueryWrapper<PartCodeRelation>()
.in(PartCodeRelation::getPartId, Arrays.asList(partIds)));
.in(PartCodeRelation::getPartId, partIds));
return saveBatchWithData(partIds, codeIds, codeDatas, createDates);
return saveBatchWithData(partInfoList, codeIds, codeDatas);
}
private String safeString(Object value) {
if (value == null) {
return null;
}
return String.valueOf(value);
}
private Boolean parseBooleanValue(Object value) {
if (value == null) {
return null;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof Number) {
return ((Number) value).intValue() != 0;
}
String text = String.valueOf(value).trim();
if (text.isEmpty()) {
return null;
}
if ("1".equals(text)) {
return true;
}
if ("0".equals(text)) {
return false;
}
return Boolean.parseBoolean(text);
}
}

View File

@@ -3,7 +3,9 @@ package com.bim.api.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bim.api.entity.ActAmtResponse;
import com.bim.api.entity.PartCodeRelation;
import com.bim.api.entity.PjDayListResponse;
import com.bim.api.mapper.PartCodeRelationMapper;
import com.bim.api.query.ProjectProgressParams;
import com.bim.api.util.ThirdPartyAuthUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@@ -15,6 +17,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service
public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, PartCodeRelation> {
@@ -29,6 +32,7 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
private static final String SUFFIX_RESULT = ":result";
private static final String SUFFIX_ERROR = ":error";
private static final String SUFFIX_RAW_ACT_AMT = ":rawActAmt";
private static final long TASK_TTL_MINUTES = 10;
private final ThirdPartyAuthUtil thirdPartyAuthUtil;
private final RedisTemplate<String, Object> redisTemplate;
@@ -45,8 +49,19 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
String status = getTaskStatusByDate(normalizedDate);
if ("COMPLETED".equals(status)) {
result.put("status", "COMPLETED");
return result;
Object cachedData = redisTemplate.opsForValue().get(buildTaskKey(normalizedDate, SUFFIX_RESULT));
if (cachedData != null) {
Integer cachedTotal = (Integer) redisTemplate.opsForValue().get(buildTaskKey(normalizedDate, SUFFIX_TOTAL));
int dbTotal = list().size();
if (cachedTotal != null && cachedTotal == dbTotal) {
result.put("status", "COMPLETED");
return result;
}
clearTaskCache(normalizedDate);
} else {
result.put("status", "COMPLETED");
return result;
}
}
String processing = (String) redisTemplate.opsForValue().get(buildProcessingKey(normalizedDate));
@@ -55,7 +70,12 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
return result;
}
Boolean lockResult = redisTemplate.opsForValue().setIfAbsent(buildProcessingKey(normalizedDate), "1");
Boolean lockResult = redisTemplate.opsForValue().setIfAbsent(
buildProcessingKey(normalizedDate),
"1",
TASK_TTL_MINUTES,
TimeUnit.MINUTES
);
if (!Boolean.TRUE.equals(lockResult)) {
result.put("status", "PROCESSING");
return result;
@@ -140,25 +160,31 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
return request;
}
private Map<String, Object> buildPjDayRequest(String projectId, String positionIds, String period) {
Map<String, Object> request = new HashMap<>();
request.put("projectId", projectId);
request.put("positionIds", positionIds);
request.put("period", period);
return request;
}
private void initTask(String date) {
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_STATUS), "STARTED");
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_TOTAL), 0);
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_CURRENT), 0);
redisTemplate.delete(buildTaskKey(date, SUFFIX_RESULT));
redisTemplate.delete(buildTaskKey(date, SUFFIX_ERROR));
redisTemplate.delete(buildTaskKey(date, SUFFIX_RAW_ACT_AMT));
clearTaskCache(date);
setTaskValue(date, SUFFIX_STATUS, "STARTED");
setTaskValue(date, SUFFIX_TOTAL, 0);
setTaskValue(date, SUFFIX_CURRENT, 0);
}
private void calculateProgressAsync(String date) {
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_STATUS), "PROCESSING");
setTaskValue(date, SUFFIX_STATUS, "PROCESSING");
new Thread(() -> {
try {
List<PartCodeRelation> relations = list();
int total = relations.size();
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_TOTAL), total);
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_CURRENT), 0);
setTaskValue(date, SUFFIX_TOTAL, total);
setTaskValue(date, SUFFIX_CURRENT, 0);
List<Map<String, Object>> results = new ArrayList<>();
List<Map<String, Object>> rawActAmtResults = new ArrayList<>();
@@ -168,6 +194,7 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
Map<String, Object> item = new HashMap<>();
item.put("partId", relation.getPartId());
item.put("codeData", relation.getCodeData());
item.put("isLeaf", relation.getIsLeaf());
try {
LocalDateTime createTime = relation.getCreateTime();
@@ -175,34 +202,43 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
String positionId = relation.getPartId();
String requestPeriod = date;
ActAmtResponse actAmtResponse = thirdPartyAuthUtil.findActAmtByPositionId(
PROJECT_ID,
positionId,
requestPeriod
);
Map<String, Object> requestParams = new HashMap<>();
requestParams.put("projectId", PROJECT_ID);
requestParams.put("positionId", positionId);
requestParams.put("period", requestPeriod);
Map<String, Object> requestParams = Boolean.TRUE.equals(relation.getIsLeaf())
? buildPjDayRequest(PROJECT_ID, positionId, requestPeriod)
: buildActAmtRequest(PROJECT_ID, positionId, requestPeriod);
Map<String, Object> rawEntry = new HashMap<>();
rawEntry.put("partId", positionId);
rawEntry.put("period", requestPeriod);
rawEntry.put("originalPeriod", originalPeriod);
rawEntry.put("request", requestParams);
rawEntry.put("response", actAmtResponse);
rawEntry.put("isLeaf", relation.getIsLeaf());
double progressData;
if (Boolean.TRUE.equals(relation.getIsLeaf())) {
ProjectProgressParams params = new ProjectProgressParams();
params.setProjectId(PROJECT_ID);
params.setPositionIds(positionId);
params.setPeriod(requestPeriod);
PjDayListResponse pjDayResponse = thirdPartyAuthUtil.findPjDayListByPositionId(params);
rawEntry.put("api", "findPjDayListByPositionId");
rawEntry.put("response", pjDayResponse);
progressData = calculateAverageByPjDay(pjDayResponse);
} else {
ActAmtResponse actAmtResponse = thirdPartyAuthUtil.findActAmtByPositionId(
PROJECT_ID,
positionId,
requestPeriod
);
rawEntry.put("api", "findActAmtByPositionId");
rawEntry.put("response", actAmtResponse);
progressData = calculateAverageByActAmt(actAmtResponse);
}
rawActAmtResults.add(rawEntry);
double progressData = 0;
if (actAmtResponse != null && actAmtResponse.getData() != null && !actAmtResponse.getData().isEmpty()) {
ActAmtResponse.ActAmtData dayData = actAmtResponse.getData().get(0);
Double actAmt = dayData.getActAmt();
Double meteringAmt = dayData.getMeteringAmt();
if (actAmt != null && meteringAmt != null && meteringAmt != 0) {
progressData = actAmt / meteringAmt;
}
if (Double.isNaN(progressData) || Double.isInfinite(progressData)) {
progressData = 0;
}
item.put("progressData", progressData);
} catch (Exception e) {
@@ -211,27 +247,94 @@ public class ProgressDataService extends ServiceImpl<PartCodeRelationMapper, Par
Map<String, Object> rawEntry = new HashMap<>();
rawEntry.put("partId", relation.getPartId());
rawEntry.put("request", buildActAmtRequest(PROJECT_ID, relation.getPartId(), date));
rawEntry.put("isLeaf", relation.getIsLeaf());
rawEntry.put("request", Boolean.TRUE.equals(relation.getIsLeaf())
? buildPjDayRequest(PROJECT_ID, relation.getPartId(), date)
: buildActAmtRequest(PROJECT_ID, relation.getPartId(), date));
rawEntry.put("api", Boolean.TRUE.equals(relation.getIsLeaf()) ? "findPjDayListByPositionId" : "findActAmtByPositionId");
rawEntry.put("error", e.getMessage());
rawActAmtResults.add(rawEntry);
}
results.add(item);
current++;
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_CURRENT), current);
setTaskValue(date, SUFFIX_CURRENT, current);
}
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_RESULT), results);
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_RAW_ACT_AMT), rawActAmtResults);
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_CURRENT), total);
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_STATUS), "COMPLETED");
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));
} catch (Exception e) {
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_STATUS), "FAILED");
redisTemplate.opsForValue().set(buildTaskKey(date, SUFFIX_ERROR), e.getMessage());
setTaskValue(date, SUFFIX_STATUS, "FAILED");
setTaskValue(date, SUFFIX_ERROR, e.getMessage());
redisTemplate.delete(buildProcessingKey(date));
}
}).start();
}
private void setTaskValue(String date, String suffix, Object value) {
redisTemplate.opsForValue().set(
buildTaskKey(date, 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 double calculateAverageByActAmt(ActAmtResponse response) {
if (response == null || response.getData() == null || response.getData().isEmpty()) {
return 0;
}
double sum = 0;
int count = 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;
}
private double calculateAverageByPjDay(PjDayListResponse response) {
if (response == null || response.getData() == null || response.getData().isEmpty()) {
return 0;
}
double sum = 0;
int count = 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++;
}
return count == 0 ? 0 : sum / count;
}
}

View File

@@ -1,13 +1,14 @@
spring:
datasource:
url: jdbc:mysql://localhost:3306/bim_project?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: root
url: jdbc:mysql://192.168.1.80:3306/bim_project?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: bim_project
password: 3nGFBMFhhMW4fjeH
data:
redis:
host: localhost
port: 6379
database: 0
password:
third-party:
api: