E商城(后端)

1 项目准备

1.1 原型

1.1.1 后台管理

image-20220722173318502

1.1.2 易购商城

1.2 功能结构

1.2.1 易购商城

  • 首页
  • 专题列表、专题详情
  • 分类列表、分类详情
  • 品牌列表、品牌详情
  • 新品首发、人气推荐
  • 优惠券列表、优惠券选择
  • 团购
  • 搜索
  • 商品详情、商品评价、商品分享
  • 购物车
  • 下单
  • 订单列表、订单详情、订单售后
  • 地址、收藏、足迹、意见反馈
  • 客服

1.2.2 后台管理

  • 首页

  • 会员管理

  • 商城管理
  • 商品管理
  • 推广管理
  • 系统管理
  • 配置管理
  • 统计报表

1.3 数据库设计

  • 新建easy_mall数据库
  • 运行sql文件
    • 先执行easymall_table.sql
    • 再执行easymall_data.sql

1.4 项目搭建

1.4.1 maven配置

image-20220722162123228

1.4.2 SpringBoot项目创建

  • 新建Spring initializer项目,选择SDK

    image-20220722162501597

  • 输入maven相关信息

    image-20220722162913180

  • 选择springboot版本和依赖

    image-20220722163104891

  • 最后输入工程名和工程存储目录,点击“Finish”完成。

1.4.3 SpringBoot工程分析

1.4.3.1 简介

SpringBoot框架是一个快速搭建Spring项目的脚手架.

  • starter启动器依赖
  • 自动配置: 自动配置整合的其他框架或者第三方库

1.4.3.2 Maven pom.xml

maven是一个java的构建工具.

  • 项目构建

  • 依赖管理: 自动管理项目需要依赖的其他java包

    在pom.xml中可以添加依赖:

    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.6.10</version>
          <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    

1.4.3.3 启动类

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

1.4.3.4 配置文件

application.properties

2 接口开发基本步骤

2.1 开发第一个后端API接口

开发后端API接口可以选择Servlet,也可以直接使用SpringMVC框架.

2.1.1 SpringMVC框架

SpringMVC框架是一个web层的MVC框架,可以用来开发Web API接口.

  • 第一步: 导入SpringMVC框架的依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
  • 第二步:开发API接口

    • 创建一个Controller控制器类: 控制器类中就包含多个API接口

      Controller类是一个spring bean组件:一定要放在main主类所在的包下.

      @RestController//标注这个类是一个Controller控制器
      public class AdminIndexController {
      
      }
      
    • 在Controller类中添加控制器方法,每个控制器方法就是一个API接口

        @RequestMapping("/admin/user/count")//标注这个方法是一个API接口方法,并指定访问路径
          public Object selectAdminUserCount(){
              return null;
          }
      
      • @RequestMapping

        这个注解可以自己指定支持的请求方式

    • @GetMapping

这个注解只支持Get请求方式

  • @PostMapping

    这个注解只支持Post请求方式

  • @PutMapping

    这个注解只支持Put请求方式

  • @DeleteMapping

    这个注解只支持Delete请求方式

  • 等等

2.1.2 Mybatis框架

