【百战商城】学习笔记1.30_后端接口编写

148 阅读6分钟

1、创建接口返回结果类BaizhanResult,匹配接口文档返回数据

image-20230130135047936

 @Data
 public class BaizhanResult implements Serializable {
     private int status;
     private String msg;
     private Object data;
     public static BaizhanResult ok(){
         BaizhanResult br = new BaizhanResult();
         br.setMsg("OK");
         br.setStatus(200);
         return br;
     }
     public static BaizhanResult ok(Object data){
         BaizhanResult br = new BaizhanResult();
         br.setMsg("OK");
         br.setStatus(200);
         br.setData(data);
         return br;
     }
     public static BaizhanResult error(String msg){
         BaizhanResult br = new BaizhanResult();
         br.setMsg(msg);
         br.setStatus(400);
         return br;
     }
 }

接口1:规格参数查询

依次创建service,mapper(已创建),serviceImpl层,控制层controller

 // service层
 public interface ItemParamService {
     BaizhanResult selectAllItemParams();
 }
 ​
 // mapper层
 @Mapper
 @Component
 public interface TbItemParamMapper extends BaseMapper<TbItemParam> {
 ​
 }
 @Service
 public class ItemParamServiceImpl implements ItemParamService {
     @Autowired
     private TbItemParamMapper itemParamMapper;
     /**
      * 查询所有规格参数
      * 注意: 异常处理问题。
      * @return
      */
     @Override
     public BaizhanResult selectAllItemParams() {
         try {
             // selectList 有条件查询多行数据。参数是条件。
             // 需要的是无条件查询所有。
             List<TbItemParam> result = itemParamMapper.selectList(null);
             return BaizhanResult.ok(result);
         }catch (Exception e){
             throw new DaoException("查询规格参数错误", e);
         }
     }
 }

控制层

 @RestController
 /**
 *@RestController相当于@Controller+@ResponseBody两个注解
 *@ResponseBody 表示该方法的返回结果直接写入 HTTP response body 中,一般在异步获取数据时使用【也就是AJAX】,在使用 *@RequestMapping后,返回值通常解析为跳转路径,但是加上 @ResponseBody 后返回结果不会被解析为跳转路径,而是直接写入 HTTP *response body 中。 比如异步获取 json 数据,加上 @ResponseBody 后,会直接返回 json 数据。
 */
 @Slf4j
 public class ItemParamController {
     @Autowired
     private ItemParamService itemParamService;
 ​
     /**
      * 查询所有的规格参数。
      * 访问数据库表格tb_item_param。
      * @return BaizhanResult - 自定义的返回结果类型。
      *  包含属性: status - 状态编码; msg - 返回字符串消息; data - 返回数据对象。
      */
     @GetMapping("/backend/itemParam/selectItemParamAll")
     public BaizhanResult selectAllItemParams(){
         try {
             log.info("查询规格参数");
             return itemParamService.selectAllItemParams();
         }catch (DaoException e){
             log.error("查询规格参数错误:" + e.getMessage());
             return BaizhanResult.error("服务器忙,请稍后重试");
         }
     }
 }

接口2:根据父类目查询所有子类目

后面只记serviceImpl和controller的处理方法

image-20230130141345986

这里返回的数据,需要新增一个“leaf”状态

