Bootstrap

简单SpringCloud项目(大致的后台流程)

1.第一步先安装一个nacous注册中心,用一个简单的单机windos版本就行,也可以使用Linux下的redis集群版本,两种选择
然后在需要运行的服务器配置文件下配置指定注册中心

spring:
  application:
    #应用名称, 向注册中心注册的名称
    name: GatewayServer
  cloud:
    nacos:
      discovery:
        #注册中心地址
        server-addr: http://localhost:8848/

2.配置gateway_server代理服务器
跨域请求配置和代理服务器

spring:
  application:
    #应用名称, 向注册中心注册的名称
    name: GatewayServer
  cloud:        
    gateway:
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true  # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求
              - "*"  #所有网站
              #- "http://localhost:8090" #指定网站
              #- "http://www.leyou.com"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "*"
              #- "GET"
              #- "POST"
              #- "DELETE"
              #- "PUT"
              #- "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期
      routes:
        # -  连续多个配置,前面用-
        # 随便命名 是当前路由的唯一标识 不可以重复  最好和服务名称有关
      - id: member_route
        # 配置请求的服务的地址
        #uri: http://localhost:14221
        # 配置请求的服务名称,必须和要访问服务注册到注册中心的名称一致
        uri: lb://MemberServer
        # 断言配置  满足下面配置的所有条件 请求才会继续
        predicates:
         #路径必须正确
         - Path=/order/**,/member/**
         #允许哪些restful类型
         - Method=GET,POST,PUT,DELETE
         - Query=id,\d+  #要有参数id并且要求id为整数
         - Before=2023-02-22T11:25:40.666+08:00[Asia/Shanghai] #匹配这个时间之前的请求  UTC时间,也就是国际统一时间  +8:00 东八区
         - After=2021-02-21T12:15:30.666+08:00[Asia/Shanghai]  #匹配这个时间之后的请求
         - Between=2021-02-21T12:15:30.666+08:00[Asia/Shanghai],2023-02-21T12:15:30.666+08:00[Asia/Shanghai]

#自定义非法IP
illegal_ip:

#配置白名单(不进行拦截的地址)
white_list:  /userLogin/authUser,/memberLogin/authMember,/userLogin/chechAccessToken,/member/sentSM,/member/insert

2.2需要在启动时,自己注入HttpMessageConverters

/**
 * @ fileName:WebFluxWithOpenFeignConfig
 * @ description: gateway整合openfeign时,会报错误,
 *  *     查看错误堆栈 HttpMessageConverters 没有被注入到容器中管理
 *  *     需要在启动时,自己注入HttpMessageConverters
 * @ author:zhz
 * @ createTime:2022/3/11 16:03
 * @ version:1.0.0
 */
@Configuration
public class WebFluxWithOpenFeignConfig {

    /**
     * 实例化HttpMessageConverters
     * @param converters
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
        return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
    }
}

2.3字符过滤器

@Component
//@Order(3)
public class IllegalCharFilter implements GlobalFilter,Ordered{


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("经过了非法字符过滤器。。。。。。。。。。。");
        //让程序继续运行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 10;
    }
}

2.4IP过滤器

@Component
public class IllegalIpFilter implements GlobalFilter, Ordered {

    @Value("${illegal_ip}")
    private String illegalIp;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("经过了非法IP过滤器。。。。。。。。。。。");
        //通过过滤方法提供的参数获取request,response
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //通过request 获取IP
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        InetAddress address = remoteAddress.getAddress();
        String hostAddress = address.getHostAddress();
        System.out.println(hostAddress+"........hostAddress...............");
        //判断请求的IP是否在拦截IP列表中
        if(illegalIp.contains(hostAddress)){
            // 不合法则返回错误信息
            Map<String, Object> map = new HashMap<>();
            map.put("errorCode", ResultStatus.ILLEGAL_IP.getCode());
            map.put("errorMessage", ResultStatus.ILLEGAL_IP.getMessage());
            try {
                byte[] datas = JSON.toJSONString(map).getBytes("utf-8");
                DataBuffer buffer = response.bufferFactory().wrap(datas);
                //添加响应头
                response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                //写入自定义错误信息,并返回
                return response.writeWith(Mono.just(buffer));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        //让程序继续运行
        return chain.filter(exchange);
    }

    /**
     *
     * @return   执行的级别,小的先执行
     */
    @Override
    public int getOrder() {
        return 5;
    }
}

2.5白名单放行

@Component
public class RedirectLoginFilter implements GlobalFilter, Ordered {

    @Resource
    private RemoLoginService remoLoginService;

