教育大系统后台搭建文档

搭建开始

创建数据库表字段

  1. 在主包下创建数据库表相关包pojo,在包下创建相关类,比如:Category表,即按照表字段写入代码:
    import lombok.Data;
    
    import java.util.Date;
    
    @Data
    public class Category {
    
        private Integer id;
    
        private Integer parentId;
    
        private String name;
    
        private Integer status;
    
        private Integer sortOrder;
    
        private Date createTime;
    
        private Date updateTime;
    }

     其中,lombok配置作用为:省去了写get、set、toString等繁琐操作,配置lombok需要在pom.xml中加入依赖:

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

    加入依赖后,在类名上方添加注解@Data即可 成功配置lombok

  2.  

    连接数据库,需要在application.yml配置文件中写入连接mysql数据库信息。

     

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: root
        url:jdbc:mysql://127.0.0.1:3306/tmallcharacterEncoding=utf8
                                                           &useSSL=false&serverTimezone=UTC
    

    同时,为了方便起见,这里使用mybatis来连接读取数据库,(目前国外用的比较多的是jpA,国内比较流行mybatis)

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.0</version>
            </dependency>

    注意,需要同时加入mysql驱动! 

  3.  

     接着就是读取数据库操作:

     

     第一种方式是使用mybatis自动配置的读取注解,在主包下创建dao包,dao包下创建类CategoryMapper.

    import com.imooc.mall.pojo.Category;
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Select;
    
    public interface CategoryMapper {
    
        @Select("select * from mall_category where id = #{id}")
        Category findById(@Param("id") Integer id);
    
        Category queryById(Integer id);
    }

    注意,这里没有加@mapper注解,(这个注解是告诉mybatis这里需要读取数据库),是因为在主函数中加入了mapper扫描注解@MapperScan(basePackages=""),注意:包名需要写全!

    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    @MapperScan(basePackages = "com.imooc.mall.dao")
    public class MallApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MallApplication.class, args);
        }
    
    }

     这里有时候可能读取不到数据库某些字段的值,可能是因为字段命名不一致:需要在配置文件中设置驼峰命名识别

    mybatis:
      configuration:
        map-underscore-to-camel-case: true

     第二种方式是配置xml文件,在resource文件夹下创建mappers包,并添加CategoryMapper.xml:

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.imooc.mall.dao.CategoryMapper">
        <sql id="Base_Column_List">
            id, parent_id, name, status, sort_order, create_time, update_time
        </sql>
    
        <select id="queryById" resultType="com.imooc.mall.pojo.Category">
            select
            <include refid="Base_Column_List" />
            from mall_category
            where id = #{id}
        </select>
    </mapper>

    配置xml文件需要编译器找到,需要在配置文件application.yml中加入:

    mybatis:
      configuration:
        map-underscore-to-camel-case: true
      mapper-locations: classpath:mappers/*.xml  
  4. 测试连接数据库:

    为了让测试类看起来更整洁,这边设计成一个表一个测试类:

    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class MallApplicationTests {
    }
    import com.imooc.mall.MallApplicationTests;
    import com.imooc.mall.pojo.Category;
    import org.junit.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    
    public class CategoryMapperTest extends MallApplicationTests{
    
    	@Autowired
    	private CategoryMapper categoryMapper;
    
    	@Test
    	public void findById() {
    		Category category = categoryMapper.findById(100001);
    		System.out.println(category.toString());
    	}
    
    	@Test
    	public void queryById() {
    		Category category = categoryMapper.queryById(100001);
    		System.out.println(category.toString());
    	}
    }

    注意:这里有时候可能8080默认端口被占用,可以通过设置配置文件修改默认端口:

    server:
        port: 8088

     如果有时端口不知道为何被占用,可以打开cmd进行进程kill,假设进程为8088,则输入:netstat -ano|findstr  8088查看进程号,再taskkill -f -pid 进程号 杀死进程,重新运行项目即可。

  5. mybatis三剑客  

    5-1. mybatis-generator

    在resource文件夹下创建mybatisConfig.xml文件,并加入如下配置(自己修改数据库连接属性,和表名字等)

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
            PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
            "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    
    <generatorConfiguration>
        <!--        自己修改对应字段-->
        <classPathEntry location="D:\mysql-connector-java-5.1.6.jar" />
    
        <context id="DB2Tables" targetRuntime="MyBatis3">
    
            <!--        不再追加xml内容-->
            <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
    
            <commentGenerator>
                <property name="suppressAllComments" value="true"/>
            </commentGenerator>
    
            <!--        自己修改对应字段-->
            <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                            connectionURL="jdbc:mysql://127.0.0.1:3306/tmall?characterEncoding=utf-8"
                            userId="root"
                            password="root">
            </jdbcConnection>
    
            <javaTypeResolver >
                <property name="forceBigDecimals" value="false" />
            </javaTypeResolver>
    
            <javaModelGenerator targetPackage="com.imooc.mall.pojo" targetProject="src/main/java">
                <property name="enableSubPackages" value="true" />
            </javaModelGenerator>
            
            <!--        自己修改对应字段-->
            <sqlMapGenerator targetPackage="mappers"  targetProject="src/main/resources">
                <property name="enableSubPackages" value="true" />
            </sqlMapGenerator>
    
            <!--        自己修改对应字段-->
            <javaClientGenerator type="XMLMAPPER" targetPackage="com.imooc.mall.dao"  targetProject="src/main/java">
                <property name="enableSubPackages" value="true" />
            </javaClientGenerator>
    
            <!--        自己修改对应字段-->
            <table tableName="mall_order" domainObjectName="Order" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"/>
            <table tableName="mall_order_item" domainObjectName="OrderItem" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"/>
    
        </context>
    </generatorConfiguration>

     然后,打开idea终端,定位到对应文件,运行命令:mvn mybatis-generator:generate

    那么在dao文件夹和mappers文件夹下会新增几个数据库操作文件啦!

    5-2. mybatis plugin

    这个插件比较坑,官网已经deprecated(淘汰)了,如果你硬要本地安装,重启的时候可能会直接闪退

    那么你需要进入C:\Users\xxx\.IntelliJIdea2019.1\config文件夹下找到plugins文件夹,进入删除mybatis文件夹,重启

    才能恢复!所以不可以安装这个插件,可以安装免费版free-mybatis-plugin,效果差不多~

     5-3. mybatis-pagehelper(后续加入)

  6. 用户模块开发

    6-1. 用户注册模块

    首先,实现dao层,在主包下创建service包,创建一个接口(IUserService)

    import com.imooc.mall.pojo.User;
    
    public interface IUserService {
    
        void register(User user);
    }
    

          在service包下创建impl包,创建类UserServiceImpl

    import com.imooc.mall.dao.UserMapper;
    import com.imooc.mall.enums.RoleEnum;
    import com.imooc.mall.pojo.User;
    import com.imooc.mall.service.IUserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.util.DigestUtils;
    
    import java.nio.charset.StandardCharsets;
    
    @Service
    public class UserServiceImpl implements IUserService {
    
        @Autowired
        private UserMapper userMapper;
        /**
         * 注册
         * @param user
         */
        @Override
        public void register(User user) {
    
            //username不能重复
            int countByUsername = userMapper.countByUsername(user.getUsername());
            if (countByUsername > 0) {
                return ResponseVo.error(USERNAME_EXIST);
            }
    
            //email不能重复
            int countByEmail = userMapper.countByEmail(user.getEmail());
            if (countByEmail > 0) {
                return ResponseVo.error(EMAIL_EXIST);
            }
    
            user.setRole(RoleEnum.CUSTOMER.getCode());
            //MD5摘要算法(Spring自带)
            user.setPassword(DigestUtils.md5DigestAsHex(
                    user.getPassword().getBytes(StandardCharsets.UTF_8)
            ));
    
            //写入数据库
            int resultCount = userMapper.insertSelective(user);
            if (resultCount == 0) {
                return ResponseVo.error(ERROR);
            }
        }
    }

             其中,这里调用了两个自定义方法,需要在userMapper.xml文件中添加如下配置:

    <select id="countByUsername" parameterType="java.lang.String" resultType="java.lang.Integer">
        select
        count(1)
        from mall_user
        where username = #{username,jdbcType=VARCHAR}
      </select>
    
      <select id="countByEmail" parameterType="java.lang.String" resultType="java.lang.Integer">
        select
        count(1)
        from mall_user
        where email = #{email,jdbcType=VARCHAR}
      </select>

            同时,记得在dao包下的userMapper接口加入这两个接口方法。

            在主包下创建controller包,包下创建UserController类,

    @RequestMapping("/user")
    @Slf4j
    public class UserController{
        
        @PostMapping("/register")
        public void register(@RequestBody User user){
            log.info("username={}", user.getUsername());
        }
    }

            备注,如要在控制台打印输出sql语句,需要在yml文件下添加配置:

    mybatis:
      configuration:
        map-underscore-to-camel-case: true
        # 控制台日志配置
       
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      mapper-locations: classpath:mappers/*.xml

              这里注意,这里的@RequestBody是接收前端传过来的json格式的报文,如果前端传过来的是form-urlencoded,这边接收需要使用@RequestParam。

  7. 错误状态码使用枚举

    在enum包下创建类ResponseEnum

    import lombok.Getter;
    
    @Getter
    public enum ResponseEnum {
    
    	ERROR(-1, "服务端错误"),
    
    	SUCCESS(0, "成功"),
    
    	PASSWORD_ERROR(1,"密码错误"),
    
    	USERNAME_EXIST(2, "用户名已存在"),
    
    	PARAM_ERROR(3, "参数错误"),
    
    	EMAIL_EXIST(4, "邮箱已存在"),
    
    	NEED_LOGIN(10, "用户未登录, 请先登录"),
    
    	USERNAME_OR_PASSWORD_ERROR(11, "用户名或密码错误"),
    
    	PRODUCT_OFF_SALE_OR_DELETE(12, "商品下架或删除"),
    
    	PRODUCT_NOT_EXIST(13, "商品不存在"),
    
    	PROODUCT_STOCK_ERROR(14, "库存不正确"),
    
    	CART_PRODUCT_NOT_EXIST(15, "购物车里无此商品"),
    
    	DELETE_SHIPPING_FAIL(16, "删除收货地址失败"),
    
    	SHIPPING_NOT_EXIST(17, "收货地址不存在"),
    
    	CART_SELECTED_IS_EMPTY(18, "请选择商品后下单"),
    
    	ORDER_NOT_EXIST(19, "订单不存在"),
    
    	ORDER_STATUS_ERROR(20, "订单状态有误"),
    
    	;
    
    	Integer code;
    
    	String desc;
    
    	ResponseEnum(Integer code, String desc) {
    		this.code = code;
    		this.desc = desc;
    	}
    }

     在主包下创建vo包,创建类ResponseVo,

    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.imooc.mall.enums.ResponseEnum;
    import lombok.Data;
    import org.springframework.validation.BindingResult;
    
    import java.util.Objects;
    
    @Data
    @JsonInclude(value = JsonInclude.Include.NON_NULL)
    public class ResponseVo<T> {
    
    	private Integer status;
    
    	private String msg;
    
    	private T data;
    
    	private ResponseVo(Integer status, String msg) {
    		this.status = status;
    		this.msg = msg;
    	}
    
    	public static <T> ResponseVo<T> success(T data) {
    		return new ResponseVo<>(ResponseEnum.SUCCESS.getCode(), data);
    	}
    
    	public static <T> ResponseVo<T> success() {
    		return new ResponseVo<>(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getDesc());
    	}
    
        public static <T> ResponseVo<T> error(ResponseEnum responseEnum) {
    		return new ResponseVo<>(responseEnum.getCode(), responseEnum.getDesc());
    	}
    }
  8. 表单验证
    import javax.validation.constraints.NotBlank;
    
    @Data
    public class UserRegisterForm {
    
    	//@NotBlank 用于 String 判断空格
    	//@NotEmpty 用于集合
    	//@NotNull
    	@NotBlank
    	private String username;
    
    	@NotBlank
    	private String password;
    
    	@NotBlank
    	private String email;
    }

    注意,务必引入

    import javax.validation.constraints.NotBlank;

             此包

  9. 登录功能模块

    首先进入userMapper.xml修改添加代码

      <select id="selectByUsername" parameterType="java.lang.String" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from mall_user
        where username = #{username,jdbcType=VARCHAR}
      </select>

     随后实现service层中的userService

    @Override
    	public ResponseVo<User> login(String username, String password) {
    		User user = userMapper.selectByUsername(username);
    		if (user == null) {
    			//用户不存在(返回:用户名或密码错误 )
    			return ResponseVo.error(ResponseEnum.USERNAME_OR_PASSWORD_ERROR);
    		}
    
    		if (!user.getPassword().equalsIgnoreCase(
    				DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)))) {
    			//密码错误(返回:用户名或密码错误 )
    			return ResponseVo.error(ResponseEnum.USERNAME_OR_PASSWORD_ERROR);
    		}
    
    		user.setPassword("");
    		return ResponseVo.success(user);
    	}

    随后进入controller层实现/user/login接口和获取用户信息的/user接口

    @PostMapping("/user/login")
    	public ResponseVo<User> login(@Valid @RequestBody UserLoginForm userLoginForm,
    								  HttpSession session) {
    		ResponseVo<User> userResponseVo = userService.login(userLoginForm.getUsername(), userLoginForm.getPassword());
    
    		//设置Session
    		session.setAttribute(MallConst.CURRENT_USER, userResponseVo.getData());
    		log.info("/login sessionId={}", session.getId());
    
    		return userResponseVo;
    	}
    
    	//session保存在内存里,改进版:token+redis
    	@GetMapping("/user")
    	public ResponseVo<User> userInfo(HttpSession session) {
    		log.info("/user sessionId={}", session.getId());
    		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
    		return ResponseVo.success(user);
    	}

    这里需要注意cookie跨域的问题,localhost和127.0.0.1两个domain之间也属于跨域,生成的cookie是不一样的!

  10. 登出功能

    @PostMapping("/user/logout")
    	public ResponseVo logout(HttpSession session) {
    		log.info("/user/logout sessionId={}", session.getId());
    		session.removeAttribute(MallConst.CURRENT_USER);
    		return ResponseVo.success();
    	}

    如果要设置session过期时间,可以进入yml文件修改:

    server:
      servlet:
        session:
          timeout: 120
  11. 统一拦截器

    首先,Interceptor拦截url,AOP拦截是以包为单位拦截,这里使用了Interceptor拦截器。首先创建类:

    @Slf4j
    public class UserLoginInterceptor implements HandlerInterceptor {
    
    	/**
    	 * true 表示继续流程,false表示中断
    	 * @param request
    	 * @param response
    	 * @param handler
    	 * @return
    	 * @throws Exception
    	 */
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    		log.info("preHandle...");
    		User user = (User) request.getSession().getAttribute(MallConst.CURRENT_USER);
    		if (user == null) {
    			log.info("user=null");
    			throw new UserLoginException();
    		}
    		return true;
    	}
    }

     为了实现拦截,需要此类:

    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
    
    	@Override
    	public void addInterceptors(InterceptorRegistry registry) {
    		registry.addInterceptor(new UserLoginInterceptor())
    				.addPathPatterns("/**")
    				.excludePathPatterns("/error", "/user/login", "/user/register", "/categories", "/products", "/products/*");
    	}
    }
    

    这边实现统一异常处理是为了能返回前端json字符串:

    @ControllerAdvice
    public class RuntimeExceptionHandler {
    
    	@ExceptionHandler(RuntimeException.class)
    	@ResponseBody
    //	@ResponseStatus(HttpStatus.FORBIDDEN)
    	public ResponseVo handle(RuntimeException e) {
    		return ResponseVo.error(ERROR, e.getMessage());
    	}
    
    	@ExceptionHandler(UserLoginException.class)
    	@ResponseBody
    	public ResponseVo userLoginHandle() {
    		return ResponseVo.error(ResponseEnum.NEED_LOGIN);
    	}
    
    
    
    
    public class UserLoginException extends RuntimeException {
    }
  12. 商品分类模块

    首先,创建数据库查询语句,进入CategoryMapper.xml

    <select id="selectAll" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from mall_category
        where status = 1
      </select>

    定义接口

    public interface ICategoryService {
    
    	ResponseVo<List<CategoryVo>> selectAll();
    
    	void findSubCategoryId(Integer id, Set<Integer> resultSet);
    }

     定义该接口实现类:(使用了lambda表达式)

    @Service
    public class CategoryServiceImpl implements ICategoryService {
    
    	@Autowired
    	private CategoryMapper categoryMapper;
    
    	/**
    	 * 耗时:http(请求微信api) > 磁盘 > 内存
    	 * mysql(内网+磁盘)
    	 * @return
    	 */
    	@Override
    	public ResponseVo<List<CategoryVo>> selectAll() {
    		List<Category> categories = categoryMapper.selectAll();
    
    		//查出parent_id=0
    //		for (Category category : categories) {
    //			if (category.getParentId().equals(ROOT_PARENT_ID)) {
    //				CategoryVo categoryVo = new CategoryVo();
    //				BeanUtils.copyProperties(category, categoryVo);
    //				categoryVoList.add(categoryVo);
    //			}
    //		}
    
    		//lambda + stream
    		List<CategoryVo> categoryVoList = categories.stream()
    				.filter(e -> e.getParentId().equals(ROOT_PARENT_ID))
    				.map(this::category2CategoryVo)
    				.sorted(Comparator.comparing(CategoryVo::getSortOrder).reversed())
    				.collect(Collectors.toList());
    
    		//查询子目录
    		findSubCategory(categoryVoList, categories);
    
    		return ResponseVo.success(categoryVoList);
    	}
    
    	@Override
    	public void findSubCategoryId(Integer id, Set<Integer> resultSet) {
    		List<Category> categories = categoryMapper.selectAll();
    		findSubCategoryId(id, resultSet, categories);
    	}
    
    	private void findSubCategoryId(Integer id, Set<Integer> resultSet, List<Category> categories) {
    		for (Category category : categories) {
    			if (category.getParentId().equals(id)) {
    				resultSet.add(category.getId());
    				findSubCategoryId(category.getId(), resultSet, categories);
    			}
    		}
    	}
    
    
    	private void findSubCategory(List<CategoryVo> categoryVoList, List<Category> categories) {
    		for (CategoryVo categoryVo : categoryVoList) {
    			List<CategoryVo> subCategoryVoList = new ArrayList<>();
    
    			for (Category category : categories) {
    				//如果查到内容,设置subCategory, 继续往下查
    				if (categoryVo.getId().equals(category.getParentId())) {
    					CategoryVo subCategoryVo = category2CategoryVo(category);
    					subCategoryVoList.add(subCategoryVo);
    				}
    
    				subCategoryVoList.sort(Comparator.comparing(CategoryVo::getSortOrder).reversed());
    				categoryVo.setSubCategories(subCategoryVoList);
    
    				findSubCategory(subCategoryVoList, categories);
    			}
    		}
    	}
    
    	private CategoryVo category2CategoryVo(Category category) {
    		CategoryVo categoryVo = new CategoryVo();
    		BeanUtils.copyProperties(category, categoryVo);
    		return categoryVo;
    	}
    }
    

    实现controller:

    @RestController
    public class CategoryController {
    
    	@Autowired
    	private ICategoryService categoryService;
    
    	@GetMapping("/categories")
    	public ResponseVo<List<CategoryVo>> selectAll() {
    		return categoryService.selectAll();
    	}
    }
  13. 商品列表接口

    在CategoryServiceImpl中实现如下两个功能,根据商品id查询此商品以及此商品的子商品: 

    @Override
    	public void findSubCategoryId(Integer id, Set<Integer> resultSet) {
    		List<Category> categories = categoryMapper.selectAll();
    		findSubCategoryId(id, resultSet, categories);
    	}
    
    	private void findSubCategoryId(Integer id, Set<Integer> resultSet, List<Category> categories) {
    		for (Category category : categories) {
    			if (category.getParentId().equals(id)) {
    				resultSet.add(category.getId());
    				findSubCategoryId(category.getId(), resultSet, categories);
    			}
    		}
    	}

     在productMapper中定义此方法:

    public interface ProductMapper {
        int deleteByPrimaryKey(Integer id);
    
        int insert(Product record);
    
        int insertSelective(Product record);
    
        Product selectByPrimaryKey(Integer id);
    
        int updateByPrimaryKeySelective(Product record);
    
        int updateByPrimaryKey(Product record);
    
        List<Product> selectByCategoryIdSet(@Param("categoryIdSet") Set<Integer> categoryIdSet); //注意,这里需要使用@Param指定你所自定义的参数名,否则,查询数据库会提示,没有该字段类型。
    

     在productMapper.xml中添加:

    <select id="selectByCategoryIdSet" resultMap="BaseResultMap">
      select
      <include refid="Base_Column_List" />
      from mall_product
      where status = 1
      <if test="categoryIdSet.size() > 0">
        and category_id in
        <foreach collection="categoryIdSet" item="item" index="index" open="(" separator="," close=")">
          #{item}
        </foreach>
      </if>
    </select>

     实现productServiceImpl类中的list方法(需要在接口中定义,这里省略):

    @Service
    @Slf4j
    public class ProductServiceImpl implements IProductService {
    
    	@Autowired
    	private ICategoryService categoryService;
    
    	@Autowired
    	private ProductMapper productMapper;
    
    	@Override
    	public ResponseVo<PageInfo> list(Integer categoryId, Integer pageNum, Integer pageSize) {
    		Set<Integer> categoryIdSet = new HashSet<>();
    		if (categoryId != null) {
    			categoryService.findSubCategoryId(categoryId, categoryIdSet);
    			categoryIdSet.add(categoryId);
    		}
    
    		PageHelper.startPage(pageNum, pageSize);
    		List<Product> productList = productMapper.selectByCategoryIdSet(categoryIdSet);
    		List<ProductVo> productVoList = productList.stream()
    				.map(e -> {
    					ProductVo productVo = new ProductVo();
    					BeanUtils.copyProperties(e, productVo);
    					return productVo;
    				})
    				.collect(Collectors.toList());
    
    		PageInfo pageInfo = new PageInfo<>(productList);
    		pageInfo.setList(productVoList);
    		return ResponseVo.success(pageInfo);
    	}
    }

    这里使用到了mybatis的分页插件,在pom.xml中导入依赖,

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.2.13</version>
    </dependency>

    然后调用pageHelper.startPage(pageNum, pageSize)即可实现分页功能,使用pageInfo打印分页信息。将会返回前端如下信息:

    {
        "status": 0,
        "data": {
            "pageNum": 1,
            "pageSize": 10,
            "size": 2,
            "orderBy": null,
            "startRow": 1,
            "endRow": 2,
            "total": 2,
            "pages": 1,
            "list": [
                {
                    "id": 1,
                    "categoryId": 3,
                    "name": "iphone7",
                    "subtitle": "双十一促销",
                    "mainImage": "mainimage.jpg",
                    "status":1,
                    "price": 7199.22
                },
                {
                    "id": 2,
                    "categoryId": 2,
                    "name": "oppo R8",
                    "subtitle": "oppo促销进行中",
                    "mainImage": "mainimage.jpg",
                    "status":1,
                    "price": 2999.11
                }
            ],
            "firstPage": 1,
            "prePage": 0,
            "nextPage": 0,
            "lastPage": 1,
            "isFirstPage": true,
            "isLastPage": true,
            "hasPreviousPage": false,
            "hasNextPage": false,
            "navigatePages": 8,
            "navigatepageNums": [
                1
            ]
        }
    }  

  14.  商品详情功能

     先创建productDetailVo(商品详情信息)类,

    @Data
    public class ProductDetailVo {
    
       private Integer id;
    
       private Integer categoryId;
    
       private String name;
    
       private String subtitle;
    
       private String mainImage;
    
       private String subImages;
    
       private String detail;
    
       private BigDecimal price;
    
       private Integer stock;
    
       private Integer status;
    
       private Date createTime;
    
       private Date updateTime;
    }

     在productServiceImpl类中加入:

        @Override
    	public ResponseVo<ProductDetailVo> detail(Integer productId) {
    		Product product = productMapper.selectByPrimaryKey(productId);
    
    		//只对确定性条件判断
    		if (product.getStatus().equals(OFF_SALE.getCode())
    				|| product.getStatus().equals(DELETE.getCode())) {
    			return ResponseVo.error(PRODUCT_OFF_SALE_OR_DELETE);
    		}
    
    		ProductDetailVo productDetailVo = new ProductDetailVo();
    		BeanUtils.copyProperties(product, productDetailVo);
    		//敏感数据处理
    		productDetailVo.setStock(product.getStock() > 100 ? 100 : product.getStock());
    		return ResponseVo.success(productDetailVo);
    	}

    实现controller类:

    @GetMapping("/products/{productId}")
    	public ResponseVo<ProductDetailVo> detail(@PathVariable Integer productId) {
    		return productService.detail(productId);
    	}
  15. redis安装

    redis安装包(win下) 

    [redis-5.0.5-bin.zip(Win)](https://imcfile.oss-cn-beijing.aliyuncs.com/shizhan/file/liaoshixiong/redis-5.0.5-bin.zip)

    redis可视化软件

    [Another.Redis.Desktop.Manager.1.2.5.exe(Win)](https://imcfile.oss-cn-beijing.aliyuncs.com/shizhan/file/liaoshixiong/Another.Redis.Desktop.Manager.1.2.5.exe)

    [Another.Redis.Desktop.Manager.1.2.5.dmg(Mac)](https://imcfile.oss-cn-beijing.aliyuncs.com/shizhan/file/liaoshixiong/Another.Redis.Desktop.Manager.1.2.5.dmg)

     

    在pom.xml中加入redis依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    在yml配置文件中加入:

    spring:
      redis:
        host: 127.0.0.1
        port: 6379
  16.  购物车模块

    16-1. 增加CartAddForm表单

    @Data
    public class CartAddForm {
    
    	@NotNull
    	private Integer productId;
    
    	private Boolean selected = true;
    }
    

       16-2. 购物车添加商品存入redis数据库:(首先,封装一个list方法,便于返回前端数据)

    @Override
    	public ResponseVo<CartVo> list(Integer uid) {
    		HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
    		String redisKey  = String.format(CART_REDIS_KEY_TEMPLATE, uid);
    		Map<String, String> entries = opsForHash.entries(redisKey);
    
    		boolean selectAll = true;
    		Integer cartTotalQuantity = 0;
    		BigDecimal cartTotalPrice = BigDecimal.ZERO;
    		CartVo cartVo = new CartVo();
    		List<CartProductVo> cartProductVoList = new ArrayList<>();
    		for (Map.Entry<String, String> entry : entries.entrySet()) {
    			Integer productId = Integer.valueOf(entry.getKey());
    			Cart cart = gson.fromJson(entry.getValue(), Cart.class);
    
    			//TODO 需要优化,使用mysql里的in
    			Product product = productMapper.selectByPrimaryKey(productId);
    			if (product != null) {
    				CartProductVo cartProductVo = new CartProductVo(productId,
    						cart.getQuantity(),
    						product.getName(),
    						product.getSubtitle(),
    						product.getMainImage(),
    						product.getPrice(),
    						product.getStatus(),
    						product.getPrice().multiply(BigDecimal.valueOf(cart.getQuantity())),
    						product.getStock(),
    						cart.getProductSelected()
    				);
    				cartProductVoList.add(cartProductVo);
    
    				if (!cart.getProductSelected()) {
    					selectAll = false;
    				}
    
    				//计算总价(只计算选中的)
    				if (cart.getProductSelected()) {
    					cartTotalPrice = cartTotalPrice.add(cartProductVo.getProductTotalPrice());
    				}
    			}
    
    			cartTotalQuantity += cart.getQuantity();
    		}
    
    		//有一个没有选中,就不叫全选
    		cartVo.setSelectedAll(selectAll);
    		cartVo.setCartTotalQuantity(cartTotalQuantity);
    		cartVo.setCartTotalPrice(cartTotalPrice);
    		cartVo.setCartProductVoList(cartProductVoList);
    		return ResponseVo.success(cartVo);
    	}
    @Service
    public class CartServiceImpl implements ICartService {
    
    	private final static String CART_REDIS_KEY_TEMPLATE = "cart_%d";
    
    	@Autowired
    	private ProductMapper productMapper;
    
    	@Autowired
    	private StringRedisTemplate redisTemplate;
    
    	private Gson gson = new Gson();
    
    	@Override
    	public ResponseVo<CartVo> add(Integer uid, CartAddForm form) {
    		Integer quantity = 1;
    
    		Product product = productMapper.selectByPrimaryKey(form.getProductId());
    
    		//商品是否存在
    		if (product == null) {
    			return ResponseVo.error(ResponseEnum.PRODUCT_NOT_EXIST);
    		}
    
    		//商品是否正常在售
    		if (!product.getStatus().equals(ProductStatusEnum.ON_SALE.getCode())) {
    			return ResponseVo.error(ResponseEnum.PRODUCT_OFF_SALE_OR_DELETE);
    		}
    
    		//商品库存是否充足
    		if (product.getStock() <= 0) {
    			return ResponseVo.error(ResponseEnum.PROODUCT_STOCK_ERROR);
    		}
    
    		//写入到redis
    		//key: cart_1
    		HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
    		String redisKey  = String.format(CART_REDIS_KEY_TEMPLATE, uid);
    
    		Cart cart;
    		String value = opsForHash.get(redisKey, String.valueOf(product.getId()));
    		if (StringUtils.isEmpty(value)) {
    			//没有该商品, 新增
    			cart = new Cart(product.getId(), quantity, form.getSelected());
    		}else {
    			//已经有了,数量+1
    			cart = gson.fromJson(value, Cart.class);
    			cart.setQuantity(cart.getQuantity() + quantity);
    		}
    
    		opsForHash.put(redisKey,
    				String.valueOf(product.getId()),
    				gson.toJson(cart));
    
    		return list(uid);
    	}
    }

        16-3. 更新购物车

    首先创建类CartUpdateForm

    @Data
    public class CartUpdateForm {
    
    	private Integer quantity;
    
    	private Boolean selected;
    }
    

      在CartServiceImpl中添加类方法update

    @Override
    	public ResponseVo<CartVo> update(Integer uid, Integer productId, CartUpdateForm form) {
    		HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
    		String redisKey  = String.format(CART_REDIS_KEY_TEMPLATE, uid);
    
    		String value = opsForHash.get(redisKey, String.valueOf(productId));
    		if (StringUtils.isEmpty(value)) {
    			//没有该商品, 报错
    			return ResponseVo.error(ResponseEnum.CART_PRODUCT_NOT_EXIST);
    		}
    
    		//已经有了,修改内容
    		Cart cart = gson.fromJson(value, Cart.class);
    		if (form.getQuantity() != null
    				&& form.getQuantity() >= 0) {
    			cart.setQuantity(form.getQuantity());
    		}
    		if (form.getSelected() != null) {
    			cart.setProductSelected(form.getSelected());
    		}
    
    		opsForHash.put(redisKey, String.valueOf(productId), gson.toJson(cart));
    		return list(uid);
    	}

    16-4. 购物车删除商品

    	@Override
    	public ResponseVo<CartVo> delete(Integer uid, Integer productId) {
    		HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
    		String redisKey  = String.format(CART_REDIS_KEY_TEMPLATE, uid);
    
    		String value = opsForHash.get(redisKey, String.valueOf(productId));
    		if (StringUtils.isEmpty(value)) {
    			//没有该商品, 报错
    			return ResponseVo.error(ResponseEnum.CART_PRODUCT_NOT_EXIST);
    		}
    
    		opsForHash.delete(redisKey, String.valueOf(productId));
    		return list(uid);
    	}
    

    16-5. 购物车中商品的全选/全不选/数量总和:

    @Override
    	public ResponseVo<CartVo> selectAll(Integer uid) {
    		HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
    		String redisKey  = String.format(CART_REDIS_KEY_TEMPLATE, uid);
    
    		for (Cart cart : listForCart(uid)) {
    			cart.setProductSelected(true);
    			opsForHash.put(redisKey,
    					String.valueOf(cart.getProductId()),
    					gson.toJson(cart));
    		}
    
    		return list(uid);
    	}
    
    	@Override
    	public ResponseVo<CartVo> unSelectAll(Integer uid) {
    		HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
    		String redisKey  = String.format(CART_REDIS_KEY_TEMPLATE, uid);
    
    		for (Cart cart : listForCart(uid)) {
    			cart.setProductSelected(false);
    			opsForHash.put(redisKey,
    					String.valueOf(cart.getProductId()),
    					gson.toJson(cart));
    		}
    
    		return list(uid);
    	}
    
    	@Override
    	public ResponseVo<Integer> sum(Integer uid) {
    		Integer sum = listForCart(uid).stream()
    				.map(Cart::getQuantity)
    				.reduce(0, Integer::sum);
    		return ResponseVo.success(sum);
    	}
    
    	public List<Cart> listForCart(Integer uid) {
    		HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
    		String redisKey  = String.format(CART_REDIS_KEY_TEMPLATE, uid);
    		Map<String, String> entries = opsForHash.entries(redisKey);
    
    		List<Cart> cartList = new ArrayList<>();
    		for (Map.Entry<String, String> entry : entries.entrySet()) {
    			cartList.add(gson.fromJson(entry.getValue(), Cart.class));
    		}
    
    		return cartList;
    	}
    

     其中,uid是指某个购物车的id。    

    16-6. controller层实现:

    @RestController
    public class CartController {
    
    	@Autowired
    	private ICartService cartService;
    
    	@GetMapping("/carts")
    	public ResponseVo<CartVo> list(HttpSession session) {
    		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
    		return cartService.list(user.getId());
    	}
    
    	@PostMapping("/carts")
    	public ResponseVo<CartVo> add(@Valid @RequestBody CartAddForm cartAddForm,
    								  HttpSession session) {
    		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
    		return cartService.add(user.getId(), cartAddForm);
    	}
    
    	@PutMapping("/carts/{productId}")
    	public ResponseVo<CartVo> update(@PathVariable Integer productId,
    									 @Valid @RequestBody CartUpdateForm form,
    									 HttpSession session) {
    		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
    		return cartService.update(user.getId(), productId, form);
    	}
    
    	@DeleteMapping("/carts/{productId}")
    	public ResponseVo<CartVo> delete(@PathVariable Integer productId,
    									 HttpSession session) {
    		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
    		return cartService.delete(user.getId(), productId);
    	}
    
    	@PutMapping("/carts/selectAll")
    	public ResponseVo<CartVo> selectAll(HttpSession session) {
    		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
    		return cartService.selectAll(user.getId());
    	}
    
    	@PutMapping("/carts/unSelectAll")
    	public ResponseVo<CartVo> unSelectAll(HttpSession session) {
    		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
    		return cartService.unSelectAll(user.getId());
    	}
    
    	@GetMapping("/carts/products/sum")
    	public ResponseVo<Integer> sum(HttpSession session) {
    		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
    		return cartService.sum(user.getId());
    	}
    }
    

     

  17.  收货地址模块
    @Service
    public class ShippingServiceImpl implements IShippingService {
    
    	@Autowired
    	private ShippingMapper shippingMapper;
    
    	@Override
    	public ResponseVo<Map<String, Integer>> add(Integer uid, ShippingForm form) {
    		Shipping shipping = new Shipping();
    		BeanUtils.copyProperties(form, shipping);
    		shipping.setUserId(uid);
    		int row = shippingMapper.insertSelective(shipping);
    		if (row == 0) {
    			return ResponseVo.error(ResponseEnum.ERROR);
    		}
    
    		Map<String, Integer> map = new HashMap<>();
    		map.put("shippingId", shipping.getId());
    
    		return ResponseVo.success(map);
    	}
    
    	@Override
    	public ResponseVo delete(Integer uid, Integer shippingId) {
    		int row = shippingMapper.deleteByIdAndUid(uid, shippingId);
    		if (row == 0) {
    			return ResponseVo.error(ResponseEnum.DELETE_SHIPPING_FAIL);
    		}
    
    		return ResponseVo.success();
    	}
    
    	@Override
    	public ResponseVo update(Integer uid, Integer shippingId, ShippingForm form) {
    		Shipping shipping = new Shipping();
    		BeanUtils.copyProperties(form, shipping);
    		shipping.setUserId(uid);
    		shipping.setId(shippingId);
    		int row = shippingMapper.updateByPrimaryKeySelective(shipping);
    		if (row == 0) {
    			return ResponseVo.error(ResponseEnum.ERROR);
    		}
    		return ResponseVo.success();
    	}
    
    	@Override
    	public ResponseVo<PageInfo> list(Integer uid, Integer pageNum, Integer pageSize) {
    		PageHelper.startPage(pageNum, pageSize);
    		List<Shipping> shippings = shippingMapper.selectByUid(uid);
    		PageInfo pageInfo = new PageInfo(shippings);
    		return ResponseVo.success(pageInfo);
    	}
    }

     

  18. 订单模块开发

    定义一个付款类型的枚举:

     

    import lombok.Getter;
    
    @Getter
    public enum PaymentTypeEnum {
    
    	PAY_ONLINE(1),
    	;
    
    	Integer code;
    
    	PaymentTypeEnum(Integer code) {
    		this.code = code;
    	}
    }

    订单状态的枚举:

    @Getter
    public enum OrderStatusEnum {
    
    	CANCELED(0, "已取消"),
    
    	NO_PAY(10, "未付款"),
    
    	PAID(20, "已付款"),
    
    	SHIPPED(40, "已发货"),
    
    	TRADE_SUCCESS(50, "交易成功"),
    
    	TRADE_CLOSE(60, "交易关闭"),
    	;
    
    	Integer code;
    
    	String desc;
    
    	OrderStatusEnum(Integer code, String desc) {
    		this.code = code;
    		this.desc = desc;
    	}
    }
    

    注意,@Transactional事务是数据库支持的,而不是springboot支持的,如果使用的是myIsam引擎,并不能实现事务功能!回滚并不是数据库写入或者修改失败才会回滚,当标注了@transactional代码段出现runtimeException异常时,程序也会回滚!

先看下需要传给前端的数据 :

 {
    "status": 0,
    "data": {
        "orderNo": 1291136461000,
        "payment": 2999.11,
        "paymentType": 1,
        "postage": 0,
        "status": 10,
        "paymentTime": null,
        "sendTime": null,
        "endTime": null,
        "closeTime": null,
        "createTime": 1291136461000,
        "orderItemVoList": [
            {
                "orderNo": 1291136461000,
                "productId": 2,
                "productName": "oppo R8",
                "productImage": "mainimage.jpg",
                "currentUnitPrice": 2999.11,
                "quantity": 1,
                "totalPrice": 2999.11,
                "createTime": null
            }
        ],
        "shippingId": 5,
        "shippingVo": {
                "id": 4,
                "userId": 13,
                "receiverName": "廖师兄",
                "receiverPhone": "010",
                "receiverMobile": "18688888888",
                "receiverProvince": "北京",
                "receiverCity": "北京市",
                "receiverDistrict": "海淀区",
                "receiverAddress": "中关村",
                "receiverZip": "100000",
                "createTime": 1485066385000,
                "updateTime": 1485066385000
            }
    }
}

创建订单方法:

@Autowired
	private ShippingMapper shippingMapper;

	@Autowired
	private ICartService cartService;

	@Autowired
	private ProductMapper productMapper;

	@Autowired
	private OrderMapper orderMapper;

	@Autowired
	private OrderItemMapper orderItemMapper;

	@Override
	@Transactional
	public ResponseVo<OrderVo> create(Integer uid, Integer shippingId) {
		//收货地址校验(总之要查出来的)
		Shipping shipping = shippingMapper.selectByUidAndShippingId(uid, shippingId);
		if (shipping == null) {
			return ResponseVo.error(ResponseEnum.SHIPPING_NOT_EXIST);
		}

		//获取购物车,校验(是否有商品、库存)
		List<Cart> cartList = cartService.listForCart(uid).stream()
				.filter(Cart::getProductSelected)
				.collect(Collectors.toList());
		if (CollectionUtils.isEmpty(cartList)) {
			return ResponseVo.error(ResponseEnum.CART_SELECTED_IS_EMPTY);
		}

		//获取cartList里的productIds
		Set<Integer> productIdSet = cartList.stream()
				.map(Cart::getProductId)
				.collect(Collectors.toSet());
		List<Product> productList = productMapper.selectByProductIdSet(productIdSet);
		Map<Integer, Product> map  = productList.stream()
				.collect(Collectors.toMap(Product::getId, product -> product));

		List<OrderItem> orderItemList = new ArrayList<>();
		Long orderNo = generateOrderNo();
		for (Cart cart : cartList) {
			//根据productId查数据库
			Product product = map.get(cart.getProductId());
			//是否有商品
			if (product == null) {
				return ResponseVo.error(ResponseEnum.PRODUCT_NOT_EXIST,
						"商品不存在. productId = " + cart.getProductId());
			}
			//商品上下架状态
			if (!ProductStatusEnum.ON_SALE.getCode().equals(product.getStatus())) {
				return ResponseVo.error(ResponseEnum.PRODUCT_OFF_SALE_OR_DELETE,
						"商品不是在售状态. " + product.getName());
			}

			//库存是否充足
			if (product.getStock() < cart.getQuantity()) {
				return ResponseVo.error(ResponseEnum.PROODUCT_STOCK_ERROR,
						"库存不正确. " + product.getName());
			}

			OrderItem orderItem = buildOrderItem(uid, orderNo, cart.getQuantity(), product);
			orderItemList.add(orderItem);

			//减库存
			product.setStock(product.getStock() - cart.getQuantity());
			int row = productMapper.updateByPrimaryKeySelective(product);
			if (row <= 0) {
				return ResponseVo.error(ResponseEnum.ERROR);
			}
		}

		//计算总价,只计算选中的商品
		//生成订单,入库:order和order_item,事务
		Order order = buildOrder(uid, orderNo, shippingId, orderItemList);

		int rowForOrder = orderMapper.insertSelective(order);
		if (rowForOrder <= 0) {
			return ResponseVo.error(ResponseEnum.ERROR);
		}

		int rowForOrderItem = orderItemMapper.batchInsert(orderItemList);
		if (rowForOrderItem <= 0) {
			return ResponseVo.error(ResponseEnum.ERROR);
		}

		//更新购物车(选中的商品)
		//Redis有事务(打包命令),不能回滚
		for (Cart cart : cartList) {
			cartService.delete(uid, cart.getProductId());
		}

		//构造orderVo
		OrderVo orderVo = buildOrderVo(order, orderItemList, shipping);
		return ResponseVo.success(orderVo);
	}

展示用户订单列表:(在OrderMapper.xml中增加selectByUid方法)

<select id="selectByUid" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from mall_order
    where user_id = #{uid,jdbcType=INTEGER}
  </select>

在orderItemMapper接口中增加:

List<OrderItem> selectByOrderNoSet(@Param("orderNoSet") Set orderNoSet);

 在xml文件中实现sql语句:

<select id="selectByOrderNoSet" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from mall_order_item
    <where>
      <if test="orderNoSet.size() > 0">
        order_no in
        <foreach collection="orderNoSet" item="item" index="index" open="(" separator="," close=")">
          #{item}
        </foreach>
      </if>
    </where>
  </select>

在shippingMapper接口中添加:

List<Shipping> selectByIdSet(@Param("idSet") Set idSet);

 在xml文件中实现:

<select id="selectByIdSet" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from mall_shipping
    <where>
      <if test="idSet.size() > 0">
        id in
        <foreach collection="idSet" item="item" index="index" open="(" separator="," close=")">
          #{item}
        </foreach>
      </if>
    </where>
  </select>

其中,比较常用的一个list转map的语法:

Map<Integer, Product> map  = productList.stream()
      .collect(Collectors.toMap(Product::getId, product -> product));

Map<Long, List<OrderItem>> orderItemMap = orderItemList.stream()
                                         .collect(Collectors.groupingBy(OrderItem::getOrderNo));

 展示某用户所有订单方法:

@Override
	public ResponseVo<PageInfo> list(Integer uid, Integer pageNum, Integer pageSize) {
		PageHelper.startPage(pageNum, pageSize);
		List<Order> orderList = orderMapper.selectByUid(uid);

		Set<Long> orderNoSet = orderList.stream()
				.map(Order::getOrderNo)
				.collect(Collectors.toSet());
		List<OrderItem> orderItemList = orderItemMapper.selectByOrderNoSet(orderNoSet);
		Map<Long, List<OrderItem>> orderItemMap = orderItemList.stream()
				.collect(Collectors.groupingBy(OrderItem::getOrderNo));

		Set<Integer> shippingIdSet = orderList.stream()
				.map(Order::getShippingId)
				.collect(Collectors.toSet());
		List<Shipping> shippingList = shippingMapper.selectByIdSet(shippingIdSet);
		Map<Integer, Shipping> shippingMap = shippingList.stream()
				.collect(Collectors.toMap(Shipping::getId, shipping -> shipping));

		List<OrderVo> orderVoList = new ArrayList<>();
		for (Order order : orderList) {
			OrderVo orderVo = buildOrderVo(order,
					orderItemMap.get(order.getOrderNo()),
					shippingMap.get(order.getShippingId()));
			orderVoList.add(orderVo);
		}
		PageInfo pageInfo = new PageInfo<>(orderList);
		pageInfo.setList(orderVoList);

		return ResponseVo.success(pageInfo);
	}

订单详情模块:

 在orderMapper.xml中加入如下方法实现语句:

<select id="selectByOrderNo" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from mall_order
    where order_no = #{orderNo,jdbcType=INTEGER}
  </select>

在 OrderServiceImpl中实现该详情方法:

@Override
	public ResponseVo<OrderVo> detail(Integer uid, Long orderNo) {
		Order order = orderMapper.selectByOrderNo(orderNo);
		if (order == null || !order.getUserId().equals(uid)) {
			return ResponseVo.error(ResponseEnum.ORDER_NOT_EXIST);
		}
		Set<Long> orderNoSet = new HashSet<>();
		orderNoSet.add(order.getOrderNo());
		List<OrderItem> orderItemList = orderItemMapper.selectByOrderNoSet(orderNoSet);

		Shipping shipping = shippingMapper.selectByPrimaryKey(order.getShippingId());

		OrderVo orderVo = buildOrderVo(order, orderItemList, shipping);
		return ResponseVo.success(orderVo);
	}

订单取消模块:

@Override
	public ResponseVo cancel(Integer uid, Long orderNo) {
		Order order = orderMapper.selectByOrderNo(orderNo);
		if (order == null || !order.getUserId().equals(uid)) {
			return ResponseVo.error(ResponseEnum.ORDER_NOT_EXIST);
		}
		//只有[未付款]订单可以取消,看自己公司业务
		if (!order.getStatus().equals(OrderStatusEnum.NO_PAY.getCode())) {
			return ResponseVo.error(ResponseEnum.ORDER_STATUS_ERROR);
		}

		order.setStatus(OrderStatusEnum.CANCELED.getCode());
		order.setCloseTime(new Date());
		int row = orderMapper.updateByPrimaryKeySelective(order);
		if (row <= 0) {
			return ResponseVo.error(ResponseEnum.ERROR);
		}

		return ResponseVo.success();
	}

orderController的实现:

@RestController
public class OrderController {

	@Autowired
	private IOrderService orderService;

	@PostMapping("/orders")
	public ResponseVo<OrderVo> create(@Valid @RequestBody OrderCreateForm form,
									  HttpSession session) {
		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
		return orderService.create(user.getId(), form.getShippingId());
	}

	@GetMapping("/orders")
	public ResponseVo<PageInfo> list(@RequestParam Integer pageNum,
									 @RequestParam Integer pageSize,
									 HttpSession session) {
		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
		return orderService.list(user.getId(), pageNum, pageSize);
	}

	@GetMapping("/orders/{orderNo}")
	public ResponseVo<OrderVo> detail(@PathVariable Long orderNo,
									  HttpSession session) {
		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
		return orderService.detail(user.getId(), orderNo);
	}

	@PutMapping("/orders/{orderNo}")
	public ResponseVo cancel(@PathVariable Long orderNo,
							 HttpSession session) {
		User user = (User) session.getAttribute(MallConst.CURRENT_USER);
		return orderService.cancel(user.getId(), orderNo);
	}
}
  1.  RabbitMq使用入项目

首先进入虚拟机安装docker,遇到yum update错误,错误提示为:"Cannot find a valid baseurl for repo"

进入vi /etc/resolv.conf加入nameserver 8.8.8.8 nameserver 8.8.4.4 保存退出,重新yum update即可

安装docker出现: No package docker available  执行

sudo yum install epel-release

随后执行,yum install -y docker-io, 报错: no docker-io package found. 那么我们用镜像源下载:

直接用下载源安装,执行命令:yum install https://get.docker.com/rpm/1.7.1/centos-6/RPMS/x86_64/docker-engine-1.7.1-1.el6.x86_64.rpm

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_37539225

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值