getProductAttrPage(AdminProductAttrPageDTO productAttrPageDTO);
+
+ /**
+ * 获得规格属性数组
+ *
+ * 注意,该方法过滤了禁用的规格
+ *
+ * @return 规格属性数组
+ */
+ List getProductAttrList();
+
+ ProductAttrBO2 addProductAttr(Integer adminId, ProductAttrAddDTO productAttrAddDTO);
+
+ Boolean updateProductAttr(Integer adminId, ProductAttrUpdateDTO productAttrUpdateDTO);
+
+ Boolean updateProductAttrStatus(Integer adminId, Integer productAttrId,
+ @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") Integer status);
+
+ ProductAttrValueBO addProductAttrValue(Integer adminId, ProductAttrValueAddDTO productAttrValueAddDTO);
+
+ Boolean updateProductAttrValue(Integer adminId, ProductAttrValueUpdateDTO productAttrValueUpdateDTO);
+
+ Boolean updateProductAttrValueStatus(Integer adminId, Integer productAttrValueId,
+ @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") Integer status);
+
+}
diff --git a/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/impl/ProductAttrServiceImpl.java b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/impl/ProductAttrServiceImpl.java
new file mode 100644
index 000000000..3c40f8098
--- /dev/null
+++ b/product/product-biz/src/main/java/cn/iocoder/mall/product/biz/service/product/impl/ProductAttrServiceImpl.java
@@ -0,0 +1,255 @@
+package cn.iocoder.mall.product.biz.service.product.impl;
+
+import cn.iocoder.common.framework.util.CollectionUtil;
+import cn.iocoder.common.framework.util.ServiceExceptionUtil;
+import cn.iocoder.common.framework.vo.PageResult;
+import cn.iocoder.mall.mybatis.enums.DeletedStatusEnum;
+import cn.iocoder.mall.product.biz.bo.attr.ProductAttrBO;
+import cn.iocoder.mall.product.biz.bo.product.ProductAttrAndValuePairBO;
+import cn.iocoder.mall.product.biz.bo.product.ProductAttrBO2;
+import cn.iocoder.mall.product.biz.bo.product.ProductAttrSimpleBO;
+import cn.iocoder.mall.product.biz.bo.product.ProductAttrValueBO;
+import cn.iocoder.mall.product.biz.convert.attr.ProductAttrConvert;
+import cn.iocoder.mall.product.biz.convert.product.ProductAttrConvert2;
+import cn.iocoder.mall.product.biz.dao.product.ProductAttrMapper;
+import cn.iocoder.mall.product.biz.dao.product.ProductAttrValueMapper;
+import cn.iocoder.mall.product.biz.dataobject.product.ProductAttrDO;
+import cn.iocoder.mall.product.biz.dataobject.product.ProductAttrValueDO;
+import cn.iocoder.mall.product.biz.dto.attr.AdminProductAttrPageDTO;
+import cn.iocoder.mall.product.biz.dto.product.ProductAttrAddDTO;
+import cn.iocoder.mall.product.biz.dto.product.ProductAttrUpdateDTO;
+import cn.iocoder.mall.product.biz.dto.product.ProductAttrValueAddDTO;
+import cn.iocoder.mall.product.biz.dto.product.ProductAttrValueUpdateDTO;
+import cn.iocoder.mall.product.biz.enums.ProductErrorCodeEnum;
+import cn.iocoder.mall.product.biz.enums.product.ProductAttrConstants;
+import cn.iocoder.mall.product.biz.service.product.ProductAttrService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.Multimaps;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 商品规格 Service 实现类
+ *
+ * @see cn.iocoder.mall.product.biz.dataobject.product.ProductAttrDO
+ * @see cn.iocoder.mall.product.biz.dataobject.product.ProductAttrValueDO
+ */
+@Service
+public class ProductAttrServiceImpl implements ProductAttrService {
+
+ @Autowired
+ private ProductAttrMapper productAttrMapper;
+ @Autowired
+ private ProductAttrValueMapper productAttrValueMapper;
+
+ @Override
+ public PageResult getProductAttrPage(AdminProductAttrPageDTO productAttrPageDTO) {
+ //查询分页
+ Page page = new Page<>(productAttrPageDTO.getPageNo(), productAttrPageDTO.getPageSize());
+ LambdaQueryWrapper queryWrapper = Wrappers.query().lambda()
+ .like(StringUtils.isNotBlank(productAttrPageDTO.getName()), ProductAttrDO::getName, productAttrPageDTO.getName())
+ .eq(ProductAttrDO::getDeleted, false);
+ IPage attrPage = productAttrMapper.selectPage(page, queryWrapper);
+ PageResult productAttrPage = ProductAttrConvert.INSTANCE.convertPage(attrPage);
+ // 将规格值拼接上去
+ if (!CollectionUtil.isEmpty(productAttrPage.getList())) {
+ Set attrIds = productAttrPage.getList().stream().map(ProductAttrBO::getId).collect(Collectors.toSet());
+ List attrValues = productAttrValueMapper.selectList(Wrappers.query().lambda()
+ .in(ProductAttrValueDO::getAttrId, attrIds)
+ .eq(ProductAttrValueDO::getDeleted, false));
+ Map> attrValueMap = attrValues.stream().collect(Collectors.groupingBy(ProductAttrValueDO::getAttrId));
+ for (ProductAttrBO productAttrBO : productAttrPage.getList()) {
+ productAttrBO.setValues(ProductAttrConvert.INSTANCE.convertAttrValue(attrValueMap.get(productAttrBO.getId())));
+ }
+ }
+ // 返回结果
+ return productAttrPage;
+ }
+
+ public List validProductAttrAndValue(Set productAttrValueIds, boolean validStatus) {
+ // 首先,校验规格值
+ List attrValues = productAttrValueMapper.selectBatchIds(productAttrValueIds);
+ if (attrValues.size() != productAttrValueIds.size()) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_VALUE_NOT_EXIST.getCode());
+ }
+ if (validStatus) {
+ // 同时,校验下状态
+ for (ProductAttrValueDO attrValue : attrValues) {
+ if (ProductAttrConstants.ATTR_STATUS_DISABLE.equals(attrValue.getStatus())) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_VALUE_NOT_EXIST.getCode());
+ }
+ }
+ }
+ // 然后,校验规格
+ Set attrIds = attrValues.stream().map(ProductAttrValueDO::getAttrId).collect(Collectors.toSet());
+ List attrs = productAttrMapper.selectBatchIds(attrIds);
+ if (attrs.size() != attrIds.size()) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_NOT_EXIST.getCode());
+ }
+ if (validStatus) {
+ // 同时,校验下状态
+ for (ProductAttrDO attr : attrs) {
+ if (ProductAttrConstants.ATTR_VALUE_STATUS_DISABLE.equals(attr.getStatus())) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_NOT_EXIST.getCode());
+ }
+ }
+ }
+ // 返回成功
+ // ProductAttrDO 的映射,方便查找。
+ Map attrMap = attrs.stream().collect(Collectors.toMap(ProductAttrDO::getId, productAttrDO -> productAttrDO));
+ return attrValues.stream().map(productAttrValueDO -> new ProductAttrAndValuePairBO()
+ .setAttrId(productAttrValueDO.getAttrId()).setAttrName(attrMap.get(productAttrValueDO.getAttrId()).getName())
+ .setAttrValueId(productAttrValueDO.getId()).setAttrValueName(productAttrValueDO.getName())).collect(Collectors.toList());
+ }
+
+
+
+ @Override
+ public List getProductAttrList() {
+ // 查询所有开启的规格数组
+ List attrDos = productAttrMapper.selectList(Wrappers.query().lambda()
+ .in(ProductAttrDO::getStatus, ProductAttrConstants.ATTR_STATUS_ENABLE)
+ .eq(ProductAttrDO::getDeleted, false));
+ List attrs = ProductAttrConvert2.INSTANCE.convert3(attrDos);
+ // 如果为空,则返回空
+ if (attrs.isEmpty()) {
+ return Collections.emptyList();
+ }
+ // 将规格值拼接上去
+ List attrValues = productAttrValueMapper.selectList(Wrappers.query().lambda()
+ .in(ProductAttrValueDO::getStatus, ProductAttrConstants.ATTR_STATUS_ENABLE)
+ .eq(ProductAttrValueDO::getDeleted, false));
+ // KEY 是 attrId ,VALUE 是 ProductAttrValueDO 数组
+ ImmutableListMultimap attrValueMap = Multimaps.index(attrValues, ProductAttrValueDO::getAttrId);
+ for (ProductAttrSimpleBO productAttrSimpleBO : attrs) {
+ productAttrSimpleBO.setValues(ProductAttrConvert2.INSTANCE.convert4(((attrValueMap).get(productAttrSimpleBO.getId()))));
+ }
+ return attrs;
+ }
+
+ @Override
+ public ProductAttrBO2 addProductAttr(Integer adminId, ProductAttrAddDTO productAttrAddDTO) {
+ // 校验规格名不重复
+ int count = productAttrMapper.selectCount(Wrappers.query().lambda()
+ .eq(ProductAttrDO::getName, productAttrAddDTO.getName())
+ .eq(ProductAttrDO::getDeleted, false));
+ if (count > 0) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_EXISTS.getCode());
+ }
+ // 插入到数据库
+ ProductAttrDO productAttrDO = ProductAttrConvert2.INSTANCE.convert(productAttrAddDTO)
+ .setStatus(ProductAttrConstants.ATTR_STATUS_ENABLE);
+ productAttrDO.setCreateTime(new Date());
+ productAttrDO.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
+ productAttrMapper.insert(productAttrDO);
+ // 返回成功
+ return ProductAttrConvert2.INSTANCE.convert(productAttrDO);
+ }
+
+ @Override
+ public Boolean updateProductAttr(Integer adminId, ProductAttrUpdateDTO productAttrUpdateDTO) {
+ // 校验存在
+ if (productAttrMapper.selectById(productAttrUpdateDTO.getId()) == null) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_NOT_EXIST.getCode());
+ }
+ // 校验规格名不重复
+ ProductAttrDO existsAttrDO = productAttrMapper.selectOne(Wrappers.query().lambda()
+ .eq(ProductAttrDO::getName, productAttrUpdateDTO.getName())
+ .eq(ProductAttrDO::getDeleted, false));
+ if (existsAttrDO != null && !existsAttrDO.getId().equals(productAttrUpdateDTO.getId())) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_EXISTS.getCode());
+ }
+ // 更新到数据库
+ ProductAttrDO updateProductAttr = ProductAttrConvert2.INSTANCE.convert(productAttrUpdateDTO);
+ productAttrMapper.updateById(updateProductAttr);
+ // 返回成功
+ return true;
+ }
+
+ @Override
+ public Boolean updateProductAttrStatus(Integer adminId, Integer productAttrId, Integer status) {
+ // 校验存在
+ ProductAttrDO productAttrDO = productAttrMapper.selectById(productAttrId);
+ if (productAttrDO == null) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_NOT_EXIST.getCode());
+ }
+ // 校验状态
+ if (productAttrDO.getStatus().equals(status)) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_STATUS_EQUALS.getCode());
+ }
+ // 更新到数据库
+ ProductAttrDO updateProductAttr = new ProductAttrDO().setId(productAttrId).setStatus(status);
+ productAttrMapper.updateById(updateProductAttr);
+ // 返回成功
+ return true;
+ }
+
+ @Override
+ public ProductAttrValueBO addProductAttrValue(Integer adminId, ProductAttrValueAddDTO productAttrValueAddDTO) {
+ // 校验规格名不重复
+ int count = productAttrValueMapper.selectCount(Wrappers.query().lambda()
+ .eq(ProductAttrValueDO::getName, productAttrValueAddDTO.getName())
+ .eq(ProductAttrValueDO::getAttrId, productAttrValueAddDTO.getAttrId())
+ .eq(ProductAttrValueDO::getDeleted, false));
+ if (count > 0) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_VALUE_EXISTS.getCode());
+ }
+ // 插入到数据库
+ ProductAttrValueDO productAttrValueDO = ProductAttrConvert2.INSTANCE.convert(productAttrValueAddDTO)
+ .setStatus(ProductAttrConstants.ATTR_VALUE_STATUS_ENABLE);
+ productAttrValueDO.setCreateTime(new Date());
+ productAttrValueDO.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
+ productAttrValueMapper.insert(productAttrValueDO);
+ // 返回成功
+ return ProductAttrConvert2.INSTANCE.convert2(productAttrValueDO);
+ }
+
+ @Override
+ public Boolean updateProductAttrValue(Integer adminId, ProductAttrValueUpdateDTO productAttrValueUpdateDTO) {
+ // 校验存在
+ ProductAttrValueDO productAttrValueDO = productAttrValueMapper.selectById(productAttrValueUpdateDTO.getId());
+ if (productAttrValueDO == null) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_VALUE_NOT_EXIST.getCode());
+ }
+ // 校验规格名不重复
+ ProductAttrValueDO existsAttrDO = productAttrValueMapper.selectOne(Wrappers.query().lambda()
+ .eq(ProductAttrValueDO::getName, productAttrValueDO.getName())
+ .eq(ProductAttrValueDO::getAttrId, productAttrValueDO.getAttrId())
+ .eq(ProductAttrValueDO::getDeleted, false));
+ if (existsAttrDO != null && !existsAttrDO.getId().equals(productAttrValueUpdateDTO.getId())) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_VALUE_EXISTS.getCode());
+ }
+ // 更新到数据库
+ ProductAttrValueDO updateProductValue = ProductAttrConvert2.INSTANCE.convert(productAttrValueUpdateDTO);
+ productAttrValueMapper.updateById(updateProductValue);
+ // 返回成功
+ return true;
+ }
+
+ @Override
+ public Boolean updateProductAttrValueStatus(Integer adminId, Integer productAttrValueId, Integer status) {
+ // 校验存在
+ ProductAttrValueDO productAttrValueDO = productAttrValueMapper.selectById(productAttrValueId);
+ if (productAttrValueDO == null) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_VALUE_NOT_EXIST.getCode());
+ }
+ // 校验状态
+ if (productAttrValueDO.getStatus().equals(status)) {
+ throw ServiceExceptionUtil.exception(ProductErrorCodeEnum.PRODUCT_ATTR_VALUE_STATUS_EQUALS.getCode());
+ }
+ // 更新到数据库
+ ProductAttrValueDO updateProductAttrValue = new ProductAttrValueDO().setId(productAttrValueId).setStatus(status);
+ productAttrValueMapper.updateById(updateProductAttrValue);
+ // 返回成功
+ return true;
+ }
+
+}
diff --git a/product/product-biz/src/main/resources/biz.properties b/product/product-biz/src/main/resources/biz.properties
new file mode 100644
index 000000000..9275a3bfb
--- /dev/null
+++ b/product/product-biz/src/main/resources/biz.properties
@@ -0,0 +1,2 @@
+##################### 业务模块 #####################
+
diff --git a/product/product-biz/src/main/resources/biz.yaml b/product/product-biz/src/main/resources/biz.yaml
new file mode 100644
index 000000000..f2aa6a7b5
--- /dev/null
+++ b/product/product-biz/src/main/resources/biz.yaml
@@ -0,0 +1,19 @@
+spring:
+ # 数据源配置项
+ datasource:
+ url: jdbc:mysql://s1.iocoder.cn:3306/mall_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8
+ driver-class-name: com.mysql.jdbc.Driver
+ username: root
+ password: 3WLiVUBEwTbvAfsh
+
+# MyBatis Plus 配置项
+mybatis-plus:
+ configuration:
+ map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
+ global-config:
+ db-config:
+ id-type: auto
+ logic-delete-value: 1 # 逻辑已删除值(默认为 1)
+ logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
+ mapper-locations: classpath*:mapper/*.xml
+ type-aliases-package: cn.iocoder.mall.product.biz.dataobject
diff --git a/product/product-biz/src/main/resources/mapper/ProductSpuMapper.xml b/product/product-biz/src/main/resources/mapper/ProductSpuMapper.xml
new file mode 100644
index 000000000..dc3d2b7c7
--- /dev/null
+++ b/product/product-biz/src/main/resources/mapper/ProductSpuMapper.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/product/product-rest/pom.xml b/product/product-rest/pom.xml
new file mode 100644
index 000000000..3ff0fd551
--- /dev/null
+++ b/product/product-rest/pom.xml
@@ -0,0 +1,41 @@
+
+
+
+ system
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ product-rest
+ 提供 system 服务的 Rest 接口的实现,提供对外调用
+
+
+
+
+ cn.iocoder.mall
+ product-biz
+ 1.0-SNAPSHOT
+
+
+
+
+ cn.iocoder.mall
+ mall-spring-boot-starter-web
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ mall-spring-boot-starter-security
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ mall-spring-boot-starter-swagger
+ 1.0-SNAPSHOT
+
+
+
+
diff --git a/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/controller/admins/AdminsProductAttrController.java b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/controller/admins/AdminsProductAttrController.java
new file mode 100644
index 000000000..f6bbb50f0
--- /dev/null
+++ b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/controller/admins/AdminsProductAttrController.java
@@ -0,0 +1,134 @@
+package cn.iocoder.mall.product.rest.controller.admins;
+
+import cn.iocoder.common.framework.vo.CommonResult;
+import cn.iocoder.common.framework.vo.PageResult;
+import cn.iocoder.mall.product.biz.bo.attr.ProductAttrBO;
+import cn.iocoder.mall.product.biz.dto.attr.AdminProductAttrPageDTO;
+import cn.iocoder.mall.product.biz.service.product.ProductAttrService;
+import cn.iocoder.mall.product.rest.convert.attr.ProductAttrConvert;
+import cn.iocoder.mall.product.rest.request.attr.AdminProductAttrPageRequest;
+import cn.iocoder.mall.product.rest.response.attr.AdminsProductAttrPageResponse;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 商品规格
+ *
+ * @author lanmao
+ * @version 1.0
+ * @date 2020/05/06 16:36
+ */
+@RestController
+@RequestMapping("admins")
+@Api("商品规格")
+public class AdminsProductAttrController {
+
+ @Autowired
+ private ProductAttrService productAttrService;
+
+ @GetMapping("/attr/page")
+ @ApiOperation("获得规格分页")
+ public CommonResult> attrPage(AdminProductAttrPageRequest request) {
+ AdminProductAttrPageDTO pageDTO = ProductAttrConvert.INSTANCE.convert(request);
+ PageResult productAttrPage = productAttrService.getProductAttrPage(pageDTO);
+ PageResult adminPageResponse = ProductAttrConvert.INSTANCE.convertPage(productAttrPage);
+ return CommonResult.success(adminPageResponse);
+ }
+
+// @GetMapping("/attr/tree")
+// @ApiOperation(value = "获得规格树结构", notes = "该接口返回的信息更为精简。一般用于前端缓存数据字典到本地。")
+// public CommonResult> tree() {
+// // 查询全列表
+// List result = productAttrService.getProductAttrList();
+// // 返回结果
+// return success(ProductAttrConvert.INSTANCE.convert(result));
+// }
+//
+// @PostMapping("/attr/add")
+// @ApiOperation(value = "创建商品规格")
+// @ApiImplicitParams({
+// @ApiImplicitParam(name = "name", value = "规格名", required = true, example = "颜色")
+// })
+// public CommonResult addAttr(@RequestParam("name") String name) {
+// // 创建 ProductAttrAddDTO 对象
+// ProductAttrAddDTO productAttrAddDTO = new ProductAttrAddDTO().setName(name);
+// // 添加
+// ProductAttrBO result = productAttrService.addProductAttr(AdminSecurityContextHolder.getContext().getAdminId(), productAttrAddDTO);
+// // 返回结果
+// return success(ProductAttrConvert.INSTANCE.convert3(result));
+// }
+//
+// @PostMapping("/attr/update")
+// @ApiOperation(value = "修改商品规格")
+// @ApiImplicitParams({
+// @ApiImplicitParam(name = "id", value = "规格编号", required = true, example = "1"),
+// @ApiImplicitParam(name = "name", value = "规格名", required = true, example = "颜色")
+// })
+// public CommonResult updateAttr(@RequestParam("id") Integer id,
+// @RequestParam("name") String name) {
+// // 创建 ProductAttrUpdateDTO 对象
+// ProductAttrUpdateDTO productAttrUpdateDTO = new ProductAttrUpdateDTO().setId(id).setName(name);
+// // 更新
+// return success(productAttrService.updateProductAttr(AdminSecurityContextHolder.getContext().getAdminId(), productAttrUpdateDTO));
+// }
+//
+// @PostMapping("/attr/update_status")
+// @ApiOperation(value = "修改商品规格状态")
+// @ApiImplicitParams({
+// @ApiImplicitParam(name = "id", value = "规格编号", required = true, example = "100"),
+// @ApiImplicitParam(name = "status", value = "状态", required = true, example = "1")
+// })
+// public CommonResult updateAttrStatus(@RequestParam("id") Integer id,
+// @RequestParam("status") Integer status) {
+// return success(productAttrService.updateProductAttrStatus(AdminSecurityContextHolder.getContext().getAdminId(), id, status));
+// }
+//
+// // TODO 芋艿 暂时不考虑 delete Attr 。因为关联逻辑比较多
+//
+// @PostMapping("/attr_value/add")
+// @ApiOperation(value = "创建商品规格值")
+// @ApiImplicitParams({
+// @ApiImplicitParam(name = "attrId", value = "规格编号", required = true, example = "100"),
+// @ApiImplicitParam(name = "name", value = "规格值", required = true, example = "蓝色")
+// })
+// public CommonResult addAttrValue(@RequestParam("attrId") Integer attrId,
+// @RequestParam("name") String name) {
+// // 创建 ProductAttrValueAddDTO 对象
+// ProductAttrValueAddDTO productAttrValueAddDTO = new ProductAttrValueAddDTO().setAttrId(attrId).setName(name);
+// // 添加
+// ProductAttrValueBO result = productAttrService.addProductAttrValue(AdminSecurityContextHolder.getContext().getAdminId(), productAttrValueAddDTO);
+// // 返回结果
+// return success(ProductAttrConvert.INSTANCE.convert4(result));
+// }
+//
+// @PostMapping("/attr_value/update")
+// @ApiOperation(value = "修改商品规格值")
+// @ApiImplicitParams({
+// @ApiImplicitParam(name = "id", value = "规格值编号", required = true, example = "100"),
+// @ApiImplicitParam(name = "name", value = "规格值", required = true, example = "蓝色")
+// })
+// public CommonResult updateAttrValue(@RequestParam("id") Integer id,
+// @RequestParam("name") String name) {
+// // 创建 ProductAttrValueUpdateDTO 对象
+// ProductAttrValueUpdateDTO productAttrValueUpdateDTO = new ProductAttrValueUpdateDTO().setId(id).setName(name);
+// // 更新
+// return success(productAttrService.updateProductAttrValue(AdminSecurityContextHolder.getContext().getAdminId(), productAttrValueUpdateDTO));
+// }
+//
+// @PostMapping("/attr_value/update_status")
+// @ApiImplicitParams({
+// @ApiImplicitParam(name = "id", value = "规格编号", required = true, example = "100"),
+// @ApiImplicitParam(name = "status", value = "状态", required = true, example = "1")
+// })
+// public CommonResult updateAttrValueStatus(@RequestParam("id") Integer id,
+// @RequestParam("status") Integer status) {
+// return success(productAttrService.updateProductAttrValueStatus(AdminSecurityContextHolder.getContext().getAdminId(), id, status));
+// }
+
+ // TODO 芋艿 暂时不考虑 delete Attr Value 。因为关联逻辑比较多
+
+}
diff --git a/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/controller/users/UserFavoriteController.java b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/controller/users/UserFavoriteController.java
new file mode 100644
index 000000000..6f5ba4993
--- /dev/null
+++ b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/controller/users/UserFavoriteController.java
@@ -0,0 +1,18 @@
+package cn.iocoder.mall.product.rest.controller.users;
+
+import io.swagger.annotations.Api;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 用户收藏
+ * @author xiaofeng
+ * @date 2019/07/07 11:06
+ * @version 1.0
+ */
+@RestController
+@RequestMapping("users/favorite")
+@Api("用户收藏")
+public class UserFavoriteController {
+
+}
diff --git a/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/convert/attr/ProductAttrConvert.java b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/convert/attr/ProductAttrConvert.java
new file mode 100644
index 000000000..13415d7c9
--- /dev/null
+++ b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/convert/attr/ProductAttrConvert.java
@@ -0,0 +1,19 @@
+package cn.iocoder.mall.product.rest.convert.attr;
+
+import cn.iocoder.common.framework.vo.PageResult;
+import cn.iocoder.mall.product.biz.bo.attr.ProductAttrBO;
+import cn.iocoder.mall.product.biz.dto.attr.AdminProductAttrPageDTO;
+import cn.iocoder.mall.product.rest.request.attr.AdminProductAttrPageRequest;
+import cn.iocoder.mall.product.rest.response.attr.AdminsProductAttrPageResponse;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface ProductAttrConvert {
+
+ ProductAttrConvert INSTANCE = Mappers.getMapper(ProductAttrConvert.class);
+
+ AdminProductAttrPageDTO convert(AdminProductAttrPageRequest bean);
+
+ PageResult convertPage(PageResult productAttrPage);
+}
diff --git a/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/request/attr/AdminProductAttrPageRequest.java b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/request/attr/AdminProductAttrPageRequest.java
new file mode 100644
index 000000000..7b663875c
--- /dev/null
+++ b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/request/attr/AdminProductAttrPageRequest.java
@@ -0,0 +1,19 @@
+package cn.iocoder.mall.product.rest.request.attr;
+
+import cn.iocoder.common.framework.vo.PageParam;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+@ApiModel("商品 - 规格模块 - 商品规格 Request")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+public class AdminProductAttrPageRequest extends PageParam {
+
+ @ApiModelProperty(value = "商品规格名字,模糊匹配", example = "材料")
+ private String name;
+
+}
diff --git a/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/response/attr/AdminsProductAttrPageResponse.java b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/response/attr/AdminsProductAttrPageResponse.java
new file mode 100644
index 000000000..0e24246c7
--- /dev/null
+++ b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/response/attr/AdminsProductAttrPageResponse.java
@@ -0,0 +1,66 @@
+package cn.iocoder.mall.product.rest.response.attr;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+import java.util.List;
+
+@ApiModel("商品管理 - 商品规格模块 - 商品规格分页信息 Response")
+@Data
+@Accessors(chain = true)
+public class AdminsProductAttrPageResponse {
+
+ /**
+ * 规格编号
+ */
+ private Integer id;
+ /**
+ * 规格名
+ */
+ private String name;
+ /**
+ * 状态
+ */
+ private Integer status;
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 规格值数组
+ */
+ private List values;
+
+ @ApiModel("规格值")
+ @Data
+ @Accessors(chain = true)
+ public static class ProductAttrValue {
+
+ /**
+ * 规格值编号
+ */
+ @ApiModelProperty(value = "规格值编号", required = true, example = "1")
+ private Integer id;
+ /**
+ * 规格值名
+ */
+ @ApiModelProperty(value = "规格值名", required = true, example = "小")
+ private String name;
+ /**
+ * 状态
+ */
+ @ApiModelProperty(value = "状态", required = true, example = "1")
+ private Integer status;
+ /**
+ * 创建时间
+ */
+ @ApiModelProperty(value = "创建时间", required = true, example = "时间戳格式")
+ private Date createTime;
+ }
+
+
+}
diff --git a/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/response/attr/AdminsProdutAttrResponse.java b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/response/attr/AdminsProdutAttrResponse.java
new file mode 100644
index 000000000..3ca9c3330
--- /dev/null
+++ b/product/product-rest/src/main/java/cn/iocoder/mall/product/rest/response/attr/AdminsProdutAttrResponse.java
@@ -0,0 +1,4 @@
+package cn.iocoder.mall.product.rest.response.attr;
+
+public class AdminsProdutAttrResponse {
+}
diff --git a/product/product-rest/src/main/resources/rest.yaml b/product/product-rest/src/main/resources/rest.yaml
new file mode 100644
index 000000000..37801cc94
--- /dev/null
+++ b/product/product-rest/src/main/resources/rest.yaml
@@ -0,0 +1,12 @@
+# 服务器的配置项
+server:
+ port: 18081
+ servlet:
+ context-path: /product-api/
+
+# Swagger 配置项
+swagger:
+ title: 商品子系统
+ description: 商品子系统
+ version: 1.0.0
+ base-package: cn.iocoder.mall.system.rest.controller
diff --git a/product/product-rpc-api/pom.xml b/product/product-rpc-api/pom.xml
new file mode 100644
index 000000000..8cb7a6478
--- /dev/null
+++ b/product/product-rpc-api/pom.xml
@@ -0,0 +1,34 @@
+
+
+
+ system
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ product-rpc-api
+
+
+
+
+ cn.iocoder.mall
+ product-biz-api
+ 1.0-SNAPSHOT
+
+
+
+
+ javax.validation
+ validation-api
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
diff --git a/product/product-rpc-api/src/main/java/cn/iocoder/mall/product/rpc/package-info.java b/product/product-rpc-api/src/main/java/cn/iocoder/mall/product/rpc/package-info.java
new file mode 100644
index 000000000..4c49428c5
--- /dev/null
+++ b/product/product-rpc-api/src/main/java/cn/iocoder/mall/product/rpc/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 提供 system 服务的 RPC 接口的定义,提供内部调用
+ */
+package cn.iocoder.mall.product.rpc;
diff --git a/product/product-rpc/pom.xml b/product/product-rpc/pom.xml
new file mode 100644
index 000000000..010865b94
--- /dev/null
+++ b/product/product-rpc/pom.xml
@@ -0,0 +1,40 @@
+
+
+
+ system
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ product-rpc
+
+
+
+
+ cn.iocoder.mall
+ product-rpc-api
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ product-biz
+ 1.0-SNAPSHOT
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-dubbo
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
diff --git a/product/product-rpc/src/main/java/cn/iocoder/mall/product/rpc/package-info.java b/product/product-rpc/src/main/java/cn/iocoder/mall/product/rpc/package-info.java
new file mode 100644
index 000000000..045fb0f93
--- /dev/null
+++ b/product/product-rpc/src/main/java/cn/iocoder/mall/product/rpc/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * 提供 product 服务的 RPC 接口的实现,提供内部调用
+ */
+package cn.iocoder.mall.product.rpc;
diff --git a/product/product-rpc/src/main/resources/rpc-local.yaml b/product/product-rpc/src/main/resources/rpc-local.yaml
new file mode 100644
index 000000000..e056170af
--- /dev/null
+++ b/product/product-rpc/src/main/resources/rpc-local.yaml
@@ -0,0 +1,14 @@
+spring:
+ # Spring Cloud 配置项
+ cloud:
+ nacos:
+ # Spring Cloud Nacos Discovery 配置项
+ discovery:
+ server-addr: s1.iocoder.cn:8848 # Nacos 服务器地址
+ namespace: local # Nacos 命名空间
+
+# Dubbo 配置项
+dubbo:
+ # Dubbo 注册中心
+ registry:
+ address: spring-cloud://s1.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址
diff --git a/product/product-rpc/src/main/resources/rpc-test.yaml b/product/product-rpc/src/main/resources/rpc-test.yaml
new file mode 100644
index 000000000..d3d0e9e69
--- /dev/null
+++ b/product/product-rpc/src/main/resources/rpc-test.yaml
@@ -0,0 +1,14 @@
+spring:
+ # Spring Cloud 配置项
+ cloud:
+ nacos:
+ # Spring Cloud Nacos Discovery 配置项
+ discovery:
+ server-addr: s1.iocoder.cn:8848 # Nacos 服务器地址
+ namespace: test # Nacos 命名空间
+
+# Dubbo 配置项
+dubbo:
+ # Dubbo 注册中心
+ registry:
+ address: spring-cloud://s1.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址
diff --git a/product/product-rpc/src/main/resources/rpc.yaml b/product/product-rpc/src/main/resources/rpc.yaml
new file mode 100644
index 000000000..e056da380
--- /dev/null
+++ b/product/product-rpc/src/main/resources/rpc.yaml
@@ -0,0 +1,40 @@
+# Dubbo 配置项
+dubbo:
+ # Spring Cloud Alibaba Dubbo 专属配置
+ cloud:
+ subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用
+ # Dubbo 提供者的协议
+ protocol:
+ name: dubbo
+ port: -1
+ # Dubbo 提供服务的扫描基础包
+ scan:
+ base-packages: cn.iocoder.mall.system.rpc.rpc
+ # Dubbo 服务提供者的配置
+ provider:
+ filter: -exception
+ SystemLogRPC:
+ version: 1.0.0
+ OAuth2RPC:
+ version: 1.0.0
+ AuthorizationRPC:
+ version: 1.0.0
+ AdminRPC:
+ version: 1.0.0
+ UserRPC:
+ version: 1.0.0
+ UserAddressRPC:
+ version: 1.0.0
+
+ # Dubbo 服务消费者的配置
+ consumer:
+ SystemLogRPC: # 用于 AccessLogInterceptor 等拦截器,记录 HTTP API 请求的访问日志
+ version: 1.0.0
+ OAuth2RPC: # 用于 AccountAuthInterceptor 拦截器,执行认证
+ version: 1.0.0
+ AuthorizationRPC: # 用于 AccountAuthInterceptor 拦截器,执行鉴权(权限验证)
+ version: 1.0.0
+ AdminRPC:
+ version: 1.0.0
+ UserRPC:
+ version: 1.0.0
diff --git a/product/product-start/pom.xml b/product/product-start/pom.xml
new file mode 100644
index 000000000..594e99ee6
--- /dev/null
+++ b/product/product-start/pom.xml
@@ -0,0 +1,104 @@
+
+
+
+ product
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ product-start
+
+
+
+
+ cn.iocoder.mall
+ common-framework
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ mall-spring-boot
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ product-service-api
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ product-service-impl
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ user-sdk
+ 1.0-SNAPSHOT
+
+
+ cn.iocoder.mall
+ system-sdk
+ 1.0-SNAPSHOT
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ io.springfox
+ springfox-swagger2
+
+
+ com.github.xiaoymin
+ swagger-bootstrap-ui
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-sentinel
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/product/product-start/src/main/java/cn/iocoder/mall/product/application/ProductApplication.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/ProductApplication.java
new file mode 100644
index 000000000..81aed8b6a
--- /dev/null
+++ b/product/product-start/src/main/java/cn/iocoder/mall/product/application/ProductApplication.java
@@ -0,0 +1,26 @@
+package cn.iocoder.mall.product.application;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.config.ConfigFileApplicationListener;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.product"})
+@EnableAsync(proxyTargetClass = true)
+public class ProductApplication {
+
+ /**
+ * 设置需要读取的配置文件的名字。
+ * 基于 {@link org.springframework.boot.context.config.ConfigFileApplicationListener#CONFIG_NAME_PROPERTY} 实现。
+ */
+ private static final String CONFIG_NAME_VALUE = "biz,rest,rpc,application";
+
+ public static void main(String[] args) {
+ // 设置环境变量
+ System.setProperty(ConfigFileApplicationListener.CONFIG_NAME_PROPERTY, CONFIG_NAME_VALUE);
+
+ // 启动 Spring Boot 应用
+ SpringApplication.run(ProductApplication.class, args);
+ }
+
+}
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductAttrController.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductAttrController.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductAttrController.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductAttrController.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductBrandController.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductBrandController.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductBrandController.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductBrandController.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductCategoryController.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductCategoryController.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductCategoryController.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductCategoryController.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductSpuController.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductSpuController.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductSpuController.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/admins/AdminsProductSpuController.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UserFavoriteController.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/users/UserFavoriteController.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UserFavoriteController.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/users/UserFavoriteController.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductCategoryController.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductCategoryController.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductCategoryController.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductCategoryController.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuCollectionController.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuCollectionController.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuCollectionController.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuCollectionController.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuController.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuController.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuController.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/controller/users/UsersProductSpuController.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/convert/ProductAttrConvert.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/convert/ProductAttrConvert.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/convert/ProductAttrConvert.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/convert/ProductAttrConvert.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/convert/ProductBrandConvert.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/convert/ProductBrandConvert.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/convert/ProductBrandConvert.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/convert/ProductBrandConvert.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/convert/ProductCategoryConvert.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/convert/ProductCategoryConvert.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/convert/ProductCategoryConvert.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/convert/ProductCategoryConvert.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/convert/ProductSpuConvert.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/convert/ProductSpuConvert.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/convert/ProductSpuConvert.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/convert/ProductSpuConvert.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrAndValuePairVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrAndValuePairVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrAndValuePairVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrAndValuePairVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrDetailVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrDetailVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrDetailVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrDetailVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrPageVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrPageVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrPageVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrPageVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrSimpleVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrSimpleVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrSimpleVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrSimpleVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueDetailVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueDetailVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueDetailVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueDetailVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueSimpleVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueSimpleVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueSimpleVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueSimpleVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductAttrValueVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductBrandVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductBrandVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductBrandVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductBrandVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductBrangPageVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductBrangPageVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductBrangPageVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductBrangPageVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductCategoryTreeNodeVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductCategoryTreeNodeVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductCategoryTreeNodeVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductCategoryTreeNodeVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductCategoryVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductCategoryVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductCategoryVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductCategoryVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSkuDetailVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSkuDetailVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSkuDetailVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSkuDetailVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuDetailVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuDetailVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuDetailVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuDetailVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuPageVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuPageVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuPageVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuPageVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/admins/AdminsProductSpuVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductAttrAndValuePairVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductAttrAndValuePairVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductAttrAndValuePairVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductAttrAndValuePairVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductCategoryVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductCategoryVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductCategoryVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductCategoryVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSkuDetailVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSkuDetailVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSkuDetailVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSkuDetailVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuDetailVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuDetailVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuDetailVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuDetailVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuPageVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuPageVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuPageVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuPageVO.java
diff --git a/product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuVO.java b/product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuVO.java
similarity index 100%
rename from product/product-application/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuVO.java
rename to product/product-start/src/main/java/cn/iocoder/mall/product/application/vo/users/UsersProductSpuVO.java
diff --git a/product/product-start/src/main/resources/application-test.yaml b/product/product-start/src/main/resources/application-test.yaml
new file mode 100644
index 000000000..22be4ba61
--- /dev/null
+++ b/product/product-start/src/main/resources/application-test.yaml
@@ -0,0 +1,6 @@
+swagger:
+ enable: true
+ title: 商品子系统
+ description: 商品子系统
+ version: 1.0.0
+ base-package: cn.iocoder.mall.product.application.controller
diff --git a/product/product-start/src/main/resources/application.yaml b/product/product-start/src/main/resources/application.yaml
new file mode 100644
index 000000000..a5f4207ce
--- /dev/null
+++ b/product/product-start/src/main/resources/application.yaml
@@ -0,0 +1,34 @@
+spring:
+ application:
+ name: product-application
+
+ # Spring Cloud 配置项
+ cloud:
+ # Spring Cloud Sentinel 配置项
+ sentinel:
+ transport:
+ dashboard: s1.iocoder.cn:12088 # Sentinel Dashboard 服务地址
+ eager: true # 项目启动时,直接连接到 Sentinel
+
+# server
+server:
+ port: 18081
+ servlet:
+ context-path: /product-api/
+
+
+
+management:
+ endpoints:
+ web:
+ exposure:
+ include: health,info,env,metrics,prometheus
+ metrics:
+ enabled: true
+
+swagger:
+ enable: true
+ title: 商品子系统
+ description: 商品子系统
+ version: 1.0.0
+ base-package: cn.iocoder.mall.product.application.controller