    @Value("${white_list}")
    private String whiteList;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("经过了转发登录过滤器。。。。。。。。。。。");
        //通过过滤方法提供的参数获取request,response
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //获取请求携带所有参数   /member/selectOne/1?a=1&b=2&c=3&accessToken=admin131321dafadf11
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        //获取IP
        String hostAddress = request.getRemoteAddress().getAddress().getHostAddress();
        System.out.println("-----------hostAddress-------"+hostAddress);
        String requestPath = request.getPath().toString();
        System.out.println("------------requestPath------------"+requestPath);
        if(requestPath.contains("/userLogin/authUser")){
            Set<Map.Entry<String, List<String>>> entryList = queryParams.entrySet();
            String userName=null;
            String passWord=null;
            for (Map.Entry<String, List<String>> stringListEntry : entryList) {
                if(stringListEntry.getKey().equals("userName")){
                    userName=stringListEntry.getValue().get(0);
                }
                if(stringListEntry.getKey().equals("passWord")){
                    passWord=stringListEntry.getValue().get(0);
                }
            }
            System.out.println("转发登录请求。。。。。。。。。。。。。"+userName+","+passWord+","+hostAddress);
            Result result = remoLoginService.authUser(userName, passWord, hostAddress);
            try {
                byte[] datas = JSON.toJSONString(result).getBytes("utf-8");
                DataBuffer buffer = response.bufferFactory().wrap(datas);
                //添加响应头
                response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                //写入自定义错误信息,并返回
                return response.writeWith(Mono.just(buffer));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        //让程序继续运行
        return chain.filter(exchange);
    }

    /**
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 6;
    }
}

2.6Sso单点登录过滤

@Component
public class SsoLoginFilter implements GlobalFilter, Ordered {

    @Resource
    private RemoLoginService remoLoginService;

    @Value("${white_list}")
    private String whiteList;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("经过了单点登录过滤器。。。。。。。。。。。");
        //通过过滤方法提供的参数获取request,response
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //获取请求路径
        RequestPath requestPath = request.getPath();
        System.out.println(requestPath.toString()+"........requestPath...............");
        //分割白名单
        String[] whitePathArray = whiteList.split(",");
        for (String whitePath : whitePathArray) {
            //如果是白名单 直接放行
            if(requestPath.toString().contains(whitePath)){
                return chain.filter(exchange);
            }
        }
        //获取请求携带所有参数   /member/selectOne/1?a=1&b=2&c=3&accessToken=admin131321dafadf11
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        //循环遍历,查看参数中是否含有accessToken  如果含有获取它对应的值,做校验
        //   entries  [a=1,b=2...accessToken=admin13]
        Set<Map.Entry<String, List<String>>> entries = queryParams.entrySet();

        //定义accessToken
        String accessToken = null;
        for (Map.Entry<String, List<String>> entry : entries) {
            //第一次  a=1   第二次  b=2 ...
            if("accessToken".equals(entry.getKey())){
                accessToken=entry.getValue().get(0);
                break;
            }
        }
        //获取IP
        String hostAddress = request.getRemoteAddress().getAddress().getHostAddress();
        boolean isTrue = remoLoginService.checkAccessToken(accessToken,hostAddress);
        //判断accessToken是否为空
        if(accessToken==null || !isTrue){
            // 不合法则返回错误信息
            Map<String, Object> map = new HashMap<>();
            map.put("errorCode", ResultStatus.TOKEN_NOT_NULL.getCode());
            map.put("errorMessage", ResultStatus.TOKEN_NOT_NULL.getMessage());
            try {
                byte[] datas = JSON.toJSONString(map).getBytes("utf-8");
                DataBuffer buffer = response.bufferFactory().wrap(datas);
                //添加响应头
                response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                //写入自定义错误信息,并返回
                return response.writeWith(Mono.just(buffer));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        //让程序继续运行
        return chain.filter(exchange);
    }

    /**
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 8;
    }
}

3.配置单点登录
3.1.单点登录配置文件配置和shiro配置

#当前服务端口号
server:
  port: 14288
#  servlet:
#    #配置上下文对象  访问的项目名称
#    context-path: /ordera
#阿里druid连接池配置
spring:
  application:
    #应用名称  一定配置,作用是注册到注册中心的名称,其他服务调用时,都是根据该名称调用
    name: SsoServer
  cloud:
    nacos:
      discovery:
        #配置nacos注册中心地址
        server-addr: http://localhost:8848
  redis:
    host: 127.0.0.1
    port: 6379
    #连接属性配置
    database: 0
    timeout: 30000ms
    jedis:
      pool:
        max-active: 20000
        max-idle: 0
        max-wait: 20000ms
#    cluster:
#      nodes: 192.168.170.166:6001,192.168.170.166:6002
#swagger配置
swagger:
  base-package: com.aaa.sso.controller
  title: "租车项目-单点登录模块"
  description: "描述"
  version: "3.0"
  contact:
    name: "AAA"
    email: "[email protected]"
    url: "https://www.baidu.com"
  terms-of-service-url: "服务条款:https://www.baidu.com"
@Configuration
public class SpringShiroConfig {

    /**
     * 实例化过滤工厂类(配置拦截路径及放行路径,配置使用SecurityManager等)
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(){
        //实例化
        ShiroFilterFactoryBean shiroFilterFactoryBean = new
                ShiroFilterFactoryBean();
        //配置使用SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        //实例化参数对象(LinkedHashMap 先后配置顺序读取)
        Map<String, String> pathMap = new LinkedHashMap<>();
        //anon  放行  authc  拦截  loginOut 退出。。。
        pathMap.put("/**","anon");
        //配置拦截或者放行路径
        shiroFilterFactoryBean.setFilterChainDefinitionMap(pathMap);
        return shiroFilterFactoryBean;
    }
    /**
     * shiro核心组件   安全管理组件   认证和授权的后台管理类
     * @return
     */
    @Bean
    public SecurityManager securityManager(){
        //实例化 SecurityManager子类,web应用使用
        DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
        //设置自定义认证器
        securityManager.setAuthenticator(customModularRealmAuthenticator());
        //设置安全数据链接桥梁   Realm
        //securityManager.setRealm(myRealm());
        List<Realm> realmList = new ArrayList<>();
        realmList.add(memberRealm());
        realmList.add(userRealm());
        securityManager.setRealms(realmList);
        return securityManager;
    }
    /**
     * 设置自定义认证器(认证策略为一个realm成功及成功)
     * @return
     */
    @Bean
    public CustomModularRealmAuthenticator customModularRealmAuthenticator(){
        CustomModularRealmAuthenticator customModularRealmAuthenticator
                =new CustomModularRealmAuthenticator();
        //配置认证策略,两个realm 只要有一个认证成功,都可以访问资源
        //无论是手机端或者管理端,登录认证后,都可以访问所有资源
        customModularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return customModularRealmAuthenticator;
    }

    /**
     * 自定义MemberRealm类
     * @return
     */
    @Bean
    public MemberRealm memberRealm(){
        //实例化安全数据链接桥梁
        MemberRealm memberRealm =new MemberRealm();
        //设置加密算法类
        memberRealm.setCredentialsMatcher(credentialsMatcher());
        return memberRealm;
    }
    /**
     * 自定义userRealm类
     * @return
     */
    @Bean
    public UserRealm userRealm(){
        //实例化安全数据链接桥梁
        UserRealm userRealm =new UserRealm();
        //设置加密算法类
        userRealm.setCredentialsMatcher(credentialsMatcher());
        return userRealm;
    }
    /**
     * 加密算法类
     * @return
     */
    @Bean
    public CredentialsMatcher credentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher =
                new HashedCredentialsMatcher();
        //设置加密算法名称
        credentialsMatcher.setHashAlgorithmName(BusinessConstant.CredentialsMatcher.ALGORITHM_NAME);
        //设置hash次数
        credentialsMatcher.setHashIterations(BusinessConstant.CredentialsMatcher.HASH_ITERATIONS);
        return credentialsMatcher;
    }
}

3.2根据登录路径访问到单点controller层

/**
 * @ fileName:UserLoginController
 * @ description: 用户登录控制器
 * @ author:zhz
 * @ createTime:2022/3/10 16:55
 * @ version:1.0.0
 */
@RestController
@RequestMapping("userLogin")
public class UserLoginController {

    //@Resource(name = "memberAuthService")
    @Autowired
    @Qualifier("userAuthService")
    private AuthService authService;
    /**
     * 会员验证功能
     * @param userName
     * @param passWord
     * @return
     */
    @GetMapping("authUser")
    public Result authUser(String userName, String  passWord,String remoteIp){
        return authService.authMemberOrUser(userName,passWord,remoteIp);
    }
    /**
     * 验证token
     * @param accessToken
     * @return
     */
    @GetMapping("checkAccessToken")
    public boolean  checkAccessToken(@RequestParam("accessToken") String accessToken,@RequestParam("remoteIp") String remoteIp){
        return authService.chechAccessToken(accessToken,remoteIp);
    }
}

3.3到实现类层

//用于判断是登录的类型
@Service("userAuthService")
public class UserAuthServiceImpl implements AuthService{

    //依赖注入request对象
    @Resource
    private HttpServletRequest request;
    //依赖注入redis模板类
    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public Result authMemberOrUser(String userName, String passWord,String remoteIp) {
        //收集用户信息
        CustomUsernamePasswordToken customUsernamePasswordToken =
                new CustomUsernamePasswordToken(userName, passWord, LoginType.USER.toString());
        //获取主体
        Subject subject = SecurityUtils.getSubject();
        try {
            //登录
            subject.login(customUsernamePasswordToken);
            //获取认证成功后的用户对象
            User user = (User)subject.getPrincipal();
            //生成token
            String accessToken = userName+ UUID.randomUUID().toString();
            //获取用户访问IP和accessToken 加密运算得到一个加密值,存入到redis中
              //获取 访问IP
           // String remoteAddr = request.getRemoteAddr();
            System.out.println("......remoteAddr..........."+remoteIp);
              //存入redis
                //设置底层存储为字符串序列化
            redisTemplate.setKeySerializer(new StringRedisSerializer());

              //存储新登录的token前,删除以前的token
            Set keys = redisTemplate.keys(userName + "*");
              // 循环删除
            for (Object key : keys) {
                redisTemplate.delete(key);
            }

            redisTemplate.opsForValue().set(accessToken,user,3, TimeUnit.DAYS);
            redisTemplate.opsForValue().get(accessToken);
            System.out.println("!11111111111111111111111111111111111111");
            System.out.println("!11111111111111111111111111111111111111");
            User o = (User)redisTemplate.opsForValue().get(accessToken);

            System.out.println("++++++++++++++++++++"+o.getUserId());

            System.out.println("我是token+++++++++++++++++++++++++"+redisTemplate.opsForValue().get(accessToken));
            // 使用获取的IP和生成的token进行加密运算,得到加密值也要存储
            Sha512Hash sha512Hash = new Sha512Hash(remoteIp,accessToken,10);
            //也要存储redis
            redisTemplate.opsForValue().set(accessToken+"CheckToken",sha512Hash.toString());
            //记录登录日志
            System.out.println("================"+accessToken);

            //返回token
            return new Result(ResultStatus.SUCCESS.getCode(),ResultStatus.SUCCESS.getMessage(),
                    accessToken);
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
        return new Result(ResultStatus.ERROR_USERNAME_PASSWORD.getCode(),
                ResultStatus.ERROR_USERNAME_PASSWORD.getMessage(),null);
    }

    @Override
    public boolean chechAccessToken(String accessToken,String remoteIp) {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        Boolean aBoolean = redisTemplate.hasKey(accessToken);
        if(!aBoolean){
            return false;
        }
        //获取 访问IP
        //String remoteAddr = request.getRemoteAddr();
        System.out.println("-------远程ip--------"+remoteIp);
        // 使用获取的IP和生成的token进行加密运算,得到加密值也要存储
        Sha512Hash sha512Hash = new Sha512Hash(remoteIp,accessToken,10);
        //获取原来存储的值
        Object saveRemoteIPAndTokenValue = redisTemplate.opsForValue().get(accessToken + "CheckToken");
        //进行比对
        if(sha512Hash.toString().equals(saveRemoteIPAndTokenValue.toString())){
            return true;
        }
        return false;
    }
}

3.3 login触发单点登录shiro认证,进入领域

public class UserRealm extends AuthorizingRealm {

