GKLBB

当你经历了暴风雨,你也就成为了暴风雨

导航

统计

java编程开发之若依vue框架 --- 后台开发

注意

代码可能过时,但是思路是相似的,要想正常使用请查看最新代码实现。

代码只是展示了核心代码,将多个文件的不同代码在同一个地方展示,且部分函数没有定义展示。请依据实际情况编写

列表分页实现

#前端调用实现

前端

复制代码
html:

//
页面添加分页组件,传入分页变量 <pagination v-show="total>0" //整个分页组件中,通过 v-show 属性来判断是否展示分页组件,如果后端返回的总记录数 total 大于 0,就展示分页组件。 :total="total" //分页组件中,通过 total 属性来获取后端返回的总记录数,从而计算出总页数,并在分页中展示。:表示传递给组件的参数 :page.sync="queryParams.pageNum" //同样的,通过 :page.sync 和 :limit.sync 属性,来绑定当前页码和每页数据数量。这些属性是和组件内部的状态进行双向绑定的,当页码或每页数据数量发生变化时,组件内部会自动刷新。 :limit.sync="queryParams.pageSize" @pagination="getList" //最后,通过 @pagination 属性,绑定一个自定义回调函数 getList,当用户点击分页组件中的页码时,将会调用该函数,来重新获取当前页码的数据。 />
js:
// 定义分页变量
queryParams: {
  pageNum: 1,
  pageSize: 10
},

methods: {
getList(newPage) { // 定义 getList 函数
this.queryParams.pageNum = newPage; // 更新页码参数
listUser(this.queryParams).then(response => { // 调用接口获取数据
this.userList = response.rows;
this.total = response.total;
});
}

// 调用后端api方法,传入参数 获取结果查询结构 
listUser(this.queryParams).then(response => {
    this.userList = response.rows; //这里面保存着响应的分页后的数据
    this.total = response.total; //总数
  }
);


}

复制代码

后台

复制代码
@PostMapping("/list") //定义/list接口,用于查询出列表
@ResponseBody //返回非页面数据
public TableDataInfo list(User user) //TableDataInfo 可以理解为分页后的数据结构,user用于条件查询
{ 
startPage();
// 此方法配合前端完成自动分页pagehelper,前端会发送当前页和页面大小,这里会保存该数据,
List<User> list = userService.selectUserList(user); //要分页的list
return getDataTable(list);//将list分页
}
复制代码

常见坑点1:selectPostById莫名其妙的分页。例如下面这段代码

复制代码
startPage(); //创建一些分页参数
List<User> list;
if(user != null){
list = userService.selectUserList(user);
} else {
list = new ArrayList<User>();
}
Post post = postService.selectPostById(1L);
return getDataTable(list);
复制代码

原因分析:这种情况下由于user存在null的情况,就会导致pageHelper生产了一个分页参数,但是没有被selectUserList执行时消费,这个参数就会一直保留在这个线程上。 当这个线程的参数再次被selectPostById错误的使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。因此分页函数startPage应该与要分页的list紧密结合

上面这个代码,应该写成下面这个样子才能保证安全。

复制代码
List<User> list;
if(user != null){
startPage();
list = userService.selectUserList(user);
} else {
list = new ArrayList<User>();
}
Post post = postService.selectPostById(1L);
return getDataTable(list);
复制代码

注意

如果改为其他数据库需修改配置application.yml文件中的属性helperDialect=你的数据库

 

表格导入导出

在实际开发中经常需要使用导入导出功能来加快数据的操作。在项目中可以使用注解来完成此项功能。 在需要被导入导出的实体类属性添加@Excel注解,目前支持参数如下:

注解参数说明

