Java Spring Boot企业微信点餐系统
(2-3 数据库设计&)
2-3 数据库设计&
数据库表设计
(3-1 开发环境搭建&)
3-1 开发环境搭建&
virtualBox安装,ifconfig,sequel pro连接数据库,虚拟机安装Maven与JDK,IDEA安装
(3-2 日志的使用&)
3-2 日志的使用&
Logback的使用与配置,
LoggerTest.java
package com.imooc;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
//@Data
public class LoggerTest {
@Test
public void test1() {
String name = "imooc";
String password = "123456";
log.debug("debug...");
log.info("name: " + name + " ,password: " + password);
log.info("name: {}, password: {}", name, password);
log.error("error...");
log.warn("warn...");
}
}
LoggerTest2.java
package com.imooc;
public class LoggerTest2 {
}
logback-spring.xml
%d - %msg%n
ERROR
DENY
ACCEPT
%msg%n
/var/log/tomcat/sell/info.%d.log
ERROR
%msg%n
/var/log/tomcat/sell/error.%d.log
application.yml,springBoot项目工程名
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://192.168.30.113/sell?characterEncoding=utf-8&useSSL=false
jpa:
show-sql: true
jackson:
default-property-inclusion: non_null
redis:
host: 192.168.30.113
port: 6379
server:
context-path: /sell
#logging:
# pattern:
# console: "%d - %msg%n"
## path: /var/log/tomcat/
# file: /var/log/tomcat/sell.log
# level:
# com.imooc.LoggerTest: debug
wechat:
mpAppId: wxd898fcb01713c658
mpAppSecret: 47ccc303338cee6e62894fxxxxxxxxxxx
openAppId: wx6ad144e54af67d87
openAppSecret: 91a2ff6d38a2bbccfb7e9f9079108e2e
mchId: 1483469312
mchKey: 06C56A89949D617xxxxxxxxxxx
keyPath: /var/weixin_cert/h5.p12
notifyUrl: http://sell.natapp4.cc/sell/pay/notify
templateId:
orderStatus: e-Cqq67QxD6YNI41iRiqawEYdFavW_7pc7LyEMb-yeQ
projectUrl:
wechatMpAuthorize: http://sell.natapp4.cc
wechatOpenAuthorize: http://sell.natapp4.cc
sell: http://sell.natapp4.cc
项目结构
(4-1 买家类目-dao(上)&)
4-1 买家类目-dao(上)&
pom.xml,springBoot jpa配置与使用
4.0.0
com.imooc
sell
0.0.1-SNAPSHOT
jar
sell
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
1.5.3.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
mysql
mysql-connector-java
org.springframework.boot
spring-boot-starter-data-jpa
org.projectlombok
lombok
com.google.code.gson
gson
com.github.binarywang
weixin-java-mp
2.7.0
cn.springboot
best-pay-sdk
1.1.0
org.springframework.boot
spring-boot-starter-freemarker
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-websocket
org.springframework.boot
spring-boot-maven-plugin
ProductCategory.java,DynamicUpdate动态更新时间
lombok插件安装更新
package com.imooc.dataobject;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Date;
@Entity
@DynamicUpdate
@Data
public class ProductCategory {
/** 类目id. */
@Id
@GeneratedValue
private Integer categoryId;
/** 类目名字. */
private String categoryName;
/** 类目编号. */
private Integer categoryType;
private Date createTime;
private Date updateTime;
public ProductCategory() {
}
public ProductCategory(String categoryName, Integer categoryType) {
this.categoryName = categoryName;
this.categoryType = categoryType;
}
}
ProductCategoryRepository.java
package com.imooc.repository;
import com.imooc.dataobject.ProductCategory;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ProductCategoryRepository extends JpaRepository {
List findByCategoryTypeIn(List categoryTypeList);
}
ProductCategoryRepositoryTest.java
package com.imooc.repository;
import com.imooc.dataobject.ProductCategory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductCategoryRepositoryTest {
@Autowired
private ProductCategoryRepository repository;
@Test
public void findOneTest() {
ProductCategory productCategory = repository.findOne(1);
System.out.println(productCategory.toString());
}
@Test
@Transactional
public void saveTest() {
ProductCategory productCategory = new ProductCategory("男生最爱", 4);
ProductCategory result = repository.save(productCategory);
Assert.assertNotNull(result);
// Assert.assertNotEquals(null, result);
}
@Test
public void findByCategoryTypeInTest() {
List list = Arrays.asList(2,3,4);
List result = repository.findByCategoryTypeIn(list);
Assert.assertNotEquals(0, result.size());
}
@Test
public void updateTest() {
// ProductCategory productCategory = repository.findOne(4);
// productCategory.setCategoryName("男生最爱1");
ProductCategory productCategory = new ProductCategory("男生最爱", 4);
ProductCategory result = repository.save(productCategory);
Assert.assertEquals(productCategory, result);
}
}
CategoryService.java
package com.imooc.service;
import com.imooc.dataobject.ProductCategory;
import java.util.List;
public interface CategoryService {
ProductCategory findOne(Integer categoryId);
List findAll();
List findByCategoryTypeIn(List categoryTypeList);
ProductCategory save(ProductCategory productCategory);
}
CategoryServiceImpl.java
package com.imooc.service.impl;
import com.imooc.dataobject.ProductCategory;
import com.imooc.repository.ProductCategoryRepository;
import com.imooc.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private ProductCategoryRepository repository;
@Override
public ProductCategory findOne(Integer categoryId) {
return repository.findOne(categoryId);
}
@Override
public List findAll() {
return repository.findAll();
}
@Override
public List findByCategoryTypeIn(List categoryTypeList) {
return repository.findByCategoryTypeIn(categoryTypeList);
}
@Override
public ProductCategory save(ProductCategory productCategory) {
return repository.save(productCategory);
}
}
(5-1 买家商品-dao&)
5-1 买家商品-dao&
ProductInfo.java
package com.imooc.dataobject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.imooc.enums.ProductStatusEnum;
import com.imooc.utils.EnumUtil;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.math.BigDecimal;
import java.util.Date;
@Entity
@Data
@DynamicUpdate
public class ProductInfo {
@Id
private String productId;
/** 名字. */
private String productName;
/** 单价. */
private BigDecimal productPrice;
/** 库存. */
private Integer productStock;
/** 描述. */
private String productDescription;
/** 小图. */
private String productIcon;
/** 状态, 0正常1下架. */
private Integer productStatus = ProductStatusEnum.UP.getCode();
/** 类目编号. */
private Integer categoryType;
private Date createTime;
private Date updateTime;
@JsonIgnore
public ProductStatusEnum getProductStatusEnum() {
return EnumUtil.getByCode(productStatus, ProductStatusEnum.class);
}
}
ProductInfoRepository.java
package com.imooc.repository;
import com.imooc.dataobject.ProductInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ProductInfoRepository extends JpaRepository {
List findByProductStatus(Integer productStatus);
}
ProductInfoRepositoryTest.java
package com.imooc.repository;
import com.imooc.dataobject.ProductInfo;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.math.BigDecimal;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductInfoRepositoryTest {
@Autowired
private ProductInfoRepository repository;
@Test
public void saveTest() {
ProductInfo productInfo = new ProductInfo();
productInfo.setProductId("123456");
productInfo.setProductName("皮蛋粥");
productInfo.setProductPrice(new BigDecimal(3.2));
productInfo.setProductStock(100);
productInfo.setProductDescription("很好喝的粥");
productInfo.setProductIcon("http://xxxxx.jpg");
productInfo.setProductStatus(0);
productInfo.setCategoryType(2);
ProductInfo result = repository.save(productInfo);
Assert.assertNotNull(result);
}
@Test
public void findByProductStatus() throws Exception {
List productInfoList = repository.findByProductStatus(0);
Assert.assertNotEquals(0, productInfoList.size());
}
}
ProductService.java
package com.imooc.service;
import com.imooc.dataobject.ProductInfo;
import com.imooc.dto.CartDTO;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
public interface ProductService {
ProductInfo findOne(String productId);
/**
* 查询所有在架商品列表
* @return
*/
List findUpAll();
Page findAll(Pageable pageable);
ProductInfo save(ProductInfo productInfo);
//加库存
void increaseStock(List cartDTOList);
//减库存
void decreaseStock(List cartDTOList);
//上架
ProductInfo onSale(String productId);
//下架
ProductInfo offSale(String productId);
}
枚举类使用
ProductStatusEnum.java
package com.imooc.enums;
import lombok.Getter;
@Getter
public enum ProductStatusEnum implements CodeEnum {
UP(0, "在架"),
DOWN(1, "下架")
;
private Integer code;
private String message;
ProductStatusEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
ProductServiceImpl.java
package com.imooc.service.impl;
import com.imooc.dataobject.ProductInfo;
import com.imooc.dto.CartDTO;
import com.imooc.enums.ProductStatusEnum;
import com.imooc.enums.ResultEnum;
import com.imooc.exception.SellException;
import com.imooc.repository.ProductInfoRepository;
import com.imooc.service.ProductService;
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 org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductInfoRepository repository;
@Override
public ProductInfo findOne(String productId) {
return repository.findOne(productId);
}
@Override
public List findUpAll() {
return repository.findByProductStatus(ProductStatusEnum.UP.getCode());
}
@Override
public Page findAll(Pageable pageable) {
return repository.findAll(pageable);
}
@Override
public ProductInfo save(ProductInfo productInfo) {
return repository.save(productInfo);
}
@Override
@Transactional
public void increaseStock(List cartDTOList) {
for (CartDTO cartDTO: cartDTOList) {
ProductInfo productInfo = repository.findOne(cartDTO.getProductId());
if (productInfo == null) {
throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
}
Integer result = productInfo.getProductStock() + cartDTO.getProductQuantity();
productInfo.setProductStock(result);
repository.save(productInfo);
}
}
@Override
@Transactional
public void decreaseStock(List cartDTOList) {
for (CartDTO cartDTO: cartDTOList) {
ProductInfo productInfo = repository.findOne(cartDTO.getProductId());
if (productInfo == null) {
throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
}
Integer result = productInfo.getProductStock() - cartDTO.getProductQuantity();
if (result < 0) {
throw new SellException(ResultEnum.PRODUCT_STOCK_ERROR);
}
productInfo.setProductStock(result);
repository.save(productInfo);
}
}
@Override
public ProductInfo onSale(String productId) {
ProductInfo productInfo = repository.findOne(productId);
if (productInfo == null) {
throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
}
if (productInfo.getProductStatusEnum() == ProductStatusEnum.UP) {
throw new SellException(ResultEnum.PRODUCT_STATUS_ERROR);
}
//更新
productInfo.setProductStatus(ProductStatusEnum.UP.getCode());
return repository.save(productInfo);
}
@Override
public ProductInfo offSale(String productId) {
ProductInfo productInfo = repository.findOne(productId);
if (productInfo == null) {
throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
}
if (productInfo.getProductStatusEnum() == ProductStatusEnum.DOWN) {
throw new SellException(ResultEnum.PRODUCT_STATUS_ERROR);
}
//更新
productInfo.setProductStatus(ProductStatusEnum.DOWN.getCode());
return repository.save(productInfo);
}
}
ProductServiceImplTest.java
Assert单元测试
package com.imooc.service.impl;
import com.imooc.dataobject.ProductInfo;
import com.imooc.enums.ProductStatusEnum;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.junit4.SpringRunner;
import java.math.BigDecimal;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductServiceImplTest {
@Autowired
private ProductServiceImpl productService;
@Test
public void findOne() throws Exception {
ProductInfo productInfo = productService.findOne("123456");
Assert.assertEquals("123456", productInfo.getProductId());
}
@Test
public void findUpAll() throws Exception {
List productInfoList = productService.findUpAll();
Assert.assertNotEquals(0, productInfoList.size());
}
@Test
public void findAll() throws Exception {
PageRequest request = new PageRequest(0, 2);
Page productInfoPage = productService.findAll(request);
// System.out.println(productInfoPage.getTotalElements());
Assert.assertNotEquals(0, productInfoPage.getTotalElements());
}
@Test
public void save() throws Exception {
ProductInfo productInfo = new ProductInfo();
productInfo.setProductId("123457");
productInfo.setProductName("皮皮虾");
productInfo.setProductPrice(new BigDecimal(3.2));
productInfo.setProductStock(100);
productInfo.setProductDescription("很好吃的虾");
productInfo.setProductIcon("http://xxxxx.jpg");
productInfo.setProductStatus(ProductStatusEnum.DOWN.getCode());
productInfo.setCategoryType(2);
ProductInfo result = productService.save(productInfo);
Assert.assertNotNull(result);
}
@Test
public void onSale() {
ProductInfo result = productService.onSale("123456");
Assert.assertEquals(ProductStatusEnum.UP, result.getProductStatusEnum());
}
@Test
public void offSale() {
ProductInfo result = productService.offSale("123456");
Assert.assertEquals(ProductStatusEnum.DOWN, result.getProductStatusEnum());
}
}
(5-3 买家商品-api(上)&)
5-3 买家商品-api(上)&
BuyerProductController.java
package com.imooc.controller;
import com.imooc.VO.ProductInfoVO;
import com.imooc.VO.ProductVO;
import com.imooc.VO.ResultVO;
import com.imooc.dataobject.ProductCategory;
import com.imooc.dataobject.ProductInfo;
import com.imooc.service.CategoryService;
import com.imooc.service.ProductService;
import com.imooc.utils.ResultVOUtil;
import org.springframework.beans.BeanUtils;
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;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/buyer/product")
public class BuyerProductController {
@Autowired
private ProductService productService;
@Autowired
private CategoryService categoryService;
@GetMapping("/list")
public ResultVO list() {
//1. 查询所有的上架商品
List productInfoList = productService.findUpAll();
//2. 查询类目(一次性查询)
// List categoryTypeList = new ArrayList();
//传统方法
// for (ProductInfo productInfo : productInfoList) {
// categoryTypeList.add(productInfo.getCategoryType());
// }
//精简方法(java8, lambda)
List categoryTypeList = productInfoList.stream()
.map(e -> e.getCategoryType())
.collect(Collectors.toList());
List productCategoryList = categoryService.findByCategoryTypeIn(categoryTypeList);
//3. 数据拼装
List productVOList = new ArrayList();
for (ProductCategory productCategory: productCategoryList) {
ProductVO productVO = new ProductVO();
productVO.setCategoryType(productCategory.getCategoryType());
productVO.setCategoryName(productCategory.getCategoryName());
List productInfoVOList = new ArrayList();
for (ProductInfo productInfo: productInfoList) {
if (productInfo.getCategoryType().equals(productCategory.getCategoryType())) {
ProductInfoVO productInfoVO = new ProductInfoVO();
BeanUtils.copyProperties(productInfo, productInfoVO);
productInfoVOList.add(productInfoVO);
}
}
productVO.setProductInfoVOList(productInfoVOList);
productVOList.add(productVO);
}
return ResultVOUtil.success(productVOList);
}
}
ResultVO.java http请求保证对象
package com.imooc.VO;
import lombok.Data;
/**
* http请求返回的最外层对象
*/
@Data
public class ResultVO {
/** 错误码. */
private Integer code;
/** 提示信息. */
private String msg;
/** 具体内容. */
private T data;
}
ProductVO.java @JsonProperty
package com.imooc.VO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* 商品(包含类目)
*/
@Data
public class ProductVO {
@JsonProperty("name")
private String categoryName;
@JsonProperty("type")
private Integer categoryType;
@JsonProperty("foods")
private List productInfoVOList;
}
ProductInfoVO.java
package com.imooc.VO;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
* 商品详情
*/
@Data
public class ProductInfoVO {
@JsonProperty("id")
private String productId;
@JsonProperty("name")
private String productName;
@JsonProperty("price")
private BigDecimal productPrice;
@JsonProperty("description")
private String productDescription;
@JsonProperty("icon")
private String productIcon;
}
ResultVOUtil.java
package com.imooc.utils;
import com.imooc.VO.ResultVO;
public class ResultVOUtil {
public static ResultVO success(Object object) {
ResultVO resultVO = new ResultVO();
resultVO.setData(object);
resultVO.setCode(0);
resultVO.setMsg("成功");
return resultVO;
}
public static ResultVO success() {
return success(null);
}
public static ResultVO error(Integer code, String msg) {
ResultVO resultVO = new ResultVO();
resultVO.setCode(code);
resultVO.setMsg(msg);
return resultVO;
}
}
BuyerProductController.java
lambda表达式遍历集合封装数据
package com.imooc.controller;
import com.imooc.VO.ProductInfoVO;
import com.imooc.VO.ProductVO;
import com.imooc.VO.ResultVO;
import com.imooc.dataobject.ProductCategory;
import com.imooc.dataobject.ProductInfo;
import com.imooc.service.CategoryService;
import com.imooc.service.ProductService;
import com.imooc.utils.ResultVOUtil;
import org.springframework.beans.BeanUtils;
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;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 买家商品
*/
@RestController
@RequestMapping("/buyer/product")
public class BuyerProductController {
@Autowired
private ProductService productService;
@Autowired
private CategoryService categoryService;
@GetMapping("/list")
public ResultVO list() {
//1. 查询所有的上架商品
List productInfoList = productService.findUpAll();
//2. 查询类目(一次性查询)
// List categoryTypeList = new ArrayList();
//传统方法
// for (ProductInfo productInfo : productInfoList) {
// categoryTypeList.add(productInfo.getCategoryType());
// }
//精简方法(java8, lambda)
List categoryTypeList = productInfoList.stream()
.map(e -> e.getCategoryType())
.collect(Collectors.toList());
List productCategoryList = categoryService.findByCategoryTypeIn(categoryTypeList);
//3. 数据拼装
List productVOList = new ArrayList();
for (ProductCategory productCategory: productCategoryList) {
ProductVO productVO = new ProductVO();
productVO.setCategoryType(productCategory.getCategoryType());
productVO.setCategoryName(productCategory.getCategoryName());
List productInfoVOList = new ArrayList();
for (ProductInfo productInfo: productInfoList) {
if (productInfo.getCategoryType().equals(productCategory.getCategoryType())) {
ProductInfoVO productInfoVO = new ProductInfoVO();
BeanUtils.copyProperties(productInfo, productInfoVO);
productInfoVOList.add(productInfoVO);
}
}
productVO.setProductInfoVOList(productInfoVOList);
productVOList.add(productVO);
}
return ResultVOUtil.success(productVOList);
}
}
设置linux中nginx配置,访问虚拟机中项目
通过域名访问
(6-4 买家订单-service创建_B&)
6-4 买家订单-service创建_B&
全局自定义异常处理类
ResultEnum.java
package com.imooc.enums;
import lombok.Getter;
@Getter
public enum ResultEnum {
SUCCESS(0, "成功"),
PARAM_ERROR(1, "参数不正确"),
PRODUCT_NOT_EXIST(10, "商品不存在"),
PRODUCT_STOCK_ERROR(11, "商品库存不正确"),
ORDER_NOT_EXIST(12, "订单不存在"),
ORDERDETAIL_NOT_EXIST(13, "订单详情不存在"),
ORDER_STATUS_ERROR(14, "订单状态不正确"),
ORDER_UPDATE_FAIL(15, "订单更新失败"),
ORDER_DETAIL_EMPTY(16, "订单详情为空"),
ORDER_PAY_STATUS_ERROR(17, "订单支付状态不正确"),
CART_EMPTY(18, "购物车为空"),
ORDER_OWNER_ERROR(19, "该订单不属于当前用户"),
WECHAT_MP_ERROR(20, "微信公众账号方面错误"),
WXPAY_NOTIFY_MONEY_VERIFY_ERROR(21, "微信支付异步通知金额校验不通过"),
ORDER_CANCEL_SUCCESS(22, "订单取消成功"),
ORDER_FINISH_SUCCESS(23, "订单完结成功"),
PRODUCT_STATUS_ERROR(24, "商品状态不正确"),
LOGIN_FAIL(25, "登录失败, 登录信息不正确"),
LOGOUT_SUCCESS(26, "登出成功"),
;
private Integer code;
private String message;
ResultEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
SellException.java
package com.imooc.exception;
import com.imooc.enums.ResultEnum;
public class SellException extends RuntimeException{
private Integer code;
public SellException(ResultEnum resultEnum) {
super(resultEnum.getMessage());
this.code = resultEnum.getCode();
}
public SellException(Integer code, String message) {
super(message);
this.code = code;
}
}
OrderServiceImpl.java
支付订单事务开启,判断集合是否为空 BeanUtils属性拷贝
package com.imooc.service.impl;
import com.imooc.converter.OrderMaster2OrderDTOConverter;
import com.imooc.dataobject.OrderDetail;
import com.imooc.dataobject.OrderMaster;
import com.imooc.dataobject.ProductInfo;
import com.imooc.dto.CartDTO;
import com.imooc.dto.OrderDTO;
import com.imooc.enums.OrderStatusEnum;
import com.imooc.enums.PayStatusEnum;
import com.imooc.enums.ResultEnum;
import com.imooc.exception.SellException;
import com.imooc.repository.OrderDetailRepository;
import com.imooc.repository.OrderMasterRepository;
import com.imooc.service.*;
import com.imooc.utils.KeyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private ProductService productService;
@Autowired
private OrderDetailRepository orderDetailRepository;
@Autowired
private OrderMasterRepository orderMasterRepository;
@Autowired
private PayService payService;
@Autowired
private PushMessageService pushMessageService;
@Autowired
private WebSocket webSocket;
@Override
@Transactional
public OrderDTO create(OrderDTO orderDTO) {
String orderId = KeyUtil.genUniqueKey();
BigDecimal orderAmount = new BigDecimal(BigInteger.ZERO);
// List cartDTOList = new ArrayList();
//1. 查询商品(数量, 价格)
for (OrderDetail orderDetail: orderDTO.getOrderDetailList()) {
ProductInfo productInfo = productService.findOne(orderDetail.getProductId());
if (productInfo == null) {
throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
}
//2. 计算订单总价
orderAmount = productInfo.getProductPrice()
.multiply(new BigDecimal(orderDetail.getProductQuantity()))
.add(orderAmount);
//订单详情入库
orderDetail.setDetailId(KeyUtil.genUniqueKey());
orderDetail.setOrderId(orderId);
BeanUtils.copyProperties(productInfo, orderDetail);
orderDetailRepository.save(orderDetail);
// CartDTO cartDTO = new CartDTO(orderDetail.getProductId(), orderDetail.getProductQuantity());
// cartDTOList.add(cartDTO);
}
//3. 写入订单数据库(orderMaster和orderDetail)
OrderMaster orderMaster = new OrderMaster();
orderDTO.setOrderId(orderId);
BeanUtils.copyProperties(orderDTO, orderMaster);
orderMaster.setOrderAmount(orderAmount);
orderMaster.setOrderStatus(OrderStatusEnum.NEW.getCode());
orderMaster.setPayStatus(PayStatusEnum.WAIT.getCode());
orderMasterRepository.save(orderMaster);
//4. 扣库存
List cartDTOList = orderDTO.getOrderDetailList().stream().map(e ->
new CartDTO(e.getProductId(), e.getProductQuantity())
).collect(Collectors.toList());
productService.decreaseStock(cartDTOList);
//发送websocket消息
webSocket.sendMessage(orderDTO.getOrderId());
return orderDTO;
}
@Override
public OrderDTO findOne(String orderId) {
OrderMaster orderMaster = orderMasterRepository.findOne(orderId);
if (orderMaster == null) {
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}
List orderDetailList = orderDetailRepository.findByOrderId(orderId);
if (CollectionUtils.isEmpty(orderDetailList)) {
throw new SellException(ResultEnum.ORDERDETAIL_NOT_EXIST);
}
OrderDTO orderDTO = new OrderDTO();
BeanUtils.copyProperties(orderMaster, orderDTO);
orderDTO.setOrderDetailList(orderDetailList);
return orderDTO;
}
@Override
public Page findList(String buyerOpenid, Pageable pageable) {
Page orderMasterPage = orderMasterRepository.findByBuyerOpenid(buyerOpenid, pageable);
List orderDTOList = OrderMaster2OrderDTOConverter.convert(orderMasterPage.getContent());
return new PageImpl(orderDTOList, pageable, orderMasterPage.getTotalElements());
}
@Override
@Transactional
public OrderDTO cancel(OrderDTO orderDTO) {
OrderMaster orderMaster = new OrderMaster();
//判断订单状态
if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
log.error("【取消订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
}
//修改订单状态
orderDTO.setOrderStatus(OrderStatusEnum.CANCEL.getCode());
BeanUtils.copyProperties(orderDTO, orderMaster);
OrderMaster updateResult = orderMasterRepository.save(orderMaster);
if (updateResult == null) {
log.error("【取消订单】更新失败, orderMaster={}", orderMaster);
throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
}
//返回库存
if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) {
log.error("【取消订单】订单中无商品详情, orderDTO={}", orderDTO);
throw new SellException(ResultEnum.ORDER_DETAIL_EMPTY);
}
List cartDTOList = orderDTO.getOrderDetailList().stream()
.map(e -> new CartDTO(e.getProductId(), e.getProductQuantity()))
.collect(Collectors.toList());
productService.increaseStock(cartDTOList);
//如果已支付, 需要退款
if (orderDTO.getPayStatus().equals(PayStatusEnum.SUCCESS.getCode())) {
payService.refund(orderDTO);
}
return orderDTO;
}
@Override
@Transactional
public OrderDTO finish(OrderDTO orderDTO) {
//判断订单状态
if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
log.error("【完结订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
}
//修改订单状态
orderDTO.setOrderStatus(OrderStatusEnum.FINISHED.getCode());
OrderMaster orderMaster = new OrderMaster();
BeanUtils.copyProperties(orderDTO, orderMaster);
OrderMaster updateResult = orderMasterRepository.save(orderMaster);
if (updateResult == null) {
log.error("【完结订单】更新失败, orderMaster={}", orderMaster);
throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
}
//推送微信模版消息
pushMessageService.orderStatus(orderDTO);
return orderDTO;
}
@Override
@Transactional
public OrderDTO paid(OrderDTO orderDTO) {
//判断订单状态
if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
log.error("【订单支付完成】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
}
//判断支付状态
if (!orderDTO.getPayStatus().equals(PayStatusEnum.WAIT.getCode())) {
log.error("【订单支付完成】订单支付状态不正确, orderDTO={}", orderDTO);
throw new SellException(ResultEnum.ORDER_PAY_STATUS_ERROR);
}
//修改支付状态
orderDTO.setPayStatus(PayStatusEnum.SUCCESS.getCode());
OrderMaster orderMaster = new OrderMaster();
BeanUtils.copyProperties(orderDTO, orderMaster);
OrderMaster updateResult = orderMasterRepository.save(orderMaster);
if (updateResult == null) {
log.error("【订单支付完成】更新失败, orderMaster={}", orderMaster);
throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
}
return orderDTO;
}
@Override
public Page findList(Pageable pageable) {
Page orderMasterPage = orderMasterRepository.findAll(pageable);
List orderDTOList = OrderMaster2OrderDTOConverter.convert(orderMasterPage.getContent());
return new PageImpl(orderDTOList, pageable, orderMasterPage.getTotalElements());
}
}
KeyUtil.java
生成唯一主键
package com.imooc.utils;
import java.util.Random;
public class KeyUtil {
/**
* 生成唯一的主键
* 格式: 时间+随机数
* @return
*/
public static synchronized String genUniqueKey() {
Random random = new Random();
Integer number = random.nextInt(900000) + 100000;
return System.currentTimeMillis() + String.valueOf(number);
}
}
BuyerOrderController.java
package com.imooc.controller;
import com.imooc.VO.ResultVO;
import com.imooc.converter.OrderForm2OrderDTOConverter;
import com.imooc.dto.OrderDTO;
import com.imooc.enums.ResultEnum;
import com.imooc.exception.SellException;
import com.imooc.form.OrderForm;
import com.imooc.service.BuyerService;
import com.imooc.service.OrderService;
import com.imooc.utils.ResultVOUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/buyer/order")
@Slf4j
public class BuyerOrderController {
@Autowired
private OrderService orderService;
@Autowired
private BuyerService buyerService;
//创建订单
@PostMapping("/create")
public ResultVO create(@Valid OrderForm orderForm,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
log.error("【创建订单】参数不正确, orderForm={}", orderForm);
throw new SellException(ResultEnum.PARAM_ERROR.getCode(),
bindingResult.getFieldError().getDefaultMessage());
}
OrderDTO orderDTO = OrderForm2OrderDTOConverter.convert(orderForm);
if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) {
log.error("【创建订单】购物车不能为空");
throw new SellException(ResultEnum.CART_EMPTY);
}
OrderDTO createResult = orderService.create(orderDTO);
Map map = new HashMap();
map.put("orderId", createResult.getOrderId());
return ResultVOUtil.success(map);
}
//订单列表
@GetMapping("/list")
public ResultVO list(@RequestParam("openid") String openid,
@RequestParam(value = "page", defaultValue = "0") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size) {
if (StringUtils.isEmpty(openid)) {
log.error("【查询订单列表】openid为空");
throw new SellException(ResultEnum.PARAM_ERROR);
}
PageRequest request = new PageRequest(page, size);
Page orderDTOPage = orderService.findList(openid, request);
return ResultVOUtil.success(orderDTOPage.getContent());
}
//订单详情
@GetMapping("/detail")
public ResultVO detail(@RequestParam("openid") String openid,
@RequestParam("orderId") String orderId) {
OrderDTO orderDTO = buyerService.findOrderOne(openid, orderId);
return ResultVOUtil.success(orderDTO);
}
//取消订单
@PostMapping("/cancel")
public ResultVO cancel(@RequestParam("openid") String openid,
@RequestParam("orderId") String orderId) {
buyerService.cancelOrder(openid, orderId);
return ResultVOUtil.success();
}
}
OrderForm.java @NotEmpty表单后台校验
package com.imooc.form;
import lombok.Data;
import org.hibernate.validator.constraints.NotEmpty;
@Data
public class OrderForm {
/**
* 买家姓名
*/
@NotEmpty(message = "姓名必填")
private String name;
/**
* 买家手机号
*/
@NotEmpty(message = "手机号必填")
private String phone;
/**
* 买家地址
*/
@NotEmpty(message = "地址必填")
private String address;
/**
* 买家微信openid
*/
@NotEmpty(message = "openid必填")
private String openid;
/**
* 购物车
*/
@NotEmpty(message = "购物车不能为空")
private String items;
}
OrderForm2OrderDTOConverter.java Json类型转换工具类 postman使用
package com.imooc.converter;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.imooc.dataobject.OrderDetail;
import com.imooc.dto.OrderDTO;
import com.imooc.enums.ResultEnum;
import com.imooc.exception.SellException;
import com.imooc.form.OrderForm;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class OrderForm2OrderDTOConverter {
public static OrderDTO convert(OrderForm orderForm) {
Gson gson = new Gson();
OrderDTO orderDTO = new OrderDTO();
orderDTO.setBuyerName(orderForm.getName());
orderDTO.setBuyerPhone(orderForm.getPhone());
orderDTO.setBuyerAddress(orderForm.getAddress());
orderDTO.setBuyerOpenid(orderForm.getOpenid());
List orderDetailList = new ArrayList();
try {
orderDetailList = gson.fromJson(orderForm.getItems(),
new TypeToken() {
}.getType());
} catch (Exception e) {
log.error("【对象转换】错误, string={}", orderForm.getItems());
throw new SellException(ResultEnum.PARAM_ERROR);
}
orderDTO.setOrderDetailList(orderDetailList);
return orderDTO;
}
}
Date转long
Date2LongSerializer.java
package com.imooc.utils.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.util.Date;
public class Date2LongSerializer extends JsonSerializer {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeNumber(date.getTime() / 1000);
}
}
OrderDTO.java @JsonIgnore值为null字段不返回
package com.imooc.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.imooc.dataobject.OrderDetail;
import com.imooc.enums.OrderStatusEnum;
import com.imooc.enums.PayStatusEnum;
import com.imooc.utils.EnumUtil;
import com.imooc.utils.serializer.Date2LongSerializer;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Data
//@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
//@JsonInclude(JsonInclude.Include.NON_NULL)
public class OrderDTO {
/** 订单id. */
private String orderId;
/** 买家名字. */
private String buyerName;
/** 买家手机号. */
private String buyerPhone;
/** 买家地址. */
private String buyerAddress;
/** 买家微信Openid. */
private String buyerOpenid;
/** 订单总金额. */
private BigDecimal orderAmount;
/** 订单状态, 默认为0新下单. */
private Integer orderStatus;
/** 支付状态, 默认为0未支付. */
private Integer payStatus;
/** 创建时间. */
@JsonSerialize(using = Date2LongSerializer.class)
private Date createTime;
/** 更新时间. */
@JsonSerialize(using = Date2LongSerializer.class)
private Date updateTime;
List orderDetailList;
@JsonIgnore
public OrderStatusEnum getOrderStatusEnum() {
return EnumUtil.getByCode(orderStatus, OrderStatusEnum.class);
}
@JsonIgnore
public PayStatusEnum getPayStatusEnum() {
return EnumUtil.getByCode(payStatus, PayStatusEnum.class);
}
}