royi-vue

若依(前后端分离版)

1.1 什么是若依?

一套权限管理的开源项目

  1. 用,减少自己的工作量
  2. 学习优秀开源项目的底层编程思想,设计思路,提高自己的编程能力

1.2登录逻辑

1.2.1 生成验证码

基本思路

1+1=?@2

1+1=?转成图片存入前端,

2存入redis 前端通过 unid 来拿到后端的值

http://localhost/dev-api/captcaImage 前端请求

它做了一个反向管理,进行代理,映射到后端,解决跨域问题

devServer: {
  host: '0.0.0.0',
      //如果希望外部网络访问就需要制定如下 localhost 是127.0.0.1 专供自己访问,告诉服务器监听0.0.0.0意味着让服务器监听每一个端口
  port: port,
  open: true,
  proxy: {
    // detail: https://cli.vuejs.org/config/#devserver-proxy
    [process.env.VUE_APP_BASE_API]: {
      target: `http://localhost:8080`,
      changeOrigin: true,
      pathRewrite: {
        ['^' + process.env.VUE_APP_BASE_API]: ''
          //将 /dev-api 替换成 ''再映射到 8080 端口
      }
    }
  },
  disableHostCheck: true
    
},

验证码后端逻辑

 @GetMapping("/captchaImage")
    public AjaxResult getCode(HttpServletResponse response) throws IOException
    {
        AjaxResult ajax = AjaxResult.success();
//        查看验证码的开头是否开启
        boolean captchaOnOff = configService.selectCaptchaOnOff();
        ajax.put("captchaOnOff", captchaOnOff);
        if (!captchaOnOff)
        {
            return ajax;
        }

        // 保存验证码信息生成 uuid
        String uuid = IdUtils.simpleUUID();
        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;

        String capStr = null, code = null;
        BufferedImage image = null;

        // 生成验证码
        if ("math".equals(captchaType))
        {
            String capText = captchaProducerMath.createText();
            capStr = capText.substring(0, capText.lastIndexOf("@"));
            code = capText.substring(capText.lastIndexOf("@") + 1);
            image = captchaProducerMath.createImage(capStr);
        }
        else if ("char".equals(captchaType))
        {
            capStr = code = captchaProducer.createText();
            image = captchaProducer.createImage(capStr);
        }
		//存入 将这个答案存入 redis
        redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        // 转换流信息写出
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try
        {
            ImageIO.write(image, "jpg", os);
        }
        catch (IOException e)
        {
            return AjaxResult.error(e.getMessage());
        }

        ajax.put("uuid", uuid);
        ajax.put("img", Base64.encode(os.toByteArray()));
        return ajax;
    }

使用kaptcha工具包进行生成

1.2.2 登录的流程

  1. 校验验证码
  2. 校验用户名与密码
  3. 生成 token

使用异步任务管理器结合线程池,实现了异步操作日志记录,和业务逻辑实现解耦合

boolean captchaOnOff = configService.selectCaptchaOnOff();
        // 验证码开关
        if (captchaOnOff)
        {
            validateCaptcha(username, code, uuid);
        }
        // 用户验证
        Authentication authentication = null;
        try
        {
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                //异步生成日志 先是生成一个任务丢到线程池内,由线程池分配一个线程让它来执行
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
//        将每次的登录记录,记录在 login_info数据库 里面
        recordLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);

1.2.3 getInfo

获取当前用户的角色与权限信息,存储到 vuex 中

前端

在 permission 内进行发送请求

router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
    to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
    /* has token*/
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done()
    } else {
      if (store.getters.roles.length === 0) {
        // 判断当前用户是否已拉取完user_info信息
        // dispatch:含有异步操作,数据提交至 actions ,可用于向后台提交数据
		//commit:同步操作,数据提交至 mutations ,可用于登录成功后读取用户信息写到缓存里
        store.dispatch('GetInfo').then(() => {
          store.dispatch('GenerateRoutes').then(accessRoutes => {
            // 根据roles权限生成可访问的路由表
            router.addRoutes(accessRoutes) // 动态添加可访问路由表
            next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
          })
        }).catch(err => {
            store.dispatch('LogOut').then(() => {
              Message.error(err)
              next({ path: '/' })
            })
          })
      } else {
        next()
      }
    }
  } else {
    // 没有token
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登录白名单,直接进入
      next()
    } else {
      next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
      NProgress.done()
    }
  }
})