参数类型默认值描述
sort int Integer.MAX_VALUE 从小到大排序
name String 列名
dateFormat String 日期格式, 如: yyyy-MM-dd
dictType String 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
readConverterExp String 读取内容转表达式 (如: 0=男,1=女,2=未知)
separator String , 分隔符,读取字符串组内容
scale int -1 BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
roundingMode int BigDecimal.ROUND_HALF_EVEN BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
celltype Enum Type.STRING 导出类型(0数字 1字符串 2图片)
height String 14 导出时在excel中每个列的高度 单位为字符
width String 16 导出时在excel中每个列的宽 单位为字符
suffix String 文字后缀,如% 90 变成90%
defaultValue String 当值为空时,字段的默认值
prompt String 提示信息
combo String Null 设置只能选择不能输入的列内容
headerBackgroundColor Enum IndexedColors.GREY_50_PERCENT 导出列头背景色IndexedColors.XXXX
headerColor Enum IndexedColors.WHITE 导出列头字体颜色IndexedColors.XXXX
backgroundColor Enum IndexedColors.WHITE 导出单元格背景色IndexedColors.XXXX
color Enum IndexedColors.BLACK 导出单元格字体颜色IndexedColors.XXXX
targetAttr String 另一个类中的属性名称,支持多级获取,以小数点隔开
isStatistics boolean false 是否自动统计数据,在最后追加一行统计数据总和
type Enum Type.ALL 字段类型(0:导出导入;1:仅导出;2:仅导入)
align Enum HorizontalAlignment.CENTER 导出对齐方式HorizontalAlignment.XXXX
handler Class ExcelHandlerAdapter.class 自定义数据处理器
args String[] {} 自定义数据处理器参数

导出

前端

复制代码
html:
<
el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" >导出</el-button>

js: // 查询参数 queryParams queryParams: { pageNum: 1, pageSize: 10, userName: undefined }, // 导出接口exportUser import { exportUser } from "@/api/system/user"; /** 导出按钮操作 */ handleExport() { const queryParams = this.queryParams; this.$confirm('是否确认导出所有用户数据项?', "警告", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" }).then(function() { return exportUser(queryParams); }).then(response => { this.download(response.msg); }).catch(function() {}); }
复制代码

 

 后端

在实体变量上添加@Excel注解

复制代码
在实体变量上添加@Excel注解

@Excel(name = "用户序号", prompt = "用户编号")
private Long userId;

@Excel(name = "用户名称")
private String userName;
    
@Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")
private String sex;

@Excel(name = "用户头像", cellType = ColumnType.IMAGE)
private String avatar;

@Excel(name = "帐号状态", dictType = "sys_normal_disable")
private String status;

