分类模块与品牌模块实现

分类模块与品牌模块实现
准备工作
使用nginx,通过域名访问本地项目
- 下载
nginx
压缩包(我这里直接下载的windows包,解压后编写配置文件,即可使用)
nginx.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| ... # 配置两个server节点即可 server { listen 80; server_name manage.leyou.com;
#charset koi8-r;
#access_log logs/host.access.log main;
location / { proxy_pass http://127.0.0.1:9001; proxy_connect_timeout 600; proxy_read_timeout 600; } }
server { listen 80; server_name api.leyou.com;
#charset koi8-r;
#access_log logs/host.access.log main;
location / { proxy_pass http://127.0.0.1:10001; proxy_connect_timeout 600; proxy_read_timeout 600; } } ...
|
跨域问题解决(采用CORS)
- 在
ly-gateway
中配置bean即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package com.leyou.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter;
@Configuration public class CorsConfig {
@Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("http://manage.leyou.com"); config.setAllowCredentials(true); config.addAllowedMethod("OPTIONS"); config.addAllowedMethod("HEAD"); config.addAllowedMethod("GET"); config.addAllowedMethod("PUT"); config.addAllowedMethod("POST"); config.addAllowedMethod("DELETE"); config.addAllowedMethod("PATCH"); config.addAllowedHeader("*"); config.setMaxAge(7200L);
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); configSource.registerCorsConfiguration("/**", config);
return new CorsFilter(configSource); } }
|
创建数据库
创建数据库leyou
导入数据库文件
leyou.sql
分类管理模块
后端
添加实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.leyou.pojo;
import lombok.Data; import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Id; import javax.persistence.Table;
@Table(name="tb_category") @Data public class Category { @Id @KeySql(useGeneratedKeys = true) private Long id; private String name; private Long parentId; private Boolean isParent; private Integer sort; }
|
注意:
实体类放到ly-item-interface
中
实体类中使用的注解需要引入mapper-core
的依赖。
编写mapper
1 2 3 4 5 6 7
| package com.leyou.item.mapper;
import com.leyou.pojo.Category; import tk.mybatis.mapper.common.Mapper;
public interface CategoryMapper extends Mapper<Category> { }
|
注意:
需要在启动类上加入@MapperScan(com.leyou.item.mapper)注解
编写service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| package com.leyou.item.service;
import com.leyou.common.enums.LyExceptionEnum; import com.leyou.common.exception.LyException; import com.leyou.item.mapper.CategoryMapper; import com.leyou.pojo.Category; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils;
import java.util.List;
@Service public class CategoryService {
@Autowired private CategoryMapper categoryMapper;
public List<Category> queryCategoryListByPid(Long pid) { Category category = new Category(); category.setParentId(pid); List<Category> categoryList = categoryMapper.select(category); if (CollectionUtils.isEmpty(categoryList)) { throw new LyException(LyExceptionEnum.CATEGORY_LIST_NOT_FOUND); } return categoryList; }
public Category addCategory(Category category) { if (category.getParentId() != CategoryConstans.FIRST_CATEGORY_PARENT_ID) { Category pCategory = categoryMapper.selectByPrimaryKey(category.getParentId()); if (pCategory != null) { if (!pCategory.getIsParent()) { pCategory.setIsParent(true); categoryMapper.updateByPrimaryKey(pCategory); } } else { throw new LyException(LyExceptionEnum.PARENT_CATEGORY_NOT_FOUND); } } if (categoryMapper.insert(category) != 1) { throw new LyException(LyExceptionEnum.SAVE_FAILURE); } return category; }
public Category saveCategory(Category category) { if (categoryMapper.updateByPrimaryKey(category) != 1) { throw new LyException(LyExceptionEnum.SAVE_FAILURE); } return category; }
public Category deleteCategory(Long categoryId) { Category category = categoryMapper.selectByPrimaryKey(categoryId); if (category == null) { throw new LyException(LyExceptionEnum.CATEGORY_NOT_FOUND); } if (category.getIsParent()) { Category param = new Category(); param.setParentId(categoryId); if (categoryMapper.select(param).size() > 0) { throw new LyException(LyExceptionEnum.DELETE_INVALID); } } if (categoryMapper.delete(category) != 1) { throw new LyException(LyExceptionEnum.SAVE_FAILURE); } return category; } }
|
新增异常枚举:
1 2 3 4 5 6 7
| > CATEGORY_LIST_NOT_FOUND(404, "未查询到分类列表"), > CATEGORY_NOT_FOUND(404, "该分类不存在"), > PARENT_CATEGORY_NOT_FOUND(404, "该父分类不存在"), > SAVE_FAILURE(500, "保存失败"), > DELETE_FAILURE(500, "删除失败"), > DELETE_INVALID(500, "该分类下含有子分类,请先删除其子分类"), >
|
新增分类常量类:
1 2 3 4 5 6 7
| > package com.leyou.pojo; > > public final class CategoryConstans { > > public static final long FIRST_CATEGORY_PARENT_ID = 0; > } >
|
编写controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| package com.leyou.item.cotroller;
import com.leyou.common.enums.LyExceptionEnum; import com.leyou.common.exception.LyException; import com.leyou.item.service.CategoryService; import com.leyou.pojo.Category; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController @RequestMapping("/category") public class CategoryController {
@Autowired private CategoryService categoryService;
@GetMapping("/list") public ResponseEntity<List<Category>> queryCategoryListByPid(@RequestParam(value = "pid", defaultValue = "0") Long pid) { return ResponseEntity.ok(categoryService.queryCategoryListByPid(pid)); }
@PostMapping() public ResponseEntity<Category> addCategory(@RequestBody Category category) { if (category == null) { throw new LyException(LyExceptionEnum.PARAM_CANNOT_BE_NULL); } return ResponseEntity.status(HttpStatus.CREATED).body(categoryService.addCategory(category)); }
@PutMapping() public ResponseEntity<Category> saveCategory(@RequestBody Category category) { if (category == null) { throw new LyException(LyExceptionEnum.PARAM_CANNOT_BE_NULL); } return ResponseEntity.ok(categoryService.saveCategory(category)); }
@DeleteMapping("/{categoryId}") public ResponseEntity<Category> deleteCategory(@PathVariable("categoryId") Long categoryId) { if (categoryId == null) { throw new LyException(LyExceptionEnum.PARAM_CANNOT_BE_NULL); } return ResponseEntity.ok(categoryService.deleteCategory(categoryId)); } }
|
前端
Category.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| <template> <v-card> <v-flex xs12 sm10> <v-tree url="/item/category/list" :isEdit="isEdit" @handleAdd="handleAdd" @handleEdit="handleEdit" @handleDelete="handleDelete" @handleClick="handleClick" /> </v-flex> </v-card> </template>
<script> export default { name: "category", data() { return { isEdit:true } }, methods: { handleAdd(node) { // console.log("add .... "); console.log(node); this.$http.post('/item/category', { "name": node.name, "sort": node.sort, "parentId": node.parentId, "isParent": node.isParent }).then((response) => { if (response.status === 201) { this.$message.info('添加成功'); } else { this.$message.error(response['message']); } }).catch(() => { this.$message.error('添加失败'); }) }, handleEdit(id, name) { console.log("edit... id: " + id + ", name: " + name) // 这个树菜单的编辑,我也没玩明白。23333333
}, handleDelete(id) { // console.log("delete ... " + id) this.$http.delete('/item/category/' + id) .then((response) => { if (response.status === 200) { this.$message.info("删除成功") } else { this.$message.error(response['message']) } }) .catch(() => { this.$message.error("删除失败") }) }, handleClick(node) { } } }; </script>
<style scoped>
</style>
|
树菜单的编辑,我也没玩明白,有玩明白了可以留言一下。
品牌管理模块
后端
添加实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.leyou.pojo;
import lombok.Data; import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Id; import javax.persistence.Table;
@Table(name = "tb_brand") @Data public class Brand { @Id @KeySql(useGeneratedKeys = true) private Long id; private String name; private String image; private Character letter; }
|
编写mapper
1 2 3 4 5 6 7
| package com.leyou.item.mapper;
import com.leyou.pojo.Brand; import tk.mybatis.mapper.common.Mapper;
public interface BrandMapper extends Mapper<Brand> { }
|
编写service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| package com.leyou.item.service;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.leyou.common.enums.LyExceptionEnum; import com.leyou.common.exception.LyException; import com.leyou.common.vo.PageResult; import com.leyou.item.mapper.BrandMapper; import com.leyou.pojo.Brand; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import tk.mybatis.mapper.entity.Example;
import java.util.List;
@Service @Transactional public class BrandService {
@Autowired private BrandMapper brandMapper;
public PageResult<Brand> queryBrandByPage(int page, int rows, String sortBy, Boolean desc, String key) { PageHelper.startPage(page, rows); Example example = new Example(Brand.class); if (StringUtils.isNoneBlank(key)) { example.createCriteria().andLike("name", "%" + key + "%"); } if (StringUtils.isNoneBlank(sortBy)) { example.setOrderByClause(sortBy + (desc ? " DESC" : " ASC")); } List<Brand> brands = brandMapper.selectByExample(example); if (brands.size() < 1) { throw new LyException(LyExceptionEnum.BRAND_LIST_NOT_FOUND); } PageInfo<Brand> pageInfo = new PageInfo<>(brands); return new PageResult<>(pageInfo.getTotal(), brands); }
public Brand addBrand(Brand brand, List<Long> categories) { if (brandMapper.insert(brand) != 1) { throw new LyException(LyExceptionEnum.SAVE_FAILURE); } insertCategoryBrand(brand, categories);
return brand; }
public Brand saveBrand(Brand brand, List<Long> categories) { if (brandMapper.updateByPrimaryKeySelective(brand) != 1) { throw new LyException(LyExceptionEnum.SAVE_FAILURE); } brandMapper.deleteCategoryBrandByBrandId(brand.getId()); insertCategoryBrand(brand, categories); return brand; }
private void insertCategoryBrand(Brand brand, List<Long> categories) { categories.forEach(categoryId -> { if (brandMapper.insertCategoryBrand(categoryId, brand.getId()) != 1) { throw new LyException(LyExceptionEnum.SAVE_FAILURE); } }); }
public Brand deleteBrand(long brandId) { Brand brand = brandMapper.selectByPrimaryKey(brandId); if (brand == null) { throw new LyException(LyExceptionEnum.BRAND_LIST_NOT_FOUND); } if(brandMapper.delete(brand) != 1){ throw new LyException(LyExceptionEnum.DELETE_FAILURE); } brandMapper.deleteCategoryBrandByBrandId(brandId); return brand; } }
|
编写controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| package com.leyou.item.cotroller;
import com.leyou.common.enums.LyExceptionEnum; import com.leyou.common.exception.LyException; import com.leyou.common.vo.PageResult; import com.leyou.item.service.BrandService; import com.leyou.pojo.Brand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController @RequestMapping("brand") public class BrandController {
@Autowired private BrandService brandService;
@GetMapping("/page") public ResponseEntity<PageResult<Brand>> queryBrandByPage(@RequestParam(value = "page", defaultValue = "1") int page, @RequestParam(value = "rows", defaultValue = "5") int rows, @RequestParam("sortBy") String sortBy, @RequestParam(value = "desc",defaultValue = "false") Boolean desc, @RequestParam("key") String key) { return ResponseEntity.ok(brandService.queryBrandByPage(page, rows, sortBy, desc, key)); }
@PostMapping public ResponseEntity<Brand> addBrand(@RequestBody Brand brand, @RequestParam("categories") List<Long> categories) { if (CollectionUtils.isEmpty(categories)) { throw new LyException(LyExceptionEnum.PARAM_INVALID); } return ResponseEntity.status(HttpStatus.CREATED).body(brandService.addBrand(brand, categories)); }
@PutMapping public ResponseEntity<Brand> editBrand(@RequestBody Brand brand, @RequestParam("categories") List<Long> categories) { if (CollectionUtils.isEmpty(categories) || brand.getId() == null) { throw new LyException(LyExceptionEnum.PARAM_INVALID); } return ResponseEntity.ok(brandService.saveBrand(brand, categories)); }
@DeleteMapping("/{brandId}") public ResponseEntity<Brand> deleteBrand(@PathVariable("brandId") long brandId) { return ResponseEntity.ok(brandService.deleteBrand(brandId)); }
}
|
文件上传微服务
在品牌管理模块的新增和修改中,需要进行上传图片。所以我们将其抽离出来,形成专门的微服务,以后所有文件上传,无论是图片或者是其他文件,都可以通过此文件上传微服务完成。
搭建项目
创建maven项目
- GroupId:
com.leyou.service
- ArtifactId:
ly-upload
编写pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?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"> <parent> <artifactId>parent</artifactId> <groupId>com.leyou</groupId> <version>1.0.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<groupId>com.leyou.service</groupId> <artifactId>ly-upload</artifactId>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>com.leyou.common</groupId> <artifactId>ly-common</artifactId> <version>${leyou.latest.version}</version> </dependency> </dependencies> </project>
|
启动类
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.leyou;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication @EnableDiscoveryClient public class LyUpload { public static void main(String[] args) { SpringApplication.run(LyUpload.class, args); } }
|
注意在网关中添加文件上传微服务的路由
编写application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| server: port: 8082 spring: application: name: upload-service servlet: multipart: max-file-size: 5MB
eureka: client: service-url: defaultZone: http://127.0.0.1:9999/eureka
ly: upload: imageServer: http://image.leyou.com/ imageTypes: - image/jpeg - image/bmp - image/png
|
优化
默认情况下,请求通过Zuul
网关,Spring MVC
会对请求做预处理和缓存,但是文件一旦缓存,会给服务器造成不必要的负担。所以我们需要上传文件的请求不进行缓存。官方给出的解决方案就是在请求的最前面加上/zuul
这样就可以绕开请求的缓存,直接将请求路由到指定的微服务。
配置nginx路径重写
修改nginx.conf
配置文件

分布式文件系统FastDFS
FastDFS简介
参考:http://www.xushuai.fun/2018/12/22/FastDFS简介/
FastDFS安装
参考:http://www.xushuai.fun/2018/12/22/FastDFS安装/
引入FastDFS-client依赖
- java-客户端

编辑pom.xml
文件,新增依赖
1 2 3 4
| <dependency> <groupId>com.github.tobato</groupId> <artifactId>fastdfs-client</artifactId> </dependency>
|
新增配置
1 2 3 4 5 6 7 8
| fdfs: so-timeout: 1501 connect-timeout: 601 thumb-image: width: 150 height: 150 tracker-list: - 192.168.136.100:22122
|
引入配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.leyou.upload.config;
import com.github.tobato.fastdfs.FdfsClientConfig; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableMBeanExport; import org.springframework.context.annotation.Import; import org.springframework.jmx.support.RegistrationPolicy;
@Configuration @Import(FdfsClientConfig.class)
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING) public class ComponetImport { }
|
编写上传逻辑
service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| package com.leyou.upload.service;
import com.github.tobato.fastdfs.domain.StorePath; import com.github.tobato.fastdfs.service.FastFileStorageClient; import com.leyou.common.enums.LyExceptionEnum; import com.leyou.common.exception.LyException; import com.leyou.upload.constans.FileTypeConstans; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.IOException;
@Service @Slf4j public class UploadService {
@Autowired private FastFileStorageClient fastFileStorageClient;
@Autowired private ImageTypeConfigProperty imageTypeConfigProperty;
public String uploadFile(MultipartFile file, String fileType) { String url = null; switch (fileType) { case FileTypeConstans.IMAGE: url = uploadImage(file); break; default: throw new LyException(LyExceptionEnum.FILE_TYPE_ERROR); } return url; }
private String uploadImage(MultipartFile file) { try { BufferedImage image = ImageIO.read(file.getInputStream()); if (image == null || image.getWidth() == 0 || image.getHeight() == 0) { throw new LyException(LyExceptionEnum.FILE_TYPE_ERROR); } String filename = file.getOriginalFilename(); String suffix = filename.substring(filename.indexOf(".") + 1);
StorePath storePath = fastFileStorageClient.uploadFile(file.getInputStream(), file.getSize(), suffix, null); return imageTypeConfigProperty.getImageServer() + storePath.getFullPath(); } catch (IOException e) { log.error("读取文件内容发生IO异常. e = {}", e); throw new LyException(LyExceptionEnum.READ_FILE_FAILURE); } } }
|
controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package com.leyou.upload.controller;
import com.leyou.common.enums.LyExceptionEnum; import com.leyou.common.exception.LyException; import com.leyou.upload.config.ImageTypeConfigProperty; import com.leyou.upload.constans.FileTypeConstans; import com.leyou.upload.service.UploadService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@RestController @RequestMapping("/upload") public class UploadController {
@Autowired private UploadService uploadService;
@Autowired private ImageTypeConfigProperty imageTypeConfigProperty;
@PostMapping("/image") public String uploadImage(@RequestParam("file") MultipartFile file) { if (!imageTypeConfigProperty.getImageTypes().contains(file.getContentType())) { throw new LyException(LyExceptionEnum.FILE_TYPE_ERROR); }
return uploadService.uploadFile(file, FileTypeConstans.IMAGE); } }
|