【业务功能篇32】Springboot+MybatisPlus 告警关键词 CRUD+ EasyExcel导入导出+分页举类处理状态类字段@EnumValue @JsonValue+切面编程实现鉴权

业务场景:老生常谈的CRUD,今天是处理一个针对告警关键词频表单的操作,核心字段为 关键词、是否开启 , 后台记录的是否开启字段并非为 是,否, 转换成了数值记录了 1表示开启, 2表示关掉。在实际开发中,对于一些状态类的字段,我们通常使用的是枚举,而保存到数据库时,我们是用的枚举的某一个属性进行保存的,这里就会有一个问题,在VO类中,如果我们直接使用枚举类型去映射数据库的对应字段保存时,往往就会因为类型不匹配导致映射失败,如果要解决这个问题,办法有很多种,Mybatis-plus提供了一种解决办法,就是使用@EnumValue、@JsonValue注解,这里我们就使用这种方式。

 controller层

package com.xxx.service;

import java.util.*;
import com.xxx.delegate.ProdProblemRuleDelegate;
import com.xxx.model.ProdProblemRule;
import org.springframework.web.bind.annotation.*;
import com.xxx.model.ProdProblemRuleParam;
import org.springframework.validation.annotation.Validated;
import com.xxx.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import com.xxx.model.ResponseVo;



@RestController
@RequestMapping(value = "/prodProblemRule", produces = {"application/json;charset=UTF-8"})
@Validated
public class ProdProblemRuleController {

    @Autowired(required=false) 
    private ProdProblemRuleDelegate delegate;  
	
    @RequestMapping(
    		value = "/delete", 
    		produces = { "application/json" }, 
    		method = RequestMethod.POST)
    public ResponseVo deleteByIds(@RequestBody List ids) 
            throws ServiceException {
    	    
		return delegate.deleteByIds(ids);
    }

	
    @RequestMapping(
    		value = "/export", 
    		produces = { "application/octet-stream" }, 
    		method = RequestMethod.POST)
    public void exportExcel(HttpServletResponse response, @RequestBody ProdProblemRuleParam queryParam) 
            throws ServiceException {
    	    
		
		delegate.exportExcel(response, queryParam);
		
    }

	
    @RequestMapping(
    		value = "/exportTemplate", 
    		produces = { "application/octet-stream" }, 
    		method = RequestMethod.POST)
    public void exportExcelTemplate(HttpServletResponse response) 
            throws ServiceException {
    	    
		
		delegate.exportExcelTemplate(response);
		
    }

	
    @RequestMapping(
    		value = "/import", 
    		produces = { "application/json" }, 
    		method = RequestMethod.POST)
    public ResponseVo importExcel(  
		@RequestPart(value="file", required=true) MultipartFile file) 
            throws ServiceException {
    	    
		return delegate.importExcel(file);
    }

	
    @RequestMapping(
    		value = "/search", 
    		produces = { "application/json" }, 
    		method = RequestMethod.POST)
    public ResponseVo selectPageList(@RequestBody ProdProblemRuleParam queryParam) 
            throws ServiceException {
    	    
		return delegate.selectPageList(queryParam);
    }

	
    @RequestMapping(
    		value = "/upsert", 
    		produces = { "application/json" }, 
    		method = RequestMethod.POST)
    public ResponseVo upsertKeyword(@RequestBody ProdProblemRule entity) 
            throws ServiceException {
    	    
		return delegate.upsertKeyword(entity);
    }
}

service层 接口

package com.xxx.xxx.delegate;

import java.util.*;
import org.springframework.web.multipart.MultipartFile;
import com.xxx.xxx.model.ProdProblemRuleParam;
import javax.servlet.http.HttpServletResponse;
import com.xxx.xxx.model.ResponseVo;
import com.xxx.exception.ServiceException;
import com.xxx.xxx.model.ProdProblemRule;



public interface ProdProblemRuleDelegate {
  
    ResponseVo deleteByIds(List ids) 
    	throws ServiceException;
  
    void exportExcel(HttpServletResponse response, ProdProblemRuleParam queryParam) 
    	throws ServiceException;
  
    void exportExcelTemplate(HttpServletResponse response) 
    	throws ServiceException;
  
    ResponseVo importExcel( MultipartFile file) 
    	throws ServiceException;
  