@Excel(name = "最后登陆时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date loginDate;
复制代码

 

 

 在Controller添加导出方法

复制代码
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:user:export')")
@GetMapping("/export")
public AjaxResult export(SysUser user)
{
    List<SysUser> list = userService.selectUserList(user);
    ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
    return util.exportExcel(list, "用户数据");
}
复制代码

 

 

导入

前端

复制代码
html:
<!-- 导入按钮 -->
<
el-button type="info" icon="el-icon-upload2" size="mini" @click="handleImport"  <!-- 触发函数 -->
 >导入</el-button>
<!-- 点击导入按钮后对话框 -->

<!-- 用户导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px">
<el-upload
ref="upload"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<!--
`:title="upload.title"`:定义对话框的标题,值为 upload.title。
`:visible.sync="upload.open"`:控制对话框是否可见,值为 upload.open。这里都是数据控制试图显示
`ref="upload"`:定义 el-upload 组件的引用名。
`:limit="1"`:上传文件数量的限制,值为 1。
`:headers="upload.headers"`:上传文件时需要发送的头部信息。
`accept=".xlsx, .xls"`:限制上传的文件类型,值为 .xlsx 和 .xls。
`:action="upload.url + '?updateSupport=' + upload.updateSupport"`:上传文件时需要发送的后端接口地址,upload.url 为接口地址,upload.updateSupport 表示是否更新已经存在的用户数据。
`:disabled="upload.isUploading"`:控制上传按钮是否可用,值为 upload.isUploading,表示当前是否正在上传文件。
`:on-progress="handleFileUploadProgress"`:监听文件上传过程中的进度,调用 handleFileUploadProgress 函数处理。
`:on-success="handleFileSuccess"`:监听文件上传成功事件,调用 handleFileSuccess 函数处理。
`:auto-upload="false"`:是否自动上传,该属性为 false,则需要手动点击上传按钮才触发上传操作。
`drag`:属性 drag 表示开启拖拽上传的功能。
-->
<i class="el-icon-upload"></i> <!-- 上传图标,使用了饿了么 UI 库中的上传图标。 -->
<div class="el-upload__text">
将文件拖到此处,或
<em>点击上传</em>
</div>
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
<el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>
</div>
<div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm">确 定</el-button>
<el-button @click="upload.open = false">取 消</el-button>
</div>
</el-dialog>



js: import { getToken } from "@/utils/auth"; // 用户导入参数 upload: { // 是否显示弹出层(用户导入) open: false, // 弹出层标题(用户导入) title: "", // 是否禁用上传 isUploading: false, // 是否更新已经存在的用户数据 updateSupport: 0, // 设置上传的请求头部 headers: { Authorization: "Bearer " + getToken() }, // 上传的地址 url: process.env.VUE_APP_BASE_API + "/system/user/importData" }, // 导入模板接口importTemplate import { importTemplate } from "@/api/system/user"; /** 导入按钮操作 */ handleImport() { this.upload.title = "用户导入"; //导入对话框标题 this.upload.open = true;    //导入对话框可见 }, /** 下载模板操作 */ importTemplate() { importTemplate().then(response => { this.download(response.msg); }); }, // 文件上传中处理 handleFileUploadProgress(event, file, fileList) { this.upload.isUploading = true; }, // 文件上传成功处理 handleFileSuccess(response, file, fileList) { this.upload.open = false; this.upload.isUploading = false; this.$refs.upload.clearFiles(); this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true }); this.getList(); }, // 提交上传文件 submitFileForm() { this.$refs.upload.submit(); }
复制代码

 后端

复制代码
在实体变量上添加@Excel注解,默认为导出导入,也可以单独设置仅导入Type.IMPORT

@Excel(name = "用户序号")
private Long id;

@Excel(name = "部门编号", type = Type.IMPORT)
private Long deptId;

@Excel(name = "用户名称")
private String userName;

/** 导出部门多个对象 */
@Excels({
    @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
    @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT)
})
private SysDept dept;

/** 导出部门单个对象 */
@Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT)
private SysDept dept;



在Controller添加导入方法,updateSupport属性为是否存在则覆盖(可选)

@Log(title = "用户管理", businessType = BusinessType.IMPORT)
@PostMapping("/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());
    LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
    String operName = loginUser.getUsername();
    String message = userService.importUser(userList, updateSupport, operName);
    return AjaxResult.success(message);
}

@GetMapping("/importTemplate")
public AjaxResult importTemplate()
{
    ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
    return util.importTemplateExcel("用户数据");
}
复制代码

 

 

提示

也可以直接到main运行此方法测试。

InputStream is = new FileInputStream(new File("D:\\test.xlsx"));
ExcelUtil<Entity> util = new ExcelUtil<Entity>(Entity.class);
List<Entity> userList = util.importExcel(is);

 

 

定义表头

导入后端

public AjaxResult export(SysUser user)
{
    List<SysUser> list = userService.selectUserList(user);
    ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
    return util.exportExcel(list, "表名", "表头");
}

 

导出后端

public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
{
    ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
    List<SysUser> userList = util.importExcel(file.getInputStream(), 1); //其中1表示标题占用行数,根据实际情况填写。
    String operName = SecurityUtils.getUsername();
    String message = userService.importUser(userList, updateSupport, operName);
    return AjaxResult.success(message);
}

 

 

表格处理器
有时候我们希望数据展现为一个特殊的格式,或者需要对数据进行其它处理。Excel注解提供了自定义数据处理器以满足各种业务场景。而实现一个数据处理器也是非常简单的。如下:

复制代码
1、在实体类用Excel注解handler属性指定自定义的数据处理器

public class User extends BaseEntity
{
    @Excel(name = "用户名称", handler = MyDataHandler.class, args = { "aaa", "bbb" })
    private String userName;
}
2、编写数据处理器MyDataHandler继承ExcelHandlerAdapter,返回值为处理后的值。

public class MyDataHandler implements ExcelHandlerAdapter
{
    @Override
    public Object format(Object value, String[] args)
    {
        // value 为单元格数据值
        // args 为excel注解args参数组
        return value;
    }
}
复制代码

 