后端逻辑

@GetMapping("getInfo")
public AjaxResult getInfo()
{
    SysUser user = SecurityUtils.getLoginUser().getUser();
    // 角色集合
    Set<String> roles = permissionService.getRolePermission(user);
    // 权限集合
    Set<String> permissions = permissionService.getMenuPermission(user);//查是什么角色然后拿到权限 如果是 admin 用户直接给所有的权限如果是其它用户就需要查 user_role 与 role 与 user 表一起拿到用户的具体的权限
    AjaxResult ajax = AjaxResult.success();
    ajax.put("user", user);
    ajax.put("roles", roles);
    ajax.put("permissions", permissions);
    return ajax;
}

1.2.4 getRouters

根据当前权限获取动态路由

@GetMapping("getRouters")
public AjaxResult getRouters()
{
    Long userId = SecurityUtils.getUserId();
    List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
    return AjaxResult.success(menuService.buildMenus(menus));
}

在 select MenuTreeByUserId 中最后调用了getChildPerms 这个方法,执行递归返回一个多级菜单的对象给前端

public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId)
{
    List<SysMenu> returnList = new ArrayList<SysMenu>();
    for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext();)
    {
        SysMenu t = (SysMenu) iterator.next();
        // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
        if (t.getParentId() == parentId)
        {
            recursionFn(list, t);//用子结点看它有没有子结点
            returnList.add(t);
        }
    }
    return returnList;
}
private void recursionFn(List<SysMenu> list, SysMenu t)
    {
        // 得到子节点列表
        List<SysMenu> childList = getChildList(list, t);
        t.setChildren(childList);
        for (SysMenu tChild : childList)
        {
            if (hasChild(list, tChild))
            {
                recursionFn(list, tChild);
            }
        }
    }

1.3用户管理

1.3.1.list

流程: 加载 vue 页面->请求后台数据

@PreAuthorize("@ss.hasPermi('system:user:list')")//对其进行权限控制
@GetMapping("/list")
public TableDataInfo list(SysUser user)
{
    startPage();//在这里用 pageHelper 进行将查询语句拦截进行封装分页
    List<SysUser> list = userService.selectUserList(user);
    return getDataTable(list);
}
@Override
@DataScope(deptAlias = "d", userAlias = "u") //查询的时候给表设置别名
public List<SysUser> selectUserList(SysUser user)
{
    return userMapper.selectUserList(user);
}
protected void startPage()
    {
        PageDomain pageDomain = TableSupport.buildPageRequest();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
        {
            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
            Boolean reasonable = pageDomain.getReasonable();
            PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
            //这里的 reasonable 是对参数进行逻辑处理,保证参数的正确性
        }
    }

1.3.2 treeselect

  1. 查出所有的部门数据

  2. 组装成一个数组

    @GetMapping("/treeselect")
    public AjaxResult treeselect(SysDept dept)
    {
        List<SysDept> depts = deptService.selectDeptList(dept);
        return AjaxResult.success(deptService.buildDeptTreeSelect(depts));
    }
    

1.3.3 查询数据

// 节点单击事件
handleNodeClick(data) {
  this.queryParams.deptId = data.id;
  this.getList();
},

然后发请求给后端,后端返回接口

1.3.4 新增按钮

前端

/** 新增按钮操作 */
handleAdd() {
  this.reset();
  this.getTreeselect();//getUser 是获取用户后台信息
  getUser().then(response => {
    this.postOptions = response.posts;
    this.roleOptions = response.roles;
    this.open = true;
    this.title = "添加用户";
    this.form.password = this.initPassword;
  });
},

后端