Mybatis框架是一个持久层的orm框架,可以用来连接访问数据库.

  • 依赖

    • mybatis

      <dependency>
          <groupId>org.mybatis.spring.boot</groupId>
          <artifactId>mybatis-spring-boot-starter</artifactId>
          <version>1.3.2</version>
      </dependency>
      
    • mysql

      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
      </dependency>
      
    • datasource

      • 可以使用spring自带的,就不需要再导入数据源依赖
    • 也可以使用其他数据源
      • c3p0
        • dbcp
      • druid
      • 等等
  • 配置

    springboot项目的配置都在resources目录下的application.properties文件:

    • 数据源

      # 数据源
      spring.datasource.driver-class-name=com.mysql.jdbc.Driver
      spring.datasource.url=jdbc:mysql://localhost:3306/easy_mall?serverTimezone=PRC
      spring.datasource.username=root
      spring.datasource.password=123456
      
    • mybatis

      扫描Mapper映射文件

      
      
      

      mybatis

      mybatis.mapper-locations=classpath:/mapper/*Mapper.xml

      
      
      
  • mapper接口

    定义Mapper接口,mapper接口定义用来执行sql语句的方法.

    public interface AdminUserMapper {
        //每个方法对应一个sql语句
        public int selectAdminUserCount();
    }
    
  • mapper映射文件

    mapper映射文件是一个xml文件,里边定义sql语句.

    建议放在resources目录下(可以新建一个目录)。

    <?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">
    <!-- namespace必须是对应的Mapper接口的全名(包名.接口名) -->
    <mapper namespace="com.edu.mall.admin.mapper.AdminUserMapper">
        <!-- id必须是对应的方法的方法名 -->
        <select id="selectAdminUserCount" resultType="int">
            select count(id) from litemall_admin
        </select>
    </mapper>
    
    • namespace
    • id
  • MapperScan

    在主类上通过MapperScan注解扫描Mapper接口

    @MapperScan("com.edu.mall.admin.mapper")
    

2.1.3 Controller调用Mapper

只要调用Mapper接口中的方法,就会执行对应的sql语句.

@Autowired//1.注入AdminUserMapper对象
AdminUserMapper adminUserMapper;

@RequestMapping("/admin/user/count")
public Object selectAdminUserCount(){
    int count = adminUserMapper.selectAdminUserCount();//2.调用方法
    Map<String,Object> result = new HashMap<>();
    result.put("errno",0);
    result.put("errmsg","成功");
    result.put("data","管理员用户数量:"+count);
    return result;
}

2.2 启动后端服务

后端springboot项目中直接内嵌了tomcat应用服务器,直接运行main主类,就可以启动tomcat并部署项目.

image-20220722174521913

2.3 接口测试工具

  • 浏览器 (只能发送get请求)
    • 浏览器中安装插件
  • 专业的工具
    • postman
    • ApiPost
    • curl
    • 等等

2.4 项目目录结构

src

​ main

​ java(java源码文件)

​ com.edu.mall.admin(基本包)

​ Application.java(主类)

​ controller

​ service

​ mapper

​ domain(po/pojo/model) : 跟数据库表映射的java实体类

​ vo : 主要是适配接口响应结果的java类

​ dto :数据传输对象,比如接收前端传递过来的参数对象

​ util : 工具类

​ common : 与业务功能无关的通用对象

​ config : 配置对象

​ resources(资源文件)

​ application.properties (配置文件)

​ mapper : Mapper映射文件

​ static : 静态资源(img/css/js/font)

​ templates : 模板文件(freemark/velocity/thymeleaf)

​ test(测试)

​ java

​ resources

pom.xml

2.5 项目三层架构

  • web层 (controller)

    只负责跟前端交互,接收请求,响应结果.

  • 业务层 (service)

    负责处理业务逻辑.

  • 持久层 (Dao/Mapper)

    只负责执行sql,访问数据库中的数据.

三层的关系:

  • 下层给给上层提供服务

    web层给前端提供接口服务;

    业务层给web层提供业务服务;

    持久层给业务层提供数据服务.

  • 上层依赖下层

    上层需要调用下层的方法完成需要的功能

3 接口开发

管理端

3.1 首页仪表盘


@RestController
@RequestMapping("/admin/dashboard")
public class AdminDashbordController {
    private final Log logger = LogFactory.getLog(AdminDashbordController.class);

    @Autowired
    private MallUserService userService;
    @Autowired
    private MallGoodsService goodsService;
    @Autowired
    private MallGoodsProductService productService;
    @Autowired
    private MallOrderService orderService;

    @GetMapping("")
    public Object info() {
        int userTotal = userService.count();
        int goodsTotal = goodsService.count();
        int productTotal = productService.count();
        int orderTotal = orderService.count();
        Map<String, Integer> data = new HashMap<>();
        data.put("userTotal", userTotal);
        data.put("goodsTotal", goodsTotal);
        data.put("productTotal", productTotal);
        data.put("orderTotal", orderTotal);
        return ResponseUtil.ok(data);
    }
}

3.2 商品类目管理

@RestController
@RequestMapping("/admin/category")
public class AdminCategoryController {
    private final Log logger = LogFactory.getLog(AdminCategoryController.class);

    @Autowired
    private LitemallCategoryService categoryService;

    @GetMapping("/list")
    public Object list() {
        List<CategoryVo> categoryVoList = new ArrayList<>();

        List<LitemallCategory> categoryList = categoryService.queryByPid(0);
        for (LitemallCategory category : categoryList) {
            CategoryVo categoryVO = new CategoryVo();
            categoryVO.setId(category.getId());
            categoryVO.setDesc(category.getDesc());
            categoryVO.setIconUrl(category.getIconUrl());
            categoryVO.setPicUrl(category.getPicUrl());
            categoryVO.setKeywords(category.getKeywords());
            categoryVO.setName(category.getName());
            categoryVO.setLevel(category.getLevel());

            List<CategoryVo> children = new ArrayList<>();
            List<LitemallCategory> subCategoryList = categoryService.queryByPid(category.getId());
            for (LitemallCategory subCategory : subCategoryList) {
                CategoryVo subCategoryVo = new CategoryVo();
                subCategoryVo.setId(subCategory.getId());
                subCategoryVo.setDesc(subCategory.getDesc());
                subCategoryVo.setIconUrl(subCategory.getIconUrl());
                subCategoryVo.setPicUrl(subCategory.getPicUrl());
                subCategoryVo.setKeywords(subCategory.getKeywords());
                subCategoryVo.setName(subCategory.getName());
                subCategoryVo.setLevel(subCategory.getLevel());

                children.add(subCategoryVo);
            }

            categoryVO.setChildren(children);
            categoryVoList.add(categoryVO);
        }

        return ResponseUtil.okList(categoryVoList);
    }

    private Object validate(LitemallCategory category) {
        String name = category.getName();
        if (StringUtils.isEmpty(name)) {
            return ResponseUtil.badArgument();
        }

        String level = category.getLevel();
        if (StringUtils.isEmpty(level)) {
            return ResponseUtil.badArgument();
        }
        if (!level.equals("L1") && !level.equals("L2")) {
            return ResponseUtil.badArgumentValue();
        }

        Integer pid = category.getPid();
        if (level.equals("L2") && (pid == null)) {
            return ResponseUtil.badArgument();
        }

        return null;
    }


    @PostMapping("/create")
    public Object create(@RequestBody LitemallCategory category) {
        Object error = validate(category);
        if (error != null) {
            return error;
        }
        categoryService.add(category);
        return ResponseUtil.ok(category);
    }

    @GetMapping("/read")
    public Object read(@NotNull Integer id) {
        LitemallCategory category = categoryService.findById(id);
        return ResponseUtil.ok(category);
    }

    @PostMapping("/update")
    public Object update(@RequestBody LitemallCategory category) {
        Object error = validate(category);
        if (error != null) {
            return error;
        }

        if (categoryService.updateById(category) == 0) {
            return ResponseUtil.updatedDataFailed();
        }
        return ResponseUtil.ok();
    }

    @PostMapping("/delete")
    public Object delete(@RequestBody LitemallCategory category) {
        Integer id = category.getId();
        if (id == null) {
            return ResponseUtil.badArgument();
        }
        categoryService.deleteById(id);
        return ResponseUtil.ok();
    }

    @GetMapping("/l1")
    public Object catL1() {
        // 所有一级分类目录
        List<LitemallCategory> l1CatList = categoryService.queryL1();
        List<Map<String, Object>> data = new ArrayList<>(l1CatList.size());
        for (LitemallCategory category : l1CatList) {
            Map<String, Object> d = new HashMap<>(2);
            d.put("value", category.getId());
            d.put("label", category.getName());
            data.add(d);
        }
        return ResponseUtil.okList(data);
    }
}

3.3 商品管理

@RestController
@RequestMapping("/admin/goods")
public class MallGoodsController {

    @Autowired
    private MallGoodsService mallGoodsService;
    @Autowired
    private MallCategoryService mallCategoryService;
    @Autowired
    private MallBrandService mallBrandService;

    @GetMapping("/list")
    public Object findGoodsList(String name, Long goodsId, String goodsSn,
                                String sort, String order,
                                @RequestParam(value="page",required = false,defaultValue = "1") Integer page, @RequestParam(value="limit",required = false,defaultValue = "20")Integer limit){
        PageHelper.startPage(page, limit);
        List<MallGoods> goodsList = mallGoodsService.findGoodsList(name, goodsId, goodsSn, sort, order);
        PageInfo<MallGoods> pageInfo = new PageInfo<>(goodsList);
        return ResponseUtils.okPage(pageInfo);
    }

    @GetMapping("/catAndBrand")
    public Object findCategoryAndBrandList(){
        //查询所有一级分类
        List<MallCategory> level1List = mallCategoryService.findCategoryListByParentId(0l);
        //循环查询一级分类下的二级分类
        for (MallCategory l1 : level1List){
            l1.setChildren(mallCategoryService.findCategoryListByParentId(l1.getId()));
        }
        //转换为VO
        List<SelectVo> level1VoList = new ArrayList<>();
        for (MallCategory l1 : level1List){
            SelectVo l1Vo = new SelectVo();
            l1Vo.setValue(l1.getId()+"");
            l1Vo.setLabel(l1.getName());
            List<SelectVo> children = new ArrayList<>();
            for(MallCategory l2 : l1.getChildren()){
                SelectVo l2Vo = new SelectVo();
                l2Vo.setValue(l2.getId()+"");
                l2Vo.setLabel(l2.getName());
                children.add(l2Vo);
            }
            l1Vo.setChildren(children);
            level1VoList.add(l1Vo);
        }

        //查询所有品牌
        List<MallBrand> allBrands = mallBrandService.findAllBrands();
        //转换为VO
        List<SelectVo> brandVoList = new ArrayList<>();
        for (MallBrand b : allBrands){
            SelectVo bVo = new SelectVo();
            bVo.setValue(b.getId()+"");
            bVo.setLabel(b.getName());
            brandVoList.add(bVo);
        }
        Map<String,Object> data = new HashMap<>();
        data.put("categoryList", level1VoList);
        data.put("brandList", brandVoList);
        return ResponseUtils.ok(data);
    }


    @PostMapping("/create")
    public Object createGoods(@RequestBody GoodsProductDto goodsProductDto){
        mallGoodsService.createGoods(goodsProductDto);
        return ResponseUtils.ok();
    }
    @PostMapping("/delete")
    public Object delGoods(@RequestBody MallGoods mallGoods){
        mallGoodsService.delGoods(mallGoods.getId());
        return ResponseUtils.ok();
    }

    @GetMapping("/detail")
    public Object findGoodsDetails(Long id){
        GoodsProductCategoryDto goodsDetails = mallGoodsService.findGoodsDetails(id);
        return ResponseUtils.ok(goodsDetails);
    }

    @PostMapping("/update")
    public Object updateGoods(@RequestBody GoodsProductDto productDto){
        mallGoodsService.updateGoods(productDto);
        return ResponseUtils.ok();
    }
}

3.4 登录


@RestController
public class AdminUserController {

    @Autowired
    private AdminUserService adminUserService;

    @PostMapping("/admin/auth/login")
    public Object login(@RequestBody AdminUser user){
        AdminUser login = adminUserService.login(user.getUsername(), user.getPassword());
        if(login!=null){
            Map<String,Object> data = new HashMap<>();
            data.put("token", JwtHelper.createToken(login.getId()));
            Map<String,Object> adminInfo = new HashMap<>();
            adminInfo.put("nickName",login.getUsername());
            adminInfo.put("avatar", login.getAvatar());
            data.put("adminInfo", adminInfo );
            return ResponseUtils.ok(data);
        }else{
            return ResponseUtils.fail(ResponseUtils.INVALID_USER, ResponseUtils.INVALID_LOGIN);
        }
    }

    @GetMapping("/admin/auth/info")
    public Object info(){
        Map<String,Object> data = new HashMap<>();
        data.put("roles",new String[]{"超级管理员"});
        data.put("name","admin123");
        data.put("perms", new String[]{"*"});
        data.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
        return ResponseUtils.ok(data);
    }
}

移动端

3.5 首页

package com.edu.mall.mobile.controller;

import com.edu.mall.mobile.utils.ResponseUtils;
import com.edu.mall.service.MobileHomeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/wx/home")
public class HomeController {

    //注入MobileHomeService
    @Autowired
    private MobileHomeService mobileHomeService;

    @RequestMapping("/index")
    public Object index(){
        Object data = mobileHomeService.getHomeIndex();
        return ResponseUtils.ok(data);
    }
}

@Service
public class MobileHomeServiceImpl implements MobileHomeService {
    //注入LitemallAdMapper
    @Autowired
    private LitemallAdMapper litemallAdMapper;
    //注入LitemallCategoryMapper
    @Autowired
    private LitemallCategoryMapper litemallCategoryMapper;
    @Autowired
    private LitemallGrouponRulesMapper litemallGrouponRulesMapper;
    @Autowired
    private LitemallBrandMapper litemallBrandMapper;
    @Autowired
    private LitemallGoodsMapper litemallGoodsMapper;
    @Autowired
    private LitemallTopicMapper litemallTopicMapper;
    @Override
    public MobileHomeVo getHomeIndex() {
        MobileHomeVo vo = new MobileHomeVo();
        //广告数据列表
        vo.setBanner(litemallAdMapper.selectAdList());
        //分类引导列表
        vo.setChannel(litemallCategoryMapper.selectLevel1List());
        //团购
        vo.setGrouponList(litemallGrouponRulesMapper.selectGroupOnRulesList());
        //品牌商
        vo.setBrandList(litemallBrandMapper.selectBrandList(4));
        //新品
        LitemallGoods litemallGoods = new LitemallGoods();
        litemallGoods.setIsNew(true);//条件
        litemallGoods.setLimit(6);
        vo.setNewGoodsList(litemallGoodsMapper.selectGoodsList(litemallGoods));
        //热卖
        litemallGoods.clear();
        litemallGoods.setIsHot(true);//条件
        litemallGoods.setLimit(6);
        vo.setHotGoodsList(litemallGoodsMapper.selectGoodsList(litemallGoods));
        //主题
        vo.setTopicList(litemallTopicMapper.selectTopicList(4));
        return vo;
    }
}

商品加载

/**
 * 商品服务
 */
