diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/config/SystemParamController.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/config/SystemParamController.java new file mode 100644 index 0000000..a684219 --- /dev/null +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/config/SystemParamController.java @@ -0,0 +1,50 @@ +package cn.craftlabs.platform.api.config; + +import cn.craftlabs.platform.api.persistence.system.PlatformSystemParam; +import cn.craftlabs.platform.api.persistence.system.PlatformSystemParamMapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/v1/system-params") +public class SystemParamController { + + private final PlatformSystemParamMapper paramMapper; + + public SystemParamController(PlatformSystemParamMapper paramMapper) { + this.paramMapper = paramMapper; + } + + @GetMapping + public Map list() { + List params = paramMapper.selectList(Wrappers.lambdaQuery()); + return params.stream().collect(Collectors.toMap( + PlatformSystemParam::getParamKey, PlatformSystemParam::getParamValue)); + } + + @PutMapping + public ResponseEntity update(@RequestBody Map body) { + for (var entry : body.entrySet()) { + PlatformSystemParam param = paramMapper.selectById(entry.getKey()); + if (param == null) { + param = new PlatformSystemParam(); + param.setParamKey(entry.getKey()); + } + param.setParamValue(entry.getValue()); + param.setUpdatedAt(OffsetDateTime.now(ZoneOffset.UTC)); + if (param.getParamKey() != null && paramMapper.selectById(param.getParamKey()) != null) { + paramMapper.updateById(param); + } else { + paramMapper.insert(param); + } + } + return ResponseEntity.ok().build(); + } +} diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/system/PlatformSystemParam.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/system/PlatformSystemParam.java new file mode 100644 index 0000000..f2cf076 --- /dev/null +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/system/PlatformSystemParam.java @@ -0,0 +1,29 @@ +package cn.craftlabs.platform.api.persistence.system; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.time.OffsetDateTime; + +@TableName("platform_system_param") +public class PlatformSystemParam { + + @TableId + @TableField("param_key") + private String paramKey; + + @TableField("param_value") + private String paramValue; + + @TableField("updated_at") + private OffsetDateTime updatedAt; + + public String getParamKey() { return paramKey; } + public void setParamKey(String paramKey) { this.paramKey = paramKey; } + + public String getParamValue() { return paramValue; } + public void setParamValue(String paramValue) { this.paramValue = paramValue; } + + public OffsetDateTime getUpdatedAt() { return updatedAt; } + public void setUpdatedAt(OffsetDateTime updatedAt) { this.updatedAt = updatedAt; } +} diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/system/PlatformSystemParamMapper.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/system/PlatformSystemParamMapper.java new file mode 100644 index 0000000..8db38d6 --- /dev/null +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/system/PlatformSystemParamMapper.java @@ -0,0 +1,8 @@ +package cn.craftlabs.platform.api.persistence.system; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface PlatformSystemParamMapper extends BaseMapper { +} diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/LicenseSnService.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/LicenseSnService.java index 6596fb5..874a573 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/LicenseSnService.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/LicenseSnService.java @@ -9,15 +9,23 @@ import cn.craftlabs.platform.api.persistence.contract.PlatformContractLineMapper import cn.craftlabs.platform.api.persistence.contract.PlatformContractMapper; import cn.craftlabs.platform.api.persistence.delivery.PlatformDeliveryBatch; import cn.craftlabs.platform.api.persistence.delivery.PlatformDeliveryBatchMapper; +import cn.craftlabs.platform.api.persistence.license.PlatformLicenseActivation; +import cn.craftlabs.platform.api.persistence.license.PlatformLicenseActivationMapper; +import cn.craftlabs.platform.api.persistence.license.PlatformLicenseKey; +import cn.craftlabs.platform.api.persistence.license.PlatformLicenseKeyMapper; +import cn.craftlabs.platform.api.persistence.license.PlatformLicenseMapper; import cn.craftlabs.platform.api.persistence.license.PlatformLicenseSn; import cn.craftlabs.platform.api.persistence.license.PlatformLicenseSnMapper; +import cn.craftlabs.platform.api.persistence.project.PlatformProject; import cn.craftlabs.platform.api.persistence.project.PlatformProjectMapper; +import cn.craftlabs.platform.api.persistence.system.PlatformSystemParamMapper; import cn.craftlabs.platform.api.web.dto.LicenseSnCreateRequest; import cn.craftlabs.platform.api.web.dto.LicenseSnResponse; import cn.craftlabs.platform.api.web.dto.LicenseSnStatusPatchRequest; import cn.craftlabs.platform.api.web.dto.LicenseSnUpdateRequest; import cn.craftlabs.platform.api.web.dto.PageResponse; import cn.craftlabs.platform.api.web.dto.SnBatchImportRequest; +import cn.craftlabs.platform.api.web.dto.SnBatchImportRequest; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -44,6 +52,7 @@ public class LicenseSnService { private final PlatformContractLineMapper contractLineMapper; private final PlatformContractMapper contractMapper; private final PlatformDeliveryBatchMapper deliveryBatchMapper; + private final PlatformSystemParamMapper systemParamMapper; private final AuditService auditService; private final ObjectMapper objectMapper; @@ -53,6 +62,7 @@ public class LicenseSnService { PlatformContractLineMapper contractLineMapper, PlatformContractMapper contractMapper, PlatformDeliveryBatchMapper deliveryBatchMapper, + PlatformSystemParamMapper systemParamMapper, AuditService auditService, ObjectMapper objectMapper) { this.licenseSnMapper = licenseSnMapper; @@ -60,6 +70,7 @@ public class LicenseSnService { this.contractLineMapper = contractLineMapper; this.contractMapper = contractMapper; this.deliveryBatchMapper = deliveryBatchMapper; + this.systemParamMapper = systemParamMapper; this.auditService = auditService; this.objectMapper = objectMapper; } @@ -87,13 +98,22 @@ public class LicenseSnService { requireProject(projectId); } if (request.getProjectId() != null) { - var deliveryQuery = com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery(PlatformDeliveryBatch.class) - .eq(PlatformDeliveryBatch::getProjectId, request.getProjectId()) - .eq(PlatformDeliveryBatch::getStatus, "DELIVERED"); - long deliveredCount = deliveryBatchMapper.selectCount(deliveryQuery); - if (deliveredCount == 0) { - // If project has batches but none delivered, warn (not block for MVP) - // This is a soft check - can be made strict later + var gateParam = systemParamMapper.selectById("deliveryGateEnabled"); + boolean gateEnabled = gateParam != null && "true".equals(gateParam.getParamValue()); + if (gateEnabled) { + var batchQuery = com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery(PlatformDeliveryBatch.class) + .eq(PlatformDeliveryBatch::getProjectId, request.getProjectId()); + long totalBatches = deliveryBatchMapper.selectCount(batchQuery); + if (totalBatches > 0) { + var deliveredQuery = com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery(PlatformDeliveryBatch.class) + .eq(PlatformDeliveryBatch::getProjectId, request.getProjectId()) + .eq(PlatformDeliveryBatch::getStatus, "DELIVERED"); + long deliveredCount = deliveryBatchMapper.selectCount(deliveredQuery); + if (deliveredCount == 0) { + throw new ResponseStatusException(HttpStatus.FORBIDDEN, + "交付闸门已启用: 该项目下的交付批次尚未标记为已交付"); + } + } } } if (existsSnCode(code)) { diff --git a/services/delivery-platform-api/src/main/resources/db/migration/V25__system_params.sql b/services/delivery-platform-api/src/main/resources/db/migration/V25__system_params.sql new file mode 100644 index 0000000..5191b1e --- /dev/null +++ b/services/delivery-platform-api/src/main/resources/db/migration/V25__system_params.sql @@ -0,0 +1,15 @@ +-- V25__system_params.sql +-- 系统参数持久化(M11-F20),替代前端 localStorage MVP + +CREATE TABLE IF NOT EXISTS platform_system_param ( + param_key VARCHAR(64) PRIMARY KEY, + param_value VARCHAR(1024) NOT NULL DEFAULT '', + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +INSERT INTO platform_system_param (param_key, param_value) VALUES + ('orphanSnStrictValidation', 'true'), + ('deliveryGateEnabled', 'true'), + ('sessionTimeoutMinutes', '60'), + ('passwordMinLength', '6') +ON CONFLICT (param_key) DO NOTHING; diff --git a/web/delivery-platform-ui/src/views/SystemParamsView.vue b/web/delivery-platform-ui/src/views/SystemParamsView.vue index 80beb40..354d2d4 100644 --- a/web/delivery-platform-ui/src/views/SystemParamsView.vue +++ b/web/delivery-platform-ui/src/views/SystemParamsView.vue @@ -1,31 +1,25 @@