所以需要新写一个接受类

 /**
  * 当前系统中需要使用的商品类型值对象。value object
  * 只在当前系统中使用。且用于传递数据使用。
  * 不于数据库有任何关联。
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
 public class TbItemCatVO extends TbItemCat {
     // 编写推导属性。
     public Boolean getLeaf(){
         return !getIsParent();
     }
     public void setLeaf(Boolean leaf){
         // 推导属性,不需要考虑赋值。
         // jackson要求,类型中除class以外的所有属性,必须成对
         // 提供getter/setter
     }
 }

impl实现类,注意 有条件查询多行数据。条件统一为QueryWrapper类型,参数第一个是字符串类型,代表具体的数据库表格字段名。不是属性名。第二个参数类型是Object。是查询条件值

 @Service
 public class ItemCategoryServiceImpl implements ItemCategoryService {
     @Autowired
     private TbItemCatMapper itemCatMapper;
 ​
     /**
      * 根据父商品类型主键,查询子商品类型集合。
      * @param id
      * @return
      */
     @Override
     public BaizhanResult selectItemCategoriesByParentId(Long id) {
         try {
             // 有条件查询多行数据。条件统一为QueryWrapper类型,
             // 条件的泛型就是要查询的实体类型。
             QueryWrapper<TbItemCat> queryWrapper =
                     new QueryWrapper<>();
             // QueryWrapper 用于封装所有要查询的条件。创建对象不做任何配置
             // 相当于无条件全数据查询。
             // 所有的条件都是基于QueryWrapper中的方法实现的。
             // 方法名称,和具体条件相关。如: 等值 eq。 大于 gt。 大于等于 ge。小于 lt。小于等于 le
             // 所有的方法参数都是2个。
             // 第一个是字符串类型,代表具体的数据库表格字段名。不是属性名。
             // 第二个参数类型是Object。是查询条件值
             // 多条件and并列,则直接多条件设置即可。
             queryWrapper.eq("parent_id", id)
                     .eq("status", 1);
 ​
             List<TbItemCat> result = itemCatMapper.selectList(queryWrapper);
             // 创建一个转换后的结果集合
             List<TbItemCatVO> localResult = new ArrayList<>(result.size());
             for(TbItemCat itemCat : result ){
                 // 创建子类型对象。
                 TbItemCatVO vo = new TbItemCatVO();
 ​
                 // 把查询结果对象中的每个属性的值,赋值到vo对象中。
                 // vo.setId(itemCat.getId());
                 // Spring提供的属性赋值工具。根据getter和setter,赋值属性。
                 // 把源itemCat中的属性,赋值到目标vo中。
                 // 同名赋值,同名属性类型也相同。相当于 vo.setId(itemCat.getId());
                 BeanUtils.copyProperties(itemCat, vo);
                 // 根据是否为父节点来设置leaf
                 vo.setLeaf(!itemCat.getIsParent());
                 localResult.add(vo);
             }
             return BaizhanResult.ok(localResult);
         }catch (Exception e){
             throw new DaoException("根据父商品类型主键,查询子商品类型集合错误", e);
         }
     }
 }
 ​

注意点:1、queryWrapper作条件查询,常用eq、gt等,而queryWrapper又作为mapper语句的条件

2、复制对象属性 BeanUtils.copyProperties

3、根据是否为父节点来设置额外leaf

4、itemCatMapper.selectList 返回符合queryWrapper条件的列表

接口3:判断商品分类是否已经添加过规格参数

impl层

 /**
      * 根据商品分类主键,查询规格
      * @param itemCatId
      * @return
      */
     @Override
     public BaizhanResult selectHaveByItemCatId(Long itemCatId) {
         try {
             // 根据条件查询一行数据。
             // 查询条件还是QueryWrapper
             QueryWrapper<TbItemParam> queryWrapper = new QueryWrapper<>();
             queryWrapper.eq("item_cat_id", itemCatId);
             // 查询有结果,返回具体对象,查询无结果,返回null。查询结果行数>1,抛出异常。
             TbItemParam itemParam = itemParamMapper.selectOne(queryWrapper);
             if (itemParam == null) {
                 // 无对应的规格,可以新增规格参数
                 return BaizhanResult.ok();
             }
             return BaizhanResult.error("商品分类有对应规格参数,请选择其他商品分类");
         }catch (Exception e){
             throw new DaoException("根据商品分类主键,检查是否存在规格参数时,错误", e);
         }
     }

控制层

 /**
      * 根据商品类型主键,查询规格参数
      * 对应商品类型有规格参数数据,返回错误结果, status = 400
      * 反之,返回正确结果,status = 200
      * @param itemCatId 商品类型主键。
      * @return
      */
     @GetMapping("/backend/itemParam/selectHaveParam")
     public BaizhanResult selectHaveByItemCatId(Long itemCatId){
         try {
             log.info("根据商品类型主键,查询规格参数");
             return itemParamService.selectHaveByItemCatId(itemCatId);
         }catch (Exception e){
             log.error(e.getMessage());
             return BaizhanResult.error("商品分类存在规格参数,请选择其他商品分类");
         }
     }

注意:1、itemParamMapper.selectOne 查询是否有一个

接口4:规格参数添加

image-20230130150052718

image-20230130145912370

前端传过来item_cat_id,param_data,所以还差商品id,创建时间created 和 更新时间updated

impl

 /**
      * 新增规格参数到数据库
      * @param itemParam
      * @return
      */
     @Override
     @Transactional(rollbackFor = {DaoException.class})
     public BaizhanResult insertItemParam(TbItemParam itemParam) {
         try {
             // 维护要新增数据的完整性
             itemParam.setId(IDUtils.genItemId()); // 生成主键
             Date now = new Date();  // 创建时间
             itemParam.setCreated(now); 
             itemParam.setUpdated(now); // 更新时间,新增数据的更新时间就是创建时间.
             // 新增数据到数据库,返回新增数据的行数
             int status = itemParamMapper.insert(itemParam);
             if (status != 1) {
                 // 新增错误
                 throw new DaoException("新增规格参数错误");
             }
             return BaizhanResult.ok();
         }catch (DaoException e){
             // 自定义抛出的异常.
             throw e;
         }catch (Exception e){
             // mapper访问数据库发生的其他异常.
             throw new DaoException("新增规格参数到数据库时,发生异常:" + e.getMessage(), e);
         }
     }