@RestController
@RequestMapping("/wx/goods")
@Validated
public class WxGoodsController {
    private final Log logger = LogFactory.getLog(WxGoodsController.class);
    /**
     * 根据条件搜素商品
     * <p>
     * 1. 这里的前五个参数都是可选的,甚至都是空
     * 2. 用户是可选登录,如果登录,则记录用户的搜索关键字
     *
     * @param categoryId 分类类目ID,可选
     * @param brandId    品牌商ID,可选
     * @param keyword    关键字,可选
     * @param isNew      是否新品,可选
     * @param isHot      是否热买,可选
     * @param userId     用户ID
     * @param page       分页页数
     * @param limit       分页大小
     * @param sort       排序方式,支持"add_time", "retail_price"或"name"
     * @param order      排序类型,顺序或者降序
     * @return 根据条件搜素的商品详情
     */
    @GetMapping("list")
    public Object list(
        Integer categoryId,
        Integer brandId,
        String keyword,
        Boolean isNew,
        Boolean isHot,
        @LoginUser Integer userId,
        @RequestParam(defaultValue = "1") Integer page,
        @RequestParam(defaultValue = "10") Integer limit,
        @Sort(accepts = {"add_time", "retail_price", "name"}) @RequestParam(defaultValue = "add_time") String sort,
        @Order @RequestParam(defaultValue = "desc") String order) {

        //添加到搜索历史
        if (userId != null && !StringUtils.isEmpty(keyword)) {
            LitemallSearchHistory searchHistoryVo = new LitemallSearchHistory();
            searchHistoryVo.setKeyword(keyword);
            searchHistoryVo.setUserId(userId);
            searchHistoryVo.setFrom("wx");
            searchHistoryService.save(searchHistoryVo);
        }

        //查询列表数据
        List<LitemallGoods> goodsList = goodsService.querySelective(categoryId, brandId, keyword, isHot, isNew, page, limit, sort, order);

        // 查询商品所属类目列表。
        List<Integer> goodsCatIds = goodsService.getCatIds(brandId, keyword, isHot, isNew);
        List<LitemallCategory> categoryList = null;
        if (goodsCatIds.size() != 0) {
            categoryList = categoryService.queryL2ByIds(goodsCatIds);
        } else {
            categoryList = new ArrayList<>(0);
        }

        PageInfo<LitemallGoods> pagedList = PageInfo.of(goodsList);

        Map<String, Object> entity = new HashMap<>();
        entity.put("list", goodsList);
        entity.put("total", pagedList.getTotal());
        entity.put("page", pagedList.getPageNum());
        entity.put("limit", pagedList.getPageSize());
        entity.put("pages", pagedList.getPages());
        entity.put("filterCategoryList", categoryList);

        // 因为这里需要返回额外的filterCategoryList参数,因此不能方便使用ResponseUtil.okList
        return ResponseUtil.ok(entity);
    }
}