userId 是如果有 userId 就是修改,如果没有 userId 就是创建

@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping(value = { "/", "/{userId}" })
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
{
    userService.checkUserDataScope(userId);
    AjaxResult ajax = AjaxResult.success();
    List<SysRole> roles = roleService.selectRoleAll();
    ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
    ajax.put("posts", postService.selectPostAll());
    if (StringUtils.isNotNull(userId))
    {
        ajax.put(AjaxResult.DATA_TAG, userService.selectUserById(userId));
        ajax.put("postIds", postService.selectPostListByUserId(userId));
        ajax.put("roleIds", roleService.selectRoleListByUserId(userId));
    }
    return ajax;
}
@PreAuthorize("@ss.hasPermi('system:user:add')")
    @Log(title = "用户管理", businessType = BusinessType.INSERT)
    @PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user)
{
    if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user.getUserName())))
    {
        return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
    }
    else if (StringUtils.isNotEmpty(user.getPhonenumber())
             && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
    {
        return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
    }
    else if (StringUtils.isNotEmpty(user.getEmail())
             && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
    {
        return AjaxResult.error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
    }
    user.setCreateBy(getUsername());
    user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
    return toAjax(userService.insertUser(user));
}
@Override
@Transactional
public int insertUser(SysUser user)
{
    // 新增用户信息
    int rows = userMapper.insertUser(user);
    // 新增用户岗位关联
    insertUserPost(user);
    // 新增用户与角色管理
    insertUserRole(user);
    return rows;
}

1.3.5 更改角色

前端

/** 修改按钮操作 */
handleUpdate(row) {
  this.reset();
  this.getTreeselect();
  const userId = row.userId || this.ids;
  getUser(userId).then(response => {
    this.form = response.data;
    this.postOptions = response.posts;
    this.roleOptions = response.roles;
    this.form.postIds = response.postIds;
    this.form.roleIds = response.roleIds;
    this.open = true;
    this.title = "修改用户";
    this.form.password = "";
  });
},
 @PreAuthorize("@ss.hasPermi('system:user:edit')")
   @Log(title = "用户管理", businessType = BusinessType.UPDATE)
   @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysUser user)
    {
        userService.checkUserAllowed(user);
        if (StringUtils.isNotEmpty(user.getPhonenumber())
                && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user)))
        {
            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
        }
        else if (StringUtils.isNotEmpty(user.getEmail())
                && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user)))
        {
            return AjaxResult.error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
        }
        user.setUpdateBy(getUsername());
        return toAjax(userService.updateUser(user));
    }
}

数据库:

  1. 修改User
  2. 重新维护它的 user_post 和 user_role
@Override
@Transactional 添加了事务如果出现了异常就会直接回滚了
    public int updateUser(SysUser user)
    {
        Long userId = user.getUserId();
        // 删除用户与角色关联
        userRoleMapper.deleteUserRoleByUserId(userId);
        // 新增用户与角色管理
        insertUserRole(user);
        // 删除用户与岗位关联
        userPostMapper.deleteUserPostByUserId(userId);
        // 新增用户与岗位管理
        insertUserPost(user);
        return userMapper.updateUser(user);
    }

1.3.6 删除数据

前端

/** 删除按钮操作 */
handleDelete(row) {
  const userIds = row.userId || this.ids;
  this.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function() {
    return delUser(userIds);
  }).then(() => {
    this.getList();
    this.$modal.msgSuccess("删除成功");
  }).catch(() => {});
},

后端

/**
 * 删除用户
 */
