diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/integration/IntegrationCatalogController.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/integration/IntegrationCatalogController.java index 908b833..8495207 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/integration/IntegrationCatalogController.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/integration/IntegrationCatalogController.java @@ -8,6 +8,8 @@ import cn.craftlabs.platform.api.web.dto.IntegrationEnvironmentResponse; import cn.craftlabs.platform.api.web.dto.PageResponse; import cn.craftlabs.platform.api.web.dto.ProductLineRequest; import cn.craftlabs.platform.api.web.dto.ProductLineResponse; +import cn.craftlabs.platform.api.web.dto.SkuMappingRequest; +import cn.craftlabs.platform.api.web.dto.SkuMappingResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; @@ -147,4 +149,28 @@ public class IntegrationCatalogController { integrationCatalogService.deleteJsonTemplate(id); return ResponseEntity.ok().build(); } + + @GetMapping("/sku-mappings") + public ResponseEntity> listSkuMappings( + @RequestParam(required = false) Long contractLineId) { + return ResponseEntity.ok(integrationCatalogService.listSkuMappings(contractLineId)); + } + + @PostMapping("/sku-mappings") + public ResponseEntity createSkuMapping( + @RequestParam Long contractLineId, @Valid @RequestBody SkuMappingRequest body) { + return ResponseEntity.ok(integrationCatalogService.createSkuMapping(contractLineId, body)); + } + + @PutMapping("/sku-mappings/{id}") + public ResponseEntity updateSkuMapping( + @PathVariable Long id, @Valid @RequestBody SkuMappingRequest body) { + return ResponseEntity.ok(integrationCatalogService.updateSkuMapping(id, body)); + } + + @DeleteMapping("/sku-mappings/{id}") + public ResponseEntity deleteSkuMapping(@PathVariable Long id) { + integrationCatalogService.deleteSkuMapping(id); + return ResponseEntity.ok().build(); + } } diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/integration/PlatformSkuMapping.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/integration/PlatformSkuMapping.java new file mode 100644 index 0000000..988b9a4 --- /dev/null +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/integration/PlatformSkuMapping.java @@ -0,0 +1,122 @@ +package cn.craftlabs.platform.api.persistence.integration; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import java.time.OffsetDateTime; + +@TableName("platform_sku_mapping") +public class PlatformSkuMapping { + + @TableId(type = IdType.AUTO) + private Long id; + + @TableField("contract_line_id") + private Long contractLineId; + + @TableField("sku_code") + private String skuCode; + + @TableField("sku_name") + private String skuName; + + @TableField("bitanswer_product_id") + private String bitanswerProductId; + + @TableField("bitanswer_template_id") + private String bitanswerTemplateId; + + @TableField("bitanswer_feature_id") + private String bitanswerFeatureId; + + @TableField + private Integer quantity; + + @TableField("created_at") + private OffsetDateTime createdAt; + + @TableField("updated_at") + private OffsetDateTime updatedAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getContractLineId() { + return contractLineId; + } + + public void setContractLineId(Long contractLineId) { + this.contractLineId = contractLineId; + } + + public String getSkuCode() { + return skuCode; + } + + public void setSkuCode(String skuCode) { + this.skuCode = skuCode; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public String getBitanswerProductId() { + return bitanswerProductId; + } + + public void setBitanswerProductId(String bitanswerProductId) { + this.bitanswerProductId = bitanswerProductId; + } + + public String getBitanswerTemplateId() { + return bitanswerTemplateId; + } + + public void setBitanswerTemplateId(String bitanswerTemplateId) { + this.bitanswerTemplateId = bitanswerTemplateId; + } + + public String getBitanswerFeatureId() { + return bitanswerFeatureId; + } + + public void setBitanswerFeatureId(String bitanswerFeatureId) { + this.bitanswerFeatureId = bitanswerFeatureId; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + public OffsetDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(OffsetDateTime createdAt) { + this.createdAt = createdAt; + } + + 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/integration/PlatformSkuMappingMapper.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/integration/PlatformSkuMappingMapper.java new file mode 100644 index 0000000..eb4b2d6 --- /dev/null +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/persistence/integration/PlatformSkuMappingMapper.java @@ -0,0 +1,7 @@ +package cn.craftlabs.platform.api.persistence.integration; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface PlatformSkuMappingMapper extends BaseMapper {} diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/IntegrationCatalogService.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/IntegrationCatalogService.java index 634ce1a..1dcda6e 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/IntegrationCatalogService.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/IntegrationCatalogService.java @@ -8,11 +8,15 @@ import cn.craftlabs.platform.api.persistence.integration.PlatformJsonTemplate; import cn.craftlabs.platform.api.persistence.integration.PlatformJsonTemplateMapper; import cn.craftlabs.platform.api.persistence.integration.PlatformProductLine; import cn.craftlabs.platform.api.persistence.integration.PlatformProductLineMapper; +import cn.craftlabs.platform.api.persistence.integration.PlatformSkuMapping; +import cn.craftlabs.platform.api.persistence.integration.PlatformSkuMappingMapper; import cn.craftlabs.platform.api.web.dto.IntegrationEnvironmentRequest; import cn.craftlabs.platform.api.web.dto.IntegrationEnvironmentResponse; import cn.craftlabs.platform.api.web.dto.PageResponse; import cn.craftlabs.platform.api.web.dto.ProductLineRequest; import cn.craftlabs.platform.api.web.dto.ProductLineResponse; +import cn.craftlabs.platform.api.web.dto.SkuMappingRequest; +import cn.craftlabs.platform.api.web.dto.SkuMappingResponse; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.springframework.http.HttpStatus; @@ -30,16 +34,19 @@ public class IntegrationCatalogService { private final PlatformIntegrationEnvironmentMapper environmentMapper; private final PlatformBitanswerIdMappingMapper idMappingMapper; private final PlatformJsonTemplateMapper jsonTemplateMapper; + private final PlatformSkuMappingMapper skuMappingMapper; public IntegrationCatalogService( PlatformProductLineMapper productLineMapper, PlatformIntegrationEnvironmentMapper environmentMapper, PlatformBitanswerIdMappingMapper idMappingMapper, - PlatformJsonTemplateMapper jsonTemplateMapper) { + PlatformJsonTemplateMapper jsonTemplateMapper, + PlatformSkuMappingMapper skuMappingMapper) { this.productLineMapper = productLineMapper; this.environmentMapper = environmentMapper; this.idMappingMapper = idMappingMapper; this.jsonTemplateMapper = jsonTemplateMapper; + this.skuMappingMapper = skuMappingMapper; } @Transactional(readOnly = true) @@ -215,6 +222,55 @@ public class IntegrationCatalogService { jsonTemplateMapper.deleteById(id); } + @Transactional(readOnly = true) + public List listSkuMappings(Long contractLineId) { + var qw = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper(); + if (contractLineId != null) qw.eq(PlatformSkuMapping::getContractLineId, contractLineId); + qw.orderByDesc(PlatformSkuMapping::getCreatedAt); + return skuMappingMapper.selectList(qw).stream().map(this::toSkuMapping).collect(Collectors.toList()); + } + + @Transactional + public SkuMappingResponse createSkuMapping(Long contractLineId, SkuMappingRequest req) { + PlatformSkuMapping row = new PlatformSkuMapping(); + row.setContractLineId(contractLineId); + row.setSkuCode(req.getSkuCode()); + row.setSkuName(req.getSkuName()); + row.setBitanswerProductId(req.getBitanswerProductId()); + row.setBitanswerTemplateId(req.getBitanswerTemplateId()); + row.setBitanswerFeatureId(req.getBitanswerFeatureId()); + row.setQuantity(req.getQuantity() != null ? req.getQuantity() : 1); + row.setCreatedAt(java.time.OffsetDateTime.now()); + row.setUpdatedAt(java.time.OffsetDateTime.now()); + skuMappingMapper.insert(row); + return toSkuMapping(row); + } + + @Transactional + public SkuMappingResponse updateSkuMapping(Long id, SkuMappingRequest req) { + PlatformSkuMapping row = skuMappingMapper.selectById(id); + if (row == null) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "sku mapping not found"); + } + row.setSkuCode(req.getSkuCode()); + row.setSkuName(req.getSkuName()); + row.setBitanswerProductId(req.getBitanswerProductId()); + row.setBitanswerTemplateId(req.getBitanswerTemplateId()); + row.setBitanswerFeatureId(req.getBitanswerFeatureId()); + row.setQuantity(req.getQuantity() != null ? req.getQuantity() : 1); + row.setUpdatedAt(java.time.OffsetDateTime.now()); + skuMappingMapper.updateById(row); + return toSkuMapping(row); + } + + @Transactional + public void deleteSkuMapping(Long id) { + if (skuMappingMapper.selectById(id) == null) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "sku mapping not found"); + } + skuMappingMapper.deleteById(id); + } + private ProductLineResponse toProductLine(PlatformProductLine row) { ProductLineResponse r = new ProductLineResponse(); r.setId(row.getId()); @@ -239,4 +295,18 @@ public class IntegrationCatalogService { r.setUpdatedAt(row.getUpdatedAt()); return r; } + + private SkuMappingResponse toSkuMapping(PlatformSkuMapping row) { + SkuMappingResponse r = new SkuMappingResponse(); + r.setId(row.getId()); + r.setContractLineId(row.getContractLineId()); + r.setSkuCode(row.getSkuCode()); + r.setSkuName(row.getSkuName()); + r.setBitanswerProductId(row.getBitanswerProductId()); + r.setBitanswerTemplateId(row.getBitanswerTemplateId()); + r.setBitanswerFeatureId(row.getBitanswerFeatureId()); + r.setQuantity(row.getQuantity()); + r.setCreatedAt(row.getCreatedAt()); + return r; + } } diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/web/dto/SkuMappingRequest.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/web/dto/SkuMappingRequest.java new file mode 100644 index 0000000..5e8578c --- /dev/null +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/web/dto/SkuMappingRequest.java @@ -0,0 +1,75 @@ +package cn.craftlabs.platform.api.web.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Size; + +public class SkuMappingRequest { + + @NotBlank + @Size(max = 64) + private String skuCode; + + @Size(max = 256) + private String skuName; + + @Size(max = 128) + private String bitanswerProductId; + + @Size(max = 128) + private String bitanswerTemplateId; + + @Size(max = 128) + private String bitanswerFeatureId; + + @Min(1) + private Integer quantity; + + public String getSkuCode() { + return skuCode; + } + + public void setSkuCode(String skuCode) { + this.skuCode = skuCode; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public String getBitanswerProductId() { + return bitanswerProductId; + } + + public void setBitanswerProductId(String bitanswerProductId) { + this.bitanswerProductId = bitanswerProductId; + } + + public String getBitanswerTemplateId() { + return bitanswerTemplateId; + } + + public void setBitanswerTemplateId(String bitanswerTemplateId) { + this.bitanswerTemplateId = bitanswerTemplateId; + } + + public String getBitanswerFeatureId() { + return bitanswerFeatureId; + } + + public void setBitanswerFeatureId(String bitanswerFeatureId) { + this.bitanswerFeatureId = bitanswerFeatureId; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } +} diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/web/dto/SkuMappingResponse.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/web/dto/SkuMappingResponse.java new file mode 100644 index 0000000..28fa818 --- /dev/null +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/web/dto/SkuMappingResponse.java @@ -0,0 +1,88 @@ +package cn.craftlabs.platform.api.web.dto; + +import java.time.OffsetDateTime; + +public class SkuMappingResponse { + + private Long id; + private Long contractLineId; + private String skuCode; + private String skuName; + private String bitanswerProductId; + private String bitanswerTemplateId; + private String bitanswerFeatureId; + private Integer quantity; + private OffsetDateTime createdAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getContractLineId() { + return contractLineId; + } + + public void setContractLineId(Long contractLineId) { + this.contractLineId = contractLineId; + } + + public String getSkuCode() { + return skuCode; + } + + public void setSkuCode(String skuCode) { + this.skuCode = skuCode; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } + + public String getBitanswerProductId() { + return bitanswerProductId; + } + + public void setBitanswerProductId(String bitanswerProductId) { + this.bitanswerProductId = bitanswerProductId; + } + + public String getBitanswerTemplateId() { + return bitanswerTemplateId; + } + + public void setBitanswerTemplateId(String bitanswerTemplateId) { + this.bitanswerTemplateId = bitanswerTemplateId; + } + + public String getBitanswerFeatureId() { + return bitanswerFeatureId; + } + + public void setBitanswerFeatureId(String bitanswerFeatureId) { + this.bitanswerFeatureId = bitanswerFeatureId; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + public OffsetDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(OffsetDateTime createdAt) { + this.createdAt = createdAt; + } +} diff --git a/services/delivery-platform-api/src/main/resources/db/migration/V20__sku_mapping.sql b/services/delivery-platform-api/src/main/resources/db/migration/V20__sku_mapping.sql new file mode 100644 index 0000000..c3a812c --- /dev/null +++ b/services/delivery-platform-api/src/main/resources/db/migration/V20__sku_mapping.sql @@ -0,0 +1,13 @@ +CREATE TABLE platform_sku_mapping ( + id BIGSERIAL PRIMARY KEY, + contract_line_id BIGINT REFERENCES platform_contract_line(id), + sku_code VARCHAR(64) NOT NULL, + sku_name VARCHAR(256), + bitanswer_product_id VARCHAR(128), + bitanswer_template_id VARCHAR(128), + bitanswer_feature_id VARCHAR(128), + quantity INT NOT NULL DEFAULT 1, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP +); +CREATE INDEX idx_sku_contract_line ON platform_sku_mapping(contract_line_id); diff --git a/web/delivery-platform-ui/src/api/platform.js b/web/delivery-platform-ui/src/api/platform.js index 10d0a47..2341913 100644 --- a/web/delivery-platform-ui/src/api/platform.js +++ b/web/delivery-platform-ui/src/api/platform.js @@ -438,6 +438,12 @@ export function deleteJsonTemplate(id) { return axios.delete(`/api/v1/integration/json-templates/${id}`); } +// —— I15-T2 M2-F08 SKU 映射 —————————————————————————— +export function listSkuMappings(params) { return axios.get('/api/v1/integration/sku-mappings', { params }); } +export function createSkuMapping(contractLineId, body) { return axios.post(`/api/v1/integration/sku-mappings?contractLineId=${contractLineId}`, body); } +export function updateSkuMapping(id, body) { return axios.put(`/api/v1/integration/sku-mappings/${id}`, body); } +export function deleteSkuMapping(id) { return axios.delete(`/api/v1/integration/sku-mappings/${id}`); } + export function listStakeholders(projectId) { return axios.get(`/api/v1/projects/${projectId}/stakeholders`); } diff --git a/web/delivery-platform-ui/src/router/index.js b/web/delivery-platform-ui/src/router/index.js index 92382fc..a968f2c 100644 --- a/web/delivery-platform-ui/src/router/index.js +++ b/web/delivery-platform-ui/src/router/index.js @@ -86,6 +86,12 @@ const routes = [ component: () => import("../views/IntegrationIdMappingView.vue"), meta: { roles: ["SYS_ADMIN", "SALES"], title: "ID 映射" }, }, + { + path: "integration/sku-mappings", + name: "integration-sku-mappings", + component: () => import("../views/IntegrationSkuMappingView.vue"), + meta: { roles: ["SYS_ADMIN"], title: "SKU 映射" }, + }, { path: "integration/json-templates", name: "integration-json-templates", diff --git a/web/delivery-platform-ui/src/views/IntegrationSkuMappingView.vue b/web/delivery-platform-ui/src/views/IntegrationSkuMappingView.vue new file mode 100644 index 0000000..710304c --- /dev/null +++ b/web/delivery-platform-ui/src/views/IntegrationSkuMappingView.vue @@ -0,0 +1,228 @@ + + + + +