    ResponseVo selectPageList(ProdProblemRuleParam queryParam) 
    	throws ServiceException;
  
    ResponseVo upsertKeyword(ProdProblemRule entity) 
    	throws ServiceException;
  
}

service层 接口实现类



package com.xxx.impl;

import com.xxx.annotation.UserPermission;
import com.xxx.domain.model.warn.ProductionProblemKeyword;
import com.xxx.enums.warn.ProdProblemKeywordSwitchEnum;
import com.xxx.delegate.ProdProblemRuleDelegate;
import com.xxx.model.ProdProblemRule;
import com.xxx.model.ProdProblemRuleParam;
import com.xxx.model.ResponseVo;
import com.xxx.service.warn.ProductionProblemKeywordService;
import com.xxx.utils.ResponseUtils;
import com.xxx.utils.StringUtils;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

import javax.servlet.http.HttpServletResponse;


/**
 * ProdProblemRuleDelegate实现层
 *
 */
@RequiredArgsConstructor
@Slf4j
@Component
public class ProdProblemRuleDelegateImpl implements ProdProblemRuleDelegate {

    private final ProductionProblemKeywordService service;

    /**
     * 根据id列表批量删除行
     *
     * @param ids id列表
     * @return 返回信息
     */
    @Override
    @UserPermission(username = {"用户1","用户2"})
    public ResponseVo deleteByIds(List ids) {
        service.delete(ids);
        return ResponseUtils.successResponse("删除成功");
    }

    /**
     * exportExcel
     *
     * @param response 返回数据
     * @param queryParam 请求参数
     */
    @Override
    @UserPermission(username = {"用户1","用户2"})
    public void exportExcel(HttpServletResponse response, ProdProblemRuleParam queryParam) {
        final List keywordList = StringUtils.splitToList(queryParam.getKeyword());
        ProdProblemKeywordSwitchEnum switchEnum = ProdProblemKeywordSwitchEnum.getByLabel(queryParam.getIsAvailable());
        service.download(response, keywordList, switchEnum);
    }

    /**
     * exportExcelTemplate
     *
     * @param response 返回数据
     */
    @Override
    public void exportExcelTemplate(HttpServletResponse response) {
        service.downloadTemplate(response);
    }

    /**
     * 导入xlsx文件
     *
     * @param file 导入xlsx文件
     * @return 导入结果
     */
    @Override
    @UserPermission(username = {"用户1","用户2"})
    public ResponseVo importExcel(MultipartFile file) {
        final String message = service.upload(file);
        return ResponseUtils.successResponse(message);
    }

    /**
     * 分页查询数据
     *
     * @param queryParam 查询参数
     * @return 查询结果
     */
    @Override
    public ResponseVo selectPageList(ProdProblemRuleParam queryParam) {
        final List keywordList = StringUtils.splitToList(queryParam.getKeyword());
        ProdProblemKeywordSwitchEnum switchEnum = ProdProblemKeywordSwitchEnum.getByLabel(queryParam.getIsAvailable());
        final Page page =
                service.selectPage(keywordList, switchEnum, queryParam.getCurrentPage(), queryParam.getPageSize());
        return ResponseUtils.successResponse(page, "查询成功");
    }

    /**
     * 新增/更新entity,根据是否有id和keyword是否判断
     *
     * @param entity entity
     * @return 新增/更新结果
     */
    @Override
    @UserPermission(username = {"用户1","用户2"})
    public ResponseVo upsertKeyword(ProdProblemRule entity) {
        final ProductionProblemKeyword keyword = new ProductionProblemKeyword();
        keyword.setId(entity.getId());
        keyword.setKeyword(entity.getKeyword());
        keyword.setIsAvailable(ProdProblemKeywordSwitchEnum.getByLabel(entity.getIsAvailable()));
        if (keyword.getId() == null) {
            return ResponseUtils.successResponse(service.insert(keyword), "新增成功");
        } else {
            return ResponseUtils.successResponse(service.update(keyword), "修改成功");
        }
    }

}

dao层 接口



package com.xxx.service.warn;

import com.xxx.domain.model.warn.ProductionProblemKeyword;
import com.xxx.enums.warn.ProdProblemKeywordSwitchEnum;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

import javax.servlet.http.HttpServletResponse;



@Service
public interface ProductionProblemKeywordService {