注意:1、工具类IDUtils.genItemId() 用于生成商品id,工具类由架构师提供的

 /**
      * 商品id生成
      */
     public static long genItemId() {
         //取当前时间的长整形值包含毫秒
         long millis = System.currentTimeMillis();
         //long millis = System.nanoTime();
         //加上两位随机数
         Random random = new Random();
         int end2 = random.nextInt(99);
         //如果不足两位前面补0
         String str = millis + String.format("%02d", end2);
         long id = new Long(str);
         return id;
     }

2、Date now = new Date()生成时间对象

3、itemParamMapper.insert(itemParam) 返回状态整数

4、使用@Transactional注解来管理事务

控制层

 /**
      * 新增规格参数到数据库
      * @param itemParam 要新增的数据
      * @return
      */
     @PostMapping("/backend/itemParam/insertItemParam")
     public BaizhanResult insertItemParam(TbItemParam itemParam){
         try {
             log.info("新增规格参数到数据库:" + itemParam.toString());
             return itemParamService.insertItemParam(itemParam);
         }catch (Exception e){
             log.error(e.getMessage());
             return BaizhanResult.error("服务器忙,请稍后重试");
         }
     }

接口5:规格参数删除

impl

 /**
      * 根据主键,删除规格参数.
      * @param id
      * @return
      */
     @Override
     @Transactional(rollbackFor = {DaoException.class})
     public BaizhanResult deleteItemParamById(Long id) {
         try {
             // 删除使用的方法是delete系列方法
             int rows = itemParamMapper.deleteById(id);
             if (rows != 1) {
                 throw new DaoException("删除规格参数错误");
             }
             return BaizhanResult.ok();
         }catch (DaoException e){
             throw e;
         }catch (Exception e){
             throw new DaoException("从数据库删除规格参数时发生错误:" + e.getMessage(), e);
         }
     }

kongzhiceng

 /**
      * 删除规格参数
      */
     @GetMapping("/backend/itemParam/deleteItemParamById")
     public BaizhanResult deleteItemParamById(Long id){
         try {
             log.info("删除规格参数:"+id);
             return itemParamService.deleteItemParamById(id);
         }catch (Exception e){
             log.error(e.getMessage());
             return BaizhanResult.error("服务器忙,请稍后重试");
         }
     }

接口6:查询商品列表接口

impl

 /**
      * 分页查询,表格 tb_item
      * @param page
      * @param rows
      * @return
      */
     @Override
     public BaizhanResult selectItemsByPage(int page, int rows) {
         try {
             // IPage<查询结果泛型> selectPage(IPage<查询结果泛型> 分页条件, QueryWrapper 查询条件)
             // IPage接口中,定义了若干方法,可以获取分页查询的总计数据行数,页数,当前页数据集合等。
             IPage<TbItem> iPage = new Page<>(page, rows);
             IPage<TbItem> resultPage = itemMapper.selectPage(iPage, null);
             
             List<TbItem> list = resultPage.getRecords(); // 当前页数据集合
             // resultPage.getSize(); // 每页多少行数据
             long total = resultPage.getTotal(); // 总计多少行数据
             // resultPage.getPages(); // 总计多少页
             // resultPage.getCurrent(); // 当前第几页
 ​
             // 使用Map传递最终查询结果
             Map<String, Object> result = new HashMap<>();
             result.put("total", total);
             result.put("result", list);
             
             return BaizhanResult.ok(result);
         }catch (Exception e){
             throw new DaoException("分页查询商品时,发生错误:"+ e.getMessage(), e);
         }
     }

注意:1、IPage泛型,也可以使用Page,参数是(页数,行数)

2、itemMapper.selectPage,参数(IPage,querywrapper),返回数据结果 IPage类型

3、由于返回结果需要的是一个data,没有封装新的实体类的话,用map传递键值对数据

image-20230130153325172

控制层

 /**
      * 分页查询商品
      * @return
      */
     @GetMapping("/backend/item/selectTbItemAllByPage")
     public BaizhanResult selectItemsByPage(@RequestParam(defaultValue = "1") int page,
                                            @RequestParam(defaultValue = "10") int rows){
         try {
             log.info("分页查询商品,第" + page + "页,每页" + rows + "行");
             return itemService.selectItemsByPage(page, rows);
         }catch (Exception e){
             log.error(e.getMessage());
             return BaizhanResult.error("服务器忙,请稍后重试");
         }
     }