3.6 分类


@RestController
@RequestMapping("/wx/category")
public class CategoryController {

    @Autowired
    private MobileCategoryService mobileCategoryService;

    @RequestMapping("/index")
    public Object getIndex(){
        Object data = mobileCategoryService.getCategoryIndex();
        return ResponseUtils.ok(data);
    }

    @RequestMapping("/current")
    public Object getCategory(Integer id){
        Object data = mobileCategoryService.getCategoryAndSub(id);
        return ResponseUtils.ok(data);
    }

}


@Service
public class MobileCategoryServiceImpl implements MobileCategoryService {

    @Autowired
    private LitemallCategoryMapper litemallCategoryMapper;

    @Override
    public MobileCategoryVo getCategoryIndex() {
        MobileCategoryVo vo = new MobileCategoryVo();
        vo.setCategoryList(litemallCategoryMapper.selectLevel1ListBlob());
        if(!vo.getCategoryList().isEmpty()){
            vo.setCurrentCategory(vo.getCategoryList().get(0));
            vo.setCurrentSubCategory(litemallCategoryMapper.selectSubListByPid(vo.getCurrentCategory().getId()));
        }
        return vo;
    }

    @Override
    public MobileCategoryVo getCategoryAndSub(Integer id) {
        MobileCategoryVo vo = new MobileCategoryVo();
        LitemallCategory litemallCategory = litemallCategoryMapper.selectByPrimaryKey(id);
        vo.setCurrentCategory(litemallCategory);
        if(litemallCategory != null){
            vo.setCurrentSubCategory(litemallCategoryMapper.selectSubListByPid(litemallCategory.getId()));
        }
        return vo;
    }
}