    /**
     * 分页查询数据
     *
     * @param keywordList 过滤条件:关键字列表,单个item时模糊查询,多个时in查询,为空时忽略
     * @param switchEnum 过滤条件:是否开启,为空时忽略
     * @param curPage 当前页号
     * @param pageSize 分页大小
     * @return 分页查询结果
     */
    Page selectPage(List keywordList, ProdProblemKeywordSwitchEnum switchEnum, Integer curPage, Integer pageSize);

    /**
     * 新增数据
     *
     * @param keyword entity
     * @return 新增后的数据(带id)
     */
    ProductionProblemKeyword insert(ProductionProblemKeyword keyword);

    /**
     * 更新数据
     *
     * @param keyword entity
     * @return 更新后的数据
     */
    ProductionProblemKeyword update(ProductionProblemKeyword keyword);

    /**
     * 根据id列表批量删除行
     *
     * @param ids id列表
     */
    void delete(List ids);

    /**
     * 根据查询条件导出xlsx文件
     *
     * @param response 返回数据
     * @param keywordList 关键字列表
     * @param switchEnum  开关枚举
     */
    void download(HttpServletResponse response, List keywordList, ProdProblemKeywordSwitchEnum switchEnum);

    /**
     * 下载仅有表头的模板文件
     *
     * @param response 返回数据
     */
    void downloadTemplate(HttpServletResponse response);

    /**
     * 通过xlsx文件导入数据
     *
     * @param file xlsx文件
     * @return 导入结果信息
     */
    String upload(MultipartFile file);

}

dao层 接口实现类



package com.xxx.service.impl.warn;

import com.xxx.domain.model.warn.ProductionProblemKeyword;
import com.xxx.enums.warn.ProdProblemKeywordSwitchEnum;
import com.xxx.service.dao.mapper.warn.ProductionProblemKeywordMapper;
import com.xxx.service.warn.ProductionProblemKeywordService;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;



@Service
@RequiredArgsConstructor
@Slf4j
public class ProductionProblemKeywordServiceImpl implements ProductionProblemKeywordService {

    private final ProductionProblemKeywordMapper mapper;

    /**
     * 分页查询数据
     *
     * @param keywordList 过滤条件:关键字列表,单个item时模糊查询,多个时in查询,为空时忽略
     * @param switchEnum 过滤条件:是否开启,为空时忽略
     * @param curPage 当前页号
     * @param pageSize 分页大小
     * @return 分页查询结果
     */
    @Override
    public Page selectPage(List keywordList, ProdProblemKeywordSwitchEnum switchEnum, Integer curPage, Integer pageSize) {
        Page page = new Page<>(curPage, pageSize);
        final QueryWrapper wrapper = getQueryWrapper(keywordList, switchEnum);
        return mapper.selectPage(page, wrapper);
    }

    /**
     * 新增数据
     *
     * @param keyword entity
     * @return 新增后的数据(带id)
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProductionProblemKeyword insert(ProductionProblemKeyword keyword) {
        final List keywordList = mapper.selectList(
                new QueryWrapper().eq("keyword", keyword.getKeyword()));
        if (CollectionUtils.isNotEmpty(keywordList)) {
            throw new IllegalArgumentException(String.format("新增失败:已存在[%s]关键字", keyword.getKeyword()));
        }
        mapper.insert(keyword);
        return keyword;
    }

    /**
     * 更新数据
     *
     * @param keyword entity
     * @return 更新后的数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProductionProblemKeyword update(ProductionProblemKeyword keyword) {
        mapper.updateById(keyword);
        return keyword;
    }

    /**
     * 根据id列表批量删除行
     *
     * @param ids id列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List ids) {
        mapper.deleteBatchIds(ids);
    }

    /**
     * 根据查询条件导出xlsx文件
     *
     * @param response 返回数据
     * @param keywordList 关键字列表
     * @param switchEnum 开关枚举
     */
    @Override
    public void download(HttpServletResponse response, List keywordList, ProdProblemKeywordSwitchEnum switchEnum) {
        final QueryWrapper wrapper = getQueryWrapper(keywordList, switchEnum);
        final List data = mapper.selectList(wrapper);
        download(response, data);
    }