@PreAuthorize("@ss.hasPermi('system:user:remove')")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public AjaxResult remove(@PathVariable Long[] userIds)
{
    if (ArrayUtils.contains(userIds, getUserId()))//判断当前用户是否在要删除的用户中,如果在就抛异常
    {
        return error("当前用户不能删除");
    }
    return toAjax(userService.deleteUserByIds(userIds));//开始删除 
}
/**
     * 批量删除用户信息
     * 
     * @param userIds 需要删除的用户ID
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteUserByIds(Long[] userIds)
    {
        for (Long userId : userIds)
        {
            checkUserAllowed(new SysUser(userId));
        }
        // 删除用户与角色关联
        userRoleMapper.deleteUserRole(userIds);
        // 删除用户与岗位关联
        userPostMapper.deleteUserPost(userIds);
        return userMapper.deleteUserByIds(userIds);// 这个并不是真正的删除只是逻辑删除,就是在数据库中有一个字段表明它是否被删除了
    }
<delete id="deleteUserByIds" parameterType="Long">
   update sys_user set del_flag = '2' where user_id in 
   <foreach collection="array" item="userId" open="(" separator="," close=")">
      #{userId}
      </foreach> 
    <!--> 可以看到其中只是改了一个字段 将 del_flag 置为2<--->
</delete>

1.4 异步任务管理器

//异步生成日志 先是生成一个任务丢到线程池内,由线程池分配一个线程让它来执行
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
/**
 * 单例模式属于 饿汉式即上来就创建了一个 对象也不管你用不用
 */
private AsyncManager(){}

private static AsyncManager me = new AsyncManager();

public static AsyncManager me()
{
    return me;
}

excute 函数需要传入一个 Task 对象 Task 类实现了Runable 接口,是一个任务由线程Thread 去执行

/**
 * 记录登录信息
 * 
 * @param username 用户名
 * @param status 状态
 * @param message 消息
 * @param args 列表
 * @return 任务task
 */
public static TimerTask recordLogininfor(final String username, final String status, final String message,
        final Object... args)
{
    final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
    final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
    return new TimerTask()
    {
        @Override
        public void run()
        {
            String address = AddressUtils.getRealAddressByIP(ip);
            StringBuilder s = new StringBuilder();
            s.append(LogUtils.getBlock(ip));
            s.append(address);
            s.append(LogUtils.getBlock(username));
            s.append(LogUtils.getBlock(status));
            s.append(LogUtils.getBlock(message));
            // 打印信息到日志
            sys_user_logger.info(s.toString(), args);
            // 获取客户端操作系统
            String os = userAgent.getOperatingSystem().getName();
            // 获取客户端浏览器
            String browser = userAgent.getBrowser().getName();
            // 封装对象
            SysLogininfor logininfor = new SysLogininfor();
            logininfor.setUserName(username);
            logininfor.setIpaddr(ip);
            logininfor.setLoginLocation(address);
            logininfor.setBrowser(browser);
            logininfor.setOs(os);
            logininfor.setMsg(message);
            // 日志状态
            if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
            {
                logininfor.setStatus(Constants.SUCCESS);
            }
            else if (Constants.LOGIN_FAIL.equals(status))
            {
                logininfor.setStatus(Constants.FAIL);
            }
            // 插入数据
            SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
        }
    };
}

封装了登录用户的信息,执行添加操作,这里并不会执行而是将任务交给线程对象来执行.

异步任务管理器,内部定义了一个线程池,然后根据业务创建添加日志的任务,解耦合,日志全部统一处理

@Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService()
{
    return new ScheduledThreadPoolExecutor(corePoolSize,
            new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build())
    {
        @Override
        protected void afterExecute(Runnable r, Throwable t)
        {
            super.afterExecute(r, t);
            Threads.printException(r, t);
        }
    };
}

1.5 代码自动生成

分别在admin src 与 ui src 目录下复制粘贴然后直接再 rebuild 一下项目实现生成修改操作

在这里插入图片描述

前后端之间使用 jwt 对 token 进行封装,然后这里作个简单的鉴别

what is jwt JSON WEB TOKEN

JWT,为了在网络应用环境间传递声明面执行的一种基于 JSON 的开放标准,可以用来认证也可用于加密。

session

session 保存在服务端内存中的一个对象,主要用来存储所有访问过该服务商的客户端信息,从而保存用户会话状态,但服务端重启用户信息也就消失

token

与 session 的原理大概相同