表格隐藏列

复制代码
有时候我们希望对列信息根据业务去动态显示,那么我们可以进行如下处理。

示例:对用户进行条件判断,符合条件则隐藏属性。导出的文件则不会显示此列信息。

@PostMapping("/export")
public void export(HttpServletResponse response, SysUser user)
{
    List<SysUser> list = userService.selectUserList(user);
    ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
    if (条件A) {
      // 不显示用户ID(单个)
      util.hideColumn("userId");
    } else if (条件B) {
      // 不显示用户名称、用户手机(多个)
      util.hideColumn("userId", "phonenumber");
    } } else if (条件C) {
      // 不显示用户邮箱、部门名称(子对象)
      util.hideColumn("email", "dept.deptName");
    }
    util.exportExcel(response, list, "用户数据");
}
复制代码

 

表格子对象

有时候对象里面还包含集合列表,例如用户管理包含多个角色需要导出,那么我们可以进行如下处理。

复制代码
SysUser.java

public class SysUser
{
    @Excel(name = "用户编号", cellType = ColumnType.NUMERIC, width = 20, needMerge = true)
    private String userId;

    @Excel(name = "用户名称", width = 20, needMerge = true)
    private String userName;

    @Excel(name = "邮箱", width = 20, needMerge = true)
    private String email;

    @Excel(name = "角色")
    private List<SysRole> roles;

    public String getUserId()
    {
        return userId;
    }

    public void setUserId(String userId)
    {
        this.userId = userId;
    }

    public String getUserName()
    {
        return userName;
    }

    public void setUserName(String userName)
    {
        this.userName = userName;
    }

    public String getEmail()
    {
        return email;
    }

    public void setEmail(String email)
    {
        this.email = email;
    }

    public List<SysRole> getRoles()
    {
        return roles;
    }

    public void setRoles(List<SysRole> roles)
    {
        this.roles = roles;
    }
}

SysRole.java

public class SysRole
{
    @Excel(name = "角色编号", cellType = ColumnType.NUMERIC)
    private String roleId;

    @Excel(name = "角色名称")
    private String roleName;

    @Excel(name = "角色字符")
    private String roleKey;

    public String getRoleId()
    {
        return roleId;
    }

    public void setRoleId(String roleId)
    {
        this.roleId = roleId;
    }

    public String getRoleName()
    {
        return roleName;
    }

    public void setRoleName(String roleName)
    {
        this.roleName = roleName;
    }

    public String getRoleKey()
    {
        return roleKey;
    }

    public void setRoleKey(String roleKey)
    {
        this.roleKey = roleKey;
    }

}
测试验证

public class Test
{
    public static void main(String[] args) throws IOException
    {
        List<SysUser> userList = new ArrayList<SysUser>();

        SysUser user1 = new SysUser();
        List<SysRole> roles1 = new ArrayList<SysRole>();

        SysRole role1 = new SysRole();
        role1.setRoleId("1");
        role1.setRoleName("超级管理员");
        role1.setRoleKey("admin_key");

        SysRole role2 = new SysRole();
        role2.setRoleId("2");
        role2.setRoleName("普通角色");
        role2.setRoleKey("common_key");
        
        SysRole role3 = new SysRole();
        role3.setRoleId("3");
        role3.setRoleName("测试角色");
        role3.setRoleKey("test_key");
        
        SysRole role4 = new SysRole();
        role4.setRoleId("4");
        role4.setRoleName("查询角色");
        role4.setRoleKey("query_key");

        roles1.add(role1);
        roles1.add(role2);
        roles1.add(role3);
        roles1.add(role4);

        user1.setUserId("1");
        user1.setUserName("admin");
        user1.setEmail("ry@qq.com");
        user1.setRoles(roles1);

        userList.add(user1);
        
        
        SysUser user2 = new SysUser();
        List<SysRole> roles2 = new ArrayList<SysRole>();

        SysRole role21 = new SysRole();
        role21.setRoleId("4");
        role21.setRoleName("研发角色");
        role21.setRoleKey("yanfa_key");

        SysRole role22 = new SysRole();
        role22.setRoleId("5");
        role22.setRoleName("销售角色");
        role22.setRoleKey("xiaoshou_key");

        roles2.add(role21);
        roles2.add(role22);

        user2.setUserId("2");
        user2.setUserName("ry");
        user2.setEmail("admin@qq.com");
        user2.setRoles(roles2);
        
        userList.add(user2);
        
        SysUser user3 = new SysUser();
        List<SysRole> roles3 = new ArrayList<SysRole>();

        SysRole role31 = new SysRole();
        role31.setRoleId("4");
        role31.setRoleName("张三角色");
        role31.setRoleKey("zs_key");

        SysRole role32 = new SysRole();
        role32.setRoleId("5");
        role32.setRoleName("李四角色");
        role32.setRoleKey("ls_key");

        roles3.add(role31);
        roles3.add(role32);

        user3.setUserId("3");
        user3.setUserName("test");
        user3.setEmail("test@qq.com");
        user3.setRoles(roles3);
        
        userList.add(user3);

        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        AjaxResult ajax = util.exportExcel(userList, "用户数据", "用户数据");
        System.out.println(ajax.toString());
    }
}
复制代码

 

 

