├── src ├── main │ ├── java │ │ └── com │ │ │ └── zx │ │ │ ├── enums │ │ │ ├── CodeEnum.java │ │ │ ├── PayStatusEnum.java │ │ │ ├── OrderStatusEnum.java │ │ │ ├── ProductStatusEnum.java │ │ │ └── ResultEnum.java │ │ │ ├── service │ │ │ ├── BuyerService.java │ │ │ ├── CategoryService.java │ │ │ ├── OrderService.java │ │ │ ├── ProductService.java │ │ │ ├── impl │ │ │ │ ├── CategoryServiceImpl.java │ │ │ │ ├── BuyerServiceImpl.java │ │ │ │ ├── ProductServiceImpl.java │ │ │ │ └── OrderServiceImpl.java │ │ │ └── RedisLock.java │ │ │ ├── vo │ │ │ ├── ResultVO.java │ │ │ ├── ProductVO.java │ │ │ └── ProductInfoVO.java │ │ │ ├── dto │ │ │ ├── CartDTO.java │ │ │ └── OrderDTO.java │ │ │ ├── WeChatOrderApplication.java │ │ │ ├── utils │ │ │ ├── KeyUtil.java │ │ │ ├── EnumUtil.java │ │ │ ├── serializer │ │ │ │ ├── String2TestStringSerializer.java │ │ │ │ └── Date2LongSerializer.java │ │ │ └── ResultVOUtil.java │ │ │ ├── repository │ │ │ ├── OrderDetailRepository.java │ │ │ ├── ProductInfoRepository.java │ │ │ ├── ProductCategoryRepository.java │ │ │ └── OrderMasterRepository.java │ │ │ ├── config │ │ │ ├── WeChatAccountConfig.java │ │ │ └── WeChatMpConfig.java │ │ │ ├── exception │ │ │ └── SellException.java │ │ │ ├── form │ │ │ └── OrderForm.java │ │ │ ├── domain │ │ │ ├── ProductInfo.java │ │ │ ├── OrderDetail.java │ │ │ ├── ProductCategory.java │ │ │ └── OrderMaster.java │ │ │ ├── converter │ │ │ ├── OrderMaster2OrderDTOConverter.java │ │ │ └── OrderForm2OrderDTOConverter.java │ │ │ └── controller │ │ │ ├── WeiXinController.java │ │ │ ├── WeChatController.java │ │ │ ├── BuyerProductController.java │ │ │ └── BuyerOrderController.java │ └── resources │ │ ├── application.yml │ │ ├── sql │ │ ├── linux.md │ │ ├── schema.sql │ │ └── API.md │ │ └── logback-spring.xml └── test │ └── java │ └── com │ └── zx │ ├── WeChatOrderApplicationTests.java │ ├── LoggerTest.java │ ├── repository │ ├── OrderDetailRepositoryTest.java │ ├── OrderMasterRepositoryTest.java │ ├── ProductInfoRepositoryTest.java │ └── ProductCategoryRepositoryTest.java │ └── service │ └── impl │ ├── CategoryServiceImplTest.java │ ├── ProductServiceImplTest.java │ └── OrderServiceImplTest.java ├── .gitignore ├── pom.xml └── README.md /src/main/java/com/zx/enums/CodeEnum.java: -------------------------------------------------------------------------------- 1 | package com.zx.enums; 2 | 3 | /** 4 | * 有code属性的枚举接口 5 | * T 泛型是为了可以自定义Code属性的类型 6 | */ 7 | public interface CodeEnum { 8 | T getCode(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/zx/service/BuyerService.java: -------------------------------------------------------------------------------- 1 | package com.zx.service; 2 | 3 | import com.zx.dto.OrderDTO; 4 | 5 | /** 6 | * 买家 7 | */ 8 | public interface BuyerService { 9 | 10 | //查询一个订单 11 | OrderDTO findOrderOne(String openid, String orderId); 12 | 13 | //取消订单 14 | OrderDTO cancelOrder(String openid, String orderId); 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ -------------------------------------------------------------------------------- /src/main/java/com/zx/vo/ResultVO.java: -------------------------------------------------------------------------------- 1 | package com.zx.vo; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * HTTP请求返回的最外层对象 7 | * Created by 97038 on 2017-07-23. 8 | */ 9 | @Data 10 | //@JsonInclude(JsonInclude.Include.NON_NULL) 11 | public class ResultVO { 12 | /** 状态码 */ 13 | private Integer code; 14 | /** 消息 */ 15 | private String msg;//消息 16 | /** 数据 */ 17 | private T data; 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/com/zx/WeChatOrderApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.zx; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class WeChatOrderApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/zx/dto/CartDTO.java: -------------------------------------------------------------------------------- 1 | package com.zx.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * 购物车 9 | */ 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class CartDTO { 14 | 15 | /** 16 | * 商品id 17 | */ 18 | private String productId; 19 | /** 20 | * 商品数量 21 | */ 22 | private Integer productQuantity; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/zx/enums/PayStatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.zx.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * 支付状态 7 | */ 8 | @Getter 9 | public enum PayStatusEnum implements CodeEnum { 10 | WAIT(0,"等待支付"), 11 | SUCCESS(1,"支付成功"); 12 | 13 | private Integer code; 14 | private String message; 15 | 16 | PayStatusEnum(Integer code, String message) { 17 | this.code = code; 18 | this.message = message; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/zx/enums/OrderStatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.zx.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * 订单状态 7 | */ 8 | @Getter 9 | public enum OrderStatusEnum { 10 | 11 | NEW(0,"新订单"), 12 | FINISHED(1,"完结"), 13 | CANCEL(2,"已取消"); 14 | 15 | private Integer code; 16 | private String message; 17 | 18 | OrderStatusEnum(Integer code, String message) { 19 | this.code = code; 20 | this.message = message; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/zx/WeChatOrderApplication.java: -------------------------------------------------------------------------------- 1 | package com.zx; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cache.annotation.EnableCaching; 6 | 7 | @SpringBootApplication 8 | @EnableCaching 9 | public class WeChatOrderApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(WeChatOrderApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/zx/enums/ProductStatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.zx.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * 商品状态 7 | * Created by 97038 on 2017-07-23. 8 | */ 9 | @Getter 10 | public enum ProductStatusEnum { 11 | 12 | UP(0,"在架"), 13 | DOWN(1,"下架"); 14 | 15 | private Integer code; 16 | private String message; 17 | 18 | ProductStatusEnum(Integer code,String message) { 19 | this.code = code; 20 | this.message = message; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/zx/utils/KeyUtil.java: -------------------------------------------------------------------------------- 1 | package com.zx.utils; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * 生成key工具类 7 | */ 8 | public class KeyUtil { 9 | 10 | /** 11 | * 生成唯一的主键 12 | * 格式:时间+随机数 13 | * @return 14 | */ 15 | public static synchronized String generateUniqueKey() { 16 | //生成长度为6位的随机数 17 | Integer i = new Random().nextInt(900000) + 100000; 18 | return System.currentTimeMillis() + String.valueOf(i); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/zx/repository/OrderDetailRepository.java: -------------------------------------------------------------------------------- 1 | package com.zx.repository; 2 | 3 | import com.zx.domain.OrderDetail; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 订单详情 10 | * Created by dell on 2017/7/24. 11 | */ 12 | public interface OrderDetailRepository extends JpaRepository { 13 | 14 | /** 15 | * 查询一个订单下所有订单详情 16 | */ 17 | List findByOrderId(String orderId); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/zx/repository/ProductInfoRepository.java: -------------------------------------------------------------------------------- 1 | package com.zx.repository; 2 | 3 | import com.zx.domain.ProductInfo; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by 97038 on 2017-07-23. 10 | */ 11 | public interface ProductInfoRepository extends JpaRepository{ 12 | /** 13 | * 通过商品状态查询所有商品 14 | */ 15 | List findByProductStatus(Integer productStatus); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/zx/config/WeChatAccountConfig.java: -------------------------------------------------------------------------------- 1 | package com.zx.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * 微信账户配置参数 9 | * 该类从yml文件中读取自定义参数,其他类中可以通过注入该类获取 10 | */ 11 | @Data 12 | @Component 13 | @ConfigurationProperties(prefix = "wechat") 14 | public class WeChatAccountConfig { 15 | 16 | private String mpAppId; 17 | private String mpAppSecret; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/zx/exception/SellException.java: -------------------------------------------------------------------------------- 1 | package com.zx.exception; 2 | 3 | import com.zx.enums.ResultEnum; 4 | 5 | /** 6 | * 系统自定义异常 7 | */ 8 | public class SellException extends RuntimeException { 9 | 10 | private Integer code; 11 | 12 | public SellException(ResultEnum resultEum) { 13 | super(resultEum.getMessage()); 14 | this.code = resultEum.getCode(); 15 | } 16 | 17 | public SellException(Integer code, String message) { 18 | super(message); 19 | this.code = code; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/zx/repository/ProductCategoryRepository.java: -------------------------------------------------------------------------------- 1 | package com.zx.repository; 2 | 3 | import com.zx.domain.ProductCategory; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by 97038 on 2017-07-22. 10 | * 商品类目dao 11 | */ 12 | public interface ProductCategoryRepository extends JpaRepository{ 13 | 14 | /** 15 | * 根据商品 类目编号集合 查询所有商品类目 16 | */ 17 | List findByCategoryTypeIn(List categoryType); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/zx/repository/OrderMasterRepository.java: -------------------------------------------------------------------------------- 1 | package com.zx.repository; 2 | 3 | import com.zx.domain.OrderMaster; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | 8 | /** 9 | * 订单主表 10 | * Created by dell on 2017/7/24. 11 | */ 12 | public interface OrderMasterRepository extends JpaRepository { 13 | /** 14 | * 查询用户(BuyerOpenid)订单,分页 15 | */ 16 | Page findByBuyerOpenid(String buyerOpenid, Pageable pageable); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/zx/service/CategoryService.java: -------------------------------------------------------------------------------- 1 | package com.zx.service; 2 | 3 | import com.zx.domain.ProductCategory; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Created by 97038 on 2017-07-22. 9 | * 类目 10 | */ 11 | public interface CategoryService { 12 | /** 根据id查询单个类目 **/ 13 | ProductCategory findOne(Integer categoryId); 14 | /** 查询所有类目 **/ 15 | List findAll(); 16 | /** 根据类目编号集合 查询 类目列表 **/ 17 | List findByCategoryTypeIn(List categoryTypeList); 18 | /** 保存类目 **/ 19 | ProductCategory save(ProductCategory productCategory); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/zx/vo/ProductVO.java: -------------------------------------------------------------------------------- 1 | package com.zx.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.zx.domain.ProductInfo; 5 | import lombok.Data; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 商品返回对象(包含类目) 11 | * Created by 97038 on 2017-07-23. 12 | */ 13 | @Data 14 | public class ProductVO { 15 | 16 | //该注解表示将此对象转为JSON格式时,该属性的key将为name 17 | @JsonProperty("name") 18 | private String categoryName; 19 | 20 | @JsonProperty("type") 21 | private Integer categoryType; 22 | 23 | @JsonProperty("foods") 24 | private List productInfoVOList; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/zx/vo/ProductInfoVO.java: -------------------------------------------------------------------------------- 1 | package com.zx.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | import java.math.BigDecimal; 7 | 8 | /** 9 | * 商品返回对象 - 减少一些属性,主要是为不将所有字段都返回前端 10 | * Created by 97038 on 2017-07-23. 11 | */ 12 | @Data 13 | public class ProductInfoVO { 14 | @JsonProperty("id") 15 | private String productId; 16 | @JsonProperty("name") 17 | private String productName; 18 | @JsonProperty("price") 19 | private BigDecimal productPrice; 20 | @JsonProperty("description") 21 | private String productDescription; 22 | @JsonProperty("icon") 23 | private String productIcon; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/zx/utils/EnumUtil.java: -------------------------------------------------------------------------------- 1 | package com.zx.utils; 2 | 3 | import com.zx.enums.CodeEnum; 4 | 5 | /** 6 | * 枚举工具类 7 | * 8 | */ 9 | public class EnumUtil { 10 | 11 | /** 12 | * 根据Code返回Message 13 | * 相当于一种修饰符,表明T这个泛型,必须继承自CodeEnum 14 | * @param code 15 | * @param enumClass 16 | * @param 17 | * @return 18 | */ 19 | public static T getByCode(Integer code, Class enumClass) { 20 | for (T each : enumClass.getEnumConstants()) { 21 | if (code.equals(each.getCode())) { 22 | return each; 23 | } 24 | } 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | context-path: /sell 3 | port: 80 4 | spring: 5 | datasource: 6 | driver-class-name: com.mysql.jdbc.Driver 7 | username: root 8 | password: 123456 9 | # url: jdbc:mysql://192.168.2.126/we_chat_order?characterEncoding=utf-8&useSSL=false 10 | url: jdbc:mysql://127.0.0.1/we_chat_order?characterEncoding=utf-8&useSSL=false 11 | jpa: 12 | show-sql: true 13 | jackson: 14 | default-property-inclusion: non_null 15 | redis: 16 | host: 127.0.0.1 17 | port: 6379 18 | 19 | 20 | #微信配置参数 21 | wechat: 22 | mpAppId: wxd898fcb01713c658 23 | mpAppSecret: 24 | mchId: 1483469312 25 | mchKey: C5245D70627C1F8E9964D494B0735025 26 | keyPath: /var/weixin_cert/h5.p12 27 | -------------------------------------------------------------------------------- /src/main/java/com/zx/utils/serializer/String2TestStringSerializer.java: -------------------------------------------------------------------------------- 1 | package com.zx.utils.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.JsonSerializer; 6 | import com.fasterxml.jackson.databind.SerializerProvider; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * SpringMVC-JSON序列化 12 | * String类型数据测试类 13 | */ 14 | public class String2TestStringSerializer extends JsonSerializer { 15 | @Override 16 | public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { 17 | jsonGenerator.writeString(s + "111111"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/zx/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.zx.service; 2 | 3 | import com.zx.domain.OrderMaster; 4 | import com.zx.dto.OrderDTO; 5 | import org.springframework.data.domain.Page; 6 | import org.springframework.data.domain.Pageable; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 订单 12 | */ 13 | public interface OrderService { 14 | /** 创建订单 */ 15 | OrderDTO create(OrderDTO orderDTO); 16 | /** 查询单个订单 */ 17 | OrderDTO findOne(String orderId); 18 | /** 查询订单列表 */ 19 | Page findList(String buyerOpenid, Pageable pageable); 20 | /** 取消订单 */ 21 | OrderDTO cancel(OrderDTO orderDTO); 22 | /** 完结订单 */ 23 | OrderDTO finish(OrderDTO orderDTO); 24 | /** 支付订单 */ 25 | OrderDTO paid(OrderDTO orderDTO); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/sql/linux.md: -------------------------------------------------------------------------------- 1 | # 虚拟机说明文档 2 | VirtualBox-5.1.22 3 | 虚拟机系统 centos7.3 4 | 账号 root 5 | 密码 123456 6 | #### 包括软件 7 | * jdk 1.8.0_111 8 | * nginx 1.11.7 9 | * mysql 5.7.17 10 | * redis 3.2.8 11 | 12 | ##### jdk 13 | * 路径 /usr/local/jdk1.8.0_111 14 | 15 | ##### nginx 16 | * 路径 /usr/local/nginx 17 | * 启动 nginx 18 | * 重启 nginx -s reload 19 | 20 | ##### mysql 21 | * 配置 /etc/my.conf 22 | * 账号 root 23 | * 密码 123456 24 | * 端口 3306 25 | * 启动 systemctl start mysqld 26 | * 停止 systemctl stop mysqld 27 | 28 | ##### redis 29 | * 路径 /usr/local/redis 30 | * 配置 /etc/reis.conf 31 | * 端口 6379 32 | * 密码 123456 33 | * 启动 systemctl start redis 34 | * 停止 systemctl stop redis 35 | 36 | ##### tomcat 37 | * 路径 /usr/local/tomcat 38 | * 启动 systemctl start tomcat 39 | * 停止 systemctl stop tomcat 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/test/java/com/zx/LoggerTest.java: -------------------------------------------------------------------------------- 1 | package com.zx; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.context.junit4.SpringRunner; 9 | 10 | /** 11 | * Created by 97038 on 2017-07-22. 12 | * 日志测试类 13 | */ 14 | @RunWith(SpringRunner.class) 15 | @SpringBootTest 16 | public class LoggerTest { 17 | //日志 18 | private final Logger logger = LoggerFactory.getLogger(LoggerTest.class); 19 | 20 | 21 | @Test 22 | public void test1(){ 23 | logger.debug("a"); 24 | logger.info("b"); 25 | logger.error("c"); 26 | logger.info("a:{},b{},c = {}",1,3,"323"); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/zx/utils/serializer/Date2LongSerializer.java: -------------------------------------------------------------------------------- 1 | package com.zx.utils.serializer; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.JsonSerializer; 6 | import com.fasterxml.jackson.databind.SerializerProvider; 7 | 8 | import java.io.IOException; 9 | import java.util.Date; 10 | 11 | /** 12 | * json序列化-SpringMVC 13 | * 对T(Date)类型的数据进行特定方法的转换 14 | */ 15 | public class Date2LongSerializer extends JsonSerializer { 16 | 17 | @Override 18 | public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { 19 | jsonGenerator.writeNumber(date.getTime()/1000); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/zx/form/OrderForm.java: -------------------------------------------------------------------------------- 1 | package com.zx.form; 2 | 3 | import lombok.Data; 4 | import org.hibernate.validator.constraints.NotEmpty; 5 | 6 | /** 7 | * 订单表单-请求数据 8 | */ 9 | @Data 10 | public class OrderForm { 11 | /** 12 | * 买家姓名 13 | */ 14 | @NotEmpty(message = "姓名不能为空") 15 | private String name; 16 | 17 | /** 18 | * 买家手机号 19 | */ 20 | @NotEmpty(message = "手机号必填") 21 | private String phone; 22 | 23 | /** 24 | * 买家地址 25 | */ 26 | @NotEmpty(message = "地址必填") 27 | private String address; 28 | 29 | /** 30 | * 买家微信openid 31 | */ 32 | @NotEmpty(message = "openid必填") 33 | private String openid; 34 | 35 | /** 36 | * 购物车 37 | */ 38 | @NotEmpty(message = "购物车不能为空") 39 | private String items; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/zx/domain/ProductInfo.java: -------------------------------------------------------------------------------- 1 | package com.zx.domain; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.Id; 7 | import java.math.BigDecimal; 8 | 9 | /** 10 | * Created by 97038 on 2017-07-23. 11 | */ 12 | @Entity 13 | @Data 14 | public class ProductInfo { 15 | 16 | /** 商品id */ 17 | @Id 18 | private String productId; 19 | /** 商品名 */ 20 | private String productName; 21 | /** 单价 */ 22 | private BigDecimal productPrice; 23 | /** 库存 */ 24 | private Integer productStock; 25 | /** 描述 */ 26 | private String productDescription; 27 | /** 小图 */ 28 | private String productIcon; 29 | /** 状态 0正常,1下架*/ 30 | private Integer productStatus; 31 | /** 类目编号 */ 32 | private Integer categoryType; 33 | 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/zx/domain/OrderDetail.java: -------------------------------------------------------------------------------- 1 | package com.zx.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import javax.persistence.Entity; 8 | import javax.persistence.Id; 9 | import java.math.BigDecimal; 10 | 11 | /** 12 | * 订单详情 13 | * Created by dell on 2017/7/24. 14 | */ 15 | @Entity 16 | @Data 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class OrderDetail { 20 | @Id 21 | private String detailId; 22 | /** 商品id*/ 23 | private String orderId; 24 | /** 商品id*/ 25 | private String productId; 26 | /** 商品名称*/ 27 | private String productName; 28 | /** 商品价格*/ 29 | private BigDecimal productPrice; 30 | /** 商品数量*/ 31 | private Integer productQuantity; 32 | /** 商品小图*/ 33 | private String productIcon; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/zx/enums/ResultEnum.java: -------------------------------------------------------------------------------- 1 | package com.zx.enums; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * 返回给前端的枚举 7 | */ 8 | @Getter 9 | public enum ResultEnum { 10 | 11 | PARAM_ERROR(1,"参数不正确"), 12 | 13 | PRODUCT_NOT_EXIST(10,"商品不存在"), 14 | PRODUCT_STOCK_ERROR(20,"库存异常"), 15 | ORDER_NOT_EXIST(30,"订单不存在"), 16 | ORDER_DETAIL_NOT_EXIST(40,"订单详情不存在"), 17 | ORDER_STATUS_ERROR(50,"订单状态异常"), 18 | ORDER_UPDATE_FAIL(60,"订单更新失败"), 19 | ORDER_DETAIL_EMPTY(70,"订单无商品详情"), 20 | ORDER_PAY_STATUS_ERROR(80,"支付状态异常"), 21 | 22 | ORDER_OWNER_ERROR(90,"该订单不属于当前用户"), 23 | 24 | WECHAT_MP_ERROR(100,"微信公众号异常"), 25 | ; 26 | 27 | 28 | private Integer code; 29 | private String message; 30 | 31 | ResultEnum(Integer code, String message) { 32 | this.code = code; 33 | this.message = message; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/zx/service/ProductService.java: -------------------------------------------------------------------------------- 1 | package com.zx.service; 2 | 3 | import com.zx.domain.ProductInfo; 4 | import com.zx.dto.CartDTO; 5 | import org.springframework.data.domain.Page; 6 | import org.springframework.data.domain.Pageable; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 商品 12 | * Created by 97038 on 2017-07-23. 13 | */ 14 | public interface ProductService { 15 | 16 | ProductInfo findOne(String productId); 17 | 18 | /** 19 | * 查询所有上架商品 20 | */ 21 | List findUpAll(); 22 | 23 | /** 24 | * 分页查询所有商品 25 | */ 26 | Page findAll(Pageable pageable); 27 | 28 | ProductInfo save(ProductInfo productInfo); 29 | 30 | /** 31 | * 加库存 32 | */ 33 | void increaseStock(List cartDTOList); 34 | /** 35 | * 减库存 36 | */ 37 | void decreaseStock(List cartDTOList); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/zx/utils/ResultVOUtil.java: -------------------------------------------------------------------------------- 1 | package com.zx.utils; 2 | 3 | import com.zx.vo.ResultVO; 4 | 5 | /** 6 | * 返回对象 工具类 7 | * Created by 97038 on 2017-07-23. 8 | */ 9 | public class ResultVOUtil { 10 | /** 11 | * 返回成功状态,以及数据 12 | */ 13 | public static ResultVO success(Object data) { 14 | ResultVO resultVO = new ResultVO(); 15 | resultVO.setCode(0); 16 | resultVO.setMsg("成功"); 17 | resultVO.setData(data); 18 | return resultVO; 19 | } 20 | 21 | /** 22 | * 返回成功状态,数据为空 23 | */ 24 | public static ResultVO success(){ 25 | return success(null); 26 | } 27 | 28 | /** 29 | * 返回错误状态, 包含错误状态码和错误消息 30 | */ 31 | public static ResultVO error(Integer code, String msg) { 32 | ResultVO resultVO = new ResultVO(); 33 | resultVO.setCode(code); 34 | resultVO.setMsg(msg); 35 | return resultVO; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/zx/converter/OrderMaster2OrderDTOConverter.java: -------------------------------------------------------------------------------- 1 | package com.zx.converter; 2 | 3 | import com.zx.domain.OrderMaster; 4 | import com.zx.dto.OrderDTO; 5 | import org.springframework.beans.BeanUtils; 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * 转换器 12 | * 订单主表 转 订单DTO 13 | */ 14 | public class OrderMaster2OrderDTOConverter { 15 | /** 16 | * 一对一 转换 17 | * @return 18 | */ 19 | public static OrderDTO convert(OrderMaster orderMaster){ 20 | //新建要转成的DTO 21 | OrderDTO orderDTO = new OrderDTO(); 22 | //将原对象的属性复制过去 23 | BeanUtils.copyProperties(orderMaster, orderDTO); 24 | //返回 25 | return orderDTO; 26 | } 27 | 28 | /** 29 | * list 转换 30 | * @param orderMasterList 31 | * @return 32 | */ 33 | public static List convert(List orderMasterList) { 34 | return orderMasterList.stream() 35 | .map(e -> convert(e)).collect(Collectors.toList()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/zx/domain/ProductCategory.java: -------------------------------------------------------------------------------- 1 | package com.zx.domain; 2 | 3 | import lombok.Data; 4 | import org.hibernate.annotations.DynamicUpdate; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.Id; 9 | import java.util.Date; 10 | 11 | /** 12 | * Created by 97038 on 2017-07-22. 13 | * 商品类目 14 | */ 15 | //如果表名和实体类名不是驼峰对应的,可以使用如下注解 16 | //@Table(name = "product_category") 17 | @Entity 18 | //下面这个注解是让Mysql中的timestamp字段能够在更新记录时自动更新 19 | //如果不加,每次更新前要从表中查出原纪录,那么timestamp字段也是原值,然后更新的时候就会更新会原值 20 | @DynamicUpdate 21 | //下面的注解是lombok框架生成实体类getter/setter/toString方法的 22 | @Data 23 | public class ProductCategory { 24 | /** 类目id **/ 25 | @Id 26 | @GeneratedValue 27 | private Integer categoryId; 28 | /** 类目名 **/ 29 | private String categoryName; 30 | /** 类目编号 **/ 31 | private Integer categoryType; 32 | 33 | public ProductCategory(String categoryName, Integer categoryType) { 34 | this.categoryName = categoryName; 35 | this.categoryType = categoryType; 36 | } 37 | 38 | public ProductCategory() { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/zx/controller/WeiXinController.java: -------------------------------------------------------------------------------- 1 | package com.zx.controller; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestParam; 7 | import org.springframework.web.bind.annotation.RestController; 8 | import org.springframework.web.client.RestTemplate; 9 | 10 | /** 11 | * 微信 12 | * 如果自己写的话,是这么写,可以使用github上的微信轮子 13 | */ 14 | @RestController 15 | @RequestMapping("/weixin") 16 | @Slf4j 17 | @Deprecated 18 | public class WeiXinController { 19 | 20 | /** 21 | * 用户同一授权后,进入该回调方法,能从该回调方法中获取用户code 22 | * @param code 23 | */ 24 | @GetMapping("auth") 25 | public void auth(@RequestParam("code")String code) { 26 | log.info("进入auth方法"); 27 | log.info("code={}",code); 28 | String url = "";//TODO 微信中用code获取token的url 29 | //向该url发送请求,获取返回的用户token、openid等信息 30 | RestTemplate restTemplate = new RestTemplate(); 31 | String response = restTemplate.getForObject(url, String.class); 32 | log.info("response={}",response); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/zx/service/impl/CategoryServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.zx.service.impl; 2 | 3 | import com.zx.domain.ProductCategory; 4 | import com.zx.repository.ProductCategoryRepository; 5 | import com.zx.service.CategoryService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Created by 97038 on 2017-07-22. 13 | * 类目 14 | */ 15 | @Service 16 | public class CategoryServiceImpl implements CategoryService { 17 | @Autowired 18 | private ProductCategoryRepository productCategoryRepository; 19 | 20 | @Override 21 | public ProductCategory findOne(Integer categoryId) { 22 | return productCategoryRepository.findOne(categoryId); 23 | } 24 | 25 | @Override 26 | public List findAll() { 27 | return productCategoryRepository.findAll(); 28 | } 29 | 30 | @Override 31 | public List findByCategoryTypeIn(List categoryTypeList) { 32 | return productCategoryRepository.findByCategoryTypeIn(categoryTypeList); 33 | } 34 | 35 | @Override 36 | public ProductCategory save(ProductCategory productCategory) { 37 | return productCategoryRepository.save(productCategory); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/zx/domain/OrderMaster.java: -------------------------------------------------------------------------------- 1 | package com.zx.domain; 2 | 3 | import com.zx.enums.OrderStatusEnum; 4 | import com.zx.enums.PayStatusEnum; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.hibernate.annotations.DynamicUpdate; 9 | 10 | import javax.persistence.Entity; 11 | import javax.persistence.Id; 12 | import java.math.BigDecimal; 13 | import java.util.Date; 14 | import java.util.List; 15 | 16 | /** 17 | * Created by dell on 2017/7/24. 18 | * 订单主表 19 | */ 20 | @Entity 21 | @Data 22 | @NoArgsConstructor 23 | @AllArgsConstructor 24 | @DynamicUpdate 25 | public class OrderMaster { 26 | @Id 27 | private String orderId; 28 | /** 买家姓名*/ 29 | private String buyerName; 30 | /** 买家电话 */ 31 | private String buyerPhone; 32 | /** 买家地址 */ 33 | private String buyerAddress; 34 | /** 买家微信openid */ 35 | private String buyerOpenid; 36 | /** 订单总金额*/ 37 | private BigDecimal orderAmount; 38 | /** 订单状态,默认0,新下单*/ 39 | private Integer orderStatus = OrderStatusEnum.NEW.getCode(); 40 | /** 支付状态,默认0,未支付*/ 41 | private Integer payStatus = PayStatusEnum.WAIT.getCode(); 42 | /** 创建时间 */ 43 | private Date createTime; 44 | /** 更新时间*/ 45 | private Date updateTime; 46 | 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/zx/config/WeChatMpConfig.java: -------------------------------------------------------------------------------- 1 | package com.zx.config; 2 | 3 | import me.chanjar.weixin.mp.api.WxMpConfigStorage; 4 | import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage; 5 | import me.chanjar.weixin.mp.api.WxMpService; 6 | import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * 微信公众号配置 13 | */ 14 | @Component 15 | public class WeChatMpConfig { 16 | /** 17 | * 注入自定义微信配置类 18 | */ 19 | @Autowired 20 | private WeChatAccountConfig accountConfig; 21 | 22 | /** 23 | * 使用该方法生成一个使用了默认配置的WxMpService对象,加入SpringBean 24 | * @return 25 | */ 26 | @Bean 27 | public WxMpService wxMpService() { 28 | WxMpService wxMpService = new WxMpServiceImpl(); 29 | wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); 30 | return wxMpService; 31 | } 32 | 33 | /** 34 | * 生成配置仓库 35 | * @return 36 | */ 37 | @Bean 38 | public WxMpConfigStorage wxMpConfigStorage() { 39 | WxMpInMemoryConfigStorage wxMpConfigStorage = new WxMpInMemoryConfigStorage(); 40 | wxMpConfigStorage.setAppId(accountConfig.getMpAppId()); 41 | wxMpConfigStorage.setSecret(accountConfig.getMpAppSecret()); 42 | return wxMpConfigStorage; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/com/zx/repository/OrderDetailRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.zx.repository; 2 | 3 | import com.zx.domain.OrderDetail; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | 11 | import java.math.BigDecimal; 12 | import java.util.List; 13 | 14 | import static org.junit.Assert.*; 15 | 16 | /** 17 | * 订单详情 18 | */ 19 | @RunWith(SpringRunner.class) 20 | @SpringBootTest 21 | public class OrderDetailRepositoryTest { 22 | 23 | @Autowired 24 | private OrderDetailRepository orderDetailRRepository; 25 | 26 | @Test 27 | public void saveTest() { 28 | OrderDetail orderDetail = new OrderDetail(); 29 | orderDetail.setDetailId("2"); 30 | orderDetail.setOrderId("1"); 31 | orderDetail.setProductIcon("xxx.jpg"); 32 | orderDetail.setProductId("3"); 33 | orderDetail.setProductName("苹果"); 34 | orderDetail.setProductPrice(new BigDecimal("8.9")); 35 | orderDetail.setProductQuantity(999); 36 | 37 | OrderDetail result = orderDetailRRepository.save(orderDetail); 38 | Assert.assertNotNull(result); 39 | 40 | } 41 | 42 | @Test 43 | public void findByOrderId() throws Exception { 44 | List list = orderDetailRRepository.findByOrderId("1"); 45 | Assert.assertNotEquals(0,list.size()); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/main/java/com/zx/service/impl/BuyerServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.zx.service.impl; 2 | 3 | import com.zx.dto.OrderDTO; 4 | import com.zx.enums.ResultEnum; 5 | import com.zx.exception.SellException; 6 | import com.zx.service.BuyerService; 7 | import com.zx.service.OrderService; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | 12 | /** 13 | * 买家 14 | * 类似装饰者模式 15 | * 将OrderService的查询订单详情和取消订单详情进行扩展 16 | * 通过openid判断用户是否是同一用户 17 | */ 18 | @Service 19 | @Slf4j 20 | public class BuyerServiceImpl implements BuyerService{ 21 | @Autowired 22 | private OrderService orderService; 23 | 24 | @Override 25 | public OrderDTO findOrderOne(String openid, String orderId) { 26 | return checkOrderOwner(openid, orderId); 27 | } 28 | 29 | @Override 30 | public OrderDTO cancelOrder(String openid, String orderId) { 31 | OrderDTO orderDTO = checkOrderOwner(openid, orderId); 32 | return orderService.cancel(orderDTO); 33 | } 34 | 35 | private OrderDTO checkOrderOwner(String openid, String orderId) { 36 | //查询 37 | OrderDTO orderDTO = orderService.findOne(orderId); 38 | if (orderDTO == null) 39 | return null; 40 | if (!orderDTO.getBuyerOpenid().equalsIgnoreCase(orderId)) { 41 | log.error("【查询订单】openid不一致.oepnid={},orderDTO={}",openid,orderDTO); 42 | throw new SellException(ResultEnum.ORDER_OWNER_ERROR); 43 | } 44 | return orderDTO; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/zx/converter/OrderForm2OrderDTOConverter.java: -------------------------------------------------------------------------------- 1 | package com.zx.converter; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonSyntaxException; 5 | import com.google.gson.reflect.TypeToken; 6 | import com.zx.domain.OrderDetail; 7 | import com.zx.dto.OrderDTO; 8 | import com.zx.enums.ResultEnum; 9 | import com.zx.exception.SellException; 10 | import com.zx.form.OrderForm; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.beans.BeanUtils; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * 转换 18 | */ 19 | @Slf4j 20 | public class OrderForm2OrderDTOConverter { 21 | /** 22 | * 转换 23 | * @return 24 | */ 25 | public static OrderDTO convert(OrderForm orderForm) { 26 | OrderDTO orderDTO = new OrderDTO(); 27 | //先将一些属性复制 28 | orderDTO.setBuyerOpenid(orderForm.getOpenid()); 29 | orderDTO.setBuyerPhone(orderForm.getPhone()); 30 | orderDTO.setBuyerAddress(orderForm.getAddress()); 31 | orderDTO.setBuyerName(orderForm.getName()); 32 | 33 | /** 34 | * 将items(json串) 转为List格式 35 | */ 36 | List orderDetailList = null; 37 | Gson gson = new Gson(); 38 | try { 39 | orderDetailList = gson.fromJson(orderForm.getItems(), 40 | new TypeToken>(){}.getType()); 41 | } catch (JsonSyntaxException e) { 42 | log.error("【对象转换】错误,String={}",orderForm.getItems()); 43 | throw new SellException(ResultEnum.PARAM_ERROR); 44 | } 45 | orderDTO.setOrderDetailList(orderDetailList); 46 | return orderDTO; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/zx/repository/OrderMasterRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.zx.repository; 2 | 3 | import com.zx.domain.OrderMaster; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.data.domain.Page; 10 | import org.springframework.data.domain.PageRequest; 11 | import org.springframework.data.domain.Pageable; 12 | import org.springframework.test.context.junit4.SpringRunner; 13 | 14 | import java.math.BigDecimal; 15 | 16 | import static org.junit.Assert.*; 17 | 18 | /** 19 | * 订单主表 20 | */ 21 | @RunWith(SpringRunner.class) 22 | @SpringBootTest 23 | public class OrderMasterRepositoryTest { 24 | 25 | @Autowired 26 | private OrderMasterRepository orderMasterRepository; 27 | 28 | @Test 29 | public void findByBuyerOpenid() throws Exception { 30 | PageRequest pageRequest = new PageRequest(0,1); 31 | Page page = orderMasterRepository.findByBuyerOpenid("2", pageRequest); 32 | System.out.println(page.getTotalElements()); 33 | Assert.assertNotEquals(0,page.getTotalElements()); 34 | } 35 | 36 | @Test 37 | public void save(){ 38 | OrderMaster orderMaster = new OrderMaster(); 39 | orderMaster.setBuyerAddress("杭州余杭"); 40 | orderMaster.setBuyerName("王五"); 41 | orderMaster.setBuyerOpenid("2"); 42 | orderMaster.setBuyerPhone("17826824998"); 43 | orderMaster.setOrderAmount(new BigDecimal("34.80")); 44 | orderMaster.setOrderId("3"); 45 | orderMasterRepository.save(orderMaster); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/main/java/com/zx/dto/OrderDTO.java: -------------------------------------------------------------------------------- 1 | package com.zx.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | import com.zx.domain.OrderDetail; 6 | import com.zx.domain.OrderMaster; 7 | import com.zx.enums.OrderStatusEnum; 8 | import com.zx.enums.PayStatusEnum; 9 | import com.zx.utils.serializer.Date2LongSerializer; 10 | import com.zx.utils.serializer.String2TestStringSerializer; 11 | import lombok.AllArgsConstructor; 12 | import lombok.Data; 13 | import lombok.NoArgsConstructor; 14 | 15 | import java.math.BigDecimal; 16 | import java.util.Date; 17 | import java.util.List; 18 | 19 | /** 20 | * 订单传输对象 21 | */ 22 | @Data 23 | @NoArgsConstructor 24 | @AllArgsConstructor 25 | //@JsonInclude(JsonInclude.Include.NON_NULL) 26 | public class OrderDTO { 27 | /** 订单主表*/ 28 | 29 | private String orderId; 30 | /** 买家姓名*/ 31 | @JsonSerialize(using = String2TestStringSerializer.class) 32 | private String buyerName; 33 | /** 买家电话 */ 34 | private String buyerPhone; 35 | /** 买家地址 */ 36 | private String buyerAddress; 37 | /** 买家微信openid */ 38 | private String buyerOpenid; 39 | /** 订单总金额*/ 40 | private BigDecimal orderAmount; 41 | /** 订单状态,默认0,新下单*/ 42 | private Integer orderStatus = OrderStatusEnum.NEW.getCode(); 43 | /** 支付状态,默认0,未支付*/ 44 | private Integer payStatus = PayStatusEnum.WAIT.getCode(); 45 | /** 创建时间 */ 46 | @JsonSerialize(using = Date2LongSerializer.class) 47 | private Date createTime; 48 | /** 更新时间*/ 49 | @JsonSerialize(using = Date2LongSerializer.class) 50 | private Date updateTime; 51 | 52 | /** 订单详情列表*/ 53 | private List orderDetailList; 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/zx/service/impl/CategoryServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package com.zx.service.impl; 2 | 3 | import com.zx.domain.ProductCategory; 4 | import com.zx.service.CategoryService; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | import static org.junit.Assert.*; 16 | 17 | /** 18 | * Created by 97038 on 2017-07-22. 19 | */ 20 | @RunWith(SpringRunner.class) 21 | @SpringBootTest 22 | public class CategoryServiceImplTest { 23 | @Autowired 24 | private CategoryService categoryService; 25 | 26 | @Test 27 | public void findOne() throws Exception { 28 | ProductCategory one = categoryService.findOne(3); 29 | Assert.assertNotNull(one); 30 | } 31 | 32 | @Test 33 | public void findAll() throws Exception { 34 | List list = categoryService.findAll(); 35 | Assert.assertNotEquals(0,list.size()); 36 | 37 | } 38 | 39 | @Test 40 | public void findByCategoryTypeIn() throws Exception { 41 | List list = Arrays.asList(3,6); 42 | List resultList = categoryService.findByCategoryTypeIn(list); 43 | Assert.assertNotEquals(0,resultList.size()); 44 | } 45 | 46 | @Test 47 | public void save() throws Exception { 48 | ProductCategory productCategory = new ProductCategory("ddd", 515); 49 | ProductCategory save = categoryService.save(productCategory); 50 | Assert.assertNotNull(save); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/test/java/com/zx/repository/ProductInfoRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.zx.repository; 2 | 3 | import com.zx.domain.ProductInfo; 4 | import com.zx.enums.ProductStatusEnum; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | import java.math.BigDecimal; 13 | import java.util.List; 14 | 15 | import static org.junit.Assert.*; 16 | 17 | /** 18 | * Created by 97038 on 2017-07-23. 19 | * 20 | * 商品信息 21 | */ 22 | @RunWith(SpringRunner.class) 23 | @SpringBootTest 24 | public class ProductInfoRepositoryTest { 25 | 26 | @Autowired 27 | private ProductInfoRepository repository; 28 | 29 | @Test 30 | public void save(){ 31 | ProductInfo productInfo = new ProductInfo(); 32 | productInfo.setProductId("222"); 33 | productInfo.setProductName("西瓜"); 34 | productInfo.setProductPrice(new BigDecimal(8.1)); 35 | productInfo.setProductStock(99); 36 | productInfo.setProductDescription("大西瓜"); 37 | productInfo.setProductIcon("http://www.ss.jpgs"); 38 | productInfo.setProductStatus(ProductStatusEnum.UP.getCode()); 39 | productInfo.setCategoryType(3); 40 | 41 | repository.save(productInfo); 42 | } 43 | 44 | @Test 45 | public void findByProductStatus() throws Exception { 46 | List list = repository.findByProductStatus(0); 47 | Assert.assertNotEquals(0,list.size()); 48 | 49 | List list2 = repository.findByProductStatus(1); 50 | Assert.assertEquals(0,list2.size()); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/test/java/com/zx/repository/ProductCategoryRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.zx.repository; 2 | 3 | import com.zx.domain.ProductCategory; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | import org.springframework.transaction.annotation.Transactional; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | 16 | /** 17 | * Created by 97038 on 2017-07-22. 18 | * 商品类目dao 测试类 19 | */ 20 | @RunWith(SpringRunner.class) 21 | @SpringBootTest 22 | public class ProductCategoryRepositoryTest { 23 | @Autowired 24 | private ProductCategoryRepository repository; 25 | 26 | @Test 27 | public void findOne(){ 28 | ProductCategory productCategory = repository.findOne(1); 29 | System.out.println(productCategory); 30 | } 31 | 32 | @Test 33 | //下面的注解表示 全部都回滚,让测试数据不保存,只测试成功与否 34 | @Transactional 35 | public void saveTest() { 36 | ProductCategory productCategory = new ProductCategory("dssdfsfsd",9); 37 | ProductCategory save = repository.save(productCategory); 38 | Assert.assertNotNull(save); 39 | } 40 | 41 | @Test 42 | public void updateTest() { 43 | ProductCategory productCategory = repository.findOne(2); 44 | productCategory.setCategoryName("dgsgdg"); 45 | repository.save(productCategory); 46 | } 47 | 48 | @Test 49 | public void findByCategoryTypeInTest() { 50 | List list = Arrays.asList(3,6); 51 | List resultList = repository.findByCategoryTypeIn(list); 52 | Assert.assertNotEquals(0,resultList.size()); 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %d - %-5level - %msg%n 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ERROR 19 | 20 | DENY 21 | 22 | ACCEPT 23 | 24 | 25 | 26 | %msg 27 | 28 | 29 | 30 | 31 | 32 | D://info.%d.log 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ERROR 41 | 42 | 43 | 44 | %msg 45 | 46 | 47 | 48 | 49 | 50 | D://error.%d.log 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/main/java/com/zx/controller/WeChatController.java: -------------------------------------------------------------------------------- 1 | package com.zx.controller; 2 | 3 | import com.zx.enums.ResultEnum; 4 | import com.zx.exception.SellException; 5 | import lombok.extern.slf4j.Slf4j; 6 | import me.chanjar.weixin.common.api.WxConsts; 7 | import me.chanjar.weixin.common.exception.WxErrorException; 8 | import me.chanjar.weixin.mp.api.WxMpService; 9 | import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RequestParam; 15 | 16 | import java.net.URLEncoder; 17 | 18 | /** 19 | * 微信 20 | */ 21 | @Controller 22 | @RequestMapping("/wechat") 23 | @Slf4j 24 | public class WeChatController { 25 | @Autowired 26 | private WxMpService wxMpService; 27 | 28 | @GetMapping("/authorize") 29 | public String authorize(@RequestParam("resultUrl") String resultUrl) { 30 | //1.配置-已经在yml加入自定义参数,并注入bean 31 | //2.调用方法 32 | String url = "/sell/wechat/userInfo"; 33 | //通过该方法返回要重定向到的url, 34 | String redirectUrl = wxMpService.oauth2buildAuthorizationUrl(url, WxConsts.OAUTH2_SCOPE_BASE, URLEncoder.encode(resultUrl)); 35 | log.info("【微信网页授权】获取code,redirectUrl={}",redirectUrl); 36 | //重定向 37 | return "redirect:" + redirectUrl; 38 | } 39 | 40 | @GetMapping("/userInfo") 41 | public String userInfo(@RequestParam("code") String code, 42 | @RequestParam("state") String returnUrl) { 43 | WxMpOAuth2AccessToken wxMpOAuth2AccessToken = new WxMpOAuth2AccessToken(); 44 | try { 45 | wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code); 46 | } catch (WxErrorException e) { 47 | log.error("【微信网页授权】异常={}",e); 48 | throw new SellException(ResultEnum.WECHAT_MP_ERROR.getCode(),e.getError().getErrorMsg()); 49 | } 50 | //获取到openid 51 | String openId = wxMpOAuth2AccessToken.getOpenId(); 52 | return "redirect:" + returnUrl + "?openid=" + openId; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/zx/service/impl/ProductServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package com.zx.service.impl; 2 | 3 | import com.zx.domain.ProductInfo; 4 | import com.zx.enums.ProductStatusEnum; 5 | import com.zx.service.ProductService; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.data.domain.Page; 12 | import org.springframework.data.domain.PageRequest; 13 | import org.springframework.test.context.junit4.SpringRunner; 14 | 15 | import java.math.BigDecimal; 16 | import java.util.List; 17 | 18 | import static org.junit.Assert.*; 19 | 20 | /** 21 | * Created by 97038 on 2017-07-23. 22 | * 商品 23 | */ 24 | @RunWith(SpringRunner.class) 25 | @SpringBootTest 26 | public class ProductServiceImplTest { 27 | @Autowired 28 | private ProductService productService; 29 | 30 | @Test 31 | public void findOne() throws Exception { 32 | ProductInfo productInfo = productService.findOne("222"); 33 | Assert.assertEquals("222",productInfo.getProductId()); 34 | } 35 | 36 | @Test 37 | public void findUpAll() throws Exception { 38 | List list = productService.findUpAll(); 39 | Assert.assertNotEquals(0,list.size()); 40 | } 41 | 42 | @Test 43 | public void findAll() throws Exception { 44 | PageRequest request = new PageRequest(0,5); 45 | Page page = productService.findAll(request); 46 | System.out.println(page.getTotalElements());//总元素 47 | System.out.println(page.getTotalPages());//总页数 48 | Assert.assertNotEquals(0,page.getTotalElements()); 49 | } 50 | 51 | @Test 52 | public void save() throws Exception { 53 | ProductInfo productInfo = new ProductInfo(); 54 | productInfo.setProductId("111"); 55 | productInfo.setProductName("雪碧"); 56 | productInfo.setProductPrice(new BigDecimal(5.6)); 57 | productInfo.setProductStock(15); 58 | productInfo.setProductDescription("冰爽一下"); 59 | productInfo.setProductIcon("http://www.ss.jpgs"); 60 | productInfo.setProductStatus(ProductStatusEnum.UP.getCode()); 61 | productInfo.setCategoryType(4); 62 | ProductInfo save = productService.save(productInfo); 63 | 64 | Assert.assertNotNull(save); 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /src/main/java/com/zx/service/RedisLock.java: -------------------------------------------------------------------------------- 1 | package com.zx.service; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.cache.annotation.Cacheable; 6 | import org.springframework.data.redis.core.StringRedisTemplate; 7 | import org.springframework.data.redis.core.ValueOperations; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.util.StringUtils; 10 | 11 | /** 12 | * redis分布式锁 13 | */ 14 | @Slf4j 15 | @Component 16 | public class RedisLock { 17 | 18 | 19 | //可以直接注入这个对象redisTemplate.opsForValue()获取的对象 20 | @Autowired 21 | ValueOperations valueOperations; 22 | /** 23 | * 加锁 24 | * 如果加锁失败,可以直接抛出异常,提醒用户,访问人数过多 25 | * @param key 26 | * @param value 当前时间 + 超时时间(long),例如 11111 + 100,也就表示了过期时间 27 | * @return 28 | */ 29 | public boolean lock(String key, String value) { 30 | //如果key不存在,则set 31 | //如果可以成功设置的话 32 | if (valueOperations.setIfAbsent(key, value)) 33 | return true; 34 | /** 35 | * 下列代码判断过期时间,解决死锁 36 | */ 37 | //如果被锁了 38 | //获取当前锁的过期时间 39 | String currentValue = valueOperations.get(key); 40 | //与当前时间比较,如果锁过期,设置新锁 41 | if (!StringUtils.isEmpty(currentValue) 42 | && Long.valueOf(currentValue) < System.currentTimeMillis()) { 43 | /** 44 | * 下列代码保证并发情况下的锁的线程安全 45 | * getAndSet()方法是同步的,因为redis是单线程的 46 | * 当多个线程同时调用该代码,只有第一个调用getAndSet()方法的线程才可以获取到 47 | * 与currentValue相同的oldValue,才会返回true,表示获取到锁; 48 | * 49 | * 当然,存在的一个缺陷就是后面的几个线程继续修改该锁的过期时间, 50 | * 导致该锁的过期时间比预期的稍微晚一点 51 | */ 52 | String oldValue = valueOperations.getAndSet(key, value); 53 | if (!StringUtils.isEmpty(oldValue) 54 | && oldValue.equals(currentValue)) 55 | return true; 56 | } 57 | //否则返回false 58 | return false; 59 | } 60 | 61 | /** 62 | * 解锁 63 | * @param key 64 | * @param value 65 | */ 66 | public void unLock(String key, String value) { 67 | try { 68 | /** 69 | * 比较redis中该锁的value和自己携带的该锁value是否相同 70 | * 如果相同,删除该锁 71 | */ 72 | String currentValue = valueOperations.get(key); 73 | if (!StringUtils.isEmpty(currentValue) 74 | && currentValue.equals(value)) { 75 | valueOperations.getOperations().delete(key); 76 | } 77 | } catch (Exception e) { 78 | log.error("【redis分布式锁】解锁异常.e={}",e); 79 | } 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.zx 7 | we_chat_order 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | we_chat_order 12 | SpringBoot微信点餐 -- 慕课网 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.4.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-configuration-processor 42 | true 43 | 44 | 45 | 46 | mysql 47 | mysql-connector-java 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-data-jpa 53 | 54 | 55 | 56 | com.google.code.gson 57 | gson 58 | 59 | 60 | 61 | org.projectlombok 62 | lombok 63 | 64 | 65 | 66 | com.github.binarywang 67 | weixin-java-mp 68 | 2.7.0 69 | 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-starter-freemarker 74 | 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-data-redis 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-maven-plugin 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/main/resources/sql/schema.sql: -------------------------------------------------------------------------------- 1 | -- 商品表 2 | CREATE TABLE product_info( 3 | product_id VARCHAR(32) NOT NULL, 4 | product_name VARCHAR(64) NOT NULL COMMENT '商品名', 5 | product_price DECIMAL(8,2) NOT NULL COMMENT '单价', 6 | product_stock INT NOT NULL COMMENT '库存', 7 | product_description VARCHAR(64) COMMENT '描述', 8 | product_icon VARCHAR(512) COMMENT '小图', 9 | category_type INT NOT NULL COMMENT '类目编号', 10 | product_status TINYINT(1) DEFAULT 0 COMMENT '商品状态, 0正常,1下架', 11 | -- MySQL5.7+ 才可以使用两个日期为current_timestamp类型的 TIMESTAMP 12 | create_time TIMESTAMP NOT NULL DEFAULT current_timestamp COMMENT '创建时间', 13 | update_time TIMESTAMP NOT NULL DEFAULT current_timestamp 14 | ON UPDATE current_timestamp COMMENT '修改时间', 15 | 16 | PRIMARY KEY (product_id) 17 | )COMMENT '商品表'; 18 | 19 | -- 类目表 20 | CREATE TABLE product_category( 21 | category_id INT NOT NULL AUTO_INCREMENT, 22 | category_name VARCHAR(64) NOT NULL COMMENT '类目名字', 23 | category_type INT NOT NULL COMMENT '类目编号', 24 | create_time TIMESTAMP NOT NULL DEFAULT current_timestamp COMMENT '创建时间', 25 | update_time TIMESTAMP NOT NULL DEFAULT current_timestamp 26 | ON UPDATE current_timestamp COMMENT '修改时间', 27 | 28 | PRIMARY KEY (category_id), 29 | UNIQUE KEY uqe_category_type(category_type) 30 | )COMMENT '类目表'; 31 | 32 | -- 订单表 33 | CREATE TABLE order_master( 34 | order_id VARCHAR(32) NOT NULL , 35 | buyer_name VARCHAR(32) NOT NULL COMMENT '买家姓名', 36 | buyer_phone VARCHAR(32) NOT NULL COMMENT '买家电话', 37 | buyer_address VARCHAR(128) NOT NULL COMMENT '买家地址', 38 | buyer_openid VARCHAR(64) NOT NULL COMMENT '买家微信openid', 39 | order_amount DECIMAL(8,2) NOT NULL COMMENT '订单总金额', 40 | order_status TINYINT(3) NOT NULL DEFAULT '0' COMMENT '订单状态,默认0新下单', 41 | pay_status TINYINT(3) NOT NULL DEFAULT '0' COMMENT '支付状态,默认0未支付', 42 | create_time TIMESTAMP NOT NULL DEFAULT current_timestamp COMMENT '创建时间', 43 | update_time TIMESTAMP NOT NULL DEFAULT current_timestamp 44 | ON UPDATE current_timestamp COMMENT '修改时间', 45 | 46 | PRIMARY KEY (order_id), 47 | KEY idx_buyer_openid(buyer_openid) 48 | )COMMENT '订单表'; 49 | 50 | -- 订单详情表 51 | CREATE TABLE order_detail( 52 | detail_id VARCHAR(32) NOT NULL , 53 | order_id VARCHAR(32) NOT NULL COMMENT '订单id', 54 | product_id VARCHAR(32) NOT NULL COMMENT '商品id', 55 | product_name VARCHAR(64) NOT NULL COMMENT '商品名称', 56 | product_price DECIMAL(8,2) NOT NULL COMMENT '商品价格', 57 | product_quantity INT NOT NULL COMMENT '商品数量', 58 | product_icon VARCHAR(512) COMMENT '商品小图', 59 | create_time TIMESTAMP NOT NULL DEFAULT current_timestamp COMMENT '创建时间', 60 | update_time TIMESTAMP NOT NULL DEFAULT current_timestamp 61 | ON UPDATE current_timestamp COMMENT '修改时间', 62 | PRIMARY KEY (detail_id), 63 | KEY idx_order_id(order_id) 64 | )COMMENT '订单详情表'; 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 | -------------------------------------------------------------------------------- /src/main/java/com/zx/controller/BuyerProductController.java: -------------------------------------------------------------------------------- 1 | package com.zx.controller; 2 | 3 | import com.zx.domain.ProductCategory; 4 | import com.zx.domain.ProductInfo; 5 | import com.zx.service.CategoryService; 6 | import com.zx.service.ProductService; 7 | import com.zx.utils.ResultVOUtil; 8 | import com.zx.vo.ProductInfoVO; 9 | import com.zx.vo.ProductVO; 10 | import com.zx.vo.ResultVO; 11 | import org.springframework.beans.BeanUtils; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.List; 20 | import java.util.stream.Collectors; 21 | 22 | /** 23 | * 买家商品控制层 24 | * Created by 97038 on 2017-07-23. 25 | */ 26 | @RestController 27 | @RequestMapping("/buyer/product") 28 | public class BuyerProductController { 29 | 30 | @Autowired 31 | private ProductService productService; 32 | 33 | @Autowired 34 | private CategoryService categoryService; 35 | 36 | @GetMapping("/list") 37 | public ResultVO list() { 38 | //1.查询所有上架商品 39 | List productInfoList = productService.findUpAll(); 40 | //2.查询类目(一次性查询,in) 41 | //提取出productInfoList中的每个元素的id到categoryTypeList 42 | List categoryTypeList = productInfoList.stream().map(e -> e.getCategoryType()).collect(Collectors.toList()); 43 | //查询出所有类目 44 | List productCategoryList = categoryService.findByCategoryTypeIn(categoryTypeList); 45 | 46 | //3.数据拼装 47 | 48 | //返回对象中的data 49 | List productVOList = new ArrayList<>(); 50 | //循环类目,每个类目是一个ProductVO 51 | productCategoryList.forEach(productCategory -> { 52 | //创建VOc 53 | ProductVO productVO = new ProductVO(); 54 | productVO.setCategoryName(productCategory.getCategoryName());//设置name 55 | productVO.setCategoryType(productCategory.getCategoryType());//设置type 56 | //该类目下的所有 productInfoVO 集合 57 | List productInfoVOList = new ArrayList<>(); 58 | //循环商品,取出该类目下所有的商品,并将其转为VO实体类 59 | //这里用了java8的stream写法,不过可能比传统写法要多循环一次 60 | productInfoList.forEach(productInfo -> { 61 | if (productInfo.getCategoryType().equals(productCategory.getCategoryType())) { 62 | //创建VO对象 63 | ProductInfoVO productInfoVO = new ProductInfoVO(); 64 | //将原对象的属性 复制到 vo对象中 65 | BeanUtils.copyProperties(productInfo, productInfoVO); 66 | //将vo对象 添加 到一个list中 67 | productInfoVOList.add(productInfoVO); 68 | } 69 | }); 70 | 71 | productVO.setProductInfoVOList(productInfoVOList); 72 | 73 | //构建好ProductVO后,添加到productVOList 74 | productVOList.add(productVO); 75 | }); 76 | 77 | //总的返回对象 78 | return ResultVOUtil.success(productVOList); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/zx/service/impl/ProductServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.zx.service.impl; 2 | 3 | import com.zx.domain.ProductInfo; 4 | import com.zx.dto.CartDTO; 5 | import com.zx.enums.ProductStatusEnum; 6 | import com.zx.enums.ResultEnum; 7 | import com.zx.exception.SellException; 8 | import com.zx.repository.ProductInfoRepository; 9 | import com.zx.service.ProductService; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.data.domain.Page; 12 | import org.springframework.data.domain.Pageable; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * Created by 97038 on 2017-07-23. 20 | * 商品 21 | */ 22 | @Service 23 | public class ProductServiceImpl implements ProductService { 24 | 25 | 26 | @Autowired 27 | private ProductInfoRepository productInfoRepository; 28 | 29 | @Override 30 | public ProductInfo findOne(String productId) { 31 | return productInfoRepository.findOne(productId); 32 | } 33 | 34 | @Override 35 | public List findUpAll() { 36 | return productInfoRepository.findByProductStatus(ProductStatusEnum.UP.getCode()); 37 | } 38 | 39 | @Override 40 | public Page findAll(Pageable pageable) { 41 | return productInfoRepository.findAll(pageable); 42 | } 43 | 44 | @Override 45 | public ProductInfo save(ProductInfo productInfo) { 46 | return productInfoRepository.save(productInfo); 47 | } 48 | 49 | 50 | /** 51 | * 增加库存 52 | * @param cartDTOList 53 | */ 54 | @Override 55 | @Transactional 56 | public void increaseStock(List cartDTOList) { 57 | //循环 58 | for (CartDTO cartDTO : cartDTOList) { 59 | //查询商品 60 | ProductInfo productInfo = productInfoRepository.findOne(cartDTO.getProductId()); 61 | //如果商品为空,抛出异常 62 | if (productInfo == null) 63 | throw new SellException(ResultEnum.PRODUCT_NOT_EXIST); 64 | //库存 + 要增加的数量 65 | Integer result = productInfo.getProductStock() + cartDTO.getProductQuantity(); 66 | //设置 67 | productInfo.setProductStock(result); 68 | //保存 69 | productInfoRepository.save(productInfo); 70 | } 71 | } 72 | 73 | /** 74 | * 减少库存 75 | * @param cartDTOList 76 | */ 77 | @Override 78 | @Transactional 79 | public void decreaseStock(List cartDTOList) { 80 | //循环 81 | for (CartDTO cartDTO : cartDTOList) { 82 | //查询商品 83 | ProductInfo productInfo = productInfoRepository.findOne(cartDTO.getProductId()); 84 | //如果商品为空,抛出异常 85 | if (productInfo == null) 86 | throw new SellException(ResultEnum.PRODUCT_NOT_EXIST); 87 | //库存 - 要减少的数量 88 | Integer result = productInfo.getProductStock() - cartDTO.getProductQuantity(); 89 | //如果减少后库存小于0,抛出异常 90 | if (result < 0) 91 | throw new SellException(ResultEnum.PRODUCT_STOCK_ERROR); 92 | //如果成功减少,则保存 93 | productInfo.setProductStock(result); 94 | productInfoRepository.save(productInfo); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/com/zx/service/impl/OrderServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package com.zx.service.impl; 2 | 3 | import com.zx.domain.OrderDetail; 4 | import com.zx.dto.OrderDTO; 5 | import com.zx.enums.OrderStatusEnum; 6 | import com.zx.enums.PayStatusEnum; 7 | import com.zx.service.OrderService; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | import org.springframework.data.domain.Page; 15 | import org.springframework.data.domain.PageRequest; 16 | import org.springframework.test.context.junit4.SpringRunner; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | 22 | import static org.junit.Assert.*; 23 | 24 | /** 25 | * 订单服务 26 | */ 27 | @RunWith(SpringRunner.class) 28 | @SpringBootTest 29 | @Slf4j 30 | public class OrderServiceImplTest { 31 | 32 | @Autowired 33 | private OrderService orderService; 34 | 35 | @Test 36 | public void create() throws Exception { 37 | OrderDTO orderDTO = new OrderDTO(); 38 | orderDTO.setBuyerName("郑星"); 39 | orderDTO.setBuyerAddress("杭州"); 40 | orderDTO.setBuyerPhone("17826824998"); 41 | orderDTO.setBuyerOpenid("22"); 42 | 43 | //订单详情 44 | 45 | OrderDetail o1 = new OrderDetail(); 46 | o1.setProductId("1"); 47 | o1.setProductQuantity(8); 48 | 49 | OrderDetail o2 = new OrderDetail(); 50 | o2.setProductId("2"); 51 | o2.setProductQuantity(20); 52 | 53 | List orderDetailList = Arrays.asList(o1, o2); 54 | 55 | orderDTO.setOrderDetailList(orderDetailList); 56 | OrderDTO result = orderService.create(orderDTO); 57 | log.info("【创建订单】result={}",result); 58 | } 59 | 60 | @Test 61 | public void findOne() throws Exception { 62 | OrderDTO orderDTO = orderService.findOne("1500994856061302911"); 63 | log.info("【查询单个订单信息】result:{}",orderDTO); 64 | Assert.assertNotNull(orderDTO); 65 | } 66 | 67 | @Test 68 | public void findList() throws Exception { 69 | Page page = orderService.findList("22", new PageRequest(0, 2)); 70 | Assert.assertNotEquals(0,page.getTotalElements()); 71 | 72 | } 73 | 74 | @Test 75 | public void cancel() throws Exception { 76 | //查询出OrderDTO 77 | OrderDTO orderDTO = orderService.findOne("1500994856061302911"); 78 | OrderDTO result = orderService.cancel(orderDTO); 79 | Assert.assertEquals(OrderStatusEnum.CANCEL.getCode(), result.getOrderStatus()); 80 | } 81 | 82 | @Test 83 | public void finish() throws Exception { 84 | //查询出OrderDTO 85 | OrderDTO orderDTO = orderService.findOne("1500994856061302911"); 86 | OrderDTO result = orderService.finish(orderDTO); 87 | Assert.assertEquals(OrderStatusEnum.FINISHED.getCode(), result.getOrderStatus()); 88 | } 89 | 90 | @Test 91 | public void paid() throws Exception { 92 | //查询出OrderDTO 93 | OrderDTO orderDTO = orderService.findOne("1500994856061302911"); 94 | OrderDTO result = orderService.paid(orderDTO); 95 | Assert.assertEquals(PayStatusEnum.SUCCESS.getCode(), orderDTO.getPayStatus()); 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/main/java/com/zx/controller/BuyerOrderController.java: -------------------------------------------------------------------------------- 1 | package com.zx.controller; 2 | 3 | import com.zx.converter.OrderForm2OrderDTOConverter; 4 | import com.zx.dto.OrderDTO; 5 | import com.zx.enums.ResultEnum; 6 | import com.zx.exception.SellException; 7 | import com.zx.form.OrderForm; 8 | import com.zx.service.BuyerService; 9 | import com.zx.service.OrderService; 10 | import com.zx.utils.ResultVOUtil; 11 | import com.zx.vo.ResultVO; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.data.domain.Page; 15 | import org.springframework.data.domain.PageRequest; 16 | import org.springframework.util.CollectionUtils; 17 | import org.springframework.util.StringUtils; 18 | import org.springframework.validation.BindingResult; 19 | import org.springframework.web.bind.annotation.*; 20 | 21 | import javax.naming.Binding; 22 | import javax.validation.Valid; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | 27 | /** 28 | * 买家订单 29 | */ 30 | @RestController 31 | @RequestMapping("/buyer/order") 32 | @Slf4j 33 | public class BuyerOrderController { 34 | 35 | @Autowired 36 | private OrderService orderService; 37 | 38 | @Autowired 39 | private BuyerService buyerService; 40 | 41 | /** 42 | * 创建订单 43 | * 返回生成的订单id 44 | */ 45 | @PostMapping("/create") 46 | public ResultVO> create(@Valid OrderForm orderForm, BindingResult bindingResult) { 47 | //校验参数是否符合规定 48 | if (bindingResult.hasErrors()) { 49 | log.error("【创建订单】参数不正确,orderForm={}", orderForm); 50 | //抛出校验时定义的默认消息 51 | throw new SellException(ResultEnum.PARAM_ERROR.getCode(), bindingResult.getFieldError().getDefaultMessage()); 52 | } 53 | //将参数转为OrderDTO 54 | OrderDTO orderDTO = OrderForm2OrderDTOConverter.convert(orderForm); 55 | if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) { 56 | log.error("【创建订单】购物车(订单详情)不能为空,orderForm={}", orderForm); 57 | throw new SellException(ResultEnum.ORDER_DETAIL_EMPTY); 58 | } 59 | //创建订单 60 | OrderDTO result = orderService.create(orderDTO); 61 | 62 | //格式转换 63 | Map map = new HashMap<>(); 64 | map.put("orderId", result.getOrderId()); 65 | 66 | return ResultVOUtil.success(map); 67 | 68 | 69 | } 70 | 71 | /** 72 | * 订单列表 73 | */ 74 | @GetMapping("/list") 75 | public ResultVO> list(@RequestParam("openid") String openid, 76 | @RequestParam(value = "page", defaultValue = "0") Integer page, 77 | @RequestParam(value = "size", defaultValue = "10") Integer size) { 78 | //参数校验 79 | if (StringUtils.isEmpty(openid)) { 80 | log.error("【查询订单列表】openid为空"); 81 | throw new SellException(ResultEnum.PARAM_ERROR); 82 | } 83 | Page orderDTOPage = orderService.findList(openid, new PageRequest(page, size)); 84 | return ResultVOUtil.success(orderDTOPage.getContent()); 85 | } 86 | 87 | /** 88 | * 订单详情 89 | */ 90 | @GetMapping("/detail") 91 | public ResultVO detail(@RequestParam("openid") String openid, 92 | @RequestParam("orderId") String orderId){ 93 | 94 | OrderDTO orderDTO = buyerService.findOrderOne(openid, orderId); 95 | return ResultVOUtil.success(orderDTO); 96 | 97 | 98 | } 99 | 100 | /** 101 | * 取消订单 102 | */ 103 | @PostMapping("/cancel") 104 | public ResultVO cancel(@RequestParam("openid") String openid, 105 | @RequestParam("orderId") String orderId) { 106 | buyerService.cancelOrder(openid, orderId); 107 | return ResultVOUtil.success(); 108 | } 109 | 110 | 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/resources/sql/API.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | ###商品列表 4 | 5 | ``` 6 | GET /sell/buyer/product/list 7 | ``` 8 | 9 | 参数 10 | 11 | ``` 12 | 无 13 | ``` 14 | 15 | 返回 16 | 17 | ``` 18 | { 19 | "code": 0, 20 | "msg": "成功", 21 | "data": [ 22 | { 23 | "name": "热榜", 24 | "type": 1, 25 | "foods": [ 26 | { 27 | "id": "123456", 28 | "name": "皮蛋粥", 29 | "price": 1.2, 30 | "description": "好吃的皮蛋粥", 31 | "icon": "http://xxx.com", 32 | } 33 | ] 34 | }, 35 | { 36 | "name": "好吃的", 37 | "type": 2, 38 | "foods": [ 39 | { 40 | "id": "123457", 41 | "name": "慕斯蛋糕", 42 | "price": 10.9, 43 | "description": "美味爽口", 44 | "icon": "http://xxx.com", 45 | } 46 | ] 47 | } 48 | ] 49 | } 50 | ``` 51 | 52 | 53 | ###创建订单 54 | 55 | ``` 56 | POST /sell/buyer/order/create 57 | ``` 58 | 59 | 参数 60 | 61 | ``` 62 | name: "张三" 63 | phone: "18868822111" 64 | address: "慕课网总部" 65 | openid: "ew3euwhd7sjw9diwkq" //用户的微信openid 66 | items: [{ 67 | productId: "1423113435324", 68 | productQuantity: 2 //购买数量 69 | }] 70 | 71 | ``` 72 | 73 | 返回 74 | 75 | ``` 76 | { 77 | "code": 0, 78 | "msg": "成功", 79 | "data": { 80 | "orderId": "147283992738221" 81 | } 82 | } 83 | ``` 84 | 85 | ###订单列表 86 | 87 | ``` 88 | GET /sell/buyer/order/list 89 | ``` 90 | 91 | 参数 92 | 93 | ``` 94 | openid: 18eu2jwk2kse3r42e2e 95 | page: 0 //从第0页开始 96 | size: 10 97 | ``` 98 | 99 | 返回 100 | 101 | ``` 102 | { 103 | "code": 0, 104 | "msg": "成功", 105 | "data": [ 106 | { 107 | "orderId": "161873371171128075", 108 | "buyerName": "张三", 109 | "buyerPhone": "18868877111", 110 | "buyerAddress": "慕课网总部", 111 | "buyerOpenid": "18eu2jwk2kse3r42e2e", 112 | "orderAmount": 0, 113 | "orderStatus": 0, 114 | "payStatus": 0, 115 | "createTime": 1490171219, 116 | "updateTime": 1490171219, 117 | "orderDetailList": null 118 | }, 119 | { 120 | "orderId": "161873371171128076", 121 | "buyerName": "张三", 122 | "buyerPhone": "18868877111", 123 | "buyerAddress": "慕课网总部", 124 | "buyerOpenid": "18eu2jwk2kse3r42e2e", 125 | "orderAmount": 0, 126 | "orderStatus": 0, 127 | "payStatus": 0, 128 | "createTime": 1490171219, 129 | "updateTime": 1490171219, 130 | "orderDetailList": null 131 | }] 132 | } 133 | ``` 134 | 135 | ###查询订单详情 136 | 137 | ``` 138 | GET /sell/buyer/order/detail 139 | ``` 140 | 141 | 参数 142 | 143 | ``` 144 | openid: 18eu2jwk2kse3r42e2e 145 | orderId: 161899085773669363 146 | ``` 147 | 148 | 返回 149 | 150 | ``` 151 | { 152 | "code": 0, 153 | "msg": "成功", 154 | "data": { 155 | "orderId": "161899085773669363", 156 | "buyerName": "李四", 157 | "buyerPhone": "18868877111", 158 | "buyerAddress": "慕课网总部", 159 | "buyerOpenid": "18eu2jwk2kse3r42e2e", 160 | "orderAmount": 18, 161 | "orderStatus": 0, 162 | "payStatus": 0, 163 | "createTime": 1490177352, 164 | "updateTime": 1490177352, 165 | "orderDetailList": [ 166 | { 167 | "detailId": "161899085974995851", 168 | "orderId": "161899085773669363", 169 | "productId": "157875196362360019", 170 | "productName": "招牌奶茶", 171 | "productPrice": 9, 172 | "productQuantity": 2, 173 | "productIcon": "http://xxx.com", 174 | "productImage": "http://xxx.com" 175 | } 176 | ] 177 | } 178 | } 179 | ``` 180 | 181 | ###取消订单 182 | 183 | ``` 184 | POST /sell/buyer/order/cancel 185 | ``` 186 | 187 | 参数 188 | 189 | ``` 190 | openid: 18eu2jwk2kse3r42e2e 191 | orderId: 161899085773669363 192 | ``` 193 | 194 | 返回 195 | 196 | ``` 197 | { 198 | "code": 0, 199 | "msg": "成功", 200 | "data": null 201 | } 202 | ``` 203 | 204 | ###获取openid 205 | 206 | ``` 207 | 重定向到 /sell/wechat/authorize 208 | ``` 209 | 210 | 参数 211 | 212 | ``` 213 | returnUrl: http://xxx.com/abc //【必填】 214 | ``` 215 | 216 | 返回 217 | 218 | ``` 219 | http://xxx.com/abc?openid=oZxSYw5ldcxv6H0EU67GgSXOUrVg 220 | ``` 221 | 222 | 223 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### SpringBoot微信点餐系统 --慕课网 2 | 3 | #### 日志的使用 4 | 一个日志方案,通常是使用slf4j作为日志框架的接口,而日志实现则有多种: 5 | * log4j:已经被作者抛弃,觉得其太烂,无法修改,直接重写出了LogBack 6 | * log4j2:和log4j无关,只是Apache借了个名而已,性能最优秀,但是目前bug有点多 7 | * logBack:log4j作者重写的日志框架,十分好; 8 | 9 | SpringBoot默认采用slf4j+logBack 10 | 11 | * 在SpringBoot中,只需要使用Logger logger = LoggerFactory.getLogger(Xxx.class)(sif4j包)即可获得日志对象; 12 | * yml文件中可以实现日志文件简单的配置: 13 | ~~~ 14 | logging: 15 | pattern: 16 | #控制台日志输出格式 17 | # console: "%d - %msg%n" 18 | #日志配置文件的路径 19 | # config: 20 | #日志文件输出路径,只是路径名 21 | # path: D://log 22 | #日志文件输出路径,包含文件名 23 | # file: D://a.log 24 | #最低输出日志等级 25 | # level: 26 | #如果下写法在level中可以配置某个类的最低输出日志等级 27 | # com.zx.LoggerTest: debug 28 | ~~~ 29 | * 也可以直接在resources文件夹下添加logback-spring.xml文件,配置日志 30 | 31 | #### 实体类getter/setter/toString方法的省略 32 | 该框架并非程序运行时动态添加,而是打包时添加到class文件中的,所以不会对性能产生影响 33 | * 在pom.xml中引入lombok jar(SpringBoot中似乎很多jar都可以不写version): 34 | ~~~ 35 | 36 | org.projectlombok 37 | lombok 38 | 39 | ~~~ 40 | * 然后在IDEA中安装插件lombok,Eclipse去lombok官网下载jar,安装到eclipse中即可 41 | * 然后在实体类上写上注释 42 | ~~~ 43 | @Data :注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 方法 44 | @Setter:注解在属性上;为属性提供 setting 方法 45 | @Getter:注解在属性上;为属性提供 getting 方法 46 | @Log4j :注解在类上;为类提供一个 属性名为log 的 log4j 日志对象 47 | @@Slf4j:注解在类上,为类提供一个 属性名为log的slf4j日志对象 48 | @NoArgsConstructor:注解在类上;为类提供一个无参的构造方法 49 | @AllArgsConstructor:注解在类上;为类提供一个全参的构造方法 50 | ~~~ 51 | 52 | #### BeanUtils工具类的使用 53 | * 可以将A实体类中所有和B实体类中属性名相同的属性的值复制到B,注意,Null值也会被复制过去 54 | * 当含有util.Date属性时,尤其是util.Date为null时,会出错;其子类sql.util是可以被支持的 55 | 56 | #### CollectionUtils类的使用 57 | * 可以判断集合是否为空或者元素为0,以及是否包含某些元素等 58 | 59 | #### timestamp格式在从数据库取出转为date后,是ms(毫秒)格式,如果需要返回的是s(秒),也就是需要除以1000 60 | * 可以构造一个类,继承JsonSerializer类,然后重写serializer()方法,在该方法中将date.getTime()/1000 61 | * 然后在被转换的实体类的对应Date属性上,加上@JsonSerializer(using=类名.class)注解即可 62 | * 这样的作用是SpringMVC把后台实体类转为JSON返回给前端时,对T类型的数据进行特殊的操作 63 | * 详见Date2LongSerializer类和OrderDTO类 64 | 65 | * 自己写了个String2TestStringSerializer类,实现了将返回的String属性末尾都加上了一串字符串 66 | 67 | #### SpringMVC,在要返回成JSON的实体类上+ @JsonInclude(JsonInclude.Include.NON_NULL)注解,不会返回空对象 68 | * 也可以直接在yml文件中作 spring:jackson:default-property-inclusion: non_null的全局配置,不用加注解 69 | * 如果需要返回默认的值,可以直接在实体类中给对应属性赋初始值 70 | 71 | #### 微信开发可以使用https://github.com/wechat-group/weixin-java-tools该项目上的依赖 72 | 73 | #### 枚举类 74 | 如果需要枚举类拥有相同的属性,可以定义一个接口; 75 | 例如一个A接口定义了getCode()和getMessage()方法; 76 | 那么所有实现该接口的枚举类都需要定义code和message属性; 77 | 然后可以写工具类,一个泛型方法,可以对实现该接口的所有枚举,通过code返回对象 78 | 79 | #### 分布式系统:物理架构中不共享主内存,通过网络发送消息合作 80 | * 多节点 81 | * 消息通信 82 | * 不共享内存 83 | * 集群是指同一服务多节点;分布式是不同服务多节点 84 | 85 | #### 分布式session 86 | * 登录时生成一个token,存入redis和cookie 87 | * 登出时删除用户的cookie即可 88 | 89 | * 可以使用spring mvc中的aop来进行登录校验 90 | * 如果失败可以抛出指定异常 91 | * 然后定义SpringMVC的ExceptionHandler,在其中指定处理该异常,跳转到登录页面 92 | * @ResponseStatus注解可以返回给前端指定的HTTP状态码 93 | * 在被@ControllerAdvice注解的异常处理类中,也可以直接返回ModelAndView等;包括@ResponseBody等注解都可以使用 94 | * 所以可以在其他位置抛出对应的AException,然后定义一个AException处理方法,将对应的错误码和错误信息直接返回给前端 95 | 96 | #### Apache ab 压测工具 简单的语句即可,非常方便 97 | 98 | #### Redis实现分布式锁 99 | ~~~ 100 | //可以直接注入这个对象redisTemplate.opsForValue()获取的对象 101 | @Autowired 102 | ValueOperations valueOperations; 103 | /** 104 | * 加锁 105 | * 如果加锁失败,可以直接抛出异常,提醒用户,访问人数过多 106 | * @param key 107 | * @param value 当前时间 + 超时时间(long),例如 11111 + 100,也就表示了过期时间 108 | * @return 109 | */ 110 | public boolean lock(String key, String value) { 111 | //如果key不存在,则set 112 | //如果可以成功设置的话 113 | if (valueOperations.setIfAbsent(key, value)) 114 | return true; 115 | /** 116 | * 下列代码判断过期时间,解决死锁 117 | */ 118 | //如果被锁了 119 | //获取当前锁的过期时间 120 | String currentValue = valueOperations.get(key); 121 | //与当前时间比较,如果锁过期,设置新锁 122 | if (!StringUtils.isEmpty(currentValue) 123 | && Long.valueOf(currentValue) < System.currentTimeMillis()) { 124 | /** 125 | * 下列代码保证并发情况下的锁的线程安全 126 | * getAndSet()方法是同步的,因为redis是单线程的 127 | * 当多个线程同时调用该代码,只有第一个调用getAndSet()方法的线程才可以获取到 128 | * 与currentValue相同的oldValue,才会返回true,表示获取到锁; 129 | * 130 | * 当然,存在的一个缺陷就是后面的几个线程继续修改该锁的过期时间, 131 | * 导致该锁的过期时间比预期的稍微晚一点 132 | */ 133 | String oldValue = valueOperations.getAndSet(key, value); 134 | if (!StringUtils.isEmpty(oldValue) 135 | && oldValue.equals(currentValue)) 136 | return true; 137 | } 138 | //否则返回false 139 | return false; 140 | } 141 | 142 | /** 143 | * 解锁 144 | * @param key 145 | * @param value 146 | */ 147 | public void unLock(String key, String value) { 148 | try { 149 | /** 150 | * 比较redis中该锁的value和自己携带的该锁value是否相同 151 | * 如果相同,删除该锁 152 | */ 153 | String currentValue = valueOperations.get(key); 154 | if (!StringUtils.isEmpty(currentValue) 155 | && currentValue.equals(value)) { 156 | valueOperations.getOperations().delete(key); 157 | } 158 | } catch (Exception e) { 159 | log.error("【redis分布式锁】解锁异常.e={}",e); 160 | } 161 | } 162 | ~~~ 163 | 164 | #### Redis作为缓存 165 | * 命中:从缓存中获取斌返回数据 166 | * 失效:超时 167 | * 更新: 数据修改后,要更新缓存中的数据 168 | 169 | 1. 在启动Application类上增加注解@EnableCaching(无需引入spring-boot-starter-cache依赖) 170 | 2. @Cacheable(value= "aa", key = "123" )注解在方法上(注意,返回值需要实现serializable接口) 171 | * value(等同于 cacheNames)表示缓存到哪个缓存中去 172 | * key就是key名 173 | 3. 其他修改、删除等注解自行百度;还有动态key(根据方法参数生成),condition(缓存条件)等; 174 | 还可以根据返回结果(要缓存的值)的值来判断是否缓存(unLess) 175 | 4. 可以在类上加@CacheConfig(cacheNames= "aa" ),这样,该类中所有方法上的缓存注解就不用加cacheNames了 176 | -------------------------------------------------------------------------------- /src/main/java/com/zx/service/impl/OrderServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.zx.service.impl; 2 | 3 | import com.zx.converter.OrderMaster2OrderDTOConverter; 4 | import com.zx.domain.OrderDetail; 5 | import com.zx.domain.OrderMaster; 6 | import com.zx.domain.ProductInfo; 7 | import com.zx.dto.CartDTO; 8 | import com.zx.dto.OrderDTO; 9 | import com.zx.enums.OrderStatusEnum; 10 | import com.zx.enums.PayStatusEnum; 11 | import com.zx.enums.ResultEnum; 12 | import com.zx.exception.SellException; 13 | import com.zx.repository.OrderDetailRepository; 14 | import com.zx.repository.OrderMasterRepository; 15 | import com.zx.service.OrderService; 16 | import com.zx.service.ProductService; 17 | import com.zx.utils.KeyUtil; 18 | import lombok.extern.slf4j.Slf4j; 19 | import org.springframework.beans.BeanUtils; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.data.domain.Page; 22 | import org.springframework.data.domain.PageImpl; 23 | import org.springframework.data.domain.Pageable; 24 | import org.springframework.stereotype.Service; 25 | import org.springframework.transaction.annotation.Transactional; 26 | import org.springframework.util.CollectionUtils; 27 | 28 | import java.math.BigDecimal; 29 | import java.math.BigInteger; 30 | import java.util.List; 31 | import java.util.stream.Collectors; 32 | 33 | /** 34 | * 订单 35 | */ 36 | @Service 37 | @Slf4j 38 | public class OrderServiceImpl implements OrderService { 39 | 40 | @Autowired 41 | private OrderMasterRepository orderMasterRepository; 42 | @Autowired 43 | private OrderDetailRepository orderDetailRepository; 44 | @Autowired 45 | private ProductService productService; 46 | /** 47 | * 创建 订单 48 | * @param orderDTO 49 | * @return 50 | */ 51 | @Override 52 | @Transactional 53 | public OrderDTO create(OrderDTO orderDTO) { 54 | //生成订单主表id 55 | String orderId = KeyUtil.generateUniqueKey(); 56 | //定义总价对象,默认为0 57 | BigDecimal orderAmount = new BigDecimal(BigInteger.ZERO); 58 | // //减库存需要传入的对象 这样写会把两个两个业务混合在一起,不太好 59 | // List cartDTOList = new ArrayList<>(); 60 | //1.查询商品(数量、价格(为了安全价格不能从前端传入)) 61 | for(OrderDetail orderDetail : orderDTO.getOrderDetailList()){ 62 | //查询商品 63 | ProductInfo productInfo = productService.findOne(orderDetail.getProductId()); 64 | //如果商品不存在,抛出商品不存在异常 65 | if(productInfo == null){ 66 | throw new SellException(ResultEnum.PRODUCT_NOT_EXIST); 67 | } 68 | //2.计算总价,每次递增,加上 查询出的商品价格乘以商品数量 69 | orderAmount = productInfo.getProductPrice().multiply(new BigDecimal(orderDetail.getProductQuantity())).add(orderAmount); 70 | //3.设置好id, 71 | orderDetail.setDetailId(KeyUtil.generateUniqueKey()); 72 | orderDetail.setOrderId(orderId); 73 | //4.将productInfo中的属性值拷贝到orderDetail中 74 | BeanUtils.copyProperties(productInfo,orderDetail); 75 | //5.拼装CartDTO对象 76 | // CartDTO cartDTO = new CartDTO(orderDetail.getProductId(),orderDetail.getProductQuantity()); 77 | // cartDTOList.add(cartDTO); 78 | } 79 | //5.保存所有 订单详情数据 80 | orderDetailRepository.save(orderDTO.getOrderDetailList()); 81 | //6.保存订单主表数据 82 | OrderMaster orderMaster = new OrderMaster(); 83 | //属性拷贝 84 | orderDTO.setOrderAmount(orderAmount); 85 | orderDTO.setOrderId(orderId); 86 | orderDTO.setOrderStatus(OrderStatusEnum.NEW.getCode()); 87 | orderDTO.setPayStatus(PayStatusEnum.WAIT.getCode()); 88 | BeanUtils.copyProperties(orderDTO,orderMaster); 89 | orderMasterRepository.save(orderMaster); 90 | //4.减库存 91 | List cartDTOList = orderDTO.getOrderDetailList().stream().map(e -> 92 | new CartDTO(e.getProductId(), e.getProductQuantity())) 93 | .collect(Collectors.toList()); 94 | productService.decreaseStock(cartDTOList); 95 | 96 | return orderDTO; 97 | } 98 | 99 | /** 100 | * 查询单个订单 101 | * @param orderId 102 | * @return 103 | */ 104 | @Override 105 | public OrderDTO findOne(String orderId) { 106 | //查询主表 107 | OrderMaster orderMaster = orderMasterRepository.findOne(orderId); 108 | //如果为空 109 | if(orderMaster == null) 110 | throw new SellException(ResultEnum.ORDER_NOT_EXIST); 111 | //查询详情集合 112 | List orderDetailList = orderDetailRepository.findByOrderId(orderId); 113 | if(CollectionUtils.isEmpty(orderDetailList)) 114 | throw new SellException(ResultEnum.ORDER_DETAIL_NOT_EXIST); 115 | //创建dto对象 116 | OrderDTO orderDTO = new OrderDTO(); 117 | //将主表信息拷贝到dto对象 118 | BeanUtils.copyProperties(orderMaster,orderDTO); 119 | //将集合放入 120 | orderDTO.setOrderDetailList(orderDetailList); 121 | //返回 122 | return orderDTO; 123 | } 124 | 125 | /** 126 | * 查询某用户订单列表 127 | * @param buyerOpenid 128 | * @param pageable 129 | * @return 130 | */ 131 | @Override 132 | public Page findList(String buyerOpenid, Pageable pageable) { 133 | //查询某个用户的所有订单主表信息 134 | Page orderMasterPage = orderMasterRepository.findByBuyerOpenid(buyerOpenid, pageable); 135 | //将orderMasterList转为DTOList 136 | List orderDTOList = OrderMaster2OrderDTOConverter.convert(orderMasterPage.getContent()); 137 | //新建要返回的page 138 | Page orderDTOPage = new PageImpl(orderDTOList,pageable,orderMasterPage.getTotalElements()); 139 | 140 | return orderDTOPage; 141 | } 142 | 143 | /** 144 | * 取消一个订单 145 | * @param orderDTO 146 | * @return 147 | */ 148 | @Override 149 | @Transactional 150 | public OrderDTO cancel(OrderDTO orderDTO) { 151 | 152 | //判断订单状态,如果不是新订单,无法退款 153 | if(!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())){ 154 | log.error("【取消订单】订单状态不正确,orderId={},orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus()); 155 | throw new SellException(ResultEnum.ORDER_STATUS_ERROR); 156 | } 157 | 158 | //修改订单状态 159 | //先修改,再拷贝到OrderMaster中保存到数据库 160 | orderDTO.setOrderStatus(OrderStatusEnum.CANCEL.getCode()); 161 | OrderMaster orderMaster = new OrderMaster(); 162 | BeanUtils.copyProperties(orderDTO, orderMaster); 163 | OrderMaster updateResult = orderMasterRepository.save(orderMaster); 164 | if (updateResult == null) { 165 | log.error("【取消订单】更新失败,orderMaster={}",orderMaster); 166 | throw new SellException(ResultEnum.ORDER_UPDATE_FAIL); 167 | } 168 | 169 | //返还库存 170 | //如果商品详情为空,抛出异常 171 | if(CollectionUtils.isEmpty(orderDTO.getOrderDetailList())){ 172 | log.error("【取消订单】订单无商品详情,orderDTO={}",orderDTO); 173 | throw new SellException(ResultEnum.ORDER_DETAIL_EMPTY); 174 | } 175 | //转为购物车对象,传入 176 | List cartDTOList = orderDTO.getOrderDetailList() 177 | .stream().map(e -> new CartDTO(e.getProductId(), e.getProductQuantity())) 178 | .collect(Collectors.toList()); 179 | //增加库存 180 | productService.increaseStock(cartDTOList); 181 | 182 | //如果已支付,退款 183 | if (orderDTO.getPayStatus().equals(PayStatusEnum.SUCCESS.getCode())) { 184 | //TODO 185 | } 186 | return orderDTO; 187 | } 188 | 189 | /** 190 | * 完结订单 191 | * @param orderDTO 192 | * @return 193 | */ 194 | @Override 195 | public OrderDTO finish(OrderDTO orderDTO) { 196 | //判断订单状态,是否为新下单 197 | if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) { 198 | log.error("【完结订单】订单状态不正确,orderId={},orderDTO={}",orderDTO.getOrderId(),orderDTO); 199 | throw new SellException(ResultEnum.ORDER_STATUS_ERROR); 200 | } 201 | //修改状态 202 | orderDTO.setOrderStatus(OrderStatusEnum.FINISHED.getCode()); 203 | OrderMaster orderMaster = new OrderMaster(); 204 | BeanUtils.copyProperties(orderDTO,orderMaster); 205 | OrderMaster updateResult = orderMasterRepository.save(orderMaster); 206 | if (updateResult == null) { 207 | log.error("【完结订单】更新失败,orderMaster={}",orderMaster); 208 | throw new SellException(ResultEnum.ORDER_UPDATE_FAIL); 209 | } 210 | return orderDTO; 211 | } 212 | 213 | /** 214 | * 支付订单 215 | * @param orderDTO 216 | * @return 217 | */ 218 | @Override 219 | @Transactional 220 | public OrderDTO paid(OrderDTO orderDTO) { 221 | //判断订单状态 222 | if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) { 223 | log.error("【支付订单】订单状态不正确,orderId={},orderDTO={}",orderDTO.getOrderId(),orderDTO); 224 | throw new SellException(ResultEnum.ORDER_STATUS_ERROR); 225 | } 226 | //判断支付状态 227 | if (!orderDTO.getPayStatus().equals(PayStatusEnum.WAIT.getCode())) { 228 | log.error("【支付订单】支付状态不正确,orderId={},orderDTO={}",orderDTO.getOrderId(),orderDTO); 229 | throw new SellException(ResultEnum.ORDER_PAY_STATUS_ERROR); 230 | } 231 | //修改支付状态 232 | orderDTO.setPayStatus(PayStatusEnum.SUCCESS.getCode()); 233 | OrderMaster orderMaster = new OrderMaster(); 234 | BeanUtils.copyProperties(orderDTO, orderMaster); 235 | OrderMaster updateResult = orderMasterRepository.save(orderMaster); 236 | if (updateResult == null) { 237 | log.error("【支付订单】更新失败,orderMaster={}",orderMaster); 238 | throw new SellException(ResultEnum.ORDER_UPDATE_FAIL); 239 | } 240 | return orderDTO; 241 | } 242 | } 243 | --------------------------------------------------------------------------------