这是【SpringBoot企业微信点餐系统实战】系列第三篇
源码地址:https://github.com/cachecats/sell
按照数据库的字段写出对应的实体类 ProductInfo
。@Data
是 lombok
的注解,用于自动生成 getter
, setter
, toString
方法,不懂的可以自行查询相关资料。
@Entity
@Data
public class ProductInfo {
<span class="hljs-meta">@Id</span>
<span class="hljs-keyword">private</span> String productId;
<span class="hljs-comment">//商品名字</span>
<span class="hljs-keyword">private</span> String productName;
<span class="hljs-comment">//商品价格</span>
<span class="hljs-keyword">private</span> BigDecimal productPrice;
<span class="hljs-comment">//商品库存</span>
<span class="hljs-keyword">private</span> Integer productStock;
<span class="hljs-comment">//商品描述</span>
<span class="hljs-keyword">private</span> String productDescription;
<span class="hljs-comment">//商品状态</span>
<span class="hljs-keyword">private</span> Integer productStatus;
<span class="hljs-comment">//商品图标</span>
<span class="hljs-keyword">private</span> String productIcon;
<span class="hljs-comment">//商品类目编号</span>
<span class="hljs-keyword">private</span> Integer categoryType;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ProductInfo</span><span class="hljs-params">()</span> </span>{
}
}
新加了一个方法 findByProductStatus
,根据商品的状态查询商品列表,用于查询所有上架商品。
package com.solo.sell.repository;
import com.solo.sell.dto.ProductInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ProductInfoRepository extends JpaRepository<ProductInfo, String>{
<span class="hljs-function">List<ProductInfo> <span class="hljs-title">findByProductStatus</span><span class="hljs-params">(Integer status)</span></span>;
}
暂时先想到以下几个方法,做到后面不够了再加。加库存和减库存后面再实现。
package com.solo.sell.service;
import com.solo.sell.dto.ProductInfo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
public interface ProductInfoService {
<span class="hljs-comment">/**
* 通过id查询单个商品
* <span class="hljs-doctag">@param</span> id
* <span class="hljs-doctag">@return</span>
*/</span>
<span class="hljs-function">ProductInfo <span class="hljs-title">findById</span><span class="hljs-params">(String id)</span></span>;
<span class="hljs-comment">/**
* 查询上架的产品
* <span class="hljs-doctag">@return</span>
*/</span>
<span class="hljs-function">List<ProductInfo> <span class="hljs-title">findUpAll</span><span class="hljs-params">()</span></span>;
<span class="hljs-comment">/**
* 查询所有商品 带分页
* <span class="hljs-doctag">@param</span> pageable
* <span class="hljs-doctag">@return</span>
*/</span>
<span class="hljs-function">Page<ProductInfo> <span class="hljs-title">findAll</span><span class="hljs-params">(Pageable pageable)</span></span>;
<span class="hljs-comment">/**
* 保存一个商品
* <span class="hljs-doctag">@param</span> productInfo
* <span class="hljs-doctag">@return</span>
*/</span>
<span class="hljs-function">ProductInfo <span class="hljs-title">save</span><span class="hljs-params">(ProductInfo productInfo)</span></span>;
<span class="hljs-comment">//加库存</span>
<span class="hljs-comment">//减库存</span>
}
实现类 ProductInfoServiceImpl
package com.solo.sell.service.impl;
import com.solo.sell.dto.ProductInfo;
import com.solo.sell.enums.ProductStatusEnum;
import com.solo.sell.repository.ProductInfoRepository;
import com.solo.sell.service.ProductInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProductInfoServiceImpl implements ProductInfoService {
@Autowired
private ProductInfoRepository repository;
@Override
public ProductInfo findById(String id) {
return repository.findById(id).get();
}
@Override
public List<ProductInfo> findUpAll() {
return repository.findByProductStatus(ProductStatusEnum.UP.getCode());
}
@Override
public Page<ProductInfo> findAll(Pageable pageable) {
return repository.findAll(pageable);
}
@Override
public ProductInfo save(ProductInfo productInfo) {
return repository.save(productInfo);
}
}
商品信息的 repository
和 service
都写完了,养成好习惯,别忘了测试。
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductInfoRepositoryTest {
<span class="hljs-meta">@Autowired</span>
<span class="hljs-keyword">private</span> ProductInfoRepository repository;
<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">add</span><span class="hljs-params">()</span></span>{
ProductInfo info = <span class="hljs-keyword">new</span> ProductInfo();
info.setProductId(<span class="hljs-string">"haha123"</span>);
info.setProductName(<span class="hljs-string">"酸菜鱼"</span>);
info.setProductPrice(<span class="hljs-keyword">new</span> BigDecimal(<span class="hljs-number">36</span>));
info.setProductDescription(<span class="hljs-string">"好吃的酸菜鱼,不可错过"</span>);
info.setProductIcon(<span class="hljs-string">"http://abc.png"</span>);
info.setProductStatus(<span class="hljs-number">0</span>);
info.setProductStock(<span class="hljs-number">10</span>);
info.setCategoryType(<span class="hljs-number">3</span>);
ProductInfo result = repository.save(info);
Assert.assertNotNull(result);
}
<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">findOne</span><span class="hljs-params">()</span></span>{
ProductInfo info = repository.findById(<span class="hljs-string">"haha123"</span>).get();
Assert.assertEquals(<span class="hljs-string">"haha123"</span>, info.getProductId());
}
<span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">findByProductStatus</span><span class="hljs-params">()</span> </span>{
List<ProductInfo> list = repository.findByProductStatus(<span class="hljs-number">0</span>);
Assert.assertNotEquals(<span class="hljs-number">0</span>, list.size());
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductInfoServiceImplTest {
@Autowired
private ProductInfoServiceImpl service;
@Test
public void findById() {
ProductInfo info = service.findById("haha123");
Assert.assertEquals("haha123", info.getProductId());
}
@Test
public void findUpAll() {
List<ProductInfo> all = service.findUpAll();
Assert.assertNotEquals(0, all.size());
}
@Test
public void findAll() {
PageRequest pageRequest = PageRequest.of(0, 3);
Page<ProductInfo> productInfoPage = service.findAll(pageRequest);
Assert.assertNotEquals(0, productInfoPage.getTotalElements());
}
@Test
public void save() {
ProductInfo info = new ProductInfo();
info.setProductId("haha456");
info.setProductName("皮皮虾");
info.setProductPrice(new BigDecimal(49));
info.setProductDescription("皮皮虾我们走");
info.setProductIcon("http://abc.png");
info.setProductStatus(0);
info.setProductStock(99);
info.setCategoryType(4);
ProductInfo save = service.save(info);
Assert.assertNotNull(save);
}
}
商品分类和商品信息开发完之后,就可以开发买家 api 了。
买家最简单的一个需求就是查询上架商品列表,先实现这个功能。
本来返回的数据格式应该是后端定的,但这个项目是先有的前端,已经规定好了数据格式,看看数据格式是什么样的吧
{
"code": 0,
"msg": "成功",
"data": [
{
"name": "热榜",
"type": 1,
"foods": [
{
"id": "123456",
"name": "皮蛋粥",
"price": 1.2,
"description": "好吃的皮蛋粥",
"icon": "http://xxx.com",
}
]
},
{
"name": "好吃的",
"type": 2,
"foods": [
{
"id": "123457",
"name": "慕斯蛋糕",
"price": 10.9,
"description": "美味爽口",
"icon": "http://xxx.com",
}
]
}
]
}
先约定一下所有返回给前端的对象都放在 VO
包下,对象名以 VO
结尾便于区分。猜测 VO
应该是 view object
的缩写吧…
返回结果有三层对象,最外层应该是整个项目统一的数据结构,我们定义为 ResultVO
。data
字段里先是商品分类 ProdoctVO
,然后每个分类里的 foods
字段才是具体商品 ProductInfoVO
。
统一返回结果 ResultVO
package com.solo.sell.VO;
import lombok.Data;
/**
返回的统一结果格式
@param <T>
*/
@Data
public class ResultVO<T> {
<span class="hljs-comment">/** 状态码 */</span>
<span class="hljs-keyword">private</span> Integer code;
<span class="hljs-comment">/** 返回信息 */</span>
<span class="hljs-keyword">private</span> String msg;
<span class="hljs-comment">/** 返回数据 */</span>
<span class="hljs-keyword">private</span> T data;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ResultVO</span><span class="hljs-params">()</span> </span>{}
}
返回商品对象 ProductVO
package com.solo.sell.VO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
商品信息(包含类目)
*/
@Data
public class ProductVO {
<span class="hljs-meta">@JsonProperty</span>(<span class="hljs-string">"name"</span>)
<span class="hljs-keyword">private</span> String categoryName;
<span class="hljs-meta">@JsonProperty</span>(<span class="hljs-string">"type"</span>)
<span class="hljs-keyword">private</span> Integer categoryType;
<span class="hljs-meta">@JsonProperty</span>(<span class="hljs-string">"foods"</span>)
<span class="hljs-keyword">private</span> List<ProductInfoVO> productInfoVOList ;
}
属性名尽量起的一看就知道是什么,不要只是叫 name
,时间久了不知道具体指哪个name
了。
但返回的数据就叫 name
怎么办呢?在属性上面加个注解 @JsonProperty("name")
,这样对应的属性名就是 name
了。
返回的商品信息 ProductInfoVO
package com.solo.sell.VO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ProductInfoVO {
<span class="hljs-meta">@JsonProperty</span>(<span class="hljs-string">"id"</span>)
<span class="hljs-keyword">private</span> String productId;
<span class="hljs-meta">@JsonProperty</span>(<span class="hljs-string">"name"</span>)
<span class="hljs-keyword">private</span> String productName;
<span class="hljs-meta">@JsonProperty</span>(<span class="hljs-string">"price"</span>)
<span class="hljs-keyword">private</span> BigDecimal productPrice;
<span class="hljs-meta">@JsonProperty</span>(<span class="hljs-string">"description"</span>)
<span class="hljs-keyword">private</span> String productDescription;
<span class="hljs-meta">@JsonProperty</span>(<span class="hljs-string">"icon"</span>)
<span class="hljs-keyword">private</span> String productIcon;
}
返回结果格式知道了,就开始写 controller
逻辑啦。
所有的 controller
都放在 controller
包下,新建 controller
包,然后新建 BuyerProductController
package com.solo.sell.controller;
import com.solo.sell.VO.ProductInfoVO;
import com.solo.sell.VO.ProductVO;
import com.solo.sell.VO.ResultVO;
import com.solo.sell.dto.ProductCategory;
import com.solo.sell.dto.ProductInfo;
import com.solo.sell.service.ProductCategoryService;
import com.solo.sell.service.ProductInfoService;
import com.solo.sell.utils.ResultVOUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/buyer/product")
@Api("买家端获取商品")
public class BuyerProductController {
<span class="hljs-meta">@Autowired</span>
<span class="hljs-keyword">private</span> ProductInfoService productInfoService;
<span class="hljs-meta">@Autowired</span>
<span class="hljs-keyword">private</span> ProductCategoryService categoryService;
<span class="hljs-meta">@ApiOperation</span>(value = <span class="hljs-string">"获取所有上架商品"</span>, notes = <span class="hljs-string">"获取所有上架商品,下架商品除外"</span>)
<span class="hljs-meta">@RequestMapping</span>(value = <span class="hljs-string">"/list"</span>, method = RequestMethod.GET)
<span class="hljs-function"><span class="hljs-keyword">public</span> ResultVO <span class="hljs-title">list</span><span class="hljs-params">()</span> </span>{
<span class="hljs-comment">// 1.查询所有上架商品</span>
List<ProductInfo> productInfoList = productInfoService.findUpAll();
<span class="hljs-comment">// 2.查询类目(一次性查询)</span>
<span class="hljs-comment">//用 java8 的特性获取到上架商品的所有类型</span>
List<Integer> categoryTypes = productInfoList.stream().map(e -> e.getCategoryType()).collect(Collectors.toList());
List<ProductCategory> productCategoryList = categoryService.findByCategoryTypeIn(categoryTypes);
List<ProductVO> productVOList = <span class="hljs-keyword">new</span> ArrayList<>();
<span class="hljs-comment">//数据拼装</span>
<span class="hljs-keyword">for</span> (ProductCategory category : productCategoryList) {
ProductVO productVO = <span class="hljs-keyword">new</span> ProductVO();
<span class="hljs-comment">//属性拷贝</span>
BeanUtils.copyProperties(category, productVO);
<span class="hljs-comment">//把类型匹配的商品添加进去</span>
List<ProductInfoVO> productInfoVOList = <span class="hljs-keyword">new</span> ArrayList<>();
<span class="hljs-keyword">for</span> (ProductInfo productInfo : productInfoList) {
<span class="hljs-keyword">if</span> (productInfo.getCategoryType().equals(category.getCategoryType())) {
ProductInfoVO productInfoVO = <span class="hljs-keyword">new</span> ProductInfoVO();
BeanUtils.copyProperties(productInfo, productInfoVO);
productInfoVOList.add(productInfoVO);
}
}
productVO.setProductInfoVOList(productInfoVOList);
productVOList.add(productVO);
}
<span class="hljs-keyword">return</span> ResultVOUtils.success(productVOList);
}
}
注释写的比较详细就不多解释了,就是按照前端要求的格式把数据拼接在一起。
@Api
和 @ApiOperation
注解可以先不用管,这是集成了 swagger
, 调试接口更加方便。下篇文章会专门讲 swagger
和 Spring Boot
的整合。
打开浏览器,输入地址 http://127.0.0.1:8080/sell/buyer/product/list
可以看到正确返回了数据
以上就是本节的内容,下期见~