    @Resource
    private RemoteUserService remoteUserService;

    /**
     * 获取授权数据的方法
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    /**
     * 获取认证数据单点方法
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
      //获取收集到用户名
      String userName =   (String)authenticationToken.getPrincipal();
      //根据用户名获取对象
        Result result = remoteUserService.queryUserByUserName(userName);
       // 失败,说明用户错没有获取到用户信息
        if(result.getCode()==500){
            throw  new AccountException();
        }
        //转换User
        User user = JSON.parseObject(JSON.toJSONString(result.getData()), User.class);

        return new SimpleAuthenticationInfo(user,
                user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),
                getName());
    }
}

3.4认证之后会触发认证方法执行,应为是多服务器需要远程调用,上面是接口,下面是远程方法

@FeignClient(value = "SystemServer")
public interface RemoteUserService {

    /**
     * 根据用户名查询用户信息
     * @param phoneNum
     * @return
     */
    @GetMapping("/user/selectUserByUserName")
    Result queryUserByUserName(@RequestParam("phoneNum") String phoneNum);
}

  @GetMapping("selectUserByUserName")
    public Result selectUserByUserName(@RequestParam("phoneNum") String phoneNum){
        //封装查询
        QueryWrapper<User> queryWrapper =new QueryWrapper<>();
        queryWrapper.eq("del_flag",0);
        queryWrapper.eq("status",0);
        queryWrapper.eq("user_name",phoneNum);// where user_name='隔壁老王'
        //查询列表
        List<User> userList = userService.list(queryWrapper);
        //判断
        if(CollUtil.isNotEmpty(userList)){
            return new Result(ResultStatus.SUCCESS.getCode(),
                    ResultStatus.SUCCESS.getMessage(),userList.get(0));
        }
        return new Result(ResultStatus.ERROR.getCode(),
                ResultStatus.ERROR.getMessage(),"没有用户信息");
    }

3.5区分领域认证类型实现类

public class CustomUsernamePasswordToken extends UsernamePasswordToken {

    private String loginType;

    /**
     * 自定CustomUsernamePasswordToken 区分是用户还是会员
     * @param username
     * @param password
     * @param loginType
     */
    public CustomUsernamePasswordToken(String username, String password, String loginType) {
        super(username, password);
        this.loginType = loginType;
    }

    public String getLoginType() {
        return loginType;
    }

    public void setLoginType(String loginType) {
        this.loginType = loginType;
    }
}

3.6单多realm判断

public class CustomModularRealmAuthenticator extends ModularRealmAuthenticator {

    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 判断getRealms()是否返回为空
        assertRealmsConfigured();
        // 强制转换回自定义的CustomizedToken  多态  向下转型
        CustomUsernamePasswordToken customizedToken = (CustomUsernamePasswordToken) authenticationToken;
        // 登录类型
        String loginType = customizedToken.getLoginType();
        // 所有Realm
        Collection<Realm> realms = getRealms();
        System.out.println(realms+"..realm对象..");
        // 登录类型对应的所有Realm
        Collection<Realm> typeRealms = new ArrayList<>();
        for (Realm realm : realms) {
            if (realm.getName().contains(loginType)){
                typeRealms.add(realm);
            }
        }
        // 判断是单Realm还是多Realm
        if (typeRealms.size() == 1){
            //单realm认证
            return doSingleRealmAuthentication(typeRealms.iterator().next(), customizedToken);
        }
        else{
            //多realm认证
            return doMultiRealmAuthentication(typeRealms, customizedToken);
        }
    }
}

4.权限服务器配置后台配置
4.1配置文件配置

#当前服务端口号
server:
  port: 14270
#  servlet:
#    #配置上下文对象  访问的项目名称
#    context-path: /ordera
#阿里druid连接池配置
spring:
  datasource:
    druid:
      url: jdbc:mysql://localhost:3306/qy142crms?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
      username: root
      password: zzgsxygwb
      initial-size: 1
      max-active: 20
      min-idle: 10
      max-wait: 10
  application:
    #应用名称  一定配置,作用是注册到注册中心的名称,其他服务调用时,都是根据该名称调用
    name:  SystemServer
  cloud:
    nacos:
      discovery:
        #配置nacos注册中心地址
        server-addr: http://localhost:8848
mybatis-plus:
  # mapper.xml配置
  mapper-locations: classpath:mapper/*.xml
  configuration:
    #控制台日志输出配置
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  #别名包配置
  type-aliases-package: com.aaa.system.entity
#swagger配置
swagger:
  base-package: com.aaa.system.controller
  title: "租车项目-会员模块-订单swagger"
  description: "描述"
  version: "3.0"
  contact:
    name: "AAA"
    email: "[email protected]"
    url: "https://www.baidu.com"
  terms-of-service-url: "服务条款:https://www.baidu.com"

4.2菜单服务实现类和sql语句

/**
 * 菜单权限表(Menu)表服务实现类
 *
 * @author makejava
 * @since 2022-03-11 20:10:43
 */
@Service("menuService")
public class MenuServiceImpl extends ServiceImpl<MenuDao, Menu> implements MenuService {

    @Resource
    private MenuDao menuDao;
    @Resource
    private RedisTemplate redisTemplate;
    /**
     * 根据用户编号查询树形数据(用户登录后左侧树菜单数据)
     * @return
     */
    @Override
    public List<TreeNode> queryTreeDataByUserId(String accessToken) {
        // System.out.println("queryTreeDataByUserId,sessionId-------"+session.getId());
      //  User user =  (User)session.getAttribute("userInfo");
        //查询所有数据
       // redisTemplate.opsForValue().set(accessToken,user,3, TimeUnit.DAYS);
        System.out.println("hhhhhhhhhhhhhhhhhhhhhhhhhhhh");
       // User accessToken1 = (User)redisTemplate.opsForValue().get(accessToken);

       // Integer userId = accessToken1.getUserId();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
       // System.out.println(userId+"       111111111111111111111111111111111");
        System.out.println("!11111111111111111111111111111111111111");
        System.out.println("!11111111111111111111111111111111111111");
        User o = (User)redisTemplate.opsForValue().get(accessToken);

        System.out.println("++++++++++++++++++++"+o.getUserId());


        List<TreeNode> treeNodes = menuDao.queryTreeDataByUserId(o.getUserId());
        //List<TreeNode> treeNodes = menuDao.queryTreeDataByUserId(user.getUserId());
        //定义一个返回的结果集
        List<TreeNode> tempTreeNodes = new ArrayList<>();
        //拼装为树形数据
        if(treeNodes!=null&&treeNodes.size()>0){
            //循环遍历
            for (com.aaa.common.entity.TreeNode treeNode : treeNodes) {
                //查找一级节点
                if(treeNode.getParentId()==0){
                    tempTreeNodes.add(treeNode);
                    //为一级的所有子节点查找并绑定孩子
                    findAndBindChild(treeNode,treeNodes);
                }
            }
        }
        return tempTreeNodes;
    }
    /**
     * 所有的树形菜单数据
     * @return 单条数据
     */
    @Override
    public List<TreeNode> queryAllTreeData() {
        //查询所有数据
        List<TreeNode> treeNodes = menuDao.queryAllTreeData();
        //定义一个返回的结果集
        List<TreeNode> tempTreeNodes = new ArrayList<>();
        //拼装为树形数据
        if(treeNodes!=null&&treeNodes.size()>0){
            //循环遍历
            for (TreeNode treeNode : treeNodes) {
                //查找一级节点
                if(treeNode.getParentId()==0){
                    tempTreeNodes.add(treeNode);
                    //为一级的所有子节点查找并绑定孩子
                    findAndBindChild(treeNode,treeNodes);
                }
            }
        }
        return tempTreeNodes;
    }