文件上传下载

上传

首先创建一张上传文件的表,例如:

drop table if exists sys_file_info;
create table sys_file_info (
  file_id           int(11)          not null auto_increment       comment '文件id',
  file_name         varchar(50)      default ''                    comment '文件名称',
  file_path         varchar(255)     default ''                    comment '文件路径',
  primary key (file_id)
) engine=innodb auto_increment=1 default charset=utf8 comment = '文件信息表';

 

前端

复制代码
html:


<el-upload
ref="upload" <!-- 给上传组件一个引用名 -->
:limit="1" <!-- 限制最大上传文件数为1 -->
accept=".jpg, .png" <!-- 只允许上传 .jpg 和 .png 格式的文件 -->
:action="upload.url" <!-- 文件上传的接口地址 -->
:headers="upload.headers" <!-- 上传请求头,用于验证用户身份等 -->
:file-list="upload.fileList" <!-- 已选中文件列表 -->
:on-progress="handleFileUploadProgress" <!-- 文件上传进度回调函数 -->
:on-success="handleFileSuccess" <!-- 文件上传成功回调函数 -->
:auto-upload="false"> <!-- 是否自动开始上传 -->
<el-button slot="trigger" size="small" type="primary">选取文件</el-button> <!-- 上传按钮,点击后弹出文件选择器 -->
<el-button style="margin-left: 10px;" size="small" type="success" :loading="upload.isUploading" @click="submitUpload">上传到服务器</el-button> <!-- 上传按钮,点击后将上传队列中的文件传输到服务器 -->
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div> <!-- 提示信息,显示文件上传的格式限制和大小限制 -->
</el-upload>

js: 
import { getToken } from "@/utils/auth";
 

data中添加属性


// 上传参数
upload: {
  // 是否禁用上传
  isUploading: false,
  // 设置上传的请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // 上传的地址
  url: process.env.VUE_APP_BASE_API + "/common/upload",
  // 上传的文件列表
  fileList: []
},
 

data中添加属性


// 上传参数
upload: {
  // 是否禁用上传
  isUploading: false,
  // 设置上传的请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // 上传的地址
  url: process.env.VUE_APP_BASE_API + "/common/upload",
  // 上传的文件列表
  fileList: []
},
 

新增按钮和修改按钮操作对应处理fileList参数


handleAdd() {
  ...
  this.upload.fileList = [];
}

handleUpdate(row) {
  ...
  this.upload.fileList = [{ name: this.form.fileName, url: this.form.filePath }];
}

添加对应事件


// 文件提交处理
submitUpload() {
  this.$refs.upload.submit(); //调用upload组件的submit方法
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
  this.upload.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
  this.upload.isUploading = false;
  this.form.filePath = response.url;
  this.msgSuccess(response.msg);
}



复制代码

后台

复制代码
在SysFileInfoController添加对应上传方法