流程上是这样的:

  • 用户使用用户名密码来请求服务器
  • 服务器进行验证用户的信息
  • 服务器通过验证发送给用户一个token
  • 客户端存储token,并在每次请求时附送上这个token值
  • 服务端验证token值,并返回数据

这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持跨域。

jwt 长什么样

jwt 本身并不安全,依赖的是 https 协议

jwt 由三段信息构成 头部,载荷,签证

三段都会进行 base64 进行加密

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

签证

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)
  • payload (base64后的)
  • secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

导入 excel

pol与easy excel讲解

常用场景

  1. 将用户信息导出为 excel
  2. 将 excel 表中的信息导入到网站数据库

使用

 // 1. 创建一个工作薄 03版本 
        Workbook workbook = new HSSFWorkbook();
//        2. 创建一个工作表
        Sheet sheet = workbook.createSheet("猿创孵化表");
//        3.创建一个行 0代表第一行
        Row row = sheet.createRow(0);
//        4.创建一个单元格 0 代表第一个单元格
        Cell cell11 = row.createCell(0);
//        5. 设置值
        cell11.setCellValue("姓名");
        Cell cell13 = row.createCell(2);
        Cell cell12 = row.createCell(1);
        cell12.setCellValue("班级");
        String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell12.setCellValue(time);
//        获得输出流
        FileOutputStream fileOutputStream = new FileOutputStream("猿创算法孵化表.xls");
//        写入位置
        workbook.write(fileOutputStream);
//        关闭流
        fileOutputStream.close();
        System.out.println("文件生成完毕");
  // 1. 创建一个工作薄 07 07的使用只有后缀名的不同与类的不同
        Workbook workbook = new XSSFWorkbook();
//        2. 创建一个工作表
        Sheet sheet = workbook.createSheet("猿创孵化表");
//        3.创建一个行 0代表第一行
        Row row = sheet.createRow(0);
//        4.创建一个单元格 0 代表第一个单元格
        Cell cell11 = row.createCell(0);
//        5. 设置值
        cell11.setCellValue("姓名");
        Cell cell13 = row.createCell(2);
        Cell cell12 = row.createCell(1);
        cell12.setCellValue("班级");
        String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell12.setCellValue(time);
//        获得输出流
        FileOutputStream fileOutputStream = new FileOutputStream("猿创算法孵化表07.xlsx");
//        写入位置
        workbook.write(fileOutputStream);
//        关闭流
        fileOutputStream.close();
        System.out.println("文件生成完毕");

大数据量时

03版本的比较快,但不能超过65536行

07版本的比较慢

为了解决07版本比较慢的这一点我们可以使用 sxxfworkbook 来加速这一过程,

它是实现 BigGridDemo 策略的流版本,它允许编写非常大的数据而不用担心内存耗尽

因为在任何时候,只有可配置的一部分保存在内存中

注意

过程中会产生临时文件,需要清理临时文件,默认由100条记录被保存在内存中,如果超过这数量,则最前面的数据被写入临时文件

@Test
public void testWrite03()throws Exception{
    long begin = System.currentTimeMillis();
    Workbook workbook = new SXSSFWorkbook();
    Sheet sheet = workbook.createSheet("猿创狗仔计划");
    for (int i = 0; i < 65536; i++) {
        Row row = sheet.createRow(i);
        for (int j = 0; j < 10; j++) {
            Cell cell = row.createCell(j);
            cell.setCellValue(j);
        }
    }
    FileOutputStream fileOutputStream = new FileOutputStream("猿创狗仔计划.xlsx");
    workbook.write(fileOutputStream);

    fileOutputStream.close();
    ((SXSSFWorkbook) workbook).dispose();
    long end = System.currentTimeMillis();
    System.out.println(end-begin);

}

工作中狠一点就是表读过来,存入集合,再遍历放入数据库,再根据数据库中的字段在前端解析

普通的 poi 太复杂,直接上 easyexcel

导入依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.0.5</version>
</dependency>
// 写法2
String fileName = "Test.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());

然后直接调 api 进行