    /**
     * 下载仅有表头的模板文件
     *
     * @param response 返回数据
     */
    @Override
    public void downloadTemplate(HttpServletResponse response) {

        //emptyList()返回一个空的List(使用前提是不会再对返回的list进行增加和删除操作);

        //好处:
        //1)new ArrayList()创建时有初始大小,占用内存,emptyList()不用创建一个新的对象,可以            //减少内存开销;
        //2)方法返回一个emptyList()时,不会报空指针异常,如果直接返回Null,没有进行非空判断会报空指针异常;
        //注意:此List与常用的List不同,它是Collections类里的静态内部类,在继承AbstractList后并没有实现add()、remove()等方法,所以返回的List不能进行增加和删除元素操作。
       
         download(response, Collections.emptyList());
    }

    /**
     * 通过xlsx文件导入数据
     *
     * @param file xlsx文件
     * @return 导入结果信息
     */
    @Override
    public String upload(MultipartFile file) {
        try {
            final ProductProblemKeywordListener listener = new ProductProblemKeywordListener();
            EasyExcel.read(file.getInputStream(), ProductionProblemKeyword.class, listener)
                    .sheet()
                    .doRead();
            return listener.getFinishMessage();
        } catch (IOException e) {
            log.error(file.getOriginalFilename() + "file read or write error: " + e.getLocalizedMessage());
            return "文件读写错误";
        }
    }

    /**
     * 构造查询条件
     *
     * @param keywordList 关键字列表
     * @param switchEnum 是否开启
     * @return 查询wrapper
     */
    @NotNull
    private QueryWrapper getQueryWrapper(List keywordList, ProdProblemKeywordSwitchEnum switchEnum) {
        final QueryWrapper wrapper = new QueryWrapper<>();
        if (CollectionUtils.isNotEmpty(keywordList)) {
            if (keywordList.size() == 1) {
                wrapper.like("keyword", keywordList.get(0));
            } else {
                wrapper.in("keyword", keywordList);
            }
        }
        if (switchEnum != null) {
            wrapper.eq("is_available", switchEnum.getValue());
        }
        return wrapper;
    }

    /**
     * 通用方法下载xlsx文件
     *
     * @param response 返回流
     * @param data 数据集合
     * @param  泛型对象class
     */
    private static  void download(HttpServletResponse response, Collection data) {
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + URLEncoder.encode("records", "UTF-8") + ".xlsx");
            try (ServletOutputStream out = response.getOutputStream()) {
                ExcelWriter excelWriter = EasyExcel.write(out).build();
                final ExcelWriterSheetBuilder builder = EasyExcel.writerSheet("data");
                WriteSheet writeSheet = builder.head(ProductionProblemKeyword.class).build();
                excelWriter.write(data, writeSheet);
                excelWriter.finish();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        } catch (UnsupportedEncodingException e) {
            log.error("unsupported encode type :{}", e.getMessage());
        }
    }

    /**
     * 对于ProductionProblemKeyword的Listener:用于数据导入
     */
    private class ProductProblemKeywordListener implements ReadListener {

        // 新增统计
        private int countInsert = 0;

        // 更细统计
        private int countUpdate = 0;

        // 错误发生行号列表
        private final List errorRowIndexes = new ArrayList<>();

        // 当前行计数
        private int count = 1;

        // 返回信息
        @Getter
        private String finishMessage;

        /**
         * 读取一行对象后的触发器
         *
         * @param row 一行数据的对象
         * @param analysisContext analysisContext
         */
        @Override
        public void invoke(ProductionProblemKeyword row, AnalysisContext analysisContext) {
            ++count;
            if (row.getIsAvailable() == null) {
                errorRowIndexes.add(count);
                return;
            }
            final ProductionProblemKeyword keyword = mapper.selectOne(new QueryWrapper()
                    .eq("keyword", row.getKeyword())
                    .last("limit 1"));
            if (keyword == null) {
                ++countInsert;
                mapper.insert(row);
            } else {
                ++countUpdate;
                keyword.setIsAvailable(row.getIsAvailable());
                mapper.updateById(keyword);
            }
        }

        /**
         * 读取完所有行后的处理方法
         *
         * @param analysisContext analysisContext
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            finishMessage = String.format(Locale.ROOT, "总计处理数据%d行. 新增%d行,更新%d行,异常行:%s",
                    count - 1, countInsert, countUpdate,
                    errorRowIndexes.stream().map(String::valueOf).collect(Collectors.joining(",")));
            log.info(finishMessage);
        }
    }

}

dao层 mapper接口



package com.xxx.service.dao.mapper.warn;

import com.xxx.domain.model.warn.ProductionProblemKeyword;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import org.apache.ibatis.annotations.Mapper;
 
@Mapper
public interface ProductionProblemKeywordMapper extends BaseMapper {
}




实体类

注意两点:

  • 用到的是 easyExcel框架的表格字段注解, @ExcelProperty 列头 value值,以及顺序index , 还有不需要展示在表格则用@ExcelIgnore 忽略,比如主键字段。无需展示给前端用户。
  • 状态值字段,表示 告警的词频是否开启,数据库用1,2  数值表示,而返回前端需要转换对应的实质意义,string类型 :是,否。用枚举类型,并且需要在注解中添加一个自定义的转换类 converter 


package com.xxx.domain.model.warn;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xxx.enums.warn.ProdProblemKeywordSwitchEnum;
import lombok.Data;

import java.io.Serializable;

/**
 * ProductionProblemKeyword
 */
@Data
@TableName("defect_keyword")
public class ProductionProblemKeyword implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键id
     */
    @TableId(value = "id", type = IdType.AUTO)
    @ExcelIgnore
    private Integer id;

    /**
     * 缺陷关键字
     */
    @TableField("keyword")
    @ExcelProperty(value = "告警规则", index = 0)
    @ColumnWidth(value = 15)
    private String keyword;

    /**
     * 1:有效,2:无效
     */
    @TableField("is_available")
    @ExcelProperty(value = "是否开启", index = 1,
            converter = ProdProblemKeywordSwitchEnum.KeywordSwitchEnumConverter.class)
    @ColumnWidth(value = 15)
    private ProdProblemKeywordSwitchEnum isAvailable;

}