@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(@RequestParam("file") MultipartFile file, SysFileInfo fileInfo) throws IOException
{
    // 上传文件路径
    String filePath = RuoYiConfig.getUploadPath();
    // 上传并返回新文件名称
    String fileName = FileUploadUtils.upload(filePath, file);
    fileInfo.setFilePath(fileName); //设置上传路径 
    return toAjax(sysFileInfoService.insertSysFileInfo(fileInfo));  //保存上传代码
}
复制代码

 

 

 

下载

前端

复制代码
添加对应按钮和事件

<el-button
  size="mini"
  type="text"
  icon="el-icon-edit"
  @click="handleDownload(scope.row)"
>下载</el-button>

// 文件下载处理
handleDownload(row) {
  var name = row.fileName;
  var url = row.filePath;
  var suffix = url.substring(url.lastIndexOf("."), url.length);
  const a = document.createElement('a')
  a.setAttribute('download', name + suffix)
  a.setAttribute('target', '_blank')
  a.setAttribute('href', url)
  a.click()
}
复制代码

 

 

权限注解

Spring Security提供了Spring EL表达式,允许我们在定义接口访问的方法上面添加注解,来控制访问权限。

#权限方法

@PreAuthorize注解用于配置接口要求用户拥有某些权限才可访问,它拥有如下方法

方法参数描述
hasPermi String 验证用户是否具备某权限
lacksPermi String 验证用户是否不具备某权限,与 hasPermi逻辑相反
hasAnyPermi String 验证用户是否具有以下任意一个权限
hasRole String 判断用户是否拥有某个角色
lacksRole String 验证用户是否不具备某角色,与 isRole逻辑相反
hasAnyRoles String 验证用户是否具有以下任意一个角色,多个逗号分隔

#使用示例

复制代码
其中@ss代表的是PermissionService (opens new window)服务,对每个接口拦截并调用PermissionService的对应方法判断接口调用者的权限。

数据权限示例。
// 符合system:user:list权限要求
@PreAuthorize("@ss.hasPermi('system:user:list')")

// 不符合system:user:list权限要求
@PreAuthorize("@ss.lacksPermi('system:user:list')")

// 符合system:user:add或system:user:edit权限要求即可
@PreAuthorize("@ss.hasAnyPermi('system:user:add,system:user:edit')")
角色权限示例。
// 属于user角色
@PreAuthorize("@ss.hasRole('user')")

// 不属于user角色
@PreAuthorize("@ss.lacksRole('user')")

// 属于user或者admin之一
@PreAuthorize("@ss.hasAnyRoles('user,admin')")
复制代码

权限提示

超级管理员拥有所有权限,不受权限约束

 

 

公开接口

如果有些接口是不需要验证权限可以公开访问的,这个时候就需要我们给接口放行。

使用注解方式,只需要在Controller的类或方法上加入@Anonymous该注解即可

 

// @PreAuthorize("@ss.xxxx('....')") 注释或删除掉原有的权限注解
@Anonymous
@GetMapping("/list")
public List<SysXxxx> list(SysXxxx xxxx)
{
    return xxxxList;
}

 

 

 

 

事务管理

这里的事务指的是数据库中的,表示一组sql要么同时成功要么同时失败回滚。新建的Spring Boot项目中,一般都会引用spring-boot-starter或者spring-boot-starter-web,而这两个起步依赖中都已经包含了对于spring-boot-starter-jdbc或spring-boot-starter-data-jpa的依赖。 当我们使用了这两个依赖的时候,框架会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager。 所以我们不需要任何额外配置就可以用@Transactional注解进行事务的使用。

提示

@Transactional注解只能应用到public可见度的方法上,可以被应用于接口定义和接口方法,方法会覆盖类上面声明的事务。

例如用户新增需要插入用户表、用户与岗位关联表、用户与角色关联表,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作, 这样可以防止出现脏数据,就可以使用事务让它实现回退。
做法非常简单,我们只需要在方法或类添加@Transactional注解即可。

复制代码
@Transactional
public int insertUser(User user)
{
    // 新增用户信息
    int rows = userMapper.insertUser(user);
    // 新增用户岗位关联
    insertUserPost(user);
    // 新增用户与角色管理
    insertUserRole(user);
    return rows;
}
复制代码

 

 