下载文件逻辑

在 rouyi-admin 中的 CommonController 中进行修改,先对后缀名进行合法性验证,然后得到

try
{
    if (!FileUtils.checkAllowDownload(fileName))//这里主要对后缀名及非法下载进行处理
    {
        throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
    }
    String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);//这里是合成一个生成后的文件名称
    String filePath = RuoYiConfig.getDownloadPath() + fileName;//通过读取royi的配置文件得到文件应该存放的位置

    response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);//设置响应类型
    FileUtils.setAttachmentResponseHeader(response, realFileName);//设置响应头
    FileUtils.writeBytes(filePath, response.getOutputStream());//这个非常重要大概是通过
    if (delete)
    {
        FileUtils.deleteFile(filePath);
    }
}
catch (Exception e)
{
    log.error("下载文件失败", e);
}

导入文件是 SysUserController.java 里面的 importData 方法负责

public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
{
    ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class); //获取这个工具类
    List<SysUser> userList = util.importExcel(file.getInputStream()); // 将文件读入流放入 在内部进行表格的处理数据封装成一个 list 返回 其中已经写入了文件
    String operName = getUsername();
    String message = userService.importUser(userList, updateSupport, operName); //对这个list 进行检索并返回消息有多少条成功或者失败
    return AjaxResult.success(message);
}

如果我们需要自定义哪些数据可以被下载我们可以在SecurityConfig中进行配置

若依的注销在其中也进行了配置

公共基础

  • 前端获取方法:this.$store.state.user.name

  • 后端获取方法

    • String username = SecurityUtils.getUsername();
      

注解

restController 注解 controller 会使return 的方法不会返回jsp 页面,或者html,配置的html 解析器不起作用,返回的内容就是return 的内容

关于translational 注解只能放在 public 方法上,spring 的默认事务规则是当遇到运行异常与error时会回滚

如果想针对检查异常进行事务回滚,可以在@Transactional注解里使用 rollbackFor属性明确指定异常。

并且出现了异常不要试图去用catch去捕获

@Transactional(rollbackFor = Exception.class)

responseBody 将 java 对象转换为 json 格式

@ControllerAdvice 和 @RestControllerAdvice都是对Controller进行增强的,可以全局捕获spring mvc抛的异常。
RestControllerAdvice = ControllerAdvice + ResponseBody

return new ScheduledThreadPoolExecutor(corePoolSize,
        new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build())
{
    @Override
    protected void afterExecute(Runnable r, Throwable t)
    {
        super.afterExecute(r, t);
        Threads.printException(r, t);
    }
};

一种新语法即是 new 一个对象的同时,重写它父类的方法

@Aspect

@Pointcut("execution(public * com.rest.module..*.*(..))")
//指定某一个包下的哪一个方法进行切面
public void getMethods() {
}
// 指定注解
@Pointcut("@annotation(com.rest.utils.SysPlatLog)")
//只要加了这个注解我就来切
public void withAnnotationMethods() {
}

3、Advice,在切入点上执行的增强处理,主要有五个注解:

@Before 在切点方法之前执行

​ @After 在切点方法之后执行

​ @AfterReturning 切点方法返回后执行

@AfterThrowing 切点方法抛异常执行

@Around 属于环绕增强,能控制切点执行前,执行后

  1. @Resource(name = “captchaProducer”)

    • @Resource默认按byName自动注入。
    • 既不指定name属性,也不指定type属性,则自动按byName方式进行查找。如果没有找到符合的bean,则回退为一个原始类型进行进行查找,如果找到就注入。
    • 只是指定了@Resource注解的name,则按name后的名字去bean元素里查找有与之相等的name属性的bean。
    • 只指定@Resource注解的type属性,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  2. @PreAuthorize(“@ss.hasPermi(‘monitor:operlog:list’)”)

    • @preAuthorize指定用户访问这个接口需要哪一部分权限,进行权限的验证
  3. @Value(“spring.port”)可以读取spring 配置文件中配置的值返回的是一个字符串

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值