文章目录

  • 什么是若依
  • 使用若依
  • 验证码的前端实现
    • 📌 前后端验证码流程说明文档
      • 1、前端初始化验证码
      • 2、前端界面显示
      • 3、后端生成验证码接口(GET /captchaImage)
      • 4、用户提交登录信息
      • 5、后端验证验证码逻辑(POST /login)
      • 6、登录失败处理(前端)
  • 新增岗位功能详细解析
    • 一、前端实现流程
      • 1. 用户触发新增操作
      • 2. 初始化表单
      • 3. 表单结构
      • 4. 表单验证规则
      • 5. 提交表单
    • 二、后端实现流程
      • 1. 控制器接收请求
      • 2. 唯一性校验
      • 3. 数据插入实现
      • 4. 实体类验证
    • 三、关键特性与设计思想
    • 四、完整数据流
  • 修改岗位功能详细解析
    • 一、前端实现流程
      • 1. 修改按钮触发 (Post.vue)
      • 2. 处理修改操作 (Post.vue)
      • 3. 获取岗位详情API (post.js)
      • 4. 表单提交处理 (Post.vue)
      • 5. 修改岗位API (post.js)
    • 二、后端实现流程
      • 1. 控制器接收请求 (SysPostController.java)
      • 2. 服务层处理 (SysPostServiceImpl.java)
      • 3. 唯一性校验逻辑 (SysPostServiceImpl.java)
      • 4. Mapper层SQL (SysPostMapper.xml)
    • 三、关键设计要点
  • 删除岗位功能详细解析
    • 一、前端实现流程
      • 1. 删除触发方式
      • 2. 删除处理逻辑
      • 3. 关键处理步骤
    • 二、后端实现流程
      • 1. 控制器入口
      • 2. 服务层实现
      • 3. 关键业务逻辑
    • 三、安全与完整性设计
      • 1. 多级保护机制
      • 2. 外键约束建议(DDL示例)
    • 四、异常处理流程
      • 1. 业务异常处理
      • 2. 前端异常处理
    • 五、设计亮点分析
    • 六、完整工作流程
  • 查询岗位功能详细解析
    • 一、查询功能分类
    • 二、分页条件查询(核心功能)
      • 1. 前端实现流程
      • 2. 后端实现流程
      • 3. 分页机制
    • 三、导出查询
      • 1. 前端实现
      • 2. 后端实现
    • 四、详情查询(单个岗位查询)
      • 1. 前端实现
      • 2. 后端实现
    • 五、查询功能设计亮点
    • 六、复杂查询场景处理
      • 统计查询
  • 岗位导出功能详细解析
    • 一、前端实现流程
      • 1. 导出按钮触发
      • 2. 导出处理逻辑
      • 3. 下载方法封装
    • 二、后端实现流程
      • 1. 控制器入口
      • 2. 实体类Excel注解


什么是若依

提示:这里可以添加本文要记录的大概内容:

若依是开源项目,便于二次开发,百度直接搜索:若依官网

1、减少了自己的代码量

2、学习优秀开源项目底层的编程思想,设计思路,提高自己的编程能力

使用若依

使用开源项目的步骤:

1、下载并运行
在这里插入图片描述
启动后端:
在这里插入图片描述

启动前端:VS CODE 插件GitHub Copilot ChatAgent模式下
在这里插入图片描述

在这里插入图片描述在这里插入图片描述
项目成功启动页面:
在这里插入图片描述

2、看懂业务流程

3、进行二次开发

验证码的前端实现

后端生成一个表达式

1+1=2
1+1=?@2

1+1=?转成图片,传到前端展示,答案2存入Redis

输入答案点击登录,就把表单存入后台了,这时候从Redis中把正确答案拿出来,两个答案对比

📌 前后端验证码流程说明文档

1、前端初始化验证码

在 Vue 的 <script setup> 中,组件挂载时会调用 getCode() 函数初始化验证码。

getCode() 函数逻辑:

getCode().then(res => {captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabledif (captchaEnabled.value) {codeUrl.value = "data:image/gif;base64," + res.imgloginForm.value.uuid = res.uuid}
})
  • captchaEnabled.value:根据后端返回决定是否启用验证码(默认启用)
  • codeUrl.value:将 base64 图片数据拼接为可直接显示的 URL
  • loginForm.value.uuid:保存后端返回的验证码唯一标识(UUID)

2、前端界面显示

在模板中通过 v-if="captchaEnabled" 控制验证码区域的显示:

<el-form-item prop="code" v-if="captchaEnabled"><el-input v-model="loginForm.code" placeholder="验证码" /><div class="login-code"><img :src="codeUrl" @click="getCode" title="点击刷新验证码" /></div>
</el-form-item>
  • 点击图片时重新调用 getCode(),刷新验证码
  • 用户输入验证码后,与 uuid 一起提交给后端

3、后端生成验证码接口(GET /captchaImage)

通过getCodeImg引入
在这里插入图片描述

找到对应代码:
在这里插入图片描述
解释:
定义一个名为 getCodeImg 的函数
request:这是封装好的 Axios 请求函数,通常是项目中封装的 HTTP 请求方法。
整个 request({ ... }) 是一个 HTTP 请求配置对象。
请求的后端接口地址是 /captchaImage

使用 HTTP 的 GET 方法发送请求,用于获取数据

设置请求超时时间为 20 秒(20000 毫秒),如果 20 秒内没有返回结果,请求将自动中断,并进入 .catch() 分支。