注意:1、@RequestParam 为请求参数注解,Get请求访问时:

  • 不加@RequestParam注解:url可带参数也可不带参数,输入 localhost:8080/list1 以及 localhost:8080/list1?userId=xxx 方法都能执行;
  • 加@RequestParam注解:url必须带有参数。也就是说你直接输入localhost:8080/list2 会报错,不会执行方法。只能输入localhost:8080/list2?userId=xxx 才能执行相应的方法

接口7:图片上传接口

impl

 /**
  * 文件上传微服务,服务实现类型
  */
 @Service
 public class FileuploadServiceImpl implements FileuploadService {
     // 把application-commonspojo中的配置注入到当前属性
     @Value("${baizhan.fastdfs.nginx}")
     private String nginxServer;
 ​
     @Override
     public BaizhanResult uploadFile(MultipartFile file) {
         // 调用工具的上传文件方法
         try {
             String[] result =
                     FastDFSClient.uploadFile(file.getInputStream(),
                             file.getOriginalFilename());
             if(result == null){
                 throw new RuntimeException("上传文件工具类发生错误");
             }
             String path = nginxServer + result[0] + "/" + result[1];
             return BaizhanResult.ok(path);
         }catch (Exception e){
             throw new RuntimeException("上传图片时发生错误:" + e.getMessage(), e);
         }
     }
 }
 ​

注意:1、上传图片到虚拟机,所以需要先开启虚拟机

 connect_timeout = 10
 network_timeout = 30
 charset = UTF-8
 http.tracker_http_port = 8080
 ​
 # 如果tracker服务器有多个,配置文件写多行
 tracker_server = 192.168.137.128:22122
 # tracker_server = 192.168.137.129:22122

当前包下的yml文件

 server:
   port: 8888
 
 spring:
   servlet:
     multipart:
       max-file-size: 5MB
       max-request-size: 10MB
   profiles:
     active: commonspojo
   application:
     name: baizhan-uploadfile

2、commons_pojo包下properties文件 软编码配置baizhan.fastdfs.nginx

 baizhan.mysql.bigad.id=89
 ​
 # 开发时如果出现开发环境和上线环境不一致时使用 软编码
 # 如果当前很多系统中出现调用固定的字符串时也可以使用 软编码
 baizhan.fastdfs.nginx=http://192.168.137.128:8888/

3、FastDFSClient.uploadFile(file.getInputStream(), file.getOriginalFilename()) 第一个参数上传文件流,第二个参数上传图片名称(为了更好获取文件扩展名)

控制层

 @RestController
 @Slf4j
 public class FileuploadController {
     @Autowired
     private FileuploadService fileuploadService;
     /**
      * 上传文件到FastDFS
      * @return
      *  图片可访问地址。
      *   http://stroage服务器IP:nginx端口/组名/M00/文件名
      */
     @PostMapping("/file/upload")
     public BaizhanResult uploadFile(MultipartFile file){
         try {
             log.info("上传图片到FastDFS");
             return fileuploadService.uploadFile(file);
         }catch (Exception e){
             log.error(e.getMessage());
             return BaizhanResult.error("服务器忙,请稍后重试");
         }
     }
 }

接口8:查询商品规格参数模板

impl

 /**
      * 根据商品类型主键,查询规格参数
      * @param itemCatId
      * @return
      */
     @Override
     public BaizhanResult selectItemParamByItemCatId(Long itemCatId) {
         try {
             QueryWrapper<TbItemParam> queryWrapper = new QueryWrapper<>();
             queryWrapper.eq("item_cat_id", itemCatId);
             TbItemParam itemParam = itemParamMapper.selectOne(queryWrapper);
             if(itemParam!=null){
                 return BaizhanResult.ok(itemParam);
             }
             return BaizhanResult.error("服务端消息-没有查询到该模板信息");
         }catch (Exception e){
             throw new DaoException("根据商品类型主键-"+itemCatId+",查询规格参数错误:" + e.getMessage(), e);
         }
     }

其实还是拿符合条件的数据,至于数据名+数据则由前端负责消除数据,剩下数据名,就成了模板

控制层

 /**
      * 根据商品类型主键,查询规格参数
      * @return
      */
     @GetMapping("/backend/itemParam/selectItemParamByItemCatId/{itemCatId}")
     public BaizhanResult selectItemParamByItemCatId(@PathVariable("itemCatId") Long itemCatId){
         try {
             log.info("根据商品类型主键-"+itemCatId+",查询规格参数");
             return itemParamService.selectItemParamByItemCatId(itemCatId);
         }catch (Exception e){
             log.error(e.getMessage());
             return BaizhanResult.error("服务器忙,请稍后重试");
         }
     }

注意:1、@GetMapping("/backend/itemParam/selectItemParamByItemCatId/{itemCatId}")中

{itemCatId}中名字无所谓,只要跟下面@PathVariable(“xxx”)对应即可