4 SpringMVC框架

4.1 请求参数的处理

  • json格式

    json格式的请求参数的接收,注解在controller方法中添加对应的java对象参数,并在参数前加上@RequestBody注解.

        @PostMapping("/admin/auth/login")
        public Object login(@RequestBody AdminAuth auth){
    
        }
    

    注意: json对象中的属性和java对象中的属性一一对应

  • 普通form-data

    两种方式:

    • 一个一个接收

      ```java

      @GetMapping("/list")
      public Object list(@RequestParam(value="page",required = false,defaultValue = "1") Integer page,
                         @RequestParam(value="limit",required = false,defaultValue = "20")Integer limit,
                         @RequestParam(value="sort",required = false,defaultValue = "add_time")String sort,
                         @RequestParam(value="order",required = false,defaultValue = "desc")String order,
                         Integer goodsSn, Integer goodsId, String name){
      
    }
```
  • @RequestParam注解
    - value设置对应的请求参数名
    - required设置这个请求参数是否必填
    - defaultValue设置这个请求参数的默认值(required是false才有意义)
    

注意:没有加RequestParam注解的参数,方法参数名必须和请求参数名一一对应

  • 使用一个java实体对象封装

  • multipart/form-data(带文件上传)

        @PostMapping("/create")
        public Object create(MultipartFile file){//MultipartFile类型的参数用来接收上传的文件
        }
    
    • MultipartFile
      • getContentType
      • getOriginalName
      • getSize
      • getBytes
      • transferTo
  • 还有一种特殊的路径参数

    • 在路径中使用占位符声明路径参数
    • 使用@PathVariable注解读取路径参数值
    • 在调用后端接口时,路径中给路径参数设置具体的值
        @GetMapping("/detail/{id}")
        public Object detail(@PathVariable("id") Integer id){
            logger.info("id:" + id);
            return ResultUtils.success();
        }
    

    http://localhost:8080/admin/goods/detail/161008