在登录页F12
在这里插入图片描述
在这里插入图片描述

打开 request 文件
在这里插入图片描述
创建一个 Axios 实例 service,用于发起 HTTP 请求
配置该实例的基础 URL 为一个环境变量,实现环境自适应
设置请求超时时间为 10 秒,防止请求长时间挂起

值在配置文件中定义,配置文件:
在这里插入图片描述
在这里插入图片描述

4、用户提交登录信息

用户点击登录时,前端调用 handleLogin() 提交数据:

userStore.login(loginForm.value)

提交内容包括:

  • username:用户名
  • password:密码
  • code:用户输入的验证码
  • uuid:当前验证码的唯一标识

5、后端验证验证码逻辑(POST /login)

登录请求:

export function login(username, password, code, uuid) {return request({url: '/login',method: 'post',data: { username, password, code, uuid }})
}

后端伪代码逻辑:

function login(username, password, code, uuid) {// 1. 检查是否启用验证码if (captchaEnabled) {// 2. 从 Redis 获取验证码String redisCode = redis.get(uuid);// 3. 验证码比对(忽略大小写)if (!redisCode || !redisCode.equalsIgnoreCase(code)) {return error("验证码错误");}// 4. 删除已使用的验证码redis.delete(uuid);}// 5. 继续账号密码验证...
}

6、登录失败处理(前端)