查询请求参数类

包含有分页属性

package com.xxx.qualitybigdata.model;

import java.util.Objects;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;




 @JsonIgnoreProperties(ignoreUnknown = true)

public class ProdProblemRuleParam implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @JsonProperty("keyword")
    private String keyword = null;
    
    @JsonProperty("isAvailable")
    private String isAvailable = null;
    
    @JsonProperty("currentPage")
    private Integer currentPage = null;
    
    @JsonProperty("pageSize")
    private Integer pageSize = null;
    
    public ProdProblemRuleParam() {
        super();
    } 
    
    /**
     * 预警规则查询参数(由任意分隔符分隔)
    **/
    public String getKeyword() {
        return keyword;
    }

    public void setKeyword(String keyword) {
        this.keyword = keyword;
    }
    
    /**
     * 预警规则开关:1:有效,2:无效
    **/
    public String getIsAvailable() {
        return isAvailable;
    }

    public void setIsAvailable(String isAvailable) {
        this.isAvailable = isAvailable;
    }
    
    /**
     * 单前页号
    **/
    public Integer getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(Integer currentPage) {
        this.currentPage = currentPage;
    }
    
    /**
     * 分页大小
    **/
    public Integer getPageSize() {
        return pageSize;
    }

    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }
    
      
    
      
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        
        
        ProdProblemRuleParam prodProblemRuleParam = (ProdProblemRuleParam) o;
        return Objects.equals(this.keyword,
                prodProblemRuleParam.keyword)
                && Objects.equals(this.isAvailable,
                prodProblemRuleParam.isAvailable)
                && Objects.equals(this.currentPage,
                prodProblemRuleParam.currentPage)
                && Objects.equals(this.pageSize,
                prodProblemRuleParam.pageSize);
    } 
    
      
    @Override
    public int hashCode() {
        return Objects.hash(keyword
                , isAvailable
                , currentPage
                , pageSize);
    } 
    
       

      
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("class ProdProblemRuleParam { ");
        
        sb.append("  keyword: ").append(keyword).append(", ");
        sb.append("  isAvailable: ").append(isAvailable).append(", ");
        sb.append("  currentPage: ").append(currentPage).append(", ");
        sb.append("  pageSize: ").append(pageSize).append(", ");
        sb.append("} ");
        return sb.toString();
      }
}

修改表参数类

为了规范,在修改表单数据的时候,传参类,我们是另外定义了一个实体类,属性基本跟表实体类一样

package com.xxx.qualitybigdata.model;

