mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-26 01:01:52 +08:00
commit
924a23003f
2
pom.xml
2
pom.xml
@ -18,7 +18,7 @@
|
||||
<module>yudao-module-system</module>
|
||||
<module>yudao-module-infra</module>
|
||||
<module>yudao-module-pay</module>
|
||||
<module>yudao-module-market</module>
|
||||
<module>yudao-module-mall</module>
|
||||
</modules>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
|
55
sql/mall.sql
Normal file
55
sql/mall.sql
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
Navicat Premium Data Transfer
|
||||
|
||||
Source Server : 127.0.0.1
|
||||
Source Server Type : MySQL
|
||||
Source Server Version : 80026
|
||||
Source Host : localhost:3306
|
||||
Source Schema : ruoyi-vue-pro
|
||||
|
||||
Target Server Type : MySQL
|
||||
Target Server Version : 80026
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 05/02/2022 00:50:30
|
||||
*/
|
||||
SET
|
||||
FOREIGN_KEY_CHECKS = 0;
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for product_category
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `product_category`;
|
||||
CREATE TABLE `product_category`
|
||||
(
|
||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '分类编号',
|
||||
`pid` bigint NOT NULL COMMENT '父分类编号',
|
||||
`name` varchar(255) NOT NULL COMMENT '分类名称',
|
||||
`icon` varchar(100) NOT NULL DEFAULT '#' COMMENT '分类图标',
|
||||
`banner_url` varchar(255) NOT NULL COMMENT '分类图片',
|
||||
`sort` int DEFAULT '0' COMMENT '分类排序',
|
||||
`description` varchar(1024) DEFAULT NULL COMMENT '分类描述',
|
||||
`status` tinyint NOT NULL COMMENT '开启状态',
|
||||
`creator` varchar(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updater` varchar(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB COMMENT='商品分类';
|
||||
|
||||
-- TODO 父级菜单的 id 处理
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `menu_type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) VALUES (2000, '商城', '', 1, 1, 0, '/mall', 'merchant', NULL, 0);
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `menu_type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) VALUES (2001, '商品管理', '', 1, 1, 2000, 'product', 'dict', NULL, 0);
|
||||
-- 菜单 SQL
|
||||
INSERT INTO `system_menu`(`name`, `permission`, `menu_type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) VALUES ('商品分类管理', '', 2, 0, 2001, 'category', '', 'mall/product/category/index', 0);
|
||||
-- 按钮父菜单ID
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
-- 按钮 SQL
|
||||
INSERT INTO `system_menu`(`name`, `permission`, `menu_type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) VALUES ('商品分类查询', 'product:category:query', 3, 1, @parentId, '', '', '', 0);
|
||||
INSERT INTO `system_menu`(`name`, `permission`, `menu_type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) VALUES ('商品分类创建', 'product:category:create', 3, 2, @parentId, '', '', '', 0);
|
||||
INSERT INTO `system_menu`(`name`, `permission`, `menu_type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) VALUES ('商品分类更新', 'product:category:update', 3, 3, @parentId, '', '', '', 0);
|
||||
INSERT INTO `system_menu`(`name`, `permission`, `menu_type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) VALUES ('商品分类删除', 'product:category:delete', 3, 4, @parentId, '', '', '', 0);
|
||||
INSERT INTO `system_menu`(`name`, `permission`, `menu_type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`) VALUES ('商品分类导出', 'product:category:export', 3, 5, @parentId, '', '', '', 0);
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.file;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
@ -23,6 +24,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.util.Date;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@ -47,6 +49,17 @@ public class FileController {
|
||||
return success(fileService.createFile(path, IoUtil.readBytes(file.getInputStream())));
|
||||
}
|
||||
|
||||
@PostMapping("/simple-upload")
|
||||
@ApiOperation("简单上传文件")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "file", value = "文件附件", required = true, dataTypeClass = MultipartFile.class),
|
||||
})
|
||||
public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file) throws Exception {
|
||||
String path = DateUtil.format(new Date(), "yyyy/MM/dd/") + file.getOriginalFilename();
|
||||
String file1 = fileService.createFile(path, IoUtil.readBytes(file.getInputStream()));
|
||||
return success(file1);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@ApiOperation("删除文件")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
|
||||
|
@ -9,7 +9,7 @@
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>yudao-module-market</artifactId>
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
@ -21,6 +21,8 @@
|
||||
<modules>
|
||||
<module>yudao-module-market-api</module>
|
||||
<module>yudao-module-market-biz</module>
|
||||
<module>yudao-module-product-api</module>
|
||||
<module>yudao-module-product-biz</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
@ -3,8 +3,8 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>yudao-module-market</artifactId>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -3,8 +3,8 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>yudao-module-market</artifactId>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
27
yudao-module-mall/yudao-module-product-api/pom.xml
Normal file
27
yudao-module-mall/yudao-module-product-api/pom.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>yudao-module-product-api</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
product 模块 API,暴露给其它模块调用
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-common</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package cn.iocoder.yudao.module.product.api;
|
@ -0,0 +1,14 @@
|
||||
package cn.iocoder.yudao.module.product.enums;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||
|
||||
/**
|
||||
* product 错误码枚举类
|
||||
* <p>
|
||||
* product 系统,使用 1-008-000-000 段
|
||||
*/
|
||||
public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 商品分类相关 1008001000============
|
||||
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1008001000, "商品分类不存在");
|
||||
}
|
67
yudao-module-mall/yudao-module-product-biz/pom.xml
Normal file
67
yudao-module-mall/yudao-module-product-biz/pom.xml
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-mall</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>yudao-module-product-biz</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
product 模块,主要实现商品相关功能
|
||||
例如:品牌、商品分类、spu、sku等功能。
|
||||
</description>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-product-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-operatelog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-weixin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,108 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;
|
||||
import cn.iocoder.yudao.module.product.convert.category.CategoryConvert;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
|
||||
import cn.iocoder.yudao.module.product.service.category.CategoryService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
|
||||
|
||||
@Api(tags = "管理后台 - 商品分类")
|
||||
@RestController
|
||||
@RequestMapping("/product/category")
|
||||
@Validated
|
||||
public class CategoryController {
|
||||
|
||||
@Resource
|
||||
private CategoryService categoryService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@ApiOperation("创建商品分类")
|
||||
@PreAuthorize("@ss.hasPermission('product:category:create')")
|
||||
public CommonResult<Long> createCategory(@Valid @RequestBody CategoryCreateReqVO createReqVO) {
|
||||
return success(categoryService.createCategory(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@ApiOperation("更新商品分类")
|
||||
@PreAuthorize("@ss.hasPermission('product:category:update')")
|
||||
public CommonResult<Boolean> updateCategory(@Valid @RequestBody CategoryUpdateReqVO updateReqVO) {
|
||||
categoryService.updateCategory(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@ApiOperation("删除商品分类")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('product:category:delete')")
|
||||
public CommonResult<Boolean> deleteCategory(@RequestParam("id") Long id) {
|
||||
categoryService.deleteCategory(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@ApiOperation("获得商品分类")
|
||||
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
@PreAuthorize("@ss.hasPermission('product:category:query')")
|
||||
public CommonResult<CategoryRespVO> getCategory(@RequestParam("id") Long id) {
|
||||
CategoryDO category = categoryService.getCategory(id);
|
||||
return success(CategoryConvert.INSTANCE.convert(category));
|
||||
}
|
||||
|
||||
@GetMapping("/listByIds")
|
||||
@ApiOperation("获得商品分类列表")
|
||||
@PreAuthorize("@ss.hasPermission('product:category:query')")
|
||||
public CommonResult<List<CategoryRespVO>> getCategoryList(@RequestParam("ids") Collection<Long> ids) {
|
||||
List<CategoryDO> list = categoryService.getCategoryList(ids);
|
||||
return success(CategoryConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
@GetMapping("/listByQuery")
|
||||
@ApiOperation("获得商品分类列表")
|
||||
@PreAuthorize("@ss.hasPermission('product:category:query')")
|
||||
public CommonResult<List<CategoryRespVO>> listByQuery(@Valid CategoryTreeListReqVO treeListReqVO) {
|
||||
List<CategoryDO> list = categoryService.getCategoryTreeList(treeListReqVO);
|
||||
list.sort(Comparator.comparing(CategoryDO::getSort));
|
||||
return success(CategoryConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@ApiOperation("获得商品分类分页")
|
||||
@PreAuthorize("@ss.hasPermission('product:category:query')")
|
||||
public CommonResult<PageResult<CategoryRespVO>> getCategoryPage(@Valid CategoryPageReqVO pageVO) {
|
||||
PageResult<CategoryDO> pageResult = categoryService.getCategoryPage(pageVO);
|
||||
return success(CategoryConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@ApiOperation("导出商品分类 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('product:category:export')")
|
||||
@OperateLog(type = EXPORT)
|
||||
public void exportCategoryExcel(@Valid CategoryExportReqVO exportReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
List<CategoryDO> list = categoryService.getCategoryList(exportReqVO);
|
||||
// 导出 Excel
|
||||
List<CategoryExcelVO> datas = CategoryConvert.INSTANCE.convertList02(list);
|
||||
ExcelUtils.write(response, "商品分类.xls", "数据", CategoryExcelVO.class, datas);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 商品分类 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class CategoryBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "父分类编号", required = true, example = "1")
|
||||
@NotNull(message = "父分类编号不能为空")
|
||||
private Long pid;
|
||||
|
||||
@ApiModelProperty(value = "分类名称", required = true, example = "办公文具")
|
||||
@NotBlank(message = "分类名称不能为空")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "分类图标")
|
||||
@NotBlank(message = "分类图标不能为空")
|
||||
private String icon;
|
||||
|
||||
@ApiModelProperty(value = "分类图片", required = true)
|
||||
@NotBlank(message = "分类图片不能为空")
|
||||
private String bannerUrl;
|
||||
|
||||
@ApiModelProperty(value = "分类排序", required = true, example = "1")
|
||||
private Integer sort;
|
||||
|
||||
@ApiModelProperty(value = "分类描述", required = true, example = "描述")
|
||||
private String description;
|
||||
|
||||
@ApiModelProperty(value = "开启状态", required = true, example = "0")
|
||||
@NotNull(message = "开启状态不能为空")
|
||||
private Integer status;
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
@ApiModel("管理后台 - 商品分类创建 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CategoryCreateReqVO extends CategoryBaseVO {
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
|
||||
|
||||
/**
|
||||
* 商品分类 Excel VO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class CategoryExcelVO {
|
||||
|
||||
@ExcelProperty("分类编号")
|
||||
private Long id;
|
||||
|
||||
@ExcelProperty("父分类编号")
|
||||
private Long pid;
|
||||
|
||||
@ExcelProperty("分类名称")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("分类图标")
|
||||
private String icon;
|
||||
|
||||
@ExcelProperty("分类图片")
|
||||
private String bannerUrl;
|
||||
|
||||
@ExcelProperty("分类排序")
|
||||
private Integer sort;
|
||||
|
||||
@ExcelProperty("分类描述")
|
||||
private String description;
|
||||
|
||||
@ExcelProperty(value = "开启状态", converter = DictConvert.class)
|
||||
@DictFormat("common_status") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
|
||||
private Integer status;
|
||||
|
||||
@ExcelProperty("创建时间")
|
||||
private Date createTime;
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@ApiModel(value = "管理后台 - 商品分类 Excel 导出 Request VO", description = "参数和 CategoryPageReqVO 是一致的")
|
||||
@Data
|
||||
public class CategoryExportReqVO {
|
||||
|
||||
@ApiModelProperty(value = "分类名称", example = "办公文具")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "开启状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ApiModelProperty(value = "开始创建时间")
|
||||
private Date beginCreateTime;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ApiModelProperty(value = "结束创建时间")
|
||||
private Date endCreateTime;
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@ApiModel("管理后台 - 商品分类分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CategoryPageReqVO extends PageParam {
|
||||
|
||||
@ApiModelProperty(value = "分类名称", example = "办公文具")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "开启状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ApiModelProperty(value = "开始创建时间")
|
||||
private Date beginCreateTime;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ApiModelProperty(value = "结束创建时间")
|
||||
private Date endCreateTime;
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
@ApiModel("管理后台 - 商品分类 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CategoryRespVO extends CategoryBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "分类编号", required = true, example = "2")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "创建时间", required = true)
|
||||
private Date createTime;
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Data
|
||||
@ApiModel(value = "管理后台 - 商品分类列表查询 Request VO", description = "参数和 CategoryPageReqVO 是一致的")
|
||||
public class CategoryTreeListReqVO extends CategoryExportReqVO {
|
||||
|
||||
@ApiModelProperty(value = "分类名称", example = "办公文具")
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "开启状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ApiModelProperty(value = "开始创建时间")
|
||||
private Date beginCreateTime;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@ApiModelProperty(value = "结束创建时间")
|
||||
private Date endCreateTime;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.product.controller.admin.category.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.annotations.*;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
@ApiModel("管理后台 - 商品分类更新 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CategoryUpdateReqVO extends CategoryBaseVO {
|
||||
|
||||
@ApiModelProperty(value = "分类编号", required = true, example = "2")
|
||||
@NotNull(message = "分类编号不能为空")
|
||||
private Long id;
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.module.product.convert.category;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
|
||||
|
||||
/**
|
||||
* 商品分类 Convert
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface CategoryConvert {
|
||||
|
||||
CategoryConvert INSTANCE = Mappers.getMapper(CategoryConvert.class);
|
||||
|
||||
CategoryDO convert(CategoryCreateReqVO bean);
|
||||
|
||||
CategoryDO convert(CategoryUpdateReqVO bean);
|
||||
|
||||
CategoryRespVO convert(CategoryDO bean);
|
||||
|
||||
List<CategoryRespVO> convertList(List<CategoryDO> list);
|
||||
|
||||
PageResult<CategoryRespVO> convertPage(PageResult<CategoryDO> page);
|
||||
|
||||
List<CategoryExcelVO> convertList02(List<CategoryDO> list);
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.category;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 商品分类 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("product_category")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CategoryDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 分类编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 父分类编号
|
||||
*/
|
||||
private Long pid;
|
||||
/**
|
||||
* 分类名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 分类图标
|
||||
*/
|
||||
private String icon;
|
||||
/**
|
||||
* 分类图片
|
||||
*/
|
||||
private String bannerUrl;
|
||||
/**
|
||||
* 分类排序
|
||||
*/
|
||||
private Integer sort;
|
||||
/**
|
||||
* 分类描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 开启状态
|
||||
*
|
||||
* 枚举 {@link TODO common_status 对应的类}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package cn.iocoder.yudao.module.product.dal.mysql.category;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryExportReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryPageReqVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品分类 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface CategoryMapper extends BaseMapperX<CategoryDO> {
|
||||
|
||||
default PageResult<CategoryDO> selectPage(CategoryPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<CategoryDO>()
|
||||
.likeIfPresent(CategoryDO::getName, reqVO.getName())
|
||||
.eqIfPresent(CategoryDO::getStatus, reqVO.getStatus())
|
||||
.betweenIfPresent(CategoryDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
|
||||
.orderByDesc(CategoryDO::getId));
|
||||
}
|
||||
|
||||
default List<CategoryDO> selectList(CategoryExportReqVO reqVO) {
|
||||
return selectList(new LambdaQueryWrapperX<CategoryDO>()
|
||||
.likeIfPresent(CategoryDO::getName, reqVO.getName())
|
||||
.eqIfPresent(CategoryDO::getStatus, reqVO.getStatus())
|
||||
.betweenIfPresent(CategoryDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
|
||||
.orderByDesc(CategoryDO::getId));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author JeromeSoar
|
||||
* @since 2022-04-24
|
||||
*/
|
||||
package cn.iocoder.yudao.module.product;
|
@ -0,0 +1,79 @@
|
||||
package cn.iocoder.yudao.module.product.service.category;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品分类 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface CategoryService {
|
||||
|
||||
/**
|
||||
* 创建商品分类
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createCategory(@Valid CategoryCreateReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新商品分类
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateCategory(@Valid CategoryUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除商品分类
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteCategory(Long id);
|
||||
|
||||
/**
|
||||
* 获得商品分类
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 商品分类
|
||||
*/
|
||||
CategoryDO getCategory(Long id);
|
||||
|
||||
/**
|
||||
* 获得商品分类列表
|
||||
*
|
||||
* @param ids 编号
|
||||
* @return 商品分类列表
|
||||
*/
|
||||
List<CategoryDO> getCategoryList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得商品分类分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 商品分类分页
|
||||
*/
|
||||
PageResult<CategoryDO> getCategoryPage(CategoryPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得商品分类列表, 用于 Excel 导出
|
||||
*
|
||||
* @param exportReqVO 查询条件
|
||||
* @return 商品分类列表
|
||||
*/
|
||||
List<CategoryDO> getCategoryList(CategoryExportReqVO exportReqVO);
|
||||
|
||||
/**
|
||||
* 获得商品分类列表
|
||||
*
|
||||
* @param treeListReqVO 查询条件
|
||||
* @return 商品分类列表
|
||||
*/
|
||||
List<CategoryDO> getCategoryTreeList(CategoryTreeListReqVO treeListReqVO);
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package cn.iocoder.yudao.module.product.service.category;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.*;
|
||||
import cn.iocoder.yudao.module.product.convert.category.CategoryConvert;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
|
||||
import cn.iocoder.yudao.module.product.dal.mysql.category.CategoryMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* 商品分类 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CategoryServiceImpl implements CategoryService {
|
||||
|
||||
@Resource
|
||||
private CategoryMapper categoryMapper;
|
||||
|
||||
@Override
|
||||
public Long createCategory(CategoryCreateReqVO createReqVO) {
|
||||
// 插入
|
||||
CategoryDO category = CategoryConvert.INSTANCE.convert(createReqVO);
|
||||
categoryMapper.insert(category);
|
||||
// 返回
|
||||
return category.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCategory(CategoryUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
this.validateCategoryExists(updateReqVO.getId());
|
||||
// 更新
|
||||
CategoryDO updateObj = CategoryConvert.INSTANCE.convert(updateReqVO);
|
||||
categoryMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteCategory(Long id) {
|
||||
// 校验存在
|
||||
this.validateCategoryExists(id);
|
||||
// 删除
|
||||
categoryMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateCategoryExists(Long id) {
|
||||
if (categoryMapper.selectById(id) == null) {
|
||||
throw exception(CATEGORY_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CategoryDO getCategory(Long id) {
|
||||
return categoryMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CategoryDO> getCategoryList(Collection<Long> ids) {
|
||||
return categoryMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CategoryDO> getCategoryPage(CategoryPageReqVO pageReqVO) {
|
||||
return categoryMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CategoryDO> getCategoryList(CategoryExportReqVO exportReqVO) {
|
||||
return categoryMapper.selectList(exportReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CategoryDO> getCategoryTreeList(CategoryTreeListReqVO treeListReqVO) {
|
||||
return categoryMapper.selectList(treeListReqVO);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
package cn.iocoder.yudao.module.product.service.category;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryCreateReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryExportReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryPageReqVO;
|
||||
import cn.iocoder.yudao.module.product.controller.admin.category.vo.CategoryUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.category.CategoryDO;
|
||||
import cn.iocoder.yudao.module.product.dal.mysql.category.CategoryMapper;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link CategoryServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Import(CategoryServiceImpl.class)
|
||||
public class CategoryServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private CategoryServiceImpl categoryService;
|
||||
|
||||
@Resource
|
||||
private CategoryMapper categoryMapper;
|
||||
|
||||
@Test
|
||||
public void testCreateCategory_success() {
|
||||
// 准备参数
|
||||
CategoryCreateReqVO reqVO = randomPojo(CategoryCreateReqVO.class);
|
||||
|
||||
// 调用
|
||||
Long categoryId = categoryService.createCategory(reqVO);
|
||||
// 断言
|
||||
assertNotNull(categoryId);
|
||||
// 校验记录的属性是否正确
|
||||
CategoryDO category = categoryMapper.selectById(categoryId);
|
||||
assertPojoEquals(reqVO, category);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCategory_success() {
|
||||
// mock 数据
|
||||
CategoryDO dbCategory = randomPojo(CategoryDO.class);
|
||||
categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
CategoryUpdateReqVO reqVO = randomPojo(CategoryUpdateReqVO.class, o -> {
|
||||
o.setId(dbCategory.getId()); // 设置更新的 ID
|
||||
});
|
||||
|
||||
// 调用
|
||||
categoryService.updateCategory(reqVO);
|
||||
// 校验是否更新正确
|
||||
CategoryDO category = categoryMapper.selectById(reqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(reqVO, category);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCategory_notExists() {
|
||||
// 准备参数
|
||||
CategoryUpdateReqVO reqVO = randomPojo(CategoryUpdateReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> categoryService.updateCategory(reqVO), CATEGORY_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteCategory_success() {
|
||||
// mock 数据
|
||||
CategoryDO dbCategory = randomPojo(CategoryDO.class);
|
||||
categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbCategory.getId();
|
||||
|
||||
// 调用
|
||||
categoryService.deleteCategory(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(categoryMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteCategory_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
|
||||
public void testGetCategoryPage() {
|
||||
// mock 数据
|
||||
CategoryDO dbCategory = randomPojo(CategoryDO.class, o -> { // 等会查询到
|
||||
o.setPid(null);
|
||||
o.setName(null);
|
||||
o.setIcon(null);
|
||||
o.setBannerUrl(null);
|
||||
o.setSort(null);
|
||||
o.setDescription(null);
|
||||
o.setStatus(null);
|
||||
o.setCreateTime(null);
|
||||
});
|
||||
categoryMapper.insert(dbCategory);
|
||||
// 测试 pid 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setPid(null)));
|
||||
// 测试 name 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null)));
|
||||
// 测试 icon 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setIcon(null)));
|
||||
// 测试 bannerUrl 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setBannerUrl(null)));
|
||||
// 测试 sort 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setSort(null)));
|
||||
// 测试 description 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setDescription(null)));
|
||||
// 测试 status 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(null)));
|
||||
// 测试 createTime 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(null)));
|
||||
// 准备参数
|
||||
CategoryPageReqVO reqVO = new CategoryPageReqVO();
|
||||
reqVO.setName(null);
|
||||
reqVO.setStatus(null);
|
||||
reqVO.setBeginCreateTime(null);
|
||||
reqVO.setEndCreateTime(null);
|
||||
|
||||
// 调用
|
||||
PageResult<CategoryDO> pageResult = categoryService.getCategoryPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbCategory, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
|
||||
public void testGetCategoryList() {
|
||||
// mock 数据
|
||||
CategoryDO dbCategory = randomPojo(CategoryDO.class, o -> { // 等会查询到
|
||||
o.setPid(null);
|
||||
o.setName(null);
|
||||
o.setIcon(null);
|
||||
o.setBannerUrl(null);
|
||||
o.setSort(null);
|
||||
o.setDescription(null);
|
||||
o.setStatus(null);
|
||||
o.setCreateTime(null);
|
||||
});
|
||||
categoryMapper.insert(dbCategory);
|
||||
// 测试 pid 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setPid(null)));
|
||||
// 测试 name 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null)));
|
||||
// 测试 icon 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setIcon(null)));
|
||||
// 测试 bannerUrl 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setBannerUrl(null)));
|
||||
// 测试 sort 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setSort(null)));
|
||||
// 测试 description 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setDescription(null)));
|
||||
// 测试 status 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(null)));
|
||||
// 测试 createTime 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(null)));
|
||||
// 准备参数
|
||||
CategoryExportReqVO reqVO = new CategoryExportReqVO();
|
||||
reqVO.setName(null);
|
||||
reqVO.setStatus(null);
|
||||
reqVO.setBeginCreateTime(null);
|
||||
reqVO.setEndCreateTime(null);
|
||||
|
||||
// 调用
|
||||
List<CategoryDO> list = categoryService.getCategoryList(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, list.size());
|
||||
assertPojoEquals(dbCategory, list.get(0));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
DELETE FROM "product_category";
|
@ -0,0 +1,17 @@
|
||||
CREATE TABLE IF NOT EXISTS "product_category" (
|
||||
"id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"pid" bigint(20) NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"icon" varchar(100),
|
||||
"banner_url" varchar(255) NOT NULL,
|
||||
"sort" int(11) NOT NULL,
|
||||
"description" varchar(1024) NOT NULL,
|
||||
"status" tinyint(4) NOT NULL,
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint(20) NOT NULL,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '商品分类';
|
@ -47,16 +47,21 @@
|
||||
<artifactId>yudao-module-market-biz</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-product-biz</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 默认引入 yudao-module-bpm-biz-flowable 实现,可以替换为 yudao-module-bpm-biz-activiti 实现-->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-bpm-biz-flowable</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>cn.iocoder.boot</groupId>-->
|
||||
<!-- <artifactId>yudao-module-bpm-biz-activiti</artifactId>-->
|
||||
<!-- <version>${revision}</version>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>cn.iocoder.boot</groupId>-->
|
||||
<!-- <artifactId>yudao-module-bpm-biz-activiti</artifactId>-->
|
||||
<!-- <version>${revision}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- spring boot 配置所需依赖 -->
|
||||
|
63
yudao-ui-admin/src/api/mall/product/category.js
Normal file
63
yudao-ui-admin/src/api/mall/product/category.js
Normal file
@ -0,0 +1,63 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 创建商品分类
|
||||
export function createCategory(data) {
|
||||
return request({
|
||||
url: '/product/category/create',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新商品分类
|
||||
export function updateCategory(data) {
|
||||
return request({
|
||||
url: '/product/category/update',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除商品分类
|
||||
export function deleteCategory(id) {
|
||||
return request({
|
||||
url: '/product/category/delete?id=' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获得商品分类
|
||||
export function getCategory(id) {
|
||||
return request({
|
||||
url: '/product/category/get?id=' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获得商品分类
|
||||
export function listCategory(query) {
|
||||
return request({
|
||||
url: '/product/category/listByQuery',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获得商品分类分页
|
||||
export function getCategoryPage(query) {
|
||||
return request({
|
||||
url: '/product/category/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 导出商品分类 Excel
|
||||
export function exportCategoryExcel(query) {
|
||||
return request({
|
||||
url: '/product/category/export-excel',
|
||||
method: 'get',
|
||||
params: query,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
@ -77,7 +77,7 @@ export default {
|
||||
dialogVisible: false,
|
||||
hideUpload: false,
|
||||
baseUrl: process.env.VUE_APP_BASE_API,
|
||||
uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
|
||||
uploadImgUrl: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/simple-upload", // 上传的图片服务器地址
|
||||
headers: {
|
||||
Authorization: "Bearer " + getToken(),
|
||||
},
|
||||
@ -127,7 +127,7 @@ export default {
|
||||
},
|
||||
// 上传成功回调
|
||||
handleUploadSuccess(res) {
|
||||
this.uploadList.push({ name: res.fileName, url: res.fileName });
|
||||
this.uploadList.push({ name: res.data, url: res.data });
|
||||
if (this.uploadList.length === this.number) {
|
||||
this.fileList = this.fileList.concat(this.uploadList);
|
||||
this.uploadList = [];
|
||||
|
330
yudao-ui-admin/src/views/mall/product/category/index.vue
Normal file
330
yudao-ui-admin/src/views/mall/product/category/index.vue
Normal file
@ -0,0 +1,330 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="分类名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入分类名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="开启状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['product:category:create']">新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">展开/折叠</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['product:category:export']">导出
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-if="refreshTable" v-loading="loading" :data="list" row-key="id" :default-expand-all="isExpandAll"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}">
|
||||
<el-table-column label="分类名称" align="center" prop="name"/>
|
||||
<el-table-column label="分类图标" align="center" prop="icon">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.icon" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分类图片" align="center" prop="bannerUrl">
|
||||
<template slot-scope="scope">
|
||||
<img v-if="scope.row.bannerUrl" :src="scope.row.bannerUrl" alt="分类图片"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分类排序" align="center" prop="sort"/>
|
||||
<el-table-column label="开启状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['product:category:update']">修改
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['product:category:delete']">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="上级分类" prop="pid">
|
||||
<Treeselect v-model="form.pid" :options="parentCategoryOptions" :normalizer="normalizer" :show-count="true"
|
||||
placeholder="上级分类"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入分类名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类图标" prop="icon">
|
||||
<el-popover placement="bottom-start" width="460" trigger="click" @show="$refs['iconSelect'].reset()">
|
||||
<IconSelect ref="iconSelect" @selected="iconSelected"/>
|
||||
<el-input slot="reference" v-model="form.icon" placeholder="点击选择分类图标" readonly>
|
||||
<svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon"
|
||||
style="height: 32px;width: 16px;"/>
|
||||
<i v-else slot="prefix" class="el-icon-search el-input__icon"/>
|
||||
</el-input>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类图片" prop="bannerUrl">
|
||||
<ImageUpload v-model="form.bannerUrl" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分类排序" prop="sort">
|
||||
<el-input-number v-model="form.sort" controls-position="right" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分类描述">
|
||||
<editor v-model="form.description" :min-height="192"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="开启状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value" :label="parseInt(dict.value)">{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
createCategory,
|
||||
deleteCategory,
|
||||
exportCategoryExcel,
|
||||
getCategory,
|
||||
getCategoryPage,
|
||||
listCategory,
|
||||
updateCategory
|
||||
} from "@/api/mall/product/category";
|
||||
import Editor from '@/components/Editor';
|
||||
import Treeselect from "@riophae/vue-treeselect";
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
import IconSelect from "@/components/IconSelect";
|
||||
import ImageUpload from '@/components/ImageUpload';
|
||||
|
||||
export default {
|
||||
name: "Category",
|
||||
components: {
|
||||
Editor, Treeselect, IconSelect, ImageUpload
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 商品分类列表
|
||||
list: [],
|
||||
// 商品分类树选项
|
||||
parentCategoryOptions: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 是否展开,默认全部折叠
|
||||
isExpandAll: false,
|
||||
// 重新渲染表格状态
|
||||
refreshTable: true,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
pid: [{required: true, message: "请选择上级分类", trigger: "blur"}],
|
||||
name: [{required: true, message: "分类名称不能为空", trigger: "blur"}],
|
||||
icon: [{required: true, message: "分类图标不能为空", trigger: "blur"}],
|
||||
bannerUrl: [{required: true, message: "分类图片不能为空", trigger: "blur"}],
|
||||
status: [{required: true, message: "开启状态不能为空", trigger: "blur"}],
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams};
|
||||
// 执行查询
|
||||
listCategory(params).then(response => {
|
||||
this.list = this.handleTree(response.data, "id", "pid");
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 选择图标
|
||||
iconSelected(name) {
|
||||
this.form.icon = name;
|
||||
},
|
||||
/** 转换菜单数据结构 */
|
||||
normalizer(node) {
|
||||
if (node.children && !node.children.length) {
|
||||
delete node.children;
|
||||
}
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
children: node.children
|
||||
};
|
||||
},
|
||||
/** 查询分类下拉树结构 */
|
||||
getTreeselect() {
|
||||
listCategory().then(response => {
|
||||
this.parentCategoryOptions = [];
|
||||
const menu = {id: 0, name: '主分类', children: []};
|
||||
menu.children = this.handleTree(response.data, "id", "pid");
|
||||
this.parentCategoryOptions.push(menu);
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
pid: undefined,
|
||||
name: undefined,
|
||||
icon: undefined,
|
||||
bannerUrl: undefined,
|
||||
sort: undefined,
|
||||
description: undefined,
|
||||
status: undefined,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 展开/折叠操作 */
|
||||
toggleExpandAll() {
|
||||
this.refreshTable = false;
|
||||
this.isExpandAll = !this.isExpandAll;
|
||||
this.$nextTick(() => {
|
||||
this.refreshTable = true;
|
||||
});
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.getTreeselect();
|
||||
this.open = true;
|
||||
this.title = "添加商品分类";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
this.getTreeselect();
|
||||
const id = row.id;
|
||||
getCategory(id).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改商品分类";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateCategory(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
createCategory(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除商品分类编号为"' + id + '"的数据项?').then(function () {
|
||||
return deleteCategory(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams};
|
||||
params.pageNo = undefined;
|
||||
params.pageSize = undefined;
|
||||
// 执行导出
|
||||
this.$modal.confirm('是否确认导出所有商品分类数据项?').then(() => {
|
||||
this.exportLoading = true;
|
||||
return exportCategoryExcel(params);
|
||||
}).then(response => {
|
||||
this.$download.excel(response, '${table.classComment}.xls');
|
||||
this.exportLoading = false;
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user