Commit 4cfe1e14 by renyizhao

动态规格

parent 1f47e2f0
...@@ -28,5 +28,7 @@ public interface ErrorCodeConstants { ...@@ -28,5 +28,7 @@ public interface ErrorCodeConstants {
ErrorCode RESOURCE_ORDER_SNAPSHOT_CREATE_FAILED = new ErrorCode(1_030_010_000, "创建订单快照失败"); ErrorCode RESOURCE_ORDER_SNAPSHOT_CREATE_FAILED = new ErrorCode(1_030_010_000, "创建订单快照失败");
ErrorCode RESOURCE_ORDER_SNAPSHOT_QUERY_FAILED = new ErrorCode(1_030_010_001, "查询订单快照失败"); ErrorCode RESOURCE_ORDER_SNAPSHOT_QUERY_FAILED = new ErrorCode(1_030_010_001, "查询订单快照失败");
ErrorCode RESOURCE_ORDER_SNAPSHOT_DELETE_FAILED = new ErrorCode(1_030_010_002, "删除订单快照失败"); ErrorCode RESOURCE_ORDER_SNAPSHOT_DELETE_FAILED = new ErrorCode(1_030_010_002, "删除订单快照失败");
ErrorCode RESOURCE_SPU_SPEC_REQUIRED = new ErrorCode(1_030_011_000, "算力资源商品至少选择一个规格");
ErrorCode RESOURCE_SPU_SPEC_NOT_EXISTS = new ErrorCode(1_030_011_001, "算力资源商品规格不存在");
} }
\ No newline at end of file
...@@ -76,8 +76,7 @@ public class ResourceSpuController { ...@@ -76,8 +76,7 @@ public class ResourceSpuController {
@Parameter(name = "id", description = "编号", required = true, example = "1024") @Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('compute:resource-spu:query')") @PreAuthorize("@ss.hasPermission('compute:resource-spu:query')")
public CommonResult<ResourceSpuRespVO> getResourceSpu(@RequestParam("id") Long id) { public CommonResult<ResourceSpuRespVO> getResourceSpu(@RequestParam("id") Long id) {
ResourceSpuDO resourceSpu = resourceSpuService.getResourceSpu(id); return success(resourceSpuService.getResourceSpu(id));
return success(BeanUtils.toBean(resourceSpu, ResourceSpuRespVO.class));
} }
@GetMapping("/page") @GetMapping("/page")
......
...@@ -16,33 +16,6 @@ public class ResourceSpuPageReqVO extends PageParam { ...@@ -16,33 +16,6 @@ public class ResourceSpuPageReqVO extends PageParam {
@Schema(description = "算力资源名称", example = "李四") @Schema(description = "算力资源名称", example = "李四")
private String name; private String name;
@Schema(description = "CPU配置")
private String cpu;
@Schema(description = "GPU配置")
private String gpu;
@Schema(description = "内存配置")
private String ram;
@Schema(description = "存储配置")
private String storage;
@Schema(description = "电源配置")
private String powerSupply;
@Schema(description = "网卡配置")
private String nic;
@Schema(description = "服务器ip")
private String ip;
@Schema(description = "初始用户名", example = "芋艿")
private String initUsername;
@Schema(description = "初始密码")
private String initPassword;
@Schema(description = "商品简介") @Schema(description = "商品简介")
private String intro; private String intro;
......
...@@ -20,53 +20,11 @@ public class ResourceSpuRespVO { ...@@ -20,53 +20,11 @@ public class ResourceSpuRespVO {
@ExcelProperty("算力资源名称") @ExcelProperty("算力资源名称")
private String name; private String name;
@Schema(description = "CPU配置", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "规格配置项 ID 列表(关联 compute_resource_config.id)")
@ExcelProperty("CPU配置") private List<Long> specConfigIds;
private String cpu;
@Schema(description = "GPU配置") @Schema(description = "规格展示 Map(key=configCategory, value=configOption)")
@ExcelProperty("GPU配置") private Map<String, String> specMap;
private String gpu;
@Schema(description = "算力")
private Double compute;
@Schema(description = "GPU数量")
private Integer gpuCount;
@Schema(description = "CPU核心数")
private Integer coreCount;
@Schema(description = "内存容量")
private Integer memoryCapacity;
@Schema(description = "内存配置", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("内存配置")
private String ram;
@Schema(description = "存储配置", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("存储配置")
private String storage;
@Schema(description = "电源配置", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("电源配置")
private String powerSupply;
@Schema(description = "网卡配置", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("网卡配置")
private String nic;
@Schema(description = "服务器ip", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("服务器ip")
private String ip;
@Schema(description = "初始用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@ExcelProperty("初始用户名")
private String initUsername;
@Schema(description = "初始密码", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("初始密码")
private String initPassword;
@Schema(description = "商品简介") @Schema(description = "商品简介")
@ExcelProperty("商品简介") @ExcelProperty("商品简介")
......
...@@ -16,52 +16,8 @@ public class ResourceSpuSaveReqVO { ...@@ -16,52 +16,8 @@ public class ResourceSpuSaveReqVO {
@NotEmpty(message = "算力资源名称不能为空") @NotEmpty(message = "算力资源名称不能为空")
private String name; private String name;
@Schema(description = "CPU配置", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "规格配置项 ID 列表(关联 compute_resource_config.id)")
@NotEmpty(message = "CPU配置不能为空") private List<Long> specConfigIds;
private String cpu;
@Schema(description = "GPU配置")
private String gpu;
@Schema(description = "算力")
private Double compute;
@Schema(description = "GPU数量")
private Integer gpuCount;
@Schema(description = "CPU核心数")
private Integer coreCount;
@Schema(description = "内存容量")
private Integer memoryCapacity;
@Schema(description = "内存配置", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "内存配置不能为空")
private String ram;
@Schema(description = "存储配置", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "存储配置不能为空")
private String storage;
@Schema(description = "电源配置", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "电源配置不能为空")
private String powerSupply;
@Schema(description = "网卡配置", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "网卡配置不能为空")
private String nic;
@Schema(description = "服务器ip", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "服务器ip不能为空")
private String ip;
@Schema(description = "初始用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
@NotEmpty(message = "初始用户名不能为空")
private String initUsername;
@Schema(description = "初始密码", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "初始密码不能为空")
private String initPassword;
@Schema(description = "商品简介") @Schema(description = "商品简介")
private String intro; private String intro;
......
...@@ -2,11 +2,10 @@ package com.luhu.computility.module.compute.controller.app.resourcesku; ...@@ -2,11 +2,10 @@ package com.luhu.computility.module.compute.controller.app.resourcesku;
import com.luhu.computility.framework.common.pojo.CommonResult; import com.luhu.computility.framework.common.pojo.CommonResult;
import com.luhu.computility.framework.common.pojo.PageResult; import com.luhu.computility.framework.common.pojo.PageResult;
import com.luhu.computility.framework.common.util.object.BeanUtils; import com.luhu.computility.module.compute.controller.admin.resourcespu.vo.ResourceSpuRespVO;
import com.luhu.computility.module.compute.controller.app.resourcesku.vo.AppResourceSkuPageReqVO; import com.luhu.computility.module.compute.controller.app.resourcesku.vo.AppResourceSkuPageReqVO;
import com.luhu.computility.module.compute.controller.app.resourcesku.vo.AppResourceSkuRespVO; import com.luhu.computility.module.compute.controller.app.resourcesku.vo.AppResourceSkuRespVO;
import com.luhu.computility.module.compute.dal.dataobject.resourcesku.ResourceSkuDO; import com.luhu.computility.module.compute.dal.dataobject.resourcesku.ResourceSkuDO;
import com.luhu.computility.module.compute.dal.dataobject.resourcespu.ResourceSpuDO;
import com.luhu.computility.module.compute.service.resourcesku.ResourceSkuService; import com.luhu.computility.module.compute.service.resourcesku.ResourceSkuService;
import com.luhu.computility.module.compute.service.resourcespu.ResourceSpuService; import com.luhu.computility.module.compute.service.resourcespu.ResourceSpuService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
...@@ -20,7 +19,6 @@ import javax.annotation.security.PermitAll; ...@@ -20,7 +19,6 @@ import javax.annotation.security.PermitAll;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
...@@ -43,59 +41,46 @@ public class AppResourceSkuController { ...@@ -43,59 +41,46 @@ public class AppResourceSkuController {
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "获得算力资源SKU分页") @Operation(summary = "获得算力资源SKU分页")
public CommonResult<PageResult<AppResourceSkuRespVO>> getResourceSkuPage(@Valid AppResourceSkuPageReqVO pageReqVO) { public CommonResult<PageResult<AppResourceSkuRespVO>> getResourceSkuPage(@Valid AppResourceSkuPageReqVO pageReqVO) {
// 根据分类ID查询该分类下的所有SPU // 拿到当前页 SPU 列表(带 specMap)
List<ResourceSpuDO> spus; List<ResourceSpuRespVO> spuVos = pageReqVO.getCategoryId() != null
if (pageReqVO.getCategoryId() != null) { ? resourceSpuService.getResourceSpuRespVoListByCategoryId(pageReqVO.getCategoryId())
// 根据分类ID查询SPU : resourceSpuService.getOnlineResourceSpuRespVoList();
spus = resourceSpuService.getResourceSpuListByCategoryId(pageReqVO.getCategoryId());
} else {
// 查询所有上架的SPU
spus = resourceSpuService.getOnlineResourceSpuList();
}
if (spus.isEmpty()) { if (spuVos.isEmpty()) {
PageResult<AppResourceSkuRespVO> emptyResult = new PageResult<>(); PageResult<AppResourceSkuRespVO> emptyResult = new PageResult<>();
emptyResult.setTotal(0L); emptyResult.setTotal(0L);
emptyResult.setList(Collections.emptyList()); emptyResult.setList(Collections.emptyList());
return success(emptyResult); return success(emptyResult);
} }
// 获取这些SPU对应的所有可用SKU // 取这些 SPU 对应的所有可用 SKU
List<Long> spuIds = spus.stream().map(ResourceSpuDO::getId).collect(Collectors.toList()); List<Long> spuIds = spuVos.stream().map(ResourceSpuRespVO::getId).collect(Collectors.toList());
List<ResourceSkuDO> skus = resourceSkuService.getResourceSkuListBySpuIds(spuIds, pageReqVO.getStatus()); List<ResourceSkuDO> skus = resourceSkuService.getResourceSkuListBySpuIds(spuIds, pageReqVO.getStatus());
// 分页处理 // 分页
int total = skus.size(); int total = skus.size();
int start = (pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize(); int start = (pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize();
int end = Math.min(start + pageReqVO.getPageSize(), total); int end = Math.min(start + pageReqVO.getPageSize(), total);
List<ResourceSkuDO> pageSkus = skus.subList(start, end); List<ResourceSkuDO> pageSkus = skus.subList(start, end);
// 组装返回数据 // 组装返回数据
Map<Long, ResourceSpuDO> spuMap = spus.stream()
.collect(Collectors.toMap(ResourceSpuDO::getId, spu -> spu));
List<AppResourceSkuRespVO> respList = pageSkus.stream().map(sku -> { List<AppResourceSkuRespVO> respList = pageSkus.stream().map(sku -> {
ResourceSpuDO spu = spuMap.get(sku.getSpuId()); ResourceSpuRespVO spuVo = spuVos.stream()
if (spu == null) { .filter(v -> v.getId().equals(sku.getSpuId()))
.findFirst().orElse(null);
if (spuVo == null) {
return null; return null;
} }
AppResourceSkuRespVO respVO = new AppResourceSkuRespVO(); AppResourceSkuRespVO respVO = new AppResourceSkuRespVO();
respVO.setId(sku.getId()); respVO.setId(sku.getId());
respVO.setName(spu.getName()); respVO.setName(spuVo.getName());
respVO.setRemark(spu.getIntro()); respVO.setRemark(spuVo.getIntro());
respVO.setPrice(sku.getPaymentPrice()); respVO.setPrice(sku.getPaymentPrice());
respVO.setFeeInfo(sku.getDurationDays() + "天"); respVO.setFeeInfo(sku.getDurationDays() + "天");
respVO.setCpu(spu.getCpu()); respVO.setSpecMap(spuVo.getSpecMap());
respVO.setGpu(spu.getGpu()); respVO.setLocation(spuVo.getLocation());
respVO.setRam(spu.getRam());
respVO.setStorage(spu.getStorage());
respVO.setPowerSupply(spu.getPowerSupply());
respVO.setNic(spu.getNic());
respVO.setLocation(spu.getLocation());
return respVO; return respVO;
}).filter(obj -> obj != null).collect(Collectors.toList()); }).filter(java.util.Objects::nonNull).collect(Collectors.toList());
PageResult<AppResourceSkuRespVO> result = new PageResult<>(); PageResult<AppResourceSkuRespVO> result = new PageResult<>();
result.setTotal((long) total); result.setTotal((long) total);
...@@ -108,17 +93,18 @@ public class AppResourceSkuController { ...@@ -108,17 +93,18 @@ public class AppResourceSkuController {
@Parameter(name = "id", description = "编号", required = true) @Parameter(name = "id", description = "编号", required = true)
public CommonResult<AppResourceSkuRespVO> getResourceSkuDetail(@RequestParam("id") Long id) { public CommonResult<AppResourceSkuRespVO> getResourceSkuDetail(@RequestParam("id") Long id) {
ResourceSkuDO resourceSku = resourceSkuService.getResourceSku(id); ResourceSkuDO resourceSku = resourceSkuService.getResourceSku(id);
ResourceSpuDO resourceSpu = resourceSpuService.getResourceSpu(resourceSku.getSpuId()); ResourceSpuRespVO resourceSpuVo = resourceSpuService.getResourceSpu(resourceSku.getSpuId());
if (resourceSpuVo == null) {
if (resourceSpu == null) {
return success(null); return success(null);
} }
AppResourceSkuRespVO respVO = new AppResourceSkuRespVO();
AppResourceSkuRespVO respVO = BeanUtils.toBean(resourceSpu, AppResourceSkuRespVO.class);
respVO.setId(resourceSku.getId()); respVO.setId(resourceSku.getId());
respVO.setName(resourceSpuVo.getName());
respVO.setRemark(resourceSpuVo.getIntro());
respVO.setPrice(resourceSku.getPaymentPrice()); respVO.setPrice(resourceSku.getPaymentPrice());
respVO.setFeeInfo(resourceSku.getDurationDays() + "天"); respVO.setFeeInfo(resourceSku.getDurationDays() + "天");
respVO.setSpecMap(resourceSpuVo.getSpecMap());
respVO.setLocation(resourceSpuVo.getLocation());
return success(respVO); return success(respVO);
} }
......
...@@ -3,6 +3,8 @@ package com.luhu.computility.module.compute.controller.app.resourcesku.vo; ...@@ -3,6 +3,8 @@ package com.luhu.computility.module.compute.controller.app.resourcesku.vo;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.util.Map;
@Schema(description = "用户 APP - 算力资源SKU Response VO") @Schema(description = "用户 APP - 算力资源SKU Response VO")
@Data @Data
...@@ -23,23 +25,8 @@ public class AppResourceSkuRespVO { ...@@ -23,23 +25,8 @@ public class AppResourceSkuRespVO {
@Schema(description = "费用信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "7天") @Schema(description = "费用信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "7天")
private String feeInfo; private String feeInfo;
@Schema(description = "CPU配置", example = "Intel Xeon E5-2680 v4") @Schema(description = "规格展示 Map(key=configCategory, value=configOption)")
private String cpu; private Map<String, String> specMap;
@Schema(description = "GPU配置", example = "NVIDIA RTX 4090")
private String gpu;
@Schema(description = "内存配置", example = "64GB DDR4")
private String ram;
@Schema(description = "存储配置", example = "2TB NVMe SSD")
private String storage;
@Schema(description = "电源配置", example = "3500W")
private String powerSupply;
@Schema(description = "网卡配置", example = "2Gbps")
private String nic;
@Schema(description = "服务器所在地", example = "深圳") @Schema(description = "服务器所在地", example = "深圳")
private String location; private String location;
......
...@@ -31,58 +31,7 @@ public class ResourceSpuDO extends BaseDO { ...@@ -31,58 +31,7 @@ public class ResourceSpuDO extends BaseDO {
* 算力资源名称 * 算力资源名称
*/ */
private String name; private String name;
/**
* CPU配置
*/
private String cpu;
/**
* GPU配置
*/
private String gpu;
/**
* 算力
*/
private Double compute;
/**
* GPU数量
*/
private Long gpuCount;
/**
* CPU核心数
*/
private Long coreCount;
/**
* 内存容量
*/
private Long memoryCapacity;
/**
* 内存配置
*/
private String ram;
/**
* 存储配置
*/
private String storage;
/**
* 电源配置
*/
private String powerSupply;
/**
* 网卡配置
*/
private String nic;
/**
* 服务器ip
*/
private String ip;
/**
* 初始用户名
*/
private String initUsername;
/**
* 初始密码
*/
private String initPassword;
/** /**
* 商品简介 * 商品简介
*/ */
......
package com.luhu.computility.module.compute.dal.dataobject.resourcespuspecvalue;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import com.luhu.computility.framework.mybatis.core.dataobject.BaseDO;
/**
* 算力资源商品-规格关联 DO
*
* @author jony
*/
@TableName("compute_resource_spu_spec_value")
@KeySequence("compute_resource_spu_spec_value_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResourceSpuSpecValueDO extends BaseDO {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 商品 SPU ID
*/
private Long spuId;
/**
* 规格项 ID(关联 compute_resource_config.id)
*/
private Long configId;
/**
* 展示排序
*/
private Integer sort;
}
...@@ -30,15 +30,6 @@ public interface ResourceSpuMapper extends BaseMapperX<ResourceSpuDO> { ...@@ -30,15 +30,6 @@ public interface ResourceSpuMapper extends BaseMapperX<ResourceSpuDO> {
.selectAs(ResourceCategoryDO::getName,ResourceSpuRespVO::getCategoryName) .selectAs(ResourceCategoryDO::getName,ResourceSpuRespVO::getCategoryName)
.leftJoin(ResourceCategoryDO.class,ResourceCategoryDO::getId,ResourceSpuDO::getCategoryId) .leftJoin(ResourceCategoryDO.class,ResourceCategoryDO::getId,ResourceSpuDO::getCategoryId)
.likeIfPresent(ResourceSpuDO::getName, reqVO.getName()) .likeIfPresent(ResourceSpuDO::getName, reqVO.getName())
.eqIfPresent(ResourceSpuDO::getCpu, reqVO.getCpu())
.eqIfPresent(ResourceSpuDO::getGpu, reqVO.getGpu())
.eqIfPresent(ResourceSpuDO::getRam, reqVO.getRam())
.eqIfPresent(ResourceSpuDO::getStorage, reqVO.getStorage())
.eqIfPresent(ResourceSpuDO::getPowerSupply, reqVO.getPowerSupply())
.eqIfPresent(ResourceSpuDO::getNic, reqVO.getNic())
.eqIfPresent(ResourceSpuDO::getIp, reqVO.getIp())
.likeIfPresent(ResourceSpuDO::getInitUsername, reqVO.getInitUsername())
.eqIfPresent(ResourceSpuDO::getInitPassword, reqVO.getInitPassword())
.likeIfPresent(ResourceSpuDO::getIntro, reqVO.getIntro()) .likeIfPresent(ResourceSpuDO::getIntro, reqVO.getIntro())
.eqIfPresent(ResourceSpuDO::getCategoryId, reqVO.getCategoryId()) .eqIfPresent(ResourceSpuDO::getCategoryId, reqVO.getCategoryId())
.eqIfPresent(ResourceSpuDO::getPicUrl, reqVO.getPicUrl()) .eqIfPresent(ResourceSpuDO::getPicUrl, reqVO.getPicUrl())
......
package com.luhu.computility.module.compute.dal.mysql.resourcespuspecvalue;
import java.util.*;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.luhu.computility.framework.mybatis.core.mapper.BaseMapperX;
import com.luhu.computility.module.compute.dal.dataobject.resourcespuspecvalue.ResourceSpuSpecValueDO;
/**
* 算力资源商品-规格关联 Mapper
*
* @author jony
*/
@Mapper
public interface ResourceSpuSpecValueMapper extends BaseMapperX<ResourceSpuSpecValueDO> {
int insertBatch(@Param("list") List<ResourceSpuSpecValueDO> list);
int deleteBySpuId(@Param("spuId") Long spuId, @Param("deleted") Boolean deleted);
List<ResourceSpuSpecValueDO> selectListBySpuId(@Param("spuId") Long spuId);
List<ResourceSpuSpecValueDO> selectListBySpuIds(@Param("spuIds") Collection<Long> spuIds);
}
...@@ -16,6 +16,7 @@ import org.springframework.stereotype.Service; ...@@ -16,6 +16,7 @@ import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -64,20 +65,6 @@ public class ComputeStatisticsApiImpl implements ComputeStatisticsApi { ...@@ -64,20 +65,6 @@ public class ComputeStatisticsApiImpl implements ComputeStatisticsApi {
return result; return result;
} }
// GPU型号分布
Map<String, Long> gpuCountMap = onlineSpuList.stream()
.filter(spu -> spu.getGpu() != null)
.collect(Collectors.groupingBy(ResourceSpuDO::getGpu, Collectors.counting()));
List<ComputeDistributionDTO.DistributionItem> gpuList = gpuCountMap.entrySet().stream()
.map(entry -> {
ComputeDistributionDTO.DistributionItem item = new ComputeDistributionDTO.DistributionItem();
item.setName(entry.getKey());
item.setValue(entry.getValue());
return item;
})
.collect(Collectors.toList());
// 算力来源分布 // 算力来源分布
Map<String, Long> sourceCountMap = onlineSpuList.stream() Map<String, Long> sourceCountMap = onlineSpuList.stream()
.filter(spu -> spu.getSource() != null) .filter(spu -> spu.getSource() != null)
...@@ -113,6 +100,10 @@ public class ComputeStatisticsApiImpl implements ComputeStatisticsApi { ...@@ -113,6 +100,10 @@ public class ComputeStatisticsApiImpl implements ComputeStatisticsApi {
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
// 阶段 1 改造后 SPU 不再保存 gpu 列,原按显卡型号分组的逻辑已无法执行,
// 暂时返回空列表,DTO 字段保留以便后续阶段 6 改造成从规格中间表统计
List<ComputeDistributionDTO.DistributionItem> gpuList = Collections.emptyList();
result.setGpu(gpuList); result.setGpu(gpuList);
result.setSource(sourceList); result.setSource(sourceList);
result.setResource(resourceList); result.setResource(resourceList);
...@@ -126,13 +117,13 @@ public class ComputeStatisticsApiImpl implements ComputeStatisticsApi { ...@@ -126,13 +117,13 @@ public class ComputeStatisticsApiImpl implements ComputeStatisticsApi {
List<ResourceSpuDO> spuList = resourceSpuMapper.selectList(); List<ResourceSpuDO> spuList = resourceSpuMapper.selectList();
ResourceAreaInfoDTO result = new ResourceAreaInfoDTO(); ResourceAreaInfoDTO result = new ResourceAreaInfoDTO();
// 提取每个资源的地区ID和算力值,用于地图统计 // 提取每个资源的地区ID;算力值在阶段 1 后不再直接存在 SPU 上,暂置 0
List<ResourceAreaInfoDTO.ResourceAreaItem> items = spuList.stream() List<ResourceAreaInfoDTO.ResourceAreaItem> items = spuList.stream()
.filter(spu -> spu.getAreaId() != null) // 只处理有地区ID的资源 .filter(spu -> spu.getAreaId() != null) // 只处理有地区ID的资源
.map(spu -> { .map(spu -> {
ResourceAreaInfoDTO.ResourceAreaItem item = new ResourceAreaInfoDTO.ResourceAreaItem(); ResourceAreaInfoDTO.ResourceAreaItem item = new ResourceAreaInfoDTO.ResourceAreaItem();
item.setAreaId(spu.getAreaId()); // 地区ID item.setAreaId(spu.getAreaId()); // 地区ID
item.setCompute(spu.getCompute()); // 算力值 item.setCompute(0.0); // TODO: 阶段 1 改造后算力从规格中间表统计,暂置 0
return item; return item;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
......
...@@ -66,4 +66,12 @@ public interface ResourceConfigService { ...@@ -66,4 +66,12 @@ public interface ResourceConfigService {
*/ */
PageResult<ResourceConfigDO> getResourceConfigPage(ResourceConfigPageReqVO pageReqVO); PageResult<ResourceConfigDO> getResourceConfigPage(ResourceConfigPageReqVO pageReqVO);
/**
* 批量获得算力资源配置(按 ID 列表)
*
* @param ids 配置 ID 列表
* @return 配置 ID → ResourceConfigDO 映射
*/
Map<Long, ResourceConfigDO> getConfigMapByIds(Collection<Long> ids);
} }
\ No newline at end of file
...@@ -11,8 +11,11 @@ import org.springframework.stereotype.Service; ...@@ -11,8 +11,11 @@ import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static com.luhu.computility.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.luhu.computility.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.luhu.computility.module.compute.enums.ErrorCodeConstants.RESOURCE_CONFIG_NOT_EXISTS; import static com.luhu.computility.module.compute.enums.ErrorCodeConstants.RESOURCE_CONFIG_NOT_EXISTS;
...@@ -94,4 +97,13 @@ public class ResourceConfigServiceImpl implements ResourceConfigService { ...@@ -94,4 +97,13 @@ public class ResourceConfigServiceImpl implements ResourceConfigService {
return resourceConfigMapper.selectPage(pageReqVO); return resourceConfigMapper.selectPage(pageReqVO);
} }
@Override
public Map<Long, ResourceConfigDO> getConfigMapByIds(Collection<Long> ids) {
if (ids == null || ids.isEmpty()) {
return Collections.emptyMap();
}
List<ResourceConfigDO> list = resourceConfigMapper.selectBatchIds(ids);
return list.stream().collect(Collectors.toMap(ResourceConfigDO::getId, e -> e, (a, b) -> a));
}
} }
\ No newline at end of file
...@@ -14,6 +14,7 @@ import com.luhu.computility.module.pay.controller.admin.notify.vo.WpgjPayNotifyD ...@@ -14,6 +14,7 @@ import com.luhu.computility.module.pay.controller.admin.notify.vo.WpgjPayNotifyD
import com.luhu.computility.module.compute.dal.dataobject.resourceorder.ResourceOrderDO; import com.luhu.computility.module.compute.dal.dataobject.resourceorder.ResourceOrderDO;
import com.luhu.computility.module.compute.dal.dataobject.resourcesku.ResourceSkuDO; import com.luhu.computility.module.compute.dal.dataobject.resourcesku.ResourceSkuDO;
import com.luhu.computility.module.compute.dal.dataobject.resourcespu.ResourceSpuDO; import com.luhu.computility.module.compute.dal.dataobject.resourcespu.ResourceSpuDO;
import com.luhu.computility.module.compute.controller.admin.resourcespu.vo.ResourceSpuRespVO;
import com.luhu.computility.framework.common.pojo.PageResult; import com.luhu.computility.framework.common.pojo.PageResult;
import com.luhu.computility.framework.common.pojo.PageParam; import com.luhu.computility.framework.common.pojo.PageParam;
...@@ -179,6 +180,6 @@ public interface ResourceOrderService { ...@@ -179,6 +180,6 @@ public interface ResourceOrderService {
* @param spu SPU信息 * @param spu SPU信息
* @return 快照ID * @return 快照ID
*/ */
Long createOrderHardwareSnapshot(ResourceOrderDO order, ResourceSkuDO sku, ResourceSpuDO spu); Long createOrderHardwareSnapshot(ResourceOrderDO order, ResourceSkuDO sku, ResourceSpuRespVO spu);
} }
\ No newline at end of file
...@@ -11,6 +11,7 @@ import com.luhu.computility.module.compute.config.ResourceOrderProperties; ...@@ -11,6 +11,7 @@ import com.luhu.computility.module.compute.config.ResourceOrderProperties;
import com.luhu.computility.module.compute.controller.admin.resourceorder.vo.ResourceOrderPageReqVO; import com.luhu.computility.module.compute.controller.admin.resourceorder.vo.ResourceOrderPageReqVO;
import com.luhu.computility.module.compute.controller.admin.resourceorder.vo.ResourceOrderRespVO; import com.luhu.computility.module.compute.controller.admin.resourceorder.vo.ResourceOrderRespVO;
import com.luhu.computility.module.compute.controller.admin.resourceorder.vo.ResourceOrderSaveReqVO; import com.luhu.computility.module.compute.controller.admin.resourceorder.vo.ResourceOrderSaveReqVO;
import com.luhu.computility.module.compute.controller.admin.resourcespu.vo.ResourceSpuRespVO;
import com.luhu.computility.module.compute.controller.app.resourceorder.vo.*; import com.luhu.computility.module.compute.controller.app.resourceorder.vo.*;
import com.luhu.computility.module.compute.controller.app.resourceordersnapshot.vo.AppResourceOrderSnapshotSaveReqVO; import com.luhu.computility.module.compute.controller.app.resourceordersnapshot.vo.AppResourceOrderSnapshotSaveReqVO;
import com.luhu.computility.module.compute.dal.dataobject.resourceorder.ResourceOrderDO; import com.luhu.computility.module.compute.dal.dataobject.resourceorder.ResourceOrderDO;
...@@ -199,7 +200,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService { ...@@ -199,7 +200,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
} }
// 获取SPU信息 // 获取SPU信息
ResourceSpuDO spu = resourceSpuService.getResourceSpu(sku.getSpuId()); com.luhu.computility.module.compute.controller.admin.resourcespu.vo.ResourceSpuRespVO spu = resourceSpuService.getResourceSpu(sku.getSpuId());
if (spu == null) { if (spu == null) {
throw exception(RESOURCE_ORDER_SPU_NOT_EXISTS); throw exception(RESOURCE_ORDER_SPU_NOT_EXISTS);
} }
...@@ -381,7 +382,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService { ...@@ -381,7 +382,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
// 快照中没有的字段(分类、位置)需要从SPU获取 // 快照中没有的字段(分类、位置)需要从SPU获取
ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId()); ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId());
if (sku != null) { if (sku != null) {
ResourceSpuDO spu = resourceSpuService.getResourceSpu(sku.getSpuId()); ResourceSpuRespVO spu = resourceSpuService.getResourceSpu(sku.getSpuId());
if (spu != null) { if (spu != null) {
respVO.setSpuId(spu.getId()); respVO.setSpuId(spu.getId());
respVO.setCategoryId(spu.getCategoryId()); respVO.setCategoryId(spu.getCategoryId());
...@@ -397,18 +398,12 @@ public class ResourceOrderServiceImpl implements ResourceOrderService { ...@@ -397,18 +398,12 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
// 快照不存在,从SKU和SPU信息读取(兼容老订单) // 快照不存在,从SKU和SPU信息读取(兼容老订单)
ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId()); ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId());
if (sku != null) { if (sku != null) {
ResourceSpuDO spu = resourceSpuService.getResourceSpu(sku.getSpuId()); ResourceSpuRespVO spu = resourceSpuService.getResourceSpu(sku.getSpuId());
if (spu != null) { if (spu != null) {
respVO.setSpuId(spu.getId()); respVO.setSpuId(spu.getId());
respVO.setSpuName(spu.getName()); respVO.setSpuName(spu.getName());
respVO.setCategoryId(spu.getCategoryId()); respVO.setCategoryId(spu.getCategoryId());
respVO.setCpu(spu.getCpu()); // 规格字段已迁移至 specMap,AppResourceOrderRespVO 上的 cpu/gpu/ram 等暂保留以便兼容老字段。
respVO.setGpu(spu.getGpu());
respVO.setRam(spu.getRam());
respVO.setStorage(spu.getStorage());
respVO.setPowerSupply(spu.getPowerSupply());
respVO.setNic(spu.getNic());
respVO.setIp(spu.getIp());
respVO.setLocation(spu.getLocation()); respVO.setLocation(spu.getLocation());
respVO.setSkuName(spu.getName() + " - " + sku.getDurationDays() + "天"); respVO.setSkuName(spu.getName() + " - " + sku.getDurationDays() + "天");
...@@ -462,7 +457,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService { ...@@ -462,7 +457,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
// 快照中没有的字段(分类、位置)需要从SPU获取 // 快照中没有的字段(分类、位置)需要从SPU获取
ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId()); ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId());
if (sku != null) { if (sku != null) {
ResourceSpuDO spu = resourceSpuService.getResourceSpu(sku.getSpuId()); ResourceSpuRespVO spu = resourceSpuService.getResourceSpu(sku.getSpuId());
if (spu != null) { if (spu != null) {
respVO.setSpuId(spu.getId()); respVO.setSpuId(spu.getId());
respVO.setCategoryId(spu.getCategoryId()); respVO.setCategoryId(spu.getCategoryId());
...@@ -478,21 +473,13 @@ public class ResourceOrderServiceImpl implements ResourceOrderService { ...@@ -478,21 +473,13 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
// 快照不存在,从SKU和SPU信息读取(兼容老订单) // 快照不存在,从SKU和SPU信息读取(兼容老订单)
ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId()); ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId());
if (sku != null) { if (sku != null) {
ResourceSpuDO spu = resourceSpuService.getResourceSpu(sku.getSpuId()); ResourceSpuRespVO spu = resourceSpuService.getResourceSpu(sku.getSpuId());
if (spu != null) { if (spu != null) {
respVO.setSpuId(spu.getId()); respVO.setSpuId(spu.getId());
respVO.setSpuName(spu.getName()); respVO.setSpuName(spu.getName());
respVO.setCategoryId(spu.getCategoryId()); respVO.setCategoryId(spu.getCategoryId());
respVO.setCpu(spu.getCpu()); // 规格字段已迁移至 specMap,AppResourceOrderRespVO 上的 cpu/gpu/ram 等暂保留以便兼容老字段。
respVO.setGpu(spu.getGpu());
respVO.setRam(spu.getRam());
respVO.setStorage(spu.getStorage());
respVO.setPowerSupply(spu.getPowerSupply());
respVO.setNic(spu.getNic());
respVO.setIp(spu.getIp());
respVO.setLocation(spu.getLocation()); respVO.setLocation(spu.getLocation());
respVO.setInitUsername(spu.getInitUsername());
respVO.setInitPassword(spu.getInitPassword());
respVO.setSkuName(spu.getName() + " - " + sku.getDurationDays() + "天"); respVO.setSkuName(spu.getName() + " - " + sku.getDurationDays() + "天");
// 设置分类名称 // 设置分类名称
...@@ -568,7 +555,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService { ...@@ -568,7 +555,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
try { try {
ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId()); ResourceSkuDO sku = resourceSkuService.getResourceSku(order.getSkuId());
if (sku != null) { if (sku != null) {
ResourceSpuDO spu = resourceSpuService.getResourceSpu(sku.getSpuId()); ResourceSpuRespVO spu = resourceSpuService.getResourceSpu(sku.getSpuId());
if (spu != null) { if (spu != null) {
createOrderHardwareSnapshot(order, sku, spu); createOrderHardwareSnapshot(order, sku, spu);
log.info("[createUserResourceOrderWithWpgj] 订单硬件配置快照创建完成,订单ID: {}", order.getId()); log.info("[createUserResourceOrderWithWpgj] 订单硬件配置快照创建完成,订单ID: {}", order.getId());
...@@ -726,7 +713,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService { ...@@ -726,7 +713,7 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
resourceSkuMapper.updateStatusBySpuId(spuId, ResourceSkuStatus.OFFLINE.getValue()); resourceSkuMapper.updateStatusBySpuId(spuId, ResourceSkuStatus.OFFLINE.getValue());
// 增加SPU销量 // 增加SPU销量
ResourceSpuDO spu = resourceSpuService.getResourceSpu(spuId); ResourceSpuRespVO spu = resourceSpuService.getResourceSpu(spuId);
if (spu != null) { if (spu != null) {
ResourceSpuDO updateSpu = new ResourceSpuDO(); ResourceSpuDO updateSpu = new ResourceSpuDO();
updateSpu.setId(spuId); updateSpu.setId(spuId);
...@@ -838,30 +825,20 @@ public class ResourceOrderServiceImpl implements ResourceOrderService { ...@@ -838,30 +825,20 @@ public class ResourceOrderServiceImpl implements ResourceOrderService {
} }
@Override @Override
public Long createOrderHardwareSnapshot(ResourceOrderDO order, ResourceSkuDO sku, ResourceSpuDO spu) { public Long createOrderHardwareSnapshot(ResourceOrderDO order, ResourceSkuDO sku, ResourceSpuRespVO spu) {
try { try {
log.info("[createOrderHardwareSnapshot] 开始创建订单硬件配置快照,订单ID: {}, SPU名称: {}, SKU租赁天数: {}", log.info("[createOrderHardwareSnapshot] 开始创建订单硬件配置快照,订单ID: {}, SPU名称: {}, SKU租赁天数: {}",
order.getId(), spu.getName(), sku.getDurationDays()); order.getId(), spu.getName(), sku.getDurationDays());
// 构建快照创建请求 // 构建快照创建请求
// 阶段 1 改造后 SPU 不再保存 cpu/gpu/ram/storage 等硬编码规格列,
// 这些列已从 ResourceSpuDO 删除,对应的 snapshot 字段暂置 null
// (后续阶段 6 改造时统一从规格中间表生成 spec_snapshot JSON)。
AppResourceOrderSnapshotSaveReqVO createReqVO = new AppResourceOrderSnapshotSaveReqVO(); AppResourceOrderSnapshotSaveReqVO createReqVO = new AppResourceOrderSnapshotSaveReqVO();
createReqVO.setOrderId(order.getId()); createReqVO.setOrderId(order.getId());
createReqVO.setUserId(order.getUserId()); createReqVO.setUserId(order.getUserId());
createReqVO.setPayOrderId(order.getPayOrderId()); createReqVO.setPayOrderId(order.getPayOrderId());
createReqVO.setCpu(spu.getCpu());
createReqVO.setGpu(spu.getGpu());
createReqVO.setRam(spu.getRam());
createReqVO.setStorage(spu.getStorage());
createReqVO.setPowerSupply(spu.getPowerSupply());
createReqVO.setNic(spu.getNic());
createReqVO.setGpuCount(spu.getGpuCount());
createReqVO.setCompute(spu.getCompute());
createReqVO.setCoreCount(spu.getCoreCount());
createReqVO.setMemoryCapacity(spu.getMemoryCapacity());
createReqVO.setLocation(spu.getLocation()); createReqVO.setLocation(spu.getLocation());
createReqVO.setIp(spu.getIp());
createReqVO.setInitUsername(spu.getInitUsername());
createReqVO.setInitPassword(spu.getInitPassword());
createReqVO.setSpuName(spu.getName()); createReqVO.setSpuName(spu.getName());
createReqVO.setDurationDays(sku.getDurationDays()); createReqVO.setDurationDays(sku.getDurationDays());
createReqVO.setRentStartTime(order.getRentStartTime()); createReqVO.setRentStartTime(order.getRentStartTime());
......
...@@ -49,7 +49,7 @@ public interface ResourceSpuService { ...@@ -49,7 +49,7 @@ public interface ResourceSpuService {
* @param id 编号 * @param id 编号
* @return 算力资源SPU表(基础配置信息) * @return 算力资源SPU表(基础配置信息)
*/ */
ResourceSpuDO getResourceSpu(Long id); ResourceSpuRespVO getResourceSpu(Long id);
/** /**
* 获得算力资源SPU表(基础配置信息)分页 * 获得算力资源SPU表(基础配置信息)分页
...@@ -86,6 +86,16 @@ public interface ResourceSpuService { ...@@ -86,6 +86,16 @@ public interface ResourceSpuService {
List<ResourceSpuDO> getOnlineResourceSpuList(); List<ResourceSpuDO> getOnlineResourceSpuList();
/** /**
* 获取所有上架的算力资源 SPU Response VO 列表(含 specMap)
*/
List<ResourceSpuRespVO> getOnlineResourceSpuRespVoList();
/**
* 根据分类ID获取 SPU Response VO 列表(含 specMap)
*/
List<ResourceSpuRespVO> getResourceSpuRespVoListByCategoryId(Long categoryId);
/**
* 更新算力资源SPU状态 * 更新算力资源SPU状态
* *
* @param id SPU ID * @param id SPU ID
......
...@@ -2,7 +2,11 @@ package com.luhu.computility.module.compute.service.resourcespu; ...@@ -2,7 +2,11 @@ package com.luhu.computility.module.compute.service.resourcespu;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.luhu.computility.module.compute.dal.dataobject.resourceconfig.ResourceConfigDO;
import com.luhu.computility.module.compute.dal.dataobject.resourcespuspecvalue.ResourceSpuSpecValueDO;
import com.luhu.computility.module.compute.dal.mysql.resourcesku.ResourceSkuMapper; import com.luhu.computility.module.compute.dal.mysql.resourcesku.ResourceSkuMapper;
import com.luhu.computility.module.compute.service.resourceconfig.ResourceConfigService;
import com.luhu.computility.module.compute.service.resourcespuspecvalue.ResourceSpuSpecValueService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
...@@ -19,7 +23,6 @@ import com.luhu.computility.module.compute.dal.mysql.resourcespu.ResourceSpuMapp ...@@ -19,7 +23,6 @@ import com.luhu.computility.module.compute.dal.mysql.resourcespu.ResourceSpuMapp
import static com.luhu.computility.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.luhu.computility.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.luhu.computility.framework.common.util.collection.CollectionUtils.convertList; import static com.luhu.computility.framework.common.util.collection.CollectionUtils.convertList;
import static com.luhu.computility.framework.common.util.collection.CollectionUtils.diffList;
import static com.luhu.computility.module.compute.enums.ErrorCodeConstants.*; import static com.luhu.computility.module.compute.enums.ErrorCodeConstants.*;
import static com.luhu.computility.module.compute.enums.ResourceSpuStatus.ONLINE; import static com.luhu.computility.module.compute.enums.ResourceSpuStatus.ONLINE;
import static com.luhu.computility.framework.common.enums.CommonDeleteStatusEnum.NOT_DELETED; import static com.luhu.computility.framework.common.enums.CommonDeleteStatusEnum.NOT_DELETED;
...@@ -39,14 +42,30 @@ public class ResourceSpuServiceImpl implements ResourceSpuService { ...@@ -39,14 +42,30 @@ public class ResourceSpuServiceImpl implements ResourceSpuService {
@Resource @Resource
private ResourceSkuMapper resourceSkuMapper; private ResourceSkuMapper resourceSkuMapper;
@Resource
private ResourceSpuSpecValueService resourceSpuSpecValueService;
@Resource
private ResourceConfigService resourceConfigService;
@Override @Override
@Transactional(rollbackFor = Exception.class)
public Long createResourceSpu(ResourceSpuSaveReqVO createReqVO) { public Long createResourceSpu(ResourceSpuSaveReqVO createReqVO) {
// 插入 // 1. 校验:至少选 1 个规格
ResourceSpuDO resourceSpu = BeanUtils.toBean(createReqVO, ResourceSpuDO.class); if (CollUtil.isEmpty(createReqVO.getSpecConfigIds())) {
resourceSpuMapper.insert(resourceSpu); throw exception(RESOURCE_SPU_SPEC_REQUIRED);
}
// 2. 校验:所有 specConfigIds 在规格字典里存在
validateSpecConfigIdsExist(createReqVO.getSpecConfigIds());
// 3. 插入 SPU
ResourceSpuDO spu = BeanUtils.toBean(createReqVO, ResourceSpuDO.class);
resourceSpuMapper.insert(spu);
// 4. 批量插入中间表
resourceSpuSpecValueService.createSpuSpecValues(spu.getId(), createReqVO.getSpecConfigIds());
// 返回 return spu.getId();
return resourceSpu.getId();
} }
@Override @Override
...@@ -54,16 +73,25 @@ public class ResourceSpuServiceImpl implements ResourceSpuService { ...@@ -54,16 +73,25 @@ public class ResourceSpuServiceImpl implements ResourceSpuService {
public void updateResourceSpu(ResourceSpuSaveReqVO updateReqVO) { public void updateResourceSpu(ResourceSpuSaveReqVO updateReqVO) {
// 校验存在 // 校验存在
validateResourceSpuExists(updateReqVO.getId()); validateResourceSpuExists(updateReqVO.getId());
// 校验:至少选 1 个规格
if (CollUtil.isEmpty(updateReqVO.getSpecConfigIds())) {
throw exception(RESOURCE_SPU_SPEC_REQUIRED);
}
// 校验:所有 specConfigIds 在规格字典里存在
validateSpecConfigIdsExist(updateReqVO.getSpecConfigIds());
// 获取更新前的状态 // 获取更新前的状态
ResourceSpuDO existingSpu = resourceSpuMapper.selectById(updateReqVO.getId()); ResourceSpuDO existingSpu = resourceSpuMapper.selectById(updateReqVO.getId());
Integer oldStatus = existingSpu.getStatus(); Integer oldStatus = existingSpu.getStatus();
// 更新SPU // 更新 SPU
ResourceSpuDO updateObj = BeanUtils.toBean(updateReqVO, ResourceSpuDO.class); ResourceSpuDO updateObj = BeanUtils.toBean(updateReqVO, ResourceSpuDO.class);
resourceSpuMapper.updateById(updateObj); resourceSpuMapper.updateById(updateObj);
// 如果状态发生变化,同步更新关联的SKU状态 // 全量替换规格关联
resourceSpuSpecValueService.replaceSpuSpecValues(updateReqVO.getId(), updateReqVO.getSpecConfigIds());
// 如果状态发生变化,同步更新关联的 SKU 状态(保留现有逻辑)
Integer newStatus = updateObj.getStatus(); Integer newStatus = updateObj.getStatus();
if (!Objects.equals(oldStatus, newStatus)) { if (!Objects.equals(oldStatus, newStatus)) {
resourceSkuMapper.updateStatusBySpuId(updateReqVO.getId(), newStatus); resourceSkuMapper.updateStatusBySpuId(updateReqVO.getId(), newStatus);
...@@ -92,14 +120,70 @@ public class ResourceSpuServiceImpl implements ResourceSpuService { ...@@ -92,14 +120,70 @@ public class ResourceSpuServiceImpl implements ResourceSpuService {
} }
} }
/**
* 校验规格配置项 ID 是否都在规格字典里存在
*/
private void validateSpecConfigIdsExist(List<Long> specConfigIds) {
if (CollUtil.isEmpty(specConfigIds)) {
return;
}
Map<Long, ResourceConfigDO> configMap = resourceConfigService.getConfigMapByIds(specConfigIds);
for (Long configId : specConfigIds) {
if (!configMap.containsKey(configId)) {
throw exception(RESOURCE_SPU_SPEC_NOT_EXISTS);
}
}
}
/**
* 填充 SPU 响应 VO 的规格 Map 和 ID 列表
*/
private void fillSpecMap(ResourceSpuRespVO vo) {
if (vo == null) {
return;
}
List<ResourceSpuSpecValueDO> specs = resourceSpuSpecValueService.getSpuSpecValueListBySpuId(vo.getId());
if (CollUtil.isEmpty(specs)) {
return;
}
// 批量查规格项
List<Long> configIds = convertList(specs, ResourceSpuSpecValueDO::getConfigId);
Map<Long, ResourceConfigDO> configMap = resourceConfigService.getConfigMapByIds(configIds);
// 转 Map<category, option>
Map<String, String> specMap = new LinkedHashMap<>();
List<Long> specConfigIds = new ArrayList<>();
for (ResourceSpuSpecValueDO sv : specs) {
ResourceConfigDO cfg = configMap.get(sv.getConfigId());
if (cfg != null) {
specMap.put(cfg.getConfigCategory(), cfg.getConfigOption());
specConfigIds.add(sv.getConfigId());
}
}
vo.setSpecMap(specMap);
vo.setSpecConfigIds(specConfigIds);
}
@Override @Override
public ResourceSpuDO getResourceSpu(Long id) { public ResourceSpuRespVO getResourceSpu(Long id) {
return resourceSpuMapper.selectById(id); ResourceSpuDO spu = resourceSpuMapper.selectById(id);
if (spu == null) {
return null;
}
ResourceSpuRespVO vo = BeanUtils.toBean(spu, ResourceSpuRespVO.class);
fillSpecMap(vo);
return vo;
} }
@Override @Override
public PageResult<ResourceSpuRespVO> getResourceSpuPage(ResourceSpuPageReqVO pageReqVO) { public PageResult<ResourceSpuRespVO> getResourceSpuPage(ResourceSpuPageReqVO pageReqVO) {
return resourceSpuMapper.selectPage(pageReqVO); PageResult<ResourceSpuRespVO> pageResult = resourceSpuMapper.selectPage(pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return pageResult;
}
pageResult.getList().forEach(this::fillSpecMap);
return pageResult;
} }
@Override @Override
...@@ -137,6 +221,28 @@ public class ResourceSpuServiceImpl implements ResourceSpuService { ...@@ -137,6 +221,28 @@ public class ResourceSpuServiceImpl implements ResourceSpuService {
} }
@Override @Override
public List<ResourceSpuRespVO> getOnlineResourceSpuRespVoList() {
List<ResourceSpuDO> list = getOnlineResourceSpuList();
if (CollUtil.isEmpty(list)) {
return Collections.emptyList();
}
List<ResourceSpuRespVO> vos = BeanUtils.toBean(list, ResourceSpuRespVO.class);
vos.forEach(this::fillSpecMap);
return vos;
}
@Override
public List<ResourceSpuRespVO> getResourceSpuRespVoListByCategoryId(Long categoryId) {
List<ResourceSpuDO> list = getResourceSpuListByCategoryId(categoryId);
if (CollUtil.isEmpty(list)) {
return Collections.emptyList();
}
List<ResourceSpuRespVO> vos = BeanUtils.toBean(list, ResourceSpuRespVO.class);
vos.forEach(this::fillSpecMap);
return vos;
}
@Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateResourceSpuStatus(Long id, Integer status) { public void updateResourceSpuStatus(Long id, Integer status) {
// 校验SPU存在 // 校验SPU存在
......
package com.luhu.computility.module.compute.service.resourcespuspecvalue;
import java.util.*;
import com.luhu.computility.module.compute.dal.dataobject.resourcespuspecvalue.ResourceSpuSpecValueDO;
/**
* 算力资源商品-规格关联 Service 接口
*
* @author jony
*/
public interface ResourceSpuSpecValueService {
/**
* 批量插入商品-规格关联
*
* @param spuId 商品 SPU ID
* @param configIds 规格项 ID 列表
*/
void createSpuSpecValues(Long spuId, List<Long> configIds);
/**
* 全量替换商品-规格关联(先删后插,update 用)
*
* @param spuId 商品 SPU ID
* @param configIds 规格项 ID 列表
*/
void replaceSpuSpecValues(Long spuId, List<Long> configIds);
/**
* 获得商品-规格关联列表
*
* @param spuId 商品 SPU ID
* @return 关联列表
*/
List<ResourceSpuSpecValueDO> getSpuSpecValueListBySpuId(Long spuId);
/**
* 批量获得商品-规格关联 Map(N+1 优化)
*
* @param spuIds 商品 SPU ID 列表
* @return key=spuId, value=关联列表
*/
Map<Long, List<ResourceSpuSpecValueDO>> getSpuSpecValueMapBySpuIds(Collection<Long> spuIds);
}
package com.luhu.computility.module.compute.service.resourcespuspecvalue;
import cn.hutool.core.collection.CollUtil;
import com.luhu.computility.module.compute.dal.dataobject.resourcespuspecvalue.ResourceSpuSpecValueDO;
import com.luhu.computility.module.compute.dal.mysql.resourcespuspecvalue.ResourceSpuSpecValueMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 算力资源商品-规格关联 Service 实现类
*
* @author jony
*/
@Service
@Validated
public class ResourceSpuSpecValueServiceImpl implements ResourceSpuSpecValueService {
@Resource
private ResourceSpuSpecValueMapper resourceSpuSpecValueMapper;
@Override
public void createSpuSpecValues(Long spuId, List<Long> configIds) {
if (CollUtil.isEmpty(configIds)) {
return;
}
List<ResourceSpuSpecValueDO> list = new ArrayList<>(configIds.size());
int sort = 0;
for (Long configId : configIds) {
ResourceSpuSpecValueDO sv = new ResourceSpuSpecValueDO();
sv.setSpuId(spuId);
sv.setConfigId(configId);
sv.setSort(sort++);
list.add(sv);
}
resourceSpuSpecValueMapper.insertBatch(list);
}
@Override
public void replaceSpuSpecValues(Long spuId, List<Long> configIds) {
// 先软删(deleted=1)该 SPU 下所有当前生效的规格关联
resourceSpuSpecValueMapper.deleteBySpuId(spuId, Boolean.TRUE);
// 再插入新规格
if (CollUtil.isNotEmpty(configIds)) {
createSpuSpecValues(spuId, configIds);
}
}
@Override
public List<ResourceSpuSpecValueDO> getSpuSpecValueListBySpuId(Long spuId) {
if (spuId == null) {
return Collections.emptyList();
}
return resourceSpuSpecValueMapper.selectListBySpuId(spuId);
}
@Override
public Map<Long, List<ResourceSpuSpecValueDO>> getSpuSpecValueMapBySpuIds(Collection<Long> spuIds) {
if (CollUtil.isEmpty(spuIds)) {
return Collections.emptyMap();
}
List<ResourceSpuSpecValueDO> all = resourceSpuSpecValueMapper.selectListBySpuIds(spuIds);
Map<Long, List<ResourceSpuSpecValueDO>> result = new HashMap<>();
for (ResourceSpuSpecValueDO sv : all) {
result.computeIfAbsent(sv.getSpuId(), k -> new ArrayList<>()).add(sv);
}
return result;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.luhu.computility.module.compute.dal.mysql.resourcespuspecvalue.ResourceSpuSpecValueMapper">
<insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">
INSERT INTO compute_resource_spu_spec_value (spu_id, config_id, sort, deleted, creator, create_time, updater, update_time)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.spuId}, #{item.configId}, #{item.sort}, b'0', #{item.creator}, NOW(), #{item.updater}, NOW())
</foreach>
</insert>
<update id="deleteBySpuId">
UPDATE compute_resource_spu_spec_value
SET deleted = #{deleted}
WHERE spu_id = #{spuId}
AND deleted = b'0'
</update>
<select id="selectListBySpuId" resultType="com.luhu.computility.module.compute.dal.dataobject.resourcespuspecvalue.ResourceSpuSpecValueDO">
SELECT id, spu_id, config_id, sort, deleted, creator, create_time, updater, update_time
FROM compute_resource_spu_spec_value
WHERE spu_id = #{spuId}
AND deleted = b'0'
</select>
<select id="selectListBySpuIds" resultType="com.luhu.computility.module.compute.dal.dataobject.resourcespuspecvalue.ResourceSpuSpecValueDO">
SELECT id, spu_id, config_id, sort, deleted, creator, create_time, updater, update_time
FROM compute_resource_spu_spec_value
WHERE spu_id IN
<foreach collection="spuIds" item="spuId" open="(" close=")" separator=",">
#{spuId}
</foreach>
AND deleted = b'0'
</select>
</mapper>
...@@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.annotation.TableName; ...@@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
...@@ -26,6 +27,7 @@ import java.util.Map; ...@@ -26,6 +27,7 @@ import java.util.Map;
@KeySequence("system_oauth2_access_token_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @KeySequence("system_oauth2_access_token_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class OAuth2AccessTokenDO extends TenantBaseDO { public class OAuth2AccessTokenDO extends TenantBaseDO {
/** /**
......
-- ============================================================
-- 算力资源商品改造 - 阶段 1:新建商品-规格关联中间表
-- 适用数据库:MySQL 5.7+ / 8.0
-- 适用表: compute_resource_spu_spec_value(新建)
-- 创建时间: 2026-08-04
-- 说明: 算力资源商品规格自由化改造的中间表
-- 存储"商品 SPU ↔ 规格项"的多对多关系
-- 替代原 SPU 表上硬编码的 cpu / gpu / ram / storage 等字段
-- 前置依赖: compute_resource_spu、compute_resource_config 表已存在
-- ============================================================
USE `new_computility`;
-- ------------------------------------------------------------
-- 1. 新建中间表
-- ------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `compute_resource_spu_spec_value` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`spu_id` BIGINT NOT NULL COMMENT '商品 SPU ID(关联 compute_resource_spu.id)',
`config_id` BIGINT NOT NULL COMMENT '规格项 ID(关联 compute_resource_config.id)',
`sort` INT NOT NULL DEFAULT 0 COMMENT '展示排序(同 SPU 内按 sort 升序)',
`deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除(0-未删,1-已删)',
`creator` VARCHAR(64) DEFAULT NULL COMMENT '创建者',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` VARCHAR(64) DEFAULT NULL COMMENT '更新者',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_spu_config_deleted` (`spu_id`, `config_id`, `deleted`),
KEY `idx_spu_id` (`spu_id`),
KEY `idx_config_id` (`config_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='算力资源商品-规格关联表(多对多)';
-- ------------------------------------------------------------
-- 2. 验证
-- ------------------------------------------------------------
-- SHOW COLUMNS FROM `compute_resource_spu_spec_value`;
-- SHOW INDEX FROM `compute_resource_spu_spec_value`;
-- SELECT COUNT(*) FROM `compute_resource_spu_spec_value`;
-- ------------------------------------------------------------
-- 3. 回滚(按需手工执行,慎用)
-- ------------------------------------------------------------
-- DROP TABLE IF EXISTS `compute_resource_spu_spec_value`;
-- ============================================================
-- 算力资源商品改造 · 阶段 2
-- 目标:清空测试数据 + 删除 SPU 表硬编码规格列和现货凭证列
-- 适用库:new_computility
-- 前置:阶段 1 已创建 compute_resource_spu_spec_value 中间表
-- 警告:破坏性 SQL,TRUNCATE 后无法恢复数据
-- ============================================================
USE `new_computility`;
-- 临时关闭外键检查,绕过 compute_resource_sku -> compute_resource_spu 的 FK 限制
-- (MySQL 不允许 TRUNCATE 存在外键被引用的父表,即便子表已空)
SET FOREIGN_KEY_CHECKS = 0;
-- 1. 清空测试数据(顺序:先订单快照,再 SPU,再 SKU)
TRUNCATE TABLE `compute_resource_order_snapshot`;
TRUNCATE TABLE `compute_resource_order`;
TRUNCATE TABLE `compute_resource_sku`;
TRUNCATE TABLE `compute_resource_spu`;
-- 恢复外键检查
SET FOREIGN_KEY_CHECKS = 1;
-- 2. 删除 SPU 表所有规格列 + 现货凭证列(ip/init_username/init_password 下移到订单/快照)
ALTER TABLE `compute_resource_spu`
DROP COLUMN `cpu`,
DROP COLUMN `gpu`,
DROP COLUMN `compute`,
DROP COLUMN `gpu_count`,
DROP COLUMN `core_count`,
DROP COLUMN `memory_capacity`,
DROP COLUMN `ram`,
DROP COLUMN `storage`,
DROP COLUMN `power_supply`,
DROP COLUMN `nic`,
DROP COLUMN `ip`,
DROP COLUMN `init_username`,
DROP COLUMN `init_password`;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment