|
|
@@ -8,6 +8,11 @@ import com.zksy.base.warning.domain.WarningItem;
|
|
|
import com.zksy.base.warning.mapper.WarningItemMapper;
|
|
|
import com.zksy.base.warning.service.WarningItemService;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
+import com.zksy.base.warning.service.WarningCatalogRules;
|
|
|
+import com.zksy.common.exception.ServiceException;
|
|
|
+import com.zksy.common.utils.SecurityUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import java.util.Objects;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
@@ -17,14 +22,14 @@ import java.util.Arrays;
|
|
|
import java.util.Date;
|
|
|
import java.util.HashSet;
|
|
|
import java.util.Locale;
|
|
|
-import java.util.Map;
|
|
|
import java.util.Set;
|
|
|
-import java.util.Collections;
|
|
|
|
|
|
@Slf4j
|
|
|
@Service
|
|
|
public class WarningItemServiceImpl extends ServiceImpl<WarningItemMapper, WarningItem> implements WarningItemService {
|
|
|
|
|
|
+ @Autowired private WarningCatalogRules catalogRules;
|
|
|
+
|
|
|
private static final Set<String> ALLOWED_STATUSES =
|
|
|
new HashSet<>(Arrays.asList("ENABLED", "DISABLED", "INVALID"));
|
|
|
|
|
|
@@ -60,17 +65,11 @@ public class WarningItemServiceImpl extends ServiceImpl<WarningItemMapper, Warni
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public boolean addWarningItem(WarningItem warningItem) {
|
|
|
validateWarningItem(warningItem, false);
|
|
|
- if (warningItem.getItemCode() == null || warningItem.getItemCode().isEmpty()) {
|
|
|
- if (warningItem.getRegion() != null && !warningItem.getRegion().trim().isEmpty()) {
|
|
|
- warningItem.setItemCode(generateItemCode(warningItem.getRegion(), warningItem.getWarningType(), warningItem.getDrainageType()));
|
|
|
- } else {
|
|
|
- // Existing integrations may still submit only warningType/warningLevel.
|
|
|
- warningItem.setItemCode(generateItemCode(warningItem.getWarningType(), warningItem.getWarningLevel()));
|
|
|
- }
|
|
|
- }
|
|
|
- if (getItemByCode(warningItem.getItemCode()) != null) {
|
|
|
- throw new IllegalArgumentException("事项编码已存在:" + warningItem.getItemCode());
|
|
|
- }
|
|
|
+ baseMapper.lockForCodeGeneration();
|
|
|
+ warningItem.setItemId(null);
|
|
|
+ warningItem.setItemCode(generateItemCode(warningItem.getWarningType(), warningItem.getWarningLevel()));
|
|
|
+ warningItem.setCreateBy(SecurityUtils.getUsername());
|
|
|
+ warningItem.setUpdateBy(SecurityUtils.getUsername());
|
|
|
if (warningItem.getStatus() == null) {
|
|
|
warningItem.setStatus("ENABLED");
|
|
|
}
|
|
|
@@ -82,10 +81,17 @@ public class WarningItemServiceImpl extends ServiceImpl<WarningItemMapper, Warni
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public boolean updateWarningItem(WarningItem warningItem) {
|
|
|
validateWarningItem(warningItem, true);
|
|
|
- WarningItem sameCodeItem = getItemByCode(warningItem.getItemCode());
|
|
|
- if (sameCodeItem != null && !sameCodeItem.getItemId().equals(warningItem.getItemId())) {
|
|
|
- throw new IllegalArgumentException("事项编码已存在:" + warningItem.getItemCode());
|
|
|
- }
|
|
|
+ WarningItem existing = baseMapper.selectForUpdate(warningItem.getItemId());
|
|
|
+ requireEditable(existing);
|
|
|
+ if (!Objects.equals(existing.getWarningType(), warningItem.getWarningType())
|
|
|
+ || !Objects.equals(existing.getWarningLevel(), warningItem.getWarningLevel())) {
|
|
|
+ throw new ServiceException("事项类型和级别确定后不可修改,请新增事项配置");
|
|
|
+ }
|
|
|
+ warningItem.setItemCode(existing.getItemCode());
|
|
|
+ warningItem.setStatus(existing.getStatus());
|
|
|
+ warningItem.setCreateBy(existing.getCreateBy());
|
|
|
+ warningItem.setCreateTime(existing.getCreateTime());
|
|
|
+ warningItem.setUpdateBy(SecurityUtils.getUsername());
|
|
|
warningItem.setUpdateTime(LocalDateTime.now());
|
|
|
return this.updateById(warningItem);
|
|
|
}
|
|
|
@@ -99,7 +105,11 @@ public class WarningItemServiceImpl extends ServiceImpl<WarningItemMapper, Warni
|
|
|
if (!ALLOWED_STATUSES.contains(status)) {
|
|
|
throw new IllegalArgumentException("不支持的事项状态:" + status);
|
|
|
}
|
|
|
+ WarningItem existing = baseMapper.selectForUpdate(itemId);
|
|
|
+ requireEditable(existing);
|
|
|
+ if ("ENABLED".equals(status)) validateWarningItem(existing, true);
|
|
|
WarningItem item = new WarningItem();
|
|
|
+ item.setUpdateBy(SecurityUtils.getUsername());
|
|
|
item.setItemId(itemId);
|
|
|
item.setStatus(status);
|
|
|
item.setUpdateTime(LocalDateTime.now());
|
|
|
@@ -118,43 +128,12 @@ public class WarningItemServiceImpl extends ServiceImpl<WarningItemMapper, Warni
|
|
|
return this.getOne(wrapper);
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public String generateItemCode(String region, String warningType, String drainageType) {
|
|
|
- String regionCode = normalizeCode(region, REGION_CODES, "区域", false);
|
|
|
- String typeCode = normalizeCode(warningType, TYPE_CODES, "预警类型", false);
|
|
|
- String subtypeCode = "";
|
|
|
- if ("PS".equals(typeCode)) {
|
|
|
- subtypeCode = normalizeCode(drainageType, DRAINAGE_CODES, "排水类型", true);
|
|
|
- }
|
|
|
-
|
|
|
- String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
|
|
- String prefix = regionCode + typeCode + subtypeCode;
|
|
|
- String codePrefix = prefix + dateStr;
|
|
|
- LambdaQueryWrapper<WarningItem> wrapper = new LambdaQueryWrapper<>();
|
|
|
- wrapper.likeRight(WarningItem::getItemCode, codePrefix);
|
|
|
- wrapper.orderByDesc(WarningItem::getItemCode);
|
|
|
- wrapper.last("LIMIT 1");
|
|
|
-
|
|
|
- WarningItem lastItem = this.getOne(wrapper);
|
|
|
- int sequence = nextSequence(lastItem, codePrefix);
|
|
|
- return codePrefix + String.format("%04d", sequence);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Legacy format retained for existing callers that have not migrated to region/type parameters.
|
|
|
- */
|
|
|
@Override
|
|
|
public String generateItemCode(String warningType, String warningLevel) {
|
|
|
- if (warningType == null || warningType.trim().isEmpty()) {
|
|
|
- throw new IllegalArgumentException("预警类型不能为空");
|
|
|
- }
|
|
|
- if (warningLevel == null || warningLevel.trim().isEmpty()) {
|
|
|
- throw new IllegalArgumentException("预警级别不能为空");
|
|
|
- }
|
|
|
- warningType = warningType.trim();
|
|
|
- warningLevel = warningLevel.trim();
|
|
|
+ catalogRules.validateTypeAndLevel(warningType, warningLevel);
|
|
|
+ if (!warningType.matches("[A-Za-z0-9_]{1,32}")) throw new ServiceException("预警类型编码仅支持字母、数字和下划线");
|
|
|
String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
|
|
- String prefix = "WI" + warningType.substring(0, Math.min(3, warningType.length())).toUpperCase() + warningLevel;
|
|
|
+ String prefix = "WI-" + warningType.toUpperCase(Locale.ROOT) + "-L" + warningLevel + "-";
|
|
|
|
|
|
LambdaQueryWrapper<WarningItem> wrapper = new LambdaQueryWrapper<>();
|
|
|
wrapper.likeRight(WarningItem::getItemCode, prefix + dateStr);
|
|
|
@@ -162,21 +141,12 @@ public class WarningItemServiceImpl extends ServiceImpl<WarningItemMapper, Warni
|
|
|
wrapper.last("LIMIT 1");
|
|
|
|
|
|
WarningItem lastItem = this.getOne(wrapper);
|
|
|
- int sequence = 1;
|
|
|
-
|
|
|
- if (lastItem != null) {
|
|
|
- String lastCode = lastItem.getItemCode();
|
|
|
- if (lastCode != null && lastCode.startsWith(prefix + dateStr) && lastCode.length() >= 4) {
|
|
|
- String lastSeqStr = lastCode.substring(lastCode.length() - 4);
|
|
|
- try {
|
|
|
- sequence = Integer.parseInt(lastSeqStr) + 1;
|
|
|
- } catch (NumberFormatException ignored) {
|
|
|
- sequence = 1;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return prefix + dateStr + String.format("%04d", sequence);
|
|
|
+ return prefix + dateStr + String.format("%04d", nextSequence(lastItem, prefix + dateStr));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void requireEditable(WarningItem item) {
|
|
|
+ if (item == null) throw new ServiceException("预警事项不存在");
|
|
|
+ if ("INVALID".equals(item.getStatus())) throw new ServiceException("作废事项不可修改或重新启用");
|
|
|
}
|
|
|
|
|
|
private int nextSequence(WarningItem lastItem, String codePrefix) {
|
|
|
@@ -187,7 +157,7 @@ public class WarningItemServiceImpl extends ServiceImpl<WarningItemMapper, Warni
|
|
|
try {
|
|
|
int sequence = Integer.parseInt(lastSeqStr) + 1;
|
|
|
if (sequence > 9999) {
|
|
|
- throw new IllegalStateException("当天该区域、类型和排水分类的事项编码已达到流水号上限");
|
|
|
+ throw new IllegalStateException("当天该类型和级别的事项编码已达到流水号上限");
|
|
|
}
|
|
|
return sequence;
|
|
|
} catch (NumberFormatException ex) {
|
|
|
@@ -195,48 +165,6 @@ public class WarningItemServiceImpl extends ServiceImpl<WarningItemMapper, Warni
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static final Map<String, String> REGION_CODES;
|
|
|
- private static final Map<String, String> TYPE_CODES;
|
|
|
- private static final Map<String, String> DRAINAGE_CODES;
|
|
|
-
|
|
|
- static {
|
|
|
- Map<String, String> regions = new java.util.HashMap<>();
|
|
|
- regions.put("CN", "CN"); regions.put("城南", "CN");
|
|
|
- regions.put("CB", "CB"); regions.put("城北", "CB");
|
|
|
- regions.put("TC", "TC"); regions.put("太常", "TC");
|
|
|
- REGION_CODES = Collections.unmodifiableMap(regions);
|
|
|
-
|
|
|
- Map<String, String> types = new java.util.HashMap<>();
|
|
|
- types.put("RQ", "RQ"); types.put("燃气", "RQ"); types.put("GAS_PIPE", "RQ");
|
|
|
- types.put("GS", "GS"); types.put("供水", "GS"); types.put("WATER_PIPE", "GS");
|
|
|
- types.put("PS", "PS"); types.put("排水", "PS"); types.put("SEWER_PIPE", "PS");
|
|
|
- types.put("YJ", "YJ"); types.put("窨井", "YJ"); types.put("MANHOLE", "YJ");
|
|
|
- TYPE_CODES = Collections.unmodifiableMap(types);
|
|
|
-
|
|
|
- Map<String, String> drainage = new java.util.HashMap<>();
|
|
|
- drainage.put("YS", "YS"); drainage.put("雨水", "YS"); drainage.put("RAIN", "YS");
|
|
|
- drainage.put("WS", "WS"); drainage.put("污水", "WS"); drainage.put("SEWAGE", "WS");
|
|
|
- DRAINAGE_CODES = Collections.unmodifiableMap(drainage);
|
|
|
- }
|
|
|
-
|
|
|
- private String normalizeCode(String value, Map<String, String> aliases, String fieldName, boolean required) {
|
|
|
- if (value == null || value.trim().isEmpty()) {
|
|
|
- if (required) {
|
|
|
- throw new IllegalArgumentException(fieldName + "不能为空");
|
|
|
- }
|
|
|
- throw new IllegalArgumentException(fieldName + "不能为空");
|
|
|
- }
|
|
|
- String normalized = value.trim().toUpperCase(Locale.ROOT);
|
|
|
- String code = aliases.get(normalized);
|
|
|
- if (code == null) {
|
|
|
- code = aliases.get(value.trim());
|
|
|
- }
|
|
|
- if (code == null) {
|
|
|
- throw new IllegalArgumentException("不支持的" + fieldName + ":" + value);
|
|
|
- }
|
|
|
- return code;
|
|
|
- }
|
|
|
-
|
|
|
private void validateWarningItem(WarningItem warningItem, boolean update) {
|
|
|
if (warningItem == null) {
|
|
|
throw new IllegalArgumentException("预警事项不能为空");
|
|
|
@@ -253,8 +181,16 @@ public class WarningItemServiceImpl extends ServiceImpl<WarningItemMapper, Warni
|
|
|
if (warningItem.getWarningLevel() == null || warningItem.getWarningLevel().trim().isEmpty()) {
|
|
|
throw new IllegalArgumentException("预警级别不能为空");
|
|
|
}
|
|
|
- if (warningItem.getDuration() != null && warningItem.getDuration() <= 0) {
|
|
|
- throw new IllegalArgumentException("预警时长必须大于0");
|
|
|
+ if (warningItem.getDuration() == null || warningItem.getDuration() <= 0 || warningItem.getDuration() > 720) {
|
|
|
+ throw new IllegalArgumentException("处置时限必须为 1 至 720 小时");
|
|
|
+ }
|
|
|
+ catalogRules.validateTypeAndLevel(warningItem.getWarningType(), warningItem.getWarningLevel());
|
|
|
+ warningItem.setLinkConfig(WarningCatalogRules.validateLinks(warningItem.getLinkConfig()));
|
|
|
+ if (warningItem.getIndustry() == null || warningItem.getIndustry().trim().isEmpty()) throw new ServiceException("请选择所属行业");
|
|
|
+ String source = warningItem.getSourceWarningCode();
|
|
|
+ warningItem.setSourceWarningCode(source == null ? "" : source.trim().toUpperCase(Locale.ROOT));
|
|
|
+ if (!warningItem.getSourceWarningCode().isEmpty() && !warningItem.getSourceWarningCode().matches("[A-Z0-9_-]{1,64}")) {
|
|
|
+ throw new ServiceException("采集报警编码仅支持字母、数字、横线和下划线,最多 64 位");
|
|
|
}
|
|
|
if (warningItem.getStatus() != null && !ALLOWED_STATUSES.contains(warningItem.getStatus())) {
|
|
|
throw new IllegalArgumentException("不支持的事项状态:" + warningItem.getStatus());
|