4.2 请求头的处理

  • 读取请求头中的token令牌

    @PostMapping("/admin/auth/info")
    public Object getAdminInfo(@RequestHeader String token){
    
    }
    

4.3 资源处理器ResourceHandler

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(FileUploadUtils.storageProfile + "/**")
                .addResourceLocations("file:"+FileUploadUtils.saveDir+"/");
    }
}

5. Mybatis框架

5.1 参数映射

CRUD增上改查语句都可能牵扯参数问题.

  • Mapper接口中方法中定义参数

    分为下边三种情况:

    1. 只有一个参数并且是简单值类型的参数(基本数据类型/字符串/日期)

      public AuthAdmin findAdminByUsername(String username);

    2. 只有一个参数但是是java对象类型或者Map类型

      public int insertGoods(MallGoods mallGoods);
      
    3. 多个参数

         public List<MallGoods> selectGoodslist(@Param("sort") String sort,                       @Param("order") String order,@Param("goodsSn")Integer goodsSn,                    @Param("goodsId")Integer goodsId, @Param("name")String name);
      
      • 每个参数都必须用@Param注解设置一个名字
  • Mapper映射文件中sql语句要使用参数

    首先在sql中parameterType指定参数类型. 要注意的是如果这里指定的是自定义的java类型,需要全类名;或者在配置文件中配置别名,就可以使用简单类名.

    mybatis.type-aliases-package=com.edu.mall.admin.domain

    然后上边三种情况sql语句中参数的使用方法:

    1. 只有一个参数并且是简单值类型的参数

      sql语句中使用时直接 : #{任意名字}

          <select id="selectAdminByUsername" parameterType="string" resultMap="adminBaseMap">
              select id,username,password,last_login_ip,last_login_time,avatar,
                     add_time,update_time,deleted,role_ids
              from litemall_admin where username=#{username}
          </select>
      
    2. 只有一个参数但是是java对象类型或者Map类型

      sql语句中使用时: #{}中直接写参数对象中的属性名就可以访问属性值.(map中的key)

          <insert id="insertGoods" parameterType="MallGoods">
              insert into litemall_goods(id,goods_sn,name,category_id,brand_id,gallery,keywords,brief,is_on_sale,sort_order,
                     pic_url,share_url,is_new,is_hot,unit,counter_price,retail_price,detail,add_time,update_time)
                     values(#{id},#{goodsSn},#{name},#{categoryId},#{brandId},#{gallery},#{keywords},#{brief},#{isOnSale},#{sortOrder},
                     #{picUrl},#{shareUrl},#{isNew},#{isHot},#{unit},#{counterPrice},#{retailPrice},#{detail},#{addTime},#{updateTime})
          </insert>
      
    3. 多个参数

      • 不用设置parameterType

      • 使用参数时,#{}中必须使用@Param注解设置的名字

5.2 结果映射

结果映射只有select查询语句需要;增删改不需要resultType或者resultMap。

结果映射分为resultType和resultMap:

  • resultType

    如果查询结果是简单值类型,直接使用resultType指定数据类型即可.

    如果查询结果要封装为java实体对象并且查询结果的列名和java属性都一一对应,直接resultType指定java类型即可.

        <select id="selectAdminUserCount" resultType="int">
            select count(id) from litemall_admin
        </select>
    
  • resultMap

    查询结果要封装为java实体对象但是查询结果的列名和java属性并不能一一对应,那么需要自己定义这个映射关系.

        <!-- 定义一个resultMap,映射数据库列名和java实体类属性名 -->
        <resultMap id="adminBaseMap" type="MallAdmin">
            <!-- 主键字段用id -->
            <id column="id" property="id" />
            <!-- 其他字段用result -->
            <result column="username" property="username" />
            <result column="password" property="password" />
            <result column="last_login_ip" property="lastLoginIp" />
            <result column="last_login_time" property="lastLoginTime" />
            <result column="avatar" property="avatar" />
            <result column="add_time" property="addTime" />
            <result column="update_time" property="updateTime" />
            <result column="deleted" property="deleted" />
            <result column="role_ids" property="roleIds" />
        </resultMap>
        <!-- resultMap属性:应用上边定义的resultMap-->
        <select id="selectAdminByUsername" parameterType="string" resultMap="adminBaseMap">
            select id,username,password,last_login_ip,last_login_time,avatar,
                   add_time,update_time,deleted,role_ids
            from litemall_admin where username=#{username}
        </select>
    

5.3 动态sql

  • where

    <where>
        <if test="goodsId!=null">
            and id=#{goodsId}
        </if>
        <if test="goodsSn!=null">
            and goods_sn=#{goodsSn}
        </if>
        <if test="name!=null and name!=''">
            name like concat('%',#{name},'%')
        </if>
    </where>
    
  • set

  • if

  • forEach

    where id in(
    <foreach collection="list" item="id" separator=",">
        #{id}
    </foreach>
    )
    

5.4 #{}和${}

  • {} sql中需要一个value值的地方,才能使用#{}

  • ${} sql中如果只是拼接一段文本,可以使用${}

    order by ${sort} ${order}

5.5 pagehelper分页插件

  • 依赖

            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>1.2.5</version>
            </dependency>
    
  • 配置

    pagehelper.helper-dialect=mysql
    
  • 使用

     //在执行查询列表的语句之前设置分页的页号和页面行数
    PageHelper.startPage(page, limit);
    List<MallGoods> mallGoods = adminGoodsMapper.selectGoodslist(sort, order, goodsSn, goodsId, name);
    //查询返回的list集合其实是一个Page,page对象中有分页相关的数据
    Page<MallGoods> page = (Page<MallGoods>) mallGoods;//强转为Page
    data.put("total", page.getTotal());
    data.put("pages", page.getPages());
    data.put("limit", page.getPageSize());
    data.put("page", page.getPageNum());
    data.put("list", page.getResult());
    

6. 日志

在应用开发中经常需要输出各种日志,可以使用第三方的日志包。

常用的日志组件有:

  • logging
  • log4j/log4j2
  • logback
  • 等等

6.1 配置

# 日志输出级别(默认是info)(debug  --->  info  --->   warn  ---> error)
logging.level.web=debug
logging.level.com.edu.mall.admin=debug

6.2 输出日志

    //Logger对象用来输出日志
    Logger logger = Logger.getLogger("adminAuthController");
    //输出
    logger.info("日志内容");

7. 加密

md5加密:

  • jdk

  • spring

        /**
         * 加密
         * @param source 指定要加密的明文
         * @return  返回密文
         */
        public static String encode(String source){
            return DigestUtils.md5DigestAsHex(source.getBytes());
        }
    

8. Token令牌

为了实现一种有状态的登陆会话,需要在登录成功的时候给每个用户发一个token令牌,当这个用户会话再发请求时必须携带自己的令牌,这样服务端就知道这次请求是属于哪个用户会话.

  • jwt

            <dependency>
                <groupId>com.auth0</groupId>
                <artifactId>java-jwt</artifactId>
                <version>3.4.1</version>
            </dependency>
    
  • 生成和验证token

    ```java

public class JwtHelper { // 秘钥 static final String SECRET = "Easymall-Token"; // 签名是有谁生成 static final String ISSUSER = "EASYMALL"; // 签名的主题 static final String SUBJECT = "this is easymall token"; // 签名的观众 static final String AUDIENCE = "MOBILEWEBAPP";

  public String createToken(Integer userId){
      try {
          Algorithm algorithm = Algorithm.HMAC256(SECRET);
          Map<String, Object> map = new HashMap<String, Object>();
          Date nowDate = new Date();
          // 过期时间:2小时
          Date expireDate = getAfterDate(nowDate,0,0,0,2,0,0);
          map.put("alg", "HS256");
          map.put("typ", "JWT");
          String token = JWT.create()
              // 设置头部信息 Header
              .withHeader(map)
              // 设置 载荷 Payload
              .withClaim("userId", userId)
              .withIssuer(ISSUSER)
              .withSubject(SUBJECT)
              .withAudience(AUDIENCE)
              // 生成签名的时间 
              .withIssuedAt(nowDate)
              // 签名过期的时间 
              .withExpiresAt(expireDate)
              // 签名 Signature
              .sign(algorithm);
          return token;
      } catch (JWTCreationException exception){
          exception.printStackTrace();
      }
      return null;
  }

  public Integer verifyTokenAndGetUserId(String token) {
      try {
          Algorithm algorithm = Algorithm.HMAC256(SECRET);
          JWTVerifier verifier = JWT.require(algorithm)
              .withIssuer(ISSUSER)
              .build();
          DecodedJWT jwt = verifier.verify(token);
          Map<String, Claim> claims = jwt.getClaims();
          Claim claim = claims.get("userId");
          return claim.asInt();
      } catch (JWTVerificationException exception){

// exception.printStackTrace(); } return 0; }

  public  Date getAfterDate(Date date, int year, int month, int day, int hour, int minute, int second){
      if(date == null){
          date = new Date();
      }

      Calendar cal = new GregorianCalendar();

      cal.setTime(date);
      if(year != 0){
          cal.add(Calendar.YEAR, year);
      }
      if(month != 0){
          cal.add(Calendar.MONTH, month);
      }
      if(day != 0){
          cal.add(Calendar.DATE, day);
      }
      if(hour != 0){
          cal.add(Calendar.HOUR_OF_DAY, hour);
      }
      if(minute != 0){
          cal.add(Calendar.MINUTE, minute);
      }
      if(second != 0){
          cal.add(Calendar.SECOND, second);
      }
      return cal.getTime();
  }

} ```