`@Transactional` 注解是 Spring 框架提供的一个事务控制注解,用于管理事务的开启和提交等行为。其作用是让带有该注解的方法在执行时被 Spring AOP 拦截,自动开启事务并在方法执行结束时提交或回滚事务。

关于 `@Transactional` 注解的限制和使用规则,以下是一些需要注意的事项:

- 该注解只能应用于 `public` 可见度的方法上,因为只有 public 方法才能被 Spring AOP 拦截并代理。如果将该注解应用于非 public 方法上,Spring AOP 将无法拦截该方法的调用,因此该注解的作用也就失效了。
- 该注解可以被应用于接口定义和接口方法,也可以被应用于类、类方法、抽象类、抽象类方法、父类方法、本类方法等不同的位置。当一个方法被声明为 `@Transactional` 之后,该方法会覆盖类上面声明的事务。也就是说,如果一个类被声明为 `@Transactional`,则该类中的所有方法都将使用该注解声明的事务设置,但是如果类中的某个方法被声明为 `@Transactional`,则该方法会使用方法上的事务设置,而忽略类上面声明的事务。
- 该注解提供了一些属性,可以用于控制事务的隔离级别、传播行为、超时时间、只读性质等。通过设置不同的属性值,我们可以对事务进行更加精细的控制和管理。
- `@Transactional` 注解的使用需要在 Spring 容器中开启事务管理功能,通常采用 `@EnableTransactionManagement` 注解或 XML 配置的方式来开启。如果没有在容器中开启事务管理功能,则 `@Transactional` 注解将不会生效。

总之,`@Transactional` 注解是一个非常实用的事务管理工具,能够帮助我们简化事务管理的代码,提高代码的可读性和可维护性。但是它的使用也需要注意一些细节和规则,以避免出现不必要的错误和异常情况。

复制代码
常见坑点1:遇到检查异常时,事务开启,也无法回滚。 例如下面这段代码,用户依旧增加成功,并没有因为后面遇到检查异常而回滚!!
@Transactional
public int insertUser(User user) throws Exception
{
    // 新增用户信息
    int rows = userMapper.insertUser(user);
    // 新增用户岗位关联
    insertUserPost(user);
    // 新增用户与角色管理
    insertUserRole(user);
    // 模拟抛出SQLException异常
    boolean flag = true;
    if (flag)
    {
        throw new SQLException("发生异常了..");
    }
    return rows;
}
原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional注解里使用 rollbackFor属性明确指定异常。
例如下面这样,就可以正常回滚:

@Transactional(rollbackFor = Exception.class)
public int insertUser(User user) throws Exception
{
    // 新增用户信息
    int rows = userMapper.insertUser(user);
    // 新增用户岗位关联
    insertUserPost(user);
    // 新增用户与角色管理
    insertUserRole(user);
    // 模拟抛出SQLException异常
    boolean flag = true;
    if (flag)
    {
        throw new SQLException("发生异常了..");
    }
    return rows;
}
复制代码

 

 