import java.util.Objects;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;




 @JsonIgnoreProperties(ignoreUnknown = true)

public class ProdProblemRule implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @JsonProperty("id")
    private Integer id = null;
    
    @JsonProperty("keyword")
    private String keyword = null;
    
    @JsonProperty("isAvailable")
    private String isAvailable = null;
    
    public ProdProblemRule() {
        super();
    } 
    
    /**
     * 主键id
    **/
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
    
    /**
     * 预警规则关键字
    **/
    public String getKeyword() {
        return keyword;
    }

    public void setKeyword(String keyword) {
        this.keyword = keyword;
    }
    
    /**
     * 预警规则开关:1:有效,2:无效
    **/
    public String getIsAvailable() {
        return isAvailable;
    }

    public void setIsAvailable(String isAvailable) {
        this.isAvailable = isAvailable;
    }
    
      
    
      
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        
        
        ProdProblemRule prodProblemRule = (ProdProblemRule) o;
        return Objects.equals(this.id,
                prodProblemRule.id)
                && Objects.equals(this.keyword,
                prodProblemRule.keyword)
                && Objects.equals(this.isAvailable,
                prodProblemRule.isAvailable);
    } 
    
      
    @Override
    public int hashCode() {
        return Objects.hash(id
                , keyword
                , isAvailable);
    } 
    
       

      
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("class ProdProblemRule { ");
        
        sb.append("  id: ").append(id).append(", ");
        sb.append("  keyword: ").append(keyword).append(", ");
        sb.append("  isAvailable: ").append(isAvailable).append(", ");
        sb.append("} ");
        return sb.toString();
      }
}

枚举类 注解 @EnumValue @JsonValue

表中涉及到有一个类似状态的字段 是否开启,数据库保存的是数值1,2  表示是,否,所以我们用mp提供的一个注解功能 ,定义一个枚举类,进行使用注解

  • 在需要存储数据库的属性上添加@EnumValue注解,
  • 在需要前端展示的属性上添加@JsonValue注解;


package com.xxx.enums.warn;

import com.xxx.utils.StringUtils;

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;

import lombok.AllArgsConstructor;
import lombok.Getter;


@AllArgsConstructor
@Getter
public enum ProdProblemKeywordSwitchEnum {

    OPEN(1, "是"),
    CLOSE(2, "否");

    // 数据库中的值
    @EnumValue
    private final Integer value;

    // 前端展示的结果
    private final String label;

    /**
     * Jackson序列化方法
     *
     * @return label
     */
    @JsonValue
    public String getLabel() {
        return label;
    }

    /**
     * 反序列化
     *
     * @param label label
     * @return 枚举
     */
    public static ProdProblemKeywordSwitchEnum getByLabel(String label) {
        for (ProdProblemKeywordSwitchEnum item : values()) {
            if (StringUtils.equals(label, item.getLabel())) {
                return item;
            }
        }
        return null;
    }

    /**
     * EasyExcel映射转换类
     */
    public static class KeywordSwitchEnumConverter implements Converter {
        /**
         * 标明Excel中的类型
         *
         * @return 标明Excel中的类型
         */
        @Override
        public CellDataTypeEnum supportExcelTypeKey() {
            return CellDataTypeEnum.STRING;
        }

        /**
         * 标明数据库中的类型
         *
         * @return 标明数据库中的类型
         */
        @Override
        public Class supportJavaTypeKey() {
            return Integer.class;
        }

        /**
         * xlsx中cell转java对象方法
         *
         * @param cellData cell中的数据
         * @param contentProperty contentProperty
         * @param globalConfiguration globalConfiguration
         * @return 转换后的枚举值
         */
        @Override
        public ProdProblemKeywordSwitchEnum convertToJavaData(ReadCellData cellData,
            ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
            return getByLabel(cellData.getStringValue());
        }

        /**
         * xlsx中java对象转cell数据方法
         *
         * @param value 枚举对象
         * @param contentProperty contentProperty
         * @param globalConfiguration globalConfiguration
         * @return 写入到cell中的数据
         */
        @Override
        public WriteCellData convertToExcelData(ProdProblemKeywordSwitchEnum value,
            ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
            return new WriteCellData<>(value.getLabel());
        }
    }
}

 用户鉴权注解类 