    /**
     * 为所有子节点查找并绑定孩子
     * @param currentTreeNode
     * @param treeNodeList
     */
    private void findAndBindChild(TreeNode currentTreeNode, List<TreeNode> treeNodeList){
        //再次循环查出的所有menu集合
        for (TreeNode treeNode : treeNodeList) {
            //判断循环节点的父节点id是否和当前节点的id相等,如果相等,说明当前循环节点是当前节点的孩子
            if(currentTreeNode.getId()==treeNode.getParentId()){
                //是孩子,就要添加到当前节点的孩子集合
                //1,获取当前节点的孩子集合
                List<TreeNode> children = currentTreeNode.getChildren();
                //2,一定要判断孩子集合是否为空,如果是第一个孩子,孩子集合肯定为空,如果使用肯定空指针
                if(children==null){
                    //3,如果为空,一定要实例化
                    children = new ArrayList<>();
                }
                // 4,根据上面判断,循环节点的父ID等于当前节点ID说明是孩子,加入
                children.add(treeNode);
                // 5,重新设置孩子集合
                currentTreeNode.setChildren(children);
                // 为了提高效率,每找到一个孩子,就从集合中删除,再次查找集合会变小,效率会变高 理论上可行,但是ArrayList不允许一边遍历一边删除元素
                // treeNodeList.remove(treeNode);
                //6,递归自己调用自己,再次判断当前循环有没有孩子,如果有执行相同操作
                findAndBindChild(treeNode,treeNodeList);
            }
        }
    }
    /**
     * 根据用户ID查询该用户对应的按钮权限集合
     * @return
     */
    @Override
    public List<String> queryButtonPermsByUserId(String accessToken) {
        //User user =  (User)session.getAttribute("userInfo");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        User o = (User)redisTemplate.opsForValue().get(accessToken);
        return menuDao.queryButtonPermsByUserId(o.getUserId());
    }

    /**
     * 新增数据
     *
     * @param menu 实例对象
     * @return 实例对象
     */
    @Override
    public Menu insert(Menu menu) {
        DateTime date = DateUtil.date();
        menu.setMenuName(menu.getLabel());
        menu.setCreateTime(date);
        menu.setUpdateTime(date);
        this.menuDao.insert(menu);
        return menu;
    }

    /**
     * 修改数据
     *
     * @param menu 实例对象
     * @return 实例对象
     */
    @Override
    public Menu update(Menu menu) {
        DateTime date = DateUtil.date();
        menu.setUpdateTime(date);
        this.menuDao.update(menu);
        return menu;
    }