复制代码
常见坑点2: 在业务层捕捉异常后,发现事务不生效。 这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。
例如:下面这段代码直接导致用户新增的事务回滚没有生效。
@Transactional
public int insertUser(User user) throws Exception
{
    // 新增用户信息
    int rows = userMapper.insertUser(user);
    // 新增用户岗位关联
    insertUserPost(user);
    // 新增用户与角色管理
    insertUserRole(user);
    // 模拟抛出SQLException异常
    boolean flag = true;
    if (flag)
    {
        try
        {
            // 谨慎:尽量不要在业务层捕捉异常并处理
            throw new SQLException("发生异常了..");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    return rows;
}
推荐做法:在业务层统一抛出异常,然后在控制层统一处理。

@Transactional
public int insertUser(User user) throws Exception
{
    // 新增用户信息
    int rows = userMapper.insertUser(user);
    // 新增用户岗位关联
    insertUserPost(user);
    // 新增用户与角色管理
    insertUserRole(user);
    // 模拟抛出SQLException异常
    boolean flag = true;
    if (flag)
    {
        throw new RuntimeException("发生异常了..");
    }
    return rows;
}
复制代码

 

 

 

Transactional注解的常用属性表:

属性说明
propagation 事务的传播行为,默认值为 REQUIRED。
isolation 事务的隔离度,默认值采用 DEFAULT
timeout 事务的超时时间,默认值为-1,不超时。如果设置了超时时间(单位秒),那么如果超过该时间限制了但事务还没有完成,则自动回滚事务。
read-only 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollbackFor 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。{xxx1.class, xxx2.class,……}
noRollbackFor 抛出 no-rollback-for 指定的异常类型,不回滚事务。{xxx1.class, xxx2.class,……}
....

提示

事务的传播机制是指如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。 即:在执行一个@Transactinal注解标注的方法时,开启了事务;当该方法还在执行中时,另一个人也触发了该方法;那么此时怎么算事务呢,这时就可以通过事务的传播机制来指定处理方式。

TransactionDefinition传播行为的常量:

常量含义
TransactionDefinition.PROPAGATION_REQUIRED 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER 以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

#

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

复制代码
上传成功后需要预览可以对该属性格式化处理

{
    field : 'filePath', 
    title: '文件预览',
    formatter: function(value, row, index) {
        return $.table.imageView(value);
    }
},
复制代码

 

这个代码块是典型的表格列格式化代码,应该将它写在完成表格的渲染逻辑中,例如 Vue.js 的 template 或 React.js 的 render() 函数中。在这里,您可以使用前端框架提供的表格组件(例如 Element、Ant Design、Bootstrap Table 等),然后定义表格的列及其对应的数据字段。对于需要格式化的数据字段,您只需要在列的定义中使用 `formatter` 属性并指定对应的处理函数即可。

以 Bootstrap Table 为例,在 Vue.js 组件中定义表格的列时,可以将上述代码段放在 columns 属性中,例如:

```javascript
<template>
<div>
<bootstrap-table :data="tableData" :columns="tableColumns" />
</div>
</template>
<script>
export default {
data() {
return {
tableData: [...], // 表格数据
tableColumns: [ // 表格列定义
{
field: 'id',
title: 'ID'
},
{
field: 'name',
title: 'Name'
},
{
field: 'filePath',
title: 'File Path',
formatter: function(value, row, index) {
return $.table.imageView(value);
}
}
]
}
}
}
</script>
```

在这个例子中,我们将文件预览列的格式化代码放在了字段定义中,设置了相应的 formatter 处理函数。这样在表格渲染时,会对数据中的 filePath 字段进行格式化处理,显示文件预览效果。

需要注意的是,这个例子中使用了 Bootstrap Table 组件,需要确保在 Vue.js 组件中正确引用了 Bootstrap Table 的前端库和样式文件,才能正常渲染和使用该表格。

 

在执行该代码后,如果数据中 `filePath` 字段有值,会对该字段进行预览处理并展示在表格中。预览的效果由 `$.table.imageView()` 方法实现,该方法将文件路径转换为预览图片、音频、视频等。

例如,如果您上传了一个名为 `test.png` 的图片文件,后端处理后将返回一个 JSON 对象,其中包含了文件路径 `filePath` 值为 `/uploads/image/test/20201110/test.png`。在该列定义中加入 `formatter` 函数并调用 `$.table.imageView()` 方法进行展示后,表格的预览列将展示出该图片的预览效果。

预览的具体效果会根据文件的类型以及前端库的实现方式而异。例如,对于图片文件,可以展示图片的缩略图或完整图像;对于音频文件,可以在表格中播放音频文件,并提供基本的控制功能;对于视频文件,可以在单元格中展示一个嵌入式的视频播放器,并提供控制和全屏等功能。根据您所选择的前端库和相关配置,预览效果可能有所不同。

 

复制代码
如需对文件格式控制,设置application.yml中的multipart属性

# 文件上传
servlet:
   multipart:
     # 单个文件大小
     max-file-size:  10MB
     # 设置总上传的文件大小
     max-request-size:  20MB
复制代码

 

 

 注意:如果只是单纯的上传一张图片没有其他参数可以使用通用方法 /common/upload
请求处理方法 com.ruoyi.web.controller.common.CommonController

 

 下载

 

posted on   GKLBB  阅读(1477)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示