在前面的服务层中,我们可以看到在方法上有加了一个自定义注解,  @UserPermission(username = {"用户1","用户2"}) ,传递的参数是用户信息,主要就是通过注解方式来做一个用户鉴权隔离,针对新增,修改,删除,导入的这些操作,给指定的用户赋予权限,那么前提就是我们要获取当前登录的用户人信息,然后通过对比是否是指定的用户群体,如果是,那么就可以支持其进行方法调用,否则就返回提示信息:用户没有权限



package com.xxx.annotation;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 注解:用于针对到用户级别的接口鉴权
 */
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface UserPermission {
    // 具有权限的用户列表
    String[] username() default {};
}

 用户鉴权切面类

AOP:Aspect Oriented Programming,翻译过来就是大名鼎鼎的“面向切面编程”,它是对面向对象的一种补充和完善。


AOP的使用场景一般有:数据源切换、事务管理、权限控制、日志打印等。根据它的名字我们不难理解,它的实现很像是将我们要实现的代码切入业务实现的逻辑中。它有以下特点:
1、侵入性小,几乎可以不改动原来逻辑的情况下把新的逻辑加入业务。
2、实现方便,使用几个注解就可以实现,使系统更容易扩展。
3、更好的复用代码,比如事务日志打印,简单逻辑适合所有情况。

AOP中注解的含义
@Aspect:切面。表示一个横切进业务的一个对象。它里面包含切入点(Pointcut)和Advice(通知)。
@Pointcut:切入点。表示需要切入的位置,比如某些类或者某些方法,也就是先定一个范围。
@Before:Advice(通知)的一种,切入点的方法体执行之前执行。
@Around:Advice(通知)的一种,环绕切入点执行也就是把切入点包裹起来执行。
@After:Advice(通知)的一种,在切入点正常运行结束后执行。
@AfterReturning:Advice(通知)的一种,在切入点正常运行结束后执行,异常则不执行
@AfterThrowing:Advice(通知)的一种,在切入点运行异常时执行。

 

@Around的作用

  • 既可以在目标方法之前织入增强动作,也可以在执行目标方法之后织入增强动作;
  • 可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标目标方法的执行;
  • 可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值; 当需要改变目标方法的返回值时,只能使用Around方法;
  • 虽然Around功能强大,但通常需要在线程安全的环境下使用。因此,如果使用普通的Before、AfterReturing增强方法就可以解决的事情,就没有必要使用Around增强处理了。 
  • 执行顺序:正常的执行顺序是:@Around ->@Before->主方法体->@Around中pjp.proceed()->@After->@AfterReturning 

    如果异常在Around中pjp.proceed()之前,则执行顺序为:@Around -> @After -> @AfterThrowing

    如果异常在Around中pjp.proceed()之后,则执行顺序为@Around ->@Before->主方法体->@Around中pjp.proceed()->@After->@AfterThrowing
     

根据切面编程的基本理解,我们在鉴权控制的设计,是需要根据是否有权限,来选择性的改变方法返回值,需要使用Around环绕通知。 



package com.xxx.aop;

import com.xxx.annotation.UserPermission;
import com.xxx.service.UserService;
import com.xxx.utils.ResponseUtils;
import com.xxx.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * 用户鉴权0aop
 *
 */
@Component
@Aspect
@RequiredArgsConstructor
public class UserPermissionAspect {

    private final UserService userService;

    /**
     * 针对到用户级别的接口鉴权
     *
     * @param joinPoint 切点
     * @param userPermission 注解
     * @return 切点返回结果或鉴权失败信息
     * @throws Throwable 异常
     */
    @Around(value = "@annotation(userPermission)")
    public Object userPermission(ProceedingJoinPoint joinPoint, UserPermission userPermission) throws Throwable {
        final String[] userList = userPermission.username();
        //注入用户接口方法,获取当前登录的用户信息
        final String Account = userService.getUser().getAccount();
        for (String username : userList) {
            //如果当前用户是指定用户群之一 那么执行目标方法  
            if (StringUtils.equalsIgnoreCase(username, Account)) {
                return joinPoint.proceed();
            }
        }
        //没有权限 那么就返回指定的 失败信息 给到前端 
        return ResponseUtils.unAuthResponse(null, "用户没有该接口的权限");
    }

}

你可能感兴趣的:(业务场景实例问题,Spring,boot,Java,spring,boot,java,mybatis)