userStore.login(loginForm.value).catch(() => {loading.value = falseif (captchaEnabled.value) {getCode() // 登录失败时刷新验证码}
})
  • 登录失败时自动刷新验证码,防止暴力破解
  • 用户点击图片也可手动刷新验证码

📋完整流程图

getCodeImg()↓
调用 request()↓
发送 GET 请求到 /captchaImage↓
请求头中设置 isToken = false(表示不需要 token)↓
等待响应(最多 20 秒)↓
返回 base64 图片和 uuid

新增岗位功能详细解析

新增功能是岗位管理系统的核心操作之一,涉及前后端协同工作。下面从用户操作到数据存储的完整流程进行详细解析:

一、前端实现流程

1. 用户触发新增操作

<el-button type="primary" plain icon="Plus"@click="handleAdd"v-hasPermi="['system:post:add']"
>新增</el-button>
  • v-hasPermi指令校验用户是否有system:post:add权限
  • 点击按钮触发handleAdd方法

2. 初始化表单

function handleAdd() {reset() // 重置表单数据open.value = true // 打开对话框title.value = "添加岗位" // 设置对话框标题
}function reset() {form.value = {postId: undefined,postCode: undefined,postName: undefined,postSort: 0,status: "0",remark: undefined}proxy.resetForm("postRef") // 重置表单验证状态
}
  • 初始化表单对象,设置默认值(如状态默认为"0"正常)
  • 重置表单验证状态,清除之前的错误提示

3. 表单结构

<el-dialog :title="title" v-model="open" width="500px"><el-form ref="postRef" :model="form" :rules="rules" label-width="80px"><el-form-item label="岗位名称" prop="postName"><el-input v-model="form.postName" placeholder="请输入岗位名称" /></el-form-item><el-form-item label="岗位编码" prop="postCode"><el-input v-model="form.postCode" placeholder="请输入编码名称" /></el-form-item><!-- 其他字段... --></el-form>
</el-dialog>
  • 使用el-dialog组件实现模态对话框
  • el-form绑定表单数据和验证规则

4. 表单验证规则

const rules = {postName: [{ required: true, message: "岗位名称不能为空", trigger: "blur" }],postCode: [{ required: true, message: "岗位编码不能为空", trigger: "blur" }],postSort: [{ required: true, message: "岗位顺序不能为空", trigger: "blur" }],
}
  • 定义字段级验证规则
  • required标记必填字段
  • trigger: 'blur'表示失去焦点时触发验证

5. 提交表单

function submitForm() {proxy.$refs["postRef"].validate(valid => {if (valid) {addPost(form.value).then(response => {proxy.$modal.msgSuccess("新增成功")open.value = false // 关闭对话框getList() // 刷新列表})}})
}
  1. 触发表单验证
  2. 验证通过后调用addPost API
  3. 显示操作成功提示
  4. 关闭对话框并刷新岗位列表

二、后端实现流程

1. 控制器接收请求

@PostMapping
@PreAuthorize("@ss.hasPermi('system:post:add')")
@Log(title = "岗位管理", businessType = BusinessType.INSERT)
public AjaxResult add(@Validated @RequestBody SysPost post) {// 唯一性校验if (!postService.checkPostNameUnique(post)) {return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");} else if (!postService.checkPostCodeUnique(post)) {return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");}// 设置创建人post.setCreateBy(getUsername());// 执行插入return toAjax(postService.insertPost(post));
}
  • @PostMapping处理POST请求
  • @PreAuthorize校验用户权限
  • @Log记录操作日志(类型为INSERT)
  • @Validated触发实体类字段验证
  • @RequestBody接收JSON格式的岗位数据

2. 唯一性校验

// 校验岗位名称唯一性@Overridepublic boolean checkPostNameUnique(SysPost post) {Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId();SysPost info = postMapper.checkPostNameUnique(post.getPostName());if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) {return UserConstants.NOT_UNIQUE;}return UserConstants.UNIQUE;}// Mapper查询
@Select("select * from sys_post where post_name = #{postName} limit 1")
SysPost checkPostNameUnique(String postName);
  • 查询数据库是否存在相同岗位名称
  • 排除当前岗位自身(更新时使用)
  • 同样逻辑校验岗位编码唯一性

3. 数据插入实现

// 服务层
public int insertPost(SysPost post) {return postMapper.insertPost(post);
}// Mapper XML
<insert id="insertPost" parameterType="SysPost" useGeneratedKeys="true" keyProperty="postId">insert into sys_post(<if test="postId != null and postId != 0">post_id,</if><if test="postCode != null and postCode != ''">post_code,</if><if test="postName != null and postName != ''">post_name,</if><if test="postSort != null">post_sort,</if><if test="status != null and status != ''">status,</if><if test="remark != null and remark != ''">remark,</if><if test="createBy != null and createBy != ''">create_by,</if>create_time)values(<if test="postId != null and postId != 0">#{postId},</if><if test="postCode != null and postCode != ''">#{postCode},</if><if test="postName != null and postName != ''">#{postName},</if><if test="postSort != null">#{postSort},</if><if test="status != null and status != ''">#{status},</if><if test="remark != null and remark != ''">#{remark},</if><if test="createBy != null and createBy != ''">#{createBy},</if>sysdate())
</insert>
  • 动态生成SQL语句
  • useGeneratedKeys="true"获取自增主键
  • 自动填充创建时间和创建人
  • 只插入非空字段,提高灵活性

4. 实体类验证

public class SysPost extends BaseEntity {/*** 岗位编码*/@Excel(name = "岗位编码")private String postCode;/*** 岗位名称*/@Excel(name = "岗位名称")private String postName;/*** 岗位排序*/@Excel(name = "岗位排序")private Integer postSort;// 其他字段
}
  • @NotBlank验证非空字符串
  • @Size限制字符串长度
  • @NotNull确保数值字段不为空
  • 验证失败自动返回错误信息给前端

三、关键特性与设计思想

  1. 多层验证机制

    • 前端:Element Plus表单验证
    • 网络层:JSON格式校验
    • 后端:JSR-303实体验证
    • 业务层:唯一性校验
    • 数据库:唯一约束(需在DDL中定义)
  2. 操作审计

    • 通过@Log注解自动记录:
     @Log(title = "岗位管理", businessType = BusinessType.INSERT)
  1. 安全控制

    • 权限校验:@PreAuthorize("@ss.hasPermi('system:post:add')"
    • 数据隔离:自动注入当前用户getUsername()
  2. 响应式设计

    • 前端:提交后自动刷新列表
    • 后端:返回标准化的AjaxResult对象
  3. 异常处理

    • 唯一性冲突返回可读性错误信息
    • 数据库操作异常全局处理

四、完整数据流

  1. 用户点击"新增"按钮
  2. 前端打开对话框并重置表单
  3. 用户填写表单并提交
  4. 前端进行字段级验证
  5. 验证通过后调用/system/post POST API
  6. 后端控制器接收请求:
    • 权限校验
    • 实体字段验证
    • 业务规则校验(唯一性)
  7. 服务层设置创建人
  8. Mapper执行动态SQL插入
  9. 返回操作结果(成功/失败)
  10. 前端提示结果并刷新列表

这种设计实现了前后端分离架构下的高效协作,通过多层验证保证数据质量,利用注解简化开发,同时确保系统的安全性和可维护性。

修改岗位功能详细解析

我将详细讲解岗位管理模块中的修改功能,从前端到后端的完整实现流程。修改功能是CRUD操作中的重要环节,涉及数据加载、校验、更新等多个关键步骤。

一、前端实现流程

1. 修改按钮触发 (Post.vue)

<el-table-column label="操作" align="center" class-name="small-padding fixed-width"><template #default="scope"><el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:post:edit']">修改</el-button></template>
</el-table-column>
  • 在表格操作列中放置修改按钮
  • 使用v-hasPermi指令进行权限控制
  • 点击时调用handleUpdate方法并传入当前行数据

2. 处理修改操作 (Post.vue)

// 修改按钮操作
function handleUpdate(row) {reset(); // 重置表单状态const postId = row.postId; // 获取当前岗位IDgetPost(postId).then(response => {form.value = response.data; // 填充表单数据open.value = true; // 打开对话框title.value = "修改岗位"; // 设置对话框标题});
}

执行流程:

  1. 重置表单状态,确保无残留数据
  2. 从行数据中获取岗位ID
  3. 调用API获取岗位详细信息
  4. 将返回数据填充到表单
  5. 打开对话框并设置标题

3. 获取岗位详情API (post.js)

// 查询岗位详细
export function getPost(postId) {return request({url: '/system/post/' + postId,method: 'get'})
}
  • 向后台发送GET请求
  • URL格式:/system/post/{postId}
  • 获取指定ID的岗位详情

4. 表单提交处理 (Post.vue)

// 提交按钮
function submitForm() {proxy.$refs["postRef"].validate(valid => {if (valid) {if (form.value.postId != undefined) {// 修改操作updatePost(form.value).then(response => {proxy.$modal.msgSuccess("修改成功");open.value = false; // 关闭对话框getList(); // 刷新列表})} else {// 新增操作...}}});
}
  • 先进行表单验证
  • 根据postId判断是修改还是新增
  • 调用updatePostAPI提交修改
  • 成功后关闭对话框并刷新列表

5. 修改岗位API (post.js)

// 修改岗位
export function updatePost(data) {return request({url: '/system/post',method: 'put',data: data})
}
  • 使用HTTP PUT方法
  • 将整个表单数据作为请求体发送
  • URL为/system/post

二、后端实现流程

1. 控制器接收请求 (SysPostController.java)

@PreAuthorize("@ss.hasPermi('system:post:edit')")
@Log(title = "岗位管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysPost post) {// 唯一性校验if (!postService.checkPostNameUnique(post)) {return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");} else if (!postService.checkPostCodeUnique(post)) {return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");}// 设置更新人post.setUpdateBy(getUsername());// 执行更新return toAjax(postService.updatePost(post));
}

执行流程:

  1. @PreAuthorize进行权限校验
  2. @Log记录操作日志
  3. @Validated进行参数校验(基于JSR-303)
  4. 校验岗位名称和编码的唯一性
  5. 设置更新人(当前登录用户)
  6. 调用服务层执行更新

2. 服务层处理 (SysPostServiceImpl.java)

@Override
public int updatePost(SysPost post) {return postMapper.updatePost(post);
}
  • 直接调用Mapper层执行更新
  • 返回影响行数

3. 唯一性校验逻辑 (SysPostServiceImpl.java)

@Override
public boolean checkPostNameUnique(SysPost post) {Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId();SysPost info = postMapper.checkPostNameUnique(post.getPostName());if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) {return UserConstants.NOT_UNIQUE;}return UserConstants.UNIQUE;
}

校验逻辑:

  1. 获取当前岗位ID(修改时为真实ID,新增时为null)
  2. 查询数据库是否存在相同名称的岗位
  3. 如果存在同名岗位:
    • 且ID不同:表示是其他岗位的同名,返回不唯一
    • 且ID相同:表示是自身,允许更新

4. Mapper层SQL (SysPostMapper.xml)

<update id="updatePost" parameterType="SysPost">update sys_post<set><if test="postCode != null and postCode != ''">post_code = #{postCode},</if><if test="postName != null and postName != ''">post_name = #{postName},</if><if test="postSort != null">post_sort = #{postSort},</if><if test="status != null and status != ''">status = #{status},</if><if test="remark != null">remark = #{remark},</if><if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>update_time = sysdate()</set>where post_id = #{postId}
</update>

SQL特点:

  1. 使用动态SQL(<set><if>标签)
  2. 只更新非空字段
  3. 自动设置更新时间
  4. 使用数据库函数设置时间(sysdate())
  5. 根据postId定位记录

三、关键设计要点

  1. 权限控制双重保障

    • 前端:v-hasPermi指令控制按钮显示
    • 后端:@PreAuthorize注解进行方法级权限校验
  2. 数据一致性保证

    • 唯一性校验:防止名称/编码重复
    • 事务管理:确保更新操作的原子性
  3. 审计字段自动化

    • 自动记录更新人:post.setUpdateBy(getUsername())
    • 自动更新时间:update_time = sysdate()
  4. 前后端校验结合

    • 前端:Element Plus表单校验
    • 后端:JSR-303参数校验 + 业务逻辑校验
  5. 性能优化

    • 动态SQL:只更新变化的字段
    • 最小化数据传输:前端只发送必要字段
  6. 用户体验优化

    • 修改前加载完整数据
    • 操作成功自动刷新列表
    • 明确的错误提示信息

删除岗位功能详细解析

删除功能是岗位管理系统中最敏感的操作之一,需要特别关注数据完整性和安全控制。下面从前后端协同角度深入解析删除功能的实现:

一、前端实现流程

1. 删除触发方式

前端支持两种删除模式:

<!-- 单个删除(行内操作) -->
<el-table-column label="操作"><template #default="scope"><el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:post:remove']">删除</el-button></template>
</el-table-column><!-- 批量删除(顶部操作栏) -->
<el-buttontype="danger"plainicon="Delete":disabled="multiple"@click="handleDelete"v-hasPermi="['system:post:remove']"
>删除</el-button>

2. 删除处理逻辑

function handleDelete(row) {// 获取待删除的岗位IDconst postIds = row.postId || ids.value;// 确认对话框proxy.$modal.confirm('是否确认删除岗位编号为"' + postIds + '"的数据项?').then(function() {// 调用删除APIreturn delPost(postIds);}).then(() => {// 删除成功后刷新列表getList();proxy.$modal.msgSuccess("删除成功");}).catch(() => {// 用户取消操作});
}

3. 关键处理步骤

  1. 获取删除目标
    • 单个删除:从行数据获取row.postId
    • 批量删除:从选中的ids数组中获取多个ID
   const postIds = row.postId || ids.value;
  1. 二次确认

    • 使用$modal.confirm显示确认对话框
    • 明确显示将被删除的岗位编号
  2. API调用

    • 调用delPost方法发送删除请求
    • 支持单个ID或ID数组(自动处理)
  3. 结果反馈

    • 成功:显示"删除成功"提示
    • 失败:全局异常处理(已在底层封装)
  4. 状态更新

    • 刷新岗位列表数据
    • 重置选中状态

二、后端实现流程

1. 控制器入口

@PreAuthorize("@ss.hasPermi('system:post:remove')")
@Log(title = "岗位管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{postIds}")
public AjaxResult remove(@PathVariable Long[] postIds) {return toAjax(postService.deletePostByIds(postIds));
}
  • @DeleteMapping:处理DELETE请求
  • @PathVariable:获取URL路径中的岗位ID数组
  • @PreAuthorize:权限校验(需system:post:remove权限)
  • @Log:记录操作日志(类型为DELETE)

2. 服务层实现

@Override
public int deletePostByIds(Long[] postIds) {// 1. 检查岗位是否被用户使用for (Long postId : postIds) {SysPost post = selectPostById(postId);if (countUserPostById(postId) > 0) {throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName()));}}// 2. 执行批量删除return postMapper.deletePostByIds(postIds);
}

3. 关键业务逻辑

  1. 使用状态检查
   // 查询岗位用户关联数量public int countUserPostById(Long postId) {return userPostMapper.countUserPostById(postId);}// SysUserPostMapper.xml<select id="countUserPostById" resultType="int">SELECT COUNT(1) FROM sys_user_post WHERE post_id = #{postId}</select>
  1. 安全删除机制

    • 遍历每个待删除岗位
    • 检查sys_user_post关联表
    • 如果存在关联用户,抛出业务异常
    • 包含岗位名称的友好错误提示
  2. 批量删除执行

   <delete id="deletePostByIds" parameterType="Long">DELETE FROM sys_post WHERE post_id IN<foreach collection="array" item="postId" open="(" separator="," close=")">#{postId}</foreach></delete>
  • 使用MyBatis的<foreach>处理ID数组
  • 生成DELETE FROM sys_post WHERE post_id IN (1,2,3)语句

三、安全与完整性设计

1. 多级保护机制

层级保护措施目的
前端v-hasPermi指令控制按钮显示
网络JWT令牌验证身份认证
应用@PreAuthorize注解方法级权限控制
数据关联检查防止误删使用中的岗位
数据库外键约束最终数据保护

2. 外键约束建议(DDL示例)

CREATE TABLE sys_user_post (user_id BIGINT NOT NULL,post_id BIGINT NOT NULL,PRIMARY KEY (user_id, post_id),FOREIGN KEY (post_id) REFERENCES sys_post(post_id)ON DELETE RESTRICT  -- 阻止删除被引用的岗位
);

四、异常处理流程

1. 业务异常处理

// 服务层抛出异常
if (countUserPostById(postId) > 0) {throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName()));
}// 全局异常处理器
@ExceptionHandler(ServiceException.class)
public AjaxResult handleServiceException(ServiceException e) {return AjaxResult.error(e.getMessage());
}
  • 返回HTTP 200状态码(前端能处理的业务异常)
  • 错误信息格式:{ code: 500, msg: "岗位已分配,不能删除" }

2. 前端异常处理

// post.js API封装
export function delPost(postId) {return request({url: '/system/post/' + postId,method: 'delete'})
}// 全局响应拦截器
service.interceptors.response.use(response => {const res = response.data;if (res.code !== 200) {// 显示后端返回的错误信息Message.error(res.msg || 'Error');return Promise.reject(new Error(res.msg || 'Error'));}return res;},error => {// 处理HTTP错误(如401, 500等)}
);

五、设计亮点分析

  1. 批量操作优化

    • 单次数据库交互完成批量删除(IN语句)
    • 减少数据库连接开销
  2. 用户友好设计

    • 明确提示哪个岗位无法删除
    • 二次确认避免误操作
    • 批量选择状态自动管理
  3. 事务完整性

   @Transactional(rollbackFor = Exception.class)public int deletePostByIds(Long[] postIds) {// 操作在同一个事务中}
  • 整个删除操作原子性执行
  • 检查与删除要么全成功,要么全回滚
  1. 前后端协作
    • RESTful风格API:DELETE /system/post/{ids}
    • 统一的ID传递格式(数组自动转换)
    • 标准化的响应格式(AjaxResult)

六、完整工作流程

  1. 前端操作

    • 用户选择单个/多个岗位
    • 点击删除按钮
    • 确认删除提示
  2. 请求发送

   DELETE /system/post/12,34,56Authorization: Bearer xxxx
  1. 后端处理

    • 权限验证(@PreAuthorize)
    • 日志记录(@Log)
    • 遍历检查每个岗位使用状态
    • 执行批量删除SQL
    • 返回操作结果
  2. 结果反馈

    • 成功:{ code: 200, msg: "操作成功" }
    • 失败:{ code: 500, msg: "经理岗位已分配,不能删除" }
  3. 前端响应

    • 显示操作结果提示
    • 刷新岗位列表
    • 重置选中状态

这种设计确保了删除操作的安全性、完整性和用户体验,通过多层校验防止数据误删,同时提供清晰的反馈帮助用户理解操作结果。

查询岗位功能详细解析

我将详细讲解岗位管理模块中的各种查询功能,包括分页查询、条件过滤、详情查询和下拉框查询等。查询功能是系统的核心基础功能,设计良好的查询机制能极大提升用户体验。

一、查询功能分类

查询类型前端调用方法后端接口主要用途
分页条件查询listPost(query)GET /system/post/list管理页面主列表展示
导出查询同分页查询POST /system/post/exportExcel导出功能
详情查询getPost(postId)GET /system/post/{postId}查看/修改单个岗位详情
下拉框查询optionselect()GET /system/post/optionselect用户管理中的岗位选择下拉框

二、分页条件查询(核心功能)

1. 前端实现流程

页面组件 (Post.vue)

<template><!-- 搜索表单 --><el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch"><el-form-item label="岗位编码" prop="postCode"><el-inputv-model="queryParams.postCode"placeholder="请输入岗位编码"clearablestyle="width: 200px"@keyup.enter="handleQuery"/></el-form-item><el-form-item><el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button><el-button icon="Refresh" @click="resetQuery">重置</el-button></el-form-item></el-form><!-- 数据表格 --><el-table :data="postList" v-loading="loading"><!-- 表格列定义 --><el-table-column label="岗位编码" align="center" prop="postCode" /></el-table><!-- 分页组件 --><pagination :total="total" v-model:page="queryParams.pageNum"v-model:limit="queryParams.pageSize"@pagination="getList"/>
</template><script setup>
import { listPost } from "@/api/system/post";// 查询参数
const queryParams = reactive({pageNum: 1,      // 当前页码pageSize: 10,     // 每页条数postCode: "",     // 岗位编码条件postName: "",     // 岗位名称条件status: ""        // 状态条件
});// 岗位列表数据
const postList = ref([]);
// 总记录数
const total = ref(0);
// 加载状态
const loading = ref(true);// 获取岗位列表
function getList() {loading.value = true;listPost(queryParams).then(response => {postList.value = response.rows;  // 当前页数据total.value = response.total;    // 总记录数loading.value = false;});
}// 处理搜索
function handleQuery() {queryParams.pageNum = 1;  // 重置到第一页getList();
}// 初始化加载数据
getList();
</script>

2. 后端实现流程

Controller层 (SysPostController.java)

@PreAuthorize("@ss.hasPermi('system:post:list')")
@GetMapping("/list")
public TableDataInfo list(SysPost post) {// 1. 启动分页startPage();  // 2. 查询数据List<SysPost> list = postService.selectPostList(post);// 3. 封装分页结果return getDataTable(list);
}

Service层 (SysPostServiceImpl.java)

@Override
public List<SysPost> selectPostList(SysPost post) {return postMapper.selectPostList(post);
}

Mapper XML (SysPostMapper.xml)

<select id="selectPostList" parameterType="SysPost" resultMap="SysPostResult">SELECT post_id, post_code, post_name, post_sort, status, create_timeFROM sys_post<where><if test="postCode != null and postCode != ''">AND post_code LIKE CONCAT('%', #{postCode}, '%')</if><if test="postName != null and postName != ''">AND post_name LIKE CONCAT('%', #{postName}, '%')</if><if test="status != null and status != ''">AND status = #{status}</if></where>ORDER BY post_sort ASC
</select>

3. 分页机制

分页关键点

  1. startPage()方法从请求参数中解析pageNumpageSize
  2. PageHelper自动改写SQL添加分页语句
  3. 分页信息存储在ThreadLocal中
  4. 查询结束后自动获取总记录数

三、导出查询

1. 前端实现

function handleExport() {proxy.download("system/post/export", {...queryParams.value}, `post_${new Date().getTime()}.xlsx`);
}

2. 后端实现

@Log(title = "岗位管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:post:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysPost post) {// 1. 查询所有符合条件的数据(不分页)List<SysPost> list = postService.selectPostList(post);// 2. 使用Excel工具类导出ExcelUtil<SysPost> util = new ExcelUtil<>(SysPost.class);util.exportExcel(response, list, "岗位数据");
}

特点

  • 复用相同的查询逻辑selectPostList
  • 不分页查询所有数据
  • 使用ExcelUtil工具类简化导出
  • 自动将实体字段映射到Excel列

四、详情查询(单个岗位查询)

1. 前端实现

// 查询岗位详细
function handleUpdate(row) {const postId = row.postId;getPost(postId).then(response => {form.value = response.data;open.value = true;});
}// API方法
export function getPost(postId) {return request({url: '/system/post/' + postId,method: 'get'})
}

2. 后端实现

@PreAuthorize("@ss.hasPermi('system:post:query')")
@GetMapping(value = "/{postId}")
public AjaxResult getInfo(@PathVariable Long postId) {return success(postService.selectPostById(postId));
}// Service实现
@Override
public SysPost selectPostById(Long postId) {return postMapper.selectPostById(postId);
}// Mapper XML
<select id="selectPostById" parameterType="Long" resultMap="SysPostResult">SELECT * FROM sys_post WHERE post_id = #{postId}
</select>

五、查询功能设计亮点

  1. 统一查询逻辑复用

    • 分页查询和导出查询共用同一套查询逻辑
    • 避免代码重复,保证数据一致性
  2. 灵活的条件组合

   SELECT * FROM sys_postWHERE 1=1/* 动态添加条件 */AND post_code LIKE '%DEV%'AND status = '0'ORDER BY post_sort ASC
  • 使用MyBatis动态SQL
  • 支持多条件自由组合
  1. 安全的分页机制

    • 最大分页限制(配置文件设置)
    • 防止恶意请求大量数据
  2. 响应式前端设计

   <el-table v-loading="loading" :data="postList"><el-table-column label="状态"><template #default="scope"><dict-tag :options="sys_normal_disable" :value="scope.row.status"/></template></el-table-column></el-table>
  • 加载状态提示
  • 字典值自动转换
  • 分页组件与表格联动
  1. 完善的权限控制
   // 列表查询权限@PreAuthorize("@ss.hasPermi('system:post:list')")// 导出权限@PreAuthorize("@ss.hasPermi('system:post:export')")// 详情查看权限@PreAuthorize("@ss.hasPermi('system:post:query')")

六、复杂查询场景处理

统计查询

// 统计岗位使用人数
@Override
public int countUserPostById(Long postId) {return userPostMapper.countUserPostById(postId);
}// Mapper
<select id="countUserPostById" resultType="int">SELECT COUNT(1) FROM sys_user_post WHERE post_id = #{postId}
</select>

岗位导出功能详细解析

导出功能是岗位管理系统的重要特性,允许用户将查询结果导出为Excel文件。下面从技术实现角度深入分析导出功能的完整流程:

一、前端实现流程

1. 导出按钮触发

<el-buttontype="warning"plainicon="Download"@click="handleExport"v-hasPermi="['system:post:export']"
>导出</el-button>
  • v-hasPermi指令校验导出权限
  • 点击触发handleExport方法

2. 导出处理逻辑

function handleExport() {// 调用封装的下载方法proxy.download("system/post/export", {...queryParams.value  // 携带当前查询条件}, `post_${new Date().getTime()}.xlsx`); // 生成带时间戳的文件名
}

3. 下载方法封装

// @/utils/request.js 在post.js内找:import request from '@/utils/request'
// 通用下载方法
export function download(url, params, filename, config) {downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })return service.post(url, params, {transformRequest: [(params) => { return tansParams(params) }],headers: { 'Content-Type': 'application/x-www-form-urlencoded' },responseType: 'blob',...config}).then(async (data) => {const isBlob = blobValidate(data)if (isBlob) {const blob = new Blob([data])saveAs(blob, filename)} else {const resText = await data.text()const rspObj = JSON.parse(resText)const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']ElMessage.error(errMsg)}downloadLoadingInstance.close()}).catch((r) => {console.error(r)ElMessage.error('下载文件出现错误,请联系管理员!')downloadLoadingInstance.close()})
}

二、后端实现流程

1. 控制器入口

@Log(title = "岗位管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:post:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysPost post) {// 查询所有符合条件的岗位List<SysPost> list = postService.selectPostList(post);// 使用Excel工具类导出ExcelUtil<SysPost> util = new ExcelUtil<>(SysPost.class);util.exportExcel(response, list, "岗位数据");
}

2. 实体类Excel注解

public class SysPost extends BaseEntity {@Excel(name = "岗位序号", cellType = ColumnType.NUMERIC)private Long postId;@Excel(name = "岗位编码")private String postCode;@Excel(name = "岗位名称")private String postName;@Excel(name = "岗位排序", cellType = ColumnType.NUMERIC)private Integer postSort;@Excel(name = "状态", readConverterExp = "0=正常,1=停用")private String status;
}

导出功能通过前后端协同实现,后端负责数据处理和Excel生成,前端负责文件下载。合理的设计可以支持从几百条到数百万条数据的导出需求,同时保证系统的稳定性和安全性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/916671.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/916671.shtml
英文地址,请注明出处:http://en.pswp.cn/news/916671.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu24安装MariaDB/MySQL后不知道root密码如何解决

Ubuntu 24.04 安装 MariaDB 后 root 密码未知&#xff1f;解决方案在此在 Ubuntu 24.04 上新安装 MariaDB 后&#xff0c;许多用户会发现自己不知道 root 用户的密码&#xff0c;甚至在安装过程中也没有提示设置密码。这是因为在较新的 MariaDB 版本中&#xff0c;默认情况下 r…

Cloudflare CDN 中设置地域限制并返回特定界面

文章目录 什么是CDN 什么是Cloudflare 注册Cloudflare 账号,添加域名、修改DNS并激活邮箱 阻止或允许特定国家或地区访问 常见规则表达式 WAF自定义规则 + 自定义错误页面 使用Workers脚本 什么是CDN CDN 是一种优化网站请求处理的机制。它是在用户访问网站 (服务器) 时用户与…

Ubuntu高频实用命令大全

Ubuntu系统中高频实用命令 以下为Ubuntu系统中高频实用命令的分类整理,涵盖系统管理、文件操作、网络配置等场景,每个命令附带简要说明: 系统信息与管理 uname -a 显示系统内核版本、主机名等详细信息。 lsb_release -a 查看Ubuntu发行版版本信息。 uptime 显示系统运行时…

关于C#的编程基础:数据类型与变量全解析

一.基本的数据类型 1.什么是数据类型 在编程语言中&#xff0c;数据类型&#xff08;Data Type&#xff09; 是对变量存储的 “数据的种类” 的定义&#xff0c;它决定了&#xff1a; 变量可以存储哪些值&#xff08;例如整数、文本、布尔值&#xff09;。这些值在内存中如何…

深入解析 Spring 获取 XML 验证模式的过程

关键要点Spring 的 XML 验证模式&#xff1a;Spring 框架在加载 XML 配置文件时&#xff0c;会根据文件内容判断使用 DTD&#xff08;文档类型定义&#xff09;或 XSD&#xff08;XML 模式定义&#xff09;进行验证。自动检测机制&#xff1a;Spring 默认使用自动检测&#xff…

复现《Local GDP Estimates Around the World》论文的完整指南

复现《Local GDP Estimates Around the World》论文的完整指南 1. 引言 1.1 论文概述 《Local GDP Estimates Around the World》是一篇重要的经济地理学研究论文&#xff0c;作者提出了一种创新的方法来估计全球范围内次国家层面的GDP数据。这项工作填补了全球经济发展研究中子…

Sql注入 之sqlmap使用教程

一、安装sqlmap 浏览器访问SQLmap官网 即可下载工具&#xff1b;需要说明的是&#xff0c;SQLmap运行依赖于python环境&#xff0c;所以在下载使用前务必在电脑及终端上安装好python环境。 通过网盘分享的文件&#xff1a;sqlmap-master.zip链接: https://pan.baidu.com/s/1YZi…

安宝特案例丨户外通信机房施工革新:AR+作业流技术破解行业难题

在数字化浪潮席卷各行各业的今天&#xff0c;传统户外通信机房建设领域正经历一场静悄悄的变革。作为信息社会的“神经枢纽”&#xff0c;户外机房的质量直接关系到通信网络的稳定性&#xff0c;但长期以来&#xff0c;这一领域却深受施工标准化不足、质量管控难、验收追溯复杂…

在 CentOS 中安装 MySQL 的过程与问题解决方案

MySQL 是一款广泛使用的开源关系型数据库管理系统&#xff0c;在 CentOS 系统中安装 MySQL 是很多开发者和运维人员常做的工作。下面将详细介绍安装过程以及可能遇到的问题和解决方案。 一、安装前的准备工作 在安装 MySQL 之前&#xff0c;需要做好一些准备工作&#xff0c;…

阿里 Qwen3 四模型齐发,字节 Coze 全面开源,GPT-5 8 月初发布!| AI Weekly 7.21-7.27

&#x1f4e2;本周AI快讯 | 1分钟速览&#x1f680;1️⃣ &#x1f9e0; 阿里 Qwen3 全系列爆发 &#xff1a;一周内密集发布四款新模型&#xff0c;包括 Qwen3-235B-A22B-Thinking-2507、Qwen3-Coder 和 Qwen3-MT&#xff0c;MMLU-Pro 成绩超越 Claude Opus 4&#xff0c;百万…

C语言第 9 天学习笔记:数组(二维数组与字符数组)

C语言第09天学习笔记&#xff1a;数组&#xff08;二维数组与字符数组&#xff09; 内容提要 数组 二维数组字符数组二维数组 定义 二维数组本质上是一个行列式组合&#xff0c;由行和列两部分组成&#xff0c;属于多维数组&#xff0c;通过行和列解读&#xff08;先行后列&…

使用OpenCV做个图片校正工具

昨天有位兄台给我发了个文件&#xff0c;是下面这个样子的&#xff1a;那一双小脚既没有裹成三寸金莲&#xff0c;又没有黑丝&#xff0c;这图片肯定不符合我的要求。我要的是这个样子的好不好&#xff1a;让他拿扫描仪重新给我规规矩矩扫一个发过来&#xff1f;他要能用扫描仪…

《不只是接口:GraphQL与RESTful的本质差异》

RESTful API凭借其与HTTP协议的天然融合&#xff0c;以资源为核心的架构理念&#xff0c;在过去十余年里构建了Web数据交互的基本秩序&#xff1b;而GraphQL的出现&#xff0c;以“按需获取”为核心的查询模式&#xff0c;打破了传统的请求-响应逻辑&#xff0c;重新定义了前端…

博士招生 | 香港大学 招收人工智能和网络安全方向 博士生

学校简介香港大学创立于 1911 年&#xff0c;是香港历史最悠久的高等学府&#xff0c;QS 2025 世界排名第 17 位。计算机科学学科在 QS 2025 学科排名中位列全球第 31 位、亚洲第 5 位。计算机系&#xff08;Department of Computer Science&#xff09;下设系统、人工智能、数…

Linux知识回顾总结----基础IO

目录 1. 理解“文件” 1.1 文件的定义 2. 回顾 C 语言的文件操作 2.1 文件操作 2.2 实现cat 2.3 可以实现打印的几种方式 3. 系统文件的IO 3.2 使用系统的接口 3.3 内部的实现 3.4 重定向 4. 文件系统的内核结构 5. 缓冲区 5.1 是什么 5.2 为什么 5.3 有什么 5.4 见见…

网络:基础概念

网络&#xff1a;基础概念 在计算机发展过程中&#xff0c;最开始每个计算机时相互独立的&#xff0c;后来人们需要用计算机合作处理任务&#xff0c;这就牵扯到了数据交换&#xff0c;所以最开始的网络就诞生了。一开始&#xff0c;网络都是局域网LAN&#xff0c;后来技术成熟…

图像识别边缘算法

文章目录1. 基本概念2. 边缘检测原理边缘类型&#xff1a;3. 常见边缘检测算法3.1 Sobel算子3.2 Canny边缘检测3.3 Laplacian算子4. Canny边缘检测详细流程流程图示例&#xff1a;详细步骤说明&#xff1a;5. 边缘检测算法比较6. 参数调优建议Canny边缘检测参数&#xff1a;Sob…

【Java Web实战】从零到一打造企业级网上购书网站系统 | 完整开发实录(终)

&#x1f9ea; 测试与质量保证 &#x1f50d; 全方位测试体系 我建立了企业级的全方位测试体系来确保系统质量&#xff1a; &#x1f9ea; 测试金字塔模型 #mermaid-svg-u4I8UuUAyxJVjcqs {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill…

QT开发---网络编程下

HTTP协议 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是互联网上应用最为广泛的协议之一&#xff0c;用于客户端和服务器之间的通信。默认端口80&#xff0c;传输层使用的是TCP协议特点无连接&#xff1a;HTTP协议是无连接的&#xff…

mac 苹果电脑 Intel 芯片(Mac X86) 安卓虚拟机 Android模拟器 的救命稻草(下载安装指南)

引言&#xff1a; 还在为你的Intel芯片MacBook&#xff08;i5, i7, i9等&#xff09;找不到合适的安卓虚拟机而发愁吗&#xff1f;随着Apple Silicon (M1/M2/M3) 芯片的普及&#xff0c;大量优秀的安卓模拟器&#xff08;如Android Studio自带的模拟器、网易MuMu等&#xff09;…