    /**
     * 通过主键删除数据
     *
     * @param menuId 主键
     * @return 是否成功
     */
    @Override
    public boolean deleteById(Integer menuId) {
        return this.menuDao.deleteById(menuId) > 0;
    }
    /**
     * 添加角色权限关联方法
     * @param roleId
     * @param menuIds
     * @return
     */
    @Override
    @Transactional
    public int addRoleAndMenu(int roleId,String menuIds) {
        //根据角色id删除该角色原来关联的所有权限
        int sucNum1 = menuDao.deleteRoleMenuByRoleId(roleId);
        int sucNum2 = 0;
        //添加新的关联
        if(!StrUtil.isEmpty(menuIds)){
            String[] menuIdArray = menuIds.split(",");
            //定义入参List
            List<Map> mapList = new ArrayList<>();
            Map  map = null;
            //循环拼装参数
            for (String menuId : menuIdArray) {
                map = new HashMap();
                map.put("roleId",roleId);
                map.put("menuId",menuId);
                mapList.add(map);
            }
            //执行添加
            sucNum2 = menuDao.addRoleAndMenu(mapList);
        }
        if(sucNum1>0&&sucNum2>0){
            return 1;
        }
        return 0;
    }
    /**
     * 根据角色ID查询该角色对应的权限ID集合
     * @param roleId
     * @return
     */
    @Override
    public List<Integer> queryMenuIdsByRoleId(int roleId) {
        return menuDao.queryMenuIdsByRoleId(roleId);
    }

}

    <resultMap type="com.aaa.system.entity.Menu" id="MenuMap">
        <result property="menuId" column="menu_id" jdbcType="INTEGER"/>
        <result property="menuName" column="menu_name" jdbcType="VARCHAR"/>
        <result property="parentId" column="parent_id" jdbcType="INTEGER"/>
        <result property="orderNum" column="order_num" jdbcType="INTEGER"/>
        <result property="url" column="url" jdbcType="VARCHAR"/>
        <result property="menuType" column="menu_type" jdbcType="VARCHAR"/>
        <result property="visible" column="visible" jdbcType="VARCHAR"/>
        <result property="perms" column="perms" jdbcType="VARCHAR"/>
        <result property="icon" column="icon" jdbcType="VARCHAR"/>
        <result property="createBy" column="create_by" jdbcType="VARCHAR"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        <result property="updateBy" column="update_by" jdbcType="VARCHAR"/>
        <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
        <result property="remark" column="remark" jdbcType="VARCHAR"/>
    </resultMap>

    <!-- 批量插入 -->
    <insert id="insertBatch" keyProperty="menuId" useGeneratedKeys="true">
        insert into qy142crms.sys_menu(menu_name, parent_id, order_num, url, menu_type, visible, perms, icon, create_by,
        create_time, update_by, update_time, remark)
        values
        <foreach collection="entities" item="entity" separator=",">
            (#{entity.menuName}, #{entity.parentId}, #{entity.orderNum}, #{entity.url}, #{entity.menuType},
            #{entity.visible}, #{entity.perms}, #{entity.icon}, #{entity.createBy}, #{entity.createTime},
            #{entity.updateBy}, #{entity.updateTime}, #{entity.remark})
        </foreach>
    </insert>
    <!-- 批量插入或按主键更新 -->
    <insert id="insertOrUpdateBatch" keyProperty="menuId" useGeneratedKeys="true">
        insert into qy142crms.sys_menu(menu_name, parent_id, order_num, url, menu_type, visible, perms, icon, create_by,
        create_time, update_by, update_time, remark)
        values
        <foreach collection="entities" item="entity" separator=",">
            (#{entity.menuName}, #{entity.parentId}, #{entity.orderNum}, #{entity.url}, #{entity.menuType},
            #{entity.visible}, #{entity.perms}, #{entity.icon}, #{entity.createBy}, #{entity.createTime},
            #{entity.updateBy}, #{entity.updateTime}, #{entity.remark})
        </foreach>
        on duplicate key update
        menu_name = values(menu_name) , parent_id = values(parent_id) , order_num = values(order_num) , url =
        values(url) , menu_type = values(menu_type) , visible = values(visible) , perms = values(perms) , icon =
        values(icon) , create_by = values(create_by) , create_time = values(create_time) , update_by = values(update_by)
        , update_time = values(update_time) , remark = values(remark)
    </insert>
    <!--根据用户编号查询树形数据-->
    <select id="queryTreeDataByUserId" resultType="com.aaa.common.entity.TreeNode">
            select  menu_id id,menu_name label,parent_id parentId,icon,url from sys_menu m where visible=0 and menu_type!='F'
            and exists (
                select 1  from sys_role_menu rm where   rm.menu_id=m.menu_id
                and  exists(
                     select 1 from  sys_user_role ur where ur.role_id=rm.role_id
                     and ur.user_id=#{userId}
                )
            )
   </select>
    <select id="queryAllTreeData" resultType="com.aaa.common.entity.TreeNode">
         select  menu_id id,menu_name label,parent_id parentId,icon,url,menu_type menuType,perms,visible,
            ifnull((select menu_name from sys_menu m2 where m1.parent_id=m2.menu_id),'根节点') parentName
             from sys_menu m1 where visible=0

    </select>
    <!--根据用户ID查询该用户对应的按钮权限集合-->
    <select id="queryButtonPermsByUserId" resultType="string">
         select  perms from sys_menu m where visible=0 and menu_type='F'
            and exists (
                select 1  from sys_role_menu rm where   rm.menu_id=m.menu_id
                and  exists(
                     select 1 from  sys_user_role ur where ur.role_id=rm.role_id
                     and ur.user_id=#{userId}
                )
            )
    </select>
    <!--新增-->
    <insert id="insert" keyProperty="menuId" useGeneratedKeys="true">
        insert into sys_menu(menu_name, parent_id, order_num, url, menu_type, visible, perms, icon, create_by, create_time, update_by, update_time, remark)
        values (#{menuName}, #{parentId}, #{orderNum}, #{url}, #{menuType}, #{visible}, #{perms}, #{icon}, #{createBy}, #{createTime}, #{updateBy}, #{updateTime}, #{remark})
    </insert>


    <!--通过主键修改数据-->
    <update id="update">
        update sys_menu
        <set>
            <if test="menuName != null and menuName != ''">
                menu_name = #{menuName},
            </if>
            <if test="parentId != null">
                parent_id = #{parentId},
            </if>
            <if test="orderNum != null">
                order_num = #{orderNum},
            </if>
            <if test="url != null and url != ''">
                url = #{url},
            </if>
            <if test="menuType != null and menuType != ''">
                menu_type = #{menuType},
            </if>
            <if test="visible != null and visible != ''">
                visible = #{visible},
            </if>
            <if test="perms != null and perms != ''">
                perms = #{perms},
            </if>
            <if test="icon != null and icon != ''">
                icon = #{icon},
            </if>
            <if test="createBy != null and createBy != ''">
                create_by = #{createBy},
            </if>
            <if test="createTime != null">
                create_time = #{createTime},
            </if>
            <if test="updateBy != null and updateBy != ''">
                update_by = #{updateBy},
            </if>
            <if test="updateTime != null">
                update_time = #{updateTime},
            </if>
            <if test="remark != null and remark != ''">
                remark = #{remark},
            </if>
        </set>
        where menu_id = #{menuId}
    </update>

    <!--通过主键删除-->
    <delete id="deleteById">
        delete from db_aiops03_crms.sys_menu where menu_id = #{menuId}
    </delete>
    <!--根据角色id删除该角色原来关联的所有权限-->
    <delete id="deleteRoleMenuByRoleId">
         delete from sys_role_menu where role_id=#{roleId}
    </delete>
    <!--添加角色权限关联方法-->
    <insert id="addRoleAndMenu">
        insert into sys_role_menu values
        <foreach collection="list" item="map" separator=",">
            (#{map.roleId},#{map.menuId})
        </foreach>
    </insert>

    <!--根据角色ID查询该角色对应的权限ID集合-->
    <select id="queryMenuIdsByRoleId" resultType="int">
           select menu_id from sys_role_menu where role_id=#{roleId}
    </select>
</mapper>

4.3角色服务实现类和sql语句

/**
 * 角色信息表(Role)表服务实现类
 *
 * @author makejava
 * @since 2022-03-14 16:52:39
 */
@Service("roleService")
public class RoleServiceImpl extends ServiceImpl<RoleDao, Role> implements RoleService {
    @Resource
    private RoleDao roleDao;
    @Override
    public List<Integer> queryRoleIdsByUserId(Integer userId) {
        return roleDao.queryRoleIdsByUserId(userId);
    }

    @Transactional
    @Override
    public int addUserAndRole(int userId, String roleIds) {
        //定义是否成功标识
        boolean isSuc=true;
        //删除以前该用户id关联的所有角色
        int resultN = roleDao.deleteUserRoleByUserId(userId);
        //判断是否为空 方法2执行
        /*if(!StrUtil.isEmpty(roleIds)){
            //不为空分割 5,6,7,8  roleIdArray=[5,6,7,8]
            String[] roleIdArray = roleIds.split(",");
            for (String roleId : roleIdArray) {
                //循环添加
                int resultNum = roleDao.addUserRole(userId, roleId);
                if (resultNum<1){
                    isSuc=false;
                }
            }
        }
         if(isSuc&&resultN>0){
            return 1;
        }
        */
        int sucNum = 0;
        //方法1执行
        if(!StrUtil.isEmpty(roleIds)){
            List<Map> mapList = new ArrayList<>();
            //不为空分割 5,6,7,8  roleIdArray=[5,6,7,8]
            String[] roleIdArray = roleIds.split(",");
            Map map = null;
            for (String roleId : roleIdArray) {
                map = new HashMap();
                map.put("userId",userId);
                map.put("roleId",roleId);
                mapList.add(map);
            }
            sucNum = roleDao.addUserAndRole(mapList);
        }
        if(resultN>0&&sucNum>0){
            return 1;
        }
        return 0;
    }


    /**
     * 通过Map作为筛选条件分页查询
     *
     * @param map
     * @return
     */
    @Override
    public PageInfo queryAll(Map map) {
        if (map.get("pageNo") == null || map.get("pageSize") == null) {
            throw new CustomException(ResultStatus.INVALID_ARGUMENT.getCode(),
                    ResultStatus.INVALID_ARGUMENT.getMessage());
        }
        Integer pageNo = Integer.valueOf(map.get("pageNo") + "");
        Integer pageSize = Integer.valueOf(map.get("pageSize") + "");
        //设值页码和每页显示数量
        PageHelper.startPage(pageNo, pageSize);
        //默认查询有效数据
        if(map.get("isHaveSatus")!=null) {
            map.put("status", "0");
        }
        //默认查询不被删除
        map.put("delFlag","0");
        //使用查询结果,构造PageInfo
        PageInfo pageInfo = new PageInfo(this.roleDao.queryAll(map));
        return pageInfo;
    }


}
<mapper namespace="com.aaa.system.dao.RoleDao">

    <resultMap type="com.aaa.system.entity.Role" id="RoleMap">
        <result property="roleId" column="role_id" jdbcType="INTEGER"/>
        <result property="roleName" column="role_name" jdbcType="VARCHAR"/>
        <result property="roleKey" column="role_key" jdbcType="VARCHAR"/>
        <result property="roleSort" column="role_sort" jdbcType="INTEGER"/>
        <result property="dataScope" column="data_scope" jdbcType="VARCHAR"/>
        <result property="status" column="status" jdbcType="VARCHAR"/>
        <result property="delFlag" column="del_flag" jdbcType="VARCHAR"/>
        <result property="createBy" column="create_by" jdbcType="VARCHAR"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        <result property="updateBy" column="update_by" jdbcType="VARCHAR"/>
        <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
        <result property="remark" column="remark" jdbcType="VARCHAR"/>
    </resultMap>

    <!-- 批量插入 -->
    <insert id="insertBatch" keyProperty="roleId" useGeneratedKeys="true">
        insert into qy142crms.sys_role(role_name, role_key, role_sort, data_scope, status, del_flag, create_by,
        create_time, update_by, update_time, remark)
        values
        <foreach collection="entities" item="entity" separator=",">
            (#{entity.roleName}, #{entity.roleKey}, #{entity.roleSort}, #{entity.dataScope}, #{entity.status},
            #{entity.delFlag}, #{entity.createBy}, #{entity.createTime}, #{entity.updateBy}, #{entity.updateTime},
            #{entity.remark})
        </foreach>
    </insert>
    <!-- 批量插入或按主键更新 -->
    <insert id="insertOrUpdateBatch" keyProperty="roleId" useGeneratedKeys="true">
        insert into qy142crms.sys_role(role_name, role_key, role_sort, data_scope, status, del_flag, create_by,
        create_time, update_by, update_time, remark)
        values
        <foreach collection="entities" item="entity" separator=",">
            (#{entity.roleName}, #{entity.roleKey}, #{entity.roleSort}, #{entity.dataScope}, #{entity.status},
            #{entity.delFlag}, #{entity.createBy}, #{entity.createTime}, #{entity.updateBy}, #{entity.updateTime},
            #{entity.remark})
        </foreach>
        on duplicate key update
        role_name = values(role_name) , role_key = values(role_key) , role_sort = values(role_sort) , data_scope =
        values(data_scope) , status = values(status) , del_flag = values(del_flag) , create_by = values(create_by) ,
        create_time = values(create_time) , update_by = values(update_by) , update_time = values(update_time) , remark =
        values(remark)
    </insert>
    <!-- 根据用户ID获取角色ID集合-->
    <select id="queryRoleIdsByUserId" resultType="int">
         select role_id  from sys_user_role where user_id=#{userId}
    </select>
    <!--根据用户id删除该用户原来关联的所有角色-->
    <delete id="deleteUserRoleByUserId">
         delete from sys_user_role where user_id=#{userId}
    </delete>
    <!--添加用户角色关联方法1  (1,1),(1,2),(1,3)-->
    <insert id="addUserAndRole">
        insert into sys_user_role values
        <foreach collection="list" item="map" separator=",">
            (#{map.userId},#{map.roleId})
        </foreach>
    </insert>

    <!--添加用户角色关联方法2-->
    <insert id="addUserRole">
        insert into sys_user_role values(#{userId},#{roleId})
    </insert>
    <!--查询单个-->
    <select id="queryById" resultMap="RoleMap">
        select
          role_id, role_name, role_key, role_sort, data_scope, status, del_flag, create_by, create_time, update_by, update_time, remark
        from db_aiops03_crms.sys_role
        where role_id = #{roleId}
    </select>

    <!--通过实体作为筛选条件查询-->
    <select id="queryAll" resultMap="RoleMap">
        select
        role_id, role_name, role_key, role_sort, data_scope, status, del_flag, create_by, create_time, update_by,
        update_time, remark
        from db_aiops03_crms.sys_role
        <where>
            <if test="roleId != null">
                and role_id = #{roleId}
            </if>
            <if test="roleName != null and roleName != ''">
                and role_name = #{roleName}
            </if>
            <if test="roleKey != null and roleKey != ''">
                and role_key = #{roleKey}
            </if>
            <if test="roleSort != null">
                and role_sort = #{roleSort}
            </if>
            <if test="dataScope != null and dataScope != ''">
                and data_scope = #{dataScope}
            </if>
            <if test="status != null and status != ''">
                and status = #{status}
            </if>
            <if test="delFlag != null and delFlag != ''">
                and del_flag = #{delFlag}
            </if>
            <if test="createBy != null and createBy != ''">
                and create_by = #{createBy}
            </if>
            <if test="createTime != null">
                and create_time = #{createTime}
            </if>
            <if test="updateBy != null and updateBy != ''">
                and update_by = #{updateBy}
            </if>
            <if test="updateTime != null">
                and update_time = #{updateTime}
            </if>
            <if test="remark != null and remark != ''">
                and remark = #{remark}
            </if>
        </where>
    </select>
</mapper>

5.手机端服务
5.1验证码和登录的方法和sql和用到的工具类

/**
 * (Member)表服务实现类
 *
 * @author makejava
 * @since 2022-03-15 19:42:05
 */
@Service("memberService")
public class MemberServiceImpl extends ServiceImpl<MemberDao, Member> implements MemberService {

    @Resource
    private MemberDao memberDao;

    @Resource
    private RedisTemplate redisTemplate;

  /*  @Resource
    private CouponHistoryService couponHistoryService;*/


    /**
     * 通过实体作为筛选条件查询
     *
     * @param pageNo 查询起始位置
     * @param pageSize 查询条数
     * @param member   实例对象
     * @return 对象列表
     */
    @Override
    public Result queryAllByParam(int pageNo, int pageSize, Member member) {
        if (pageNo == 0 || pageSize == 0){
            throw new CustomException(ExceptionConstant.INVALID_ARGUMENT.getErrorCode(),
                    ExceptionConstant.INVALID_ARGUMENT.getErrorMessage());
        }
        PageHelper.startPage(pageNo,pageSize);
        PageInfo<Member> pageInfo = new PageInfo<>(memberDao.queryAll(member));
        return new Result(ReturnStatus.SUCCESS.getReturnCode(),
                ReturnStatus.SUCCESS.getReturnMsg(), pageInfo);
    }



    @Override
    public Result sentSM(String phoneNum) {
        //生成验证码
        String verifyCode = SMSUtil.createVerifyCode();
        //发送验证码到手机
        SMSUtil.sendSMS(phoneNum,verifyCode);
        //把验证码存放起来,存放发送时间,用来验证是否过期   验证正确与否
        // Session session = SessionUtil.getSession();
        //把验证码存入第三方缓存,并设置过期时间
        redisTemplate.opsForValue().set(phoneNum,verifyCode,
                180, TimeUnit.SECONDS);
       /* //写入注册方法中
       long  diffDate  = DateUtil.date().getTime()-存放验证的时间;
        if(diffDate/1000/60>10){
            //提示验证码过期
        }
        if(session.getAttribute(phoneNum+"vc").equals(verifyCode)){

        }*/
        //session.setAttribute(phoneNum+"vc",verifyCode);
        //session.setAttribute(phoneNum+"st", DateUtil.date());
        return    new Result(ResultStatus.SUCCESS.getCode(),
                ResultStatus.SUCCESS.getMessage(),
                verifyCode);
    }

    /**
     * 新增数据
     *
     * @param member 实例对象
     * @return 实例对象
     */
    @Override
    @Transactional
    public Member insert(Member member) {
        //验证验证码
        //获取验证码
        String code = member.getCode();
        //从redis中获取存储的验证码
        Object verifyCoide = redisTemplate.opsForValue().get(member.getPhoneNum());
        if(verifyCoide==null||!verifyCoide.equals(code)){
            throw new CustomException(ResultStatus.ERROR_VERIFY_CODE.getCode(),
                    ResultStatus.ERROR_VERIFY_CODE.getMessage());
        }
        //获取原始密码
        String password = member.getPassword();
        //随机盐值
        String pwSalt = UUID.randomUUID().toString();
        //计算密码
        Sha512Hash sha512Hash = new Sha512Hash(password,pwSalt,
                BusinessConstant.CredentialsMatcher.HASH_ITERATIONS);
        member.setPassword(sha512Hash.toString());
        member.setPwSalt(pwSalt);
        member.setStatus(0);
        member.setMemberLevelId(1);
        member.setAccountBalance(0.0);
        member.setIntegration(0);
        member.setGrowth(0.0);
        member.setHistoryIntegration("0");
        this.memberDao.insert(member);
        //优惠券历史表添加记录 注册送优惠券功能
        //获取新增用户的ID
    /*    Integer id = member.getId();
        CouponHistory couponHistory =new CouponHistory();
        couponHistory.setCouponId(1l);
        couponHistory.setMemberId(Long.valueOf(id));
        couponHistory.setCouponCode("100001");
        couponHistory.setGetType(0);
        couponHistory.setCreateTime(DateUtil.date());
        couponHistory.setUseStatus(1);
        couponHistory.setUseTime(null);
        couponHistory.setOrderId(null);
        couponHistoryService.insert(couponHistory);*/
        return member;
    }
}
<mapper namespace="com.aaa.member.dao.MemberDao">

    <resultMap type="com.aaa.member.entity.Member" id="MemberMap">
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="username" column="username" jdbcType="VARCHAR"/>
        <result property="password" column="password" jdbcType="VARCHAR"/>
        <result property="pwSalt" column="pw_salt" jdbcType="VARCHAR"/>
        <result property="status" column="status" jdbcType="INTEGER"/>
        <result property="idType" column="id_type" jdbcType="VARCHAR"/>
        <result property="pathFront" column="path_front" jdbcType="VARCHAR"/>
        <result property="pathReverse" column="path_reverse" jdbcType="VARCHAR"/>
        <result property="name" column="name" jdbcType="VARCHAR"/>
        <result property="contactName" column="contact_name" jdbcType="VARCHAR"/>
        <result property="contactPhone" column="contact_phone" jdbcType="VARCHAR"/>
        <result property="contactAddress" column="contact_address" jdbcType="VARCHAR"/>
        <result property="licenseNo" column="license_no" jdbcType="VARCHAR"/>
        <result property="sex" column="sex" jdbcType="VARCHAR"/>
        <result property="phoneNum" column="phone_num" jdbcType="VARCHAR"/>
        <result property="address" column="address" jdbcType="VARCHAR"/>
        <result property="email" column="email" jdbcType="VARCHAR"/>
        <result property="memberLevelId" column="member_level_id" jdbcType="INTEGER"/>
        <result property="accountBalance" column="Account_Balance" jdbcType="NUMERIC"/>
        <result property="integration" column="integration" jdbcType="INTEGER"/>
        <result property="growth" column="growth" jdbcType="NUMERIC"/>
        <result property="historyIntegration" column="history_integration" jdbcType="VARCHAR"/>
        <result property="openId" column="open_id" jdbcType="INTEGER"/>
        <result property="token" column="token" jdbcType="VARCHAR"/>
        <result property="idCard" column="id_card" jdbcType="VARCHAR"/>
        <result property="headPic" column="head_pic" jdbcType="VARCHAR"/>
    </resultMap>




    <!--新增所有列-->
    <insert id="insert" keyProperty="id" useGeneratedKeys="true">
        insert into mbs_member(username, password, pw_salt, status, id_type, path_front, path_reverse, name, contact_name, contact_phone, contact_address, license_no, sex, phone_num, address, email, member_level_id, Account_Balance, integration, growth, history_integration, open_id, token, id_card, head_pic)
        values (#{username}, #{password}, #{pwSalt}, #{status}, #{idType}, #{pathFront}, #{pathReverse}, #{name}, #{contactName}, #{contactPhone}, #{contactAddress}, #{licenseNo}, #{sex}, #{phoneNum}, #{address}, #{email}, #{memberLevelId}, #{accountBalance}, #{integration}, #{growth}, #{historyIntegration}, #{openId}, #{token}, #{idCard}, #{headPic})
    </insert>

    <!-- 批量插入 -->
    <insert id="insertBatch" keyProperty="id" useGeneratedKeys="true">
        insert into qy142crms.mbs_member(username, password, pw_salt, status, id_type, path_front, path_reverse, name,
        contact_name, contact_phone, contact_address, license_no, sex, phone_num, address, email, member_level_id,
        Account_Balance, integration, growth, history_integration, open_id, token, id_card, head_pic)
        values
        <foreach collection="entities" item="entity" separator=",">
            (#{entity.username}, #{entity.password}, #{entity.pwSalt}, #{entity.status}, #{entity.idType},
            #{entity.pathFront}, #{entity.pathReverse}, #{entity.name}, #{entity.contactName}, #{entity.contactPhone},
            #{entity.contactAddress}, #{entity.licenseNo}, #{entity.sex}, #{entity.phoneNum}, #{entity.address},
            #{entity.email}, #{entity.memberLevelId}, #{entity.accountBalance}, #{entity.integration}, #{entity.growth},
            #{entity.historyIntegration}, #{entity.openId}, #{entity.token}, #{entity.idCard}, #{entity.headPic})
        </foreach>
    </insert>
    <!-- 批量插入或按主键更新 -->
    <insert id="insertOrUpdateBatch" keyProperty="id" useGeneratedKeys="true">
        insert into qy142crms.mbs_member(username, password, pw_salt, status, id_type, path_front, path_reverse, name,
        contact_name, contact_phone, contact_address, license_no, sex, phone_num, address, email, member_level_id,
        Account_Balance, integration, growth, history_integration, open_id, token, id_card, head_pic)
        values
        <foreach collection="entities" item="entity" separator=",">
            (#{entity.username}, #{entity.password}, #{entity.pwSalt}, #{entity.status}, #{entity.idType},
            #{entity.pathFront}, #{entity.pathReverse}, #{entity.name}, #{entity.contactName}, #{entity.contactPhone},
            #{entity.contactAddress}, #{entity.licenseNo}, #{entity.sex}, #{entity.phoneNum}, #{entity.address},
            #{entity.email}, #{entity.memberLevelId}, #{entity.accountBalance}, #{entity.integration}, #{entity.growth},
            #{entity.historyIntegration}, #{entity.openId}, #{entity.token}, #{entity.idCard}, #{entity.headPic})
        </foreach>
        on duplicate key update
        username = values(username) , password = values(password) , pw_salt = values(pw_salt) , status = values(status)
        , id_type = values(id_type) , path_front = values(path_front) , path_reverse = values(path_reverse) , name =
        values(name) , contact_name = values(contact_name) , contact_phone = values(contact_phone) , contact_address =
        values(contact_address) , license_no = values(license_no) , sex = values(sex) , phone_num = values(phone_num) ,
        address = values(address) , email = values(email) , member_level_id = values(member_level_id) , Account_Balance
        = values(Account_Balance) , integration = values(integration) , growth = values(growth) , history_integration =
        values(history_integration) , open_id = values(open_id) , token = values(token) , id_card = values(id_card) ,
        head_pic = values(head_pic)
    </insert>


    <!--通过实体作为筛选条件查询-->
    <select id="queryAll" resultMap="MemberMap">
        select
        a.id, a.username, password, pw_salt, status, id_type, path_front, path_reverse, a.name, contact_name, contact_phone,
        contact_address, license_no, sex, phone_num, address, email, member_level_id, Account_Balance, integration,
        growth, history_integration, open_id, token, head_pic,b.name levelName from
        mbs_member a left join mbs_member_level b on
        a.member_level_id=b.id
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="username != null and username != ''">
                and a.username like '%' #{username} '%'
            </if>
            <if test="password != null and password != ''">
                and password = #{password}
            </if>
            <if test="pwSalt != null and pwSalt != ''">
                and pw_salt = #{pwSalt}
            </if>
            <if test="status != null">
                and status = #{status}
            </if>
            <if test="idType != null and idType != ''">
                and id_type = #{idType}
            </if>
            <if test="pathFront != null and pathFront != ''">
                and path_front = #{pathFront}
            </if>
            <if test="pathReverse != null and pathReverse != ''">
                and path_reverse = #{pathReverse}
            </if>
            <if test="name != null and name != ''">
                and a.name like '%' #{name} '%'
            </if>
            <if test="contactName != null and contactName != ''">
                and contact_name = #{contactName}
            </if>
            <if test="contactPhone != null and contactPhone != ''">
                and contact_phone = #{contactPhone}
            </if>
            <if test="contactAddress != null and contactAddress != ''">
                and contact_address = #{contactAddress}
            </if>
            <if test="licenseNo != null and licenseNo != ''">
                and license_no = #{licenseNo}
            </if>
            <if test="sex != null and sex != ''">
                and sex = #{sex}
            </if>
            <if test="phoneNum != null and phoneNum != ''">
                and phone_num = #{phoneNum}
            </if>
            <if test="address != null and address != ''">
                and address = #{address}
            </if>
            <if test="email != null and email != ''">
                and email = #{email}
            </if>
            <if test="memberLevelId != null">
                and member_level_id = #{memberLevelId}
            </if>
            <if test="accountBalance != null">
                and Account_Balance = #{accountBalance}
            </if>
            <if test="integration != null">
                and integration = #{integration}
            </if>
            <if test="growth != null and growth != 0 ">
                and growth = #{growth}
            </if>
            <if test="historyIntegration != null and historyIntegration != ''">
                and history_integration = #{historyIntegration}
            </if>
            <if test="openId != null">
                and open_id = #{openId}
            </if>
            <if test="token != null and token != ''">
                and token = #{token}
            </if>
            <if test="headPic != null and headPic != ''">
                and head_pic = #{headPic}
            </if>
        </where>
    </select>

</mapper>

/**
 * @ fileName:SMSUtil
 * @ description:
 * @ author:zhz
 * @ createTime:2021/7/29 15:22
 * @ version:1.0.0
 */
public class SMSUtil {

    /**
     * 发送验证码
     * @param phoneNumbers  电话号码
     * @param verifyCode  验证码
     * @return
     */
   /* public static  String  sendSMS(String phoneNumbers,String verifyCode){

        try {
            //设置超时时间-可自行调整
            System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
            System.setProperty("sun.net.client.defaultReadTimeout", "10000");
            //初始化ascClient需要的几个参数
            final String product = "Dysmsapi";//短信API产品名称(短信产品名固定,无需修改)
            final String domain = "dysmsapi.aliyuncs.com";//短信API产品域名(接口地址固定,无需修改)
            //替换成你的AK
            final String accessKeyId = "LTAI4GEH3ciDi5SVsvFVi6H2";//你的accessKeyId,参考本文档步骤2
            final String accessKeySecret = "l0I6MhbsKPGH1tLmVE25L5naKKO79C";//你的accessKeySecret,参考本文档步骤2
            //初始化ascClient,暂时不支持多region(请勿修改)
            IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId,
                    accessKeySecret);
            DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
            IAcsClient acsClient = new DefaultAcsClient(profile);

            //组装请求对象
            SendSmsRequest request = new SendSmsRequest();
            //使用post提交
            request.setMethod(MethodType.POST);
            //必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式;发送国际/港澳台消息时,接收号码格式为国际区号+号码,如“85200000000”
            request.setPhoneNumbers(phoneNumbers);
            //必填:短信签名-可在短信控制台中找到
            request.setSignName(BusinessConstant.Sms.SIGNNAME);
            //必填:短信模板-可在短信控制台中找到,发送国际/港澳台消息时,请使用国际/港澳台短信模版
            request.setTemplateCode(BusinessConstant.Sms.TEMPLATE_CODE);
            //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
            //友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
             //参考:request.setTemplateParam("{\"变量1\":\"值1\",\"变量2\":\"值2\",\"变量3\":\"值3\"}")
            request.setTemplateParam("{\"code\":\""+verifyCode+"\"}");
            //可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段)
            //request.setSmsUpExtendCode("90997");

            //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
            request.setOutId("yourOutId");
          //请求失败这里会抛ClientException异常
            SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
            if(sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) {
                  return "suc";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "error";
    }*/

    /**
     * 使用AK&SK初始化账号Client
     *
     * @param accessKeyId
     * @param accessKeySecret
     * @return Client
     * @throws Exception
     */
    public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
        Config config = new Config()
                // 您的AccessKey ID
                .setAccessKeyId(accessKeyId)
                // 您的AccessKey Secret
                .setAccessKeySecret(accessKeySecret);
        // 访问的域名
        config.endpoint = "dysmsapi.aliyuncs.com";
        return new com.aliyun.dysmsapi20170525.Client(config);
    }

    /**
     * 新版本的发送接口
     *
     * @param phoneNumbers
     * @param verifyCode
     * @return
     */
    public static String sendSMS(String phoneNumbers, String verifyCode) {
        try {
            // java.util.List<String> args = java.util.Arrays.asList(args_);
/*        final String accessKeyId = "LTAI4GEH3ciDi5SVsvFVi6H2";//你的accessKeyId,参考本文档步骤2
        final String accessKeySecret = "l0I6MhbsKPGH1tLmVE25L5naKKO79C";//你的accessKeySecret,参考本文档步骤2*/
            com.aliyun.dysmsapi20170525.Client client = SMSUtil.createClient("LTAI4GEH3ciDi5SVsvFVi6H2", "l0I6MhbsKPGH1tLmVE25L5naKKO79C");
            SendSmsRequest request = new SendSmsRequest();
            //使用post提交
            //request.setMethod(MethodType.POST);
            //必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式;发送国际/港澳台消息时,接收号码格式为国际区号+号码,如“85200000000”
            request.setPhoneNumbers(phoneNumbers);
            //必填:短信签名-可在短信控制台中找到
            request.setSignName(BusinessConstant.Sms.SIGNNAME);
            //必填:短信模板-可在短信控制台中找到,发送国际/港澳台消息时,请使用国际/港澳台短信模版
            request.setTemplateCode(BusinessConstant.Sms.TEMPLATE_CODE);
            //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
            //友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
            //参考:request.setTemplateParam("{\"变量1\":\"值1\",\"变量2\":\"值2\",\"变量3\":\"值3\"}")
            request.setTemplateParam("{\"code\":\"" + verifyCode + "\"}");
            //可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段)
            //request.setSmsUpExtendCode("90997");

            //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
            request.setOutId("yourOutId");
            // 复制代码运行请自行打印 API 的返回值
            SendSmsResponse sendSmsResponse = client.sendSms(request);
            if (sendSmsResponse.getBody().getCode() != null && sendSmsResponse.getBody().getCode().endsWith("OK")) {
                return "suc";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "error";
    }

    /**
     * 生成验证码
     *
     * @return
     */
    public static String createVerifyCode() {
        Random random = new Random();
        return String.valueOf(random.nextInt(900000) + 100000);
    }

    public static void main(String[] args) {
        String s = SMSUtil.sendSMS("17508926376", createVerifyCode());
        System.out.println(s+"++");
        if("suc".equals(s)){
            System.out.println("成功!");
        }
    }

}

5.2aop操作日志,作用与整个服务器,远程调用新增日志

package com.aaa.member.util;

import com.aaa.common.bo.OperLog;
import com.aaa.common.bo.User;
import com.aaa.common.constant.BusinessConstant;
import com.aaa.member.service.OperLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;

/**
 * fileName:OperationLogUtil
 * description:切面功能的实现类,操作日志记录类 通知或增强
 * author: znb
 * createTime:2020/12/3 0003 08:53
 * version:1.0.0
 */
@Component
@Aspect //通知或增强类 注释
@SuppressWarnings("all")
public class OperationLogUtil {

    @Autowired
    private OperLogService operLogService;

    @Autowired
    private HttpSession session;

//    @Pointcut(value = "execution()")
//    private void pointCut(){}

    /**
     * 操作日志记录功能
     * @param joinPoint
     */
    @AfterReturning(pointcut = "execution(* com.aaa.member.controller.*.insert*(..))" +
                    "||execution(* com.aaa.member.controller.*.update*(..))" +
                    "||execution(* com.aaa.member.controller.*.delete*(..))")
    public void recordOperLog(JoinPoint joinPoint){
        OperLog operLog = new OperLog();
        operLog.setTitle("会员管理模块");
        //获取连接点的方法名称
        String methodName = joinPoint.getSignature().getName();
        //定义业务类型变量
        int businessType = 0;
        if (methodName.contains("insert")){
            businessType = BusinessConstant.OperationLogType.INSERT;
        }else if (methodName.contains("update")){
            businessType = BusinessConstant.OperationLogType.UPDATE;
        }else {
            businessType = BusinessConstant.OperationLogType.DELETE;
        }
        //设置日志业务操作类型
        operLog.setBusinessType(businessType);
        //获取目标对象的名称
        String targetName = joinPoint.getTarget().getClass().getName();
        //操作方法名称
        operLog.setMethod(targetName+"."+methodName);
        operLog.setOperatorType(BusinessConstant.OperLogCustomerType.BACKUSER);
        User user = (User)session.getAttribute("userInfo");
        if (user!=null){
            operLog.setOperName(user.getLoginName());
            operLog.setDeptName(user.getDeptId()+"");
        }
        //获取Request对象
        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request =  requestAttributes.getRequest();
        //获取IP地址
        String remoteAddr = request.getRemoteAddr();
        operLog.setOperIp(remoteAddr);
        operLog.setOperUrl(request.getRequestURI());
        //根据获取到的IP地址,调用远程接口,判断IP归属地,如果获取不到,说明是内网IP
        operLog.setOperLocation("内网IP");
        //获取连接点的参数
        Object[] args = joinPoint.getArgs();
        if (args!=null&&args.length>0){
            String simpleName = args[0].getClass().getSimpleName();
            operLog.setOperParam(simpleName);
        }
        operLog.setStatus(0);
        operLog.setErrorMsg("异常类名称");
        operLog.setOperTime(new Date());
        //执行添加操作
        operLogService.insert(operLog);
    }
}

/**
 * 操作日志记录(OperLog)表服务接口
 *
 * @author makejava
 * @since 2020-12-03 08:39:00
 */
@FeignClient("SystemServer")
public interface OperLogService {


    /**
     * 远程调用服务contentService中 新增数据
     *
     * @param operLog 实例对象
     * @return 实例对象
     */
    @PostMapping("/operLog/insert")
    OperLog insert(@RequestBody OperLog operLog);


}

5.3mybatis-pulas分页插件

/**
 * @ fileName:MybatisPlusConfig
 * @ description:
 * @ author:葛爷独创代码
 * @ createTime:2022/3/16 17:20
 * @ version:1.0.0
 */
@Configuration
public class MybatisPlusConfig {
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
;