文章目錄
- 什么是若依
- 使用若依
- 驗證碼的前端實現
- 📌 前后端驗證碼流程說明文檔
- 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 Chat
,Agent
模式下
項目成功啟動頁面:
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 圖片數據拼接為可直接顯示的 URLloginForm.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() // 刷新列表})}})
}
- 觸發表單驗證
- 驗證通過后調用
addPost
API - 顯示操作成功提示
- 關閉對話框并刷新崗位列表
二、后端實現流程
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
確保數值字段不為空- 驗證失敗自動返回錯誤信息給前端
三、關鍵特性與設計思想
-
多層驗證機制:
- 前端:Element Plus表單驗證
- 網絡層:JSON格式校驗
- 后端:JSR-303實體驗證
- 業務層:唯一性校驗
- 數據庫:唯一約束(需在DDL中定義)
-
操作審計:
- 通過
@Log
注解自動記錄:
- 通過
@Log(title = "崗位管理", businessType = BusinessType.INSERT)
-
安全控制:
- 權限校驗:
@PreAuthorize("@ss.hasPermi('system:post:add')"
- 數據隔離:自動注入當前用戶
getUsername()
- 權限校驗:
-
響應式設計:
- 前端:提交后自動刷新列表
- 后端:返回標準化的AjaxResult對象
-
異常處理:
- 唯一性沖突返回可讀性錯誤信息
- 數據庫操作異常全局處理
四、完整數據流
- 用戶點擊"新增"按鈕
- 前端打開對話框并重置表單
- 用戶填寫表單并提交
- 前端進行字段級驗證
- 驗證通過后調用
/system/post
POST API - 后端控制器接收請求:
- 權限校驗
- 實體字段驗證
- 業務規則校驗(唯一性)
- 服務層設置創建人
- Mapper執行動態SQL插入
- 返回操作結果(成功/失敗)
- 前端提示結果并刷新列表
這種設計實現了前后端分離架構下的高效協作,通過多層驗證保證數據質量,利用注解簡化開發,同時確保系統的安全性和可維護性。
修改崗位功能詳細解析
我將詳細講解崗位管理模塊中的修改功能,從前端到后端的完整實現流程。修改功能是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 = "修改崗位"; // 設置對話框標題});
}
執行流程:
- 重置表單狀態,確保無殘留數據
- 從行數據中獲取崗位ID
- 調用API獲取崗位詳細信息
- 將返回數據填充到表單
- 打開對話框并設置標題
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
判斷是修改還是新增 - 調用
updatePost
API提交修改 - 成功后關閉對話框并刷新列表
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));
}
執行流程:
@PreAuthorize
進行權限校驗@Log
記錄操作日志@Validated
進行參數校驗(基于JSR-303)- 校驗崗位名稱和編碼的唯一性
- 設置更新人(當前登錄用戶)
- 調用服務層執行更新
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;
}
校驗邏輯:
- 獲取當前崗位ID(修改時為真實ID,新增時為null)
- 查詢數據庫是否存在相同名稱的崗位
- 如果存在同名崗位:
- 且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特點:
- 使用動態SQL(
<set>
和<if>
標簽) - 只更新非空字段
- 自動設置更新時間
- 使用數據庫函數設置時間(sysdate())
- 根據postId定位記錄
三、關鍵設計要點
-
權限控制雙重保障
- 前端:
v-hasPermi
指令控制按鈕顯示 - 后端:
@PreAuthorize
注解進行方法級權限校驗
- 前端:
-
數據一致性保證
- 唯一性校驗:防止名稱/編碼重復
- 事務管理:確保更新操作的原子性
-
審計字段自動化
- 自動記錄更新人:
post.setUpdateBy(getUsername())
- 自動更新時間:
update_time = sysdate()
- 自動記錄更新人:
-
前后端校驗結合
- 前端:Element Plus表單校驗
- 后端:JSR-303參數校驗 + 業務邏輯校驗
-
性能優化
- 動態SQL:只更新變化的字段
- 最小化數據傳輸:前端只發送必要字段
-
用戶體驗優化
- 修改前加載完整數據
- 操作成功自動刷新列表
- 明確的錯誤提示信息
刪除崗位功能詳細解析
刪除功能是崗位管理系統中最敏感的操作之一,需要特別關注數據完整性和安全控制。下面從前后端協同角度深入解析刪除功能的實現:
一、前端實現流程
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. 關鍵處理步驟
- 獲取刪除目標:
- 單個刪除:從行數據獲取
row.postId
- 批量刪除:從選中的
ids
數組中獲取多個ID
- 單個刪除:從行數據獲取
const postIds = row.postId || ids.value;
-
二次確認:
- 使用
$modal.confirm
顯示確認對話框 - 明確顯示將被刪除的崗位編號
- 使用
-
API調用:
- 調用
delPost
方法發送刪除請求 - 支持單個ID或ID數組(自動處理)
- 調用
-
結果反饋:
- 成功:顯示"刪除成功"提示
- 失敗:全局異常處理(已在底層封裝)
-
狀態更新:
- 刷新崗位列表數據
- 重置選中狀態
二、后端實現流程
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. 關鍵業務邏輯
- 使用狀態檢查:
// 查詢崗位用戶關聯數量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>
-
安全刪除機制:
- 遍歷每個待刪除崗位
- 檢查
sys_user_post
關聯表 - 如果存在關聯用戶,拋出業務異常
- 包含崗位名稱的友好錯誤提示
-
批量刪除執行:
<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等)}
);
五、設計亮點分析
-
批量操作優化:
- 單次數據庫交互完成批量刪除(IN語句)
- 減少數據庫連接開銷
-
用戶友好設計:
- 明確提示哪個崗位無法刪除
- 二次確認避免誤操作
- 批量選擇狀態自動管理
-
事務完整性:
@Transactional(rollbackFor = Exception.class)public int deletePostByIds(Long[] postIds) {// 操作在同一個事務中}
- 整個刪除操作原子性執行
- 檢查與刪除要么全成功,要么全回滾
- 前后端協作:
- RESTful風格API:
DELETE /system/post/{ids}
- 統一的ID傳遞格式(數組自動轉換)
- 標準化的響應格式(AjaxResult)
- RESTful風格API:
六、完整工作流程
-
前端操作:
- 用戶選擇單個/多個崗位
- 點擊刪除按鈕
- 確認刪除提示
-
請求發送:
DELETE /system/post/12,34,56Authorization: Bearer xxxx
-
后端處理:
- 權限驗證(@PreAuthorize)
- 日志記錄(@Log)
- 遍歷檢查每個崗位使用狀態
- 執行批量刪除SQL
- 返回操作結果
-
結果反饋:
- 成功:
{ code: 200, msg: "操作成功" }
- 失敗:
{ code: 500, msg: "經理崗位已分配,不能刪除" }
- 成功:
-
前端響應:
- 顯示操作結果提示
- 刷新崗位列表
- 重置選中狀態
這種設計確保了刪除操作的安全性、完整性和用戶體驗,通過多層校驗防止數據誤刪,同時提供清晰的反饋幫助用戶理解操作結果。
查詢崗位功能詳細解析
我將詳細講解崗位管理模塊中的各種查詢功能,包括分頁查詢、條件過濾、詳情查詢和下拉框查詢等。查詢功能是系統的核心基礎功能,設計良好的查詢機制能極大提升用戶體驗。
一、查詢功能分類
查詢類型 | 前端調用方法 | 后端接口 | 主要用途 |
---|---|---|---|
分頁條件查詢 | listPost(query) | GET /system/post/list | 管理頁面主列表展示 |
導出查詢 | 同分頁查詢 | POST /system/post/export | Excel導出功能 |
詳情查詢 | 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. 分頁機制
分頁關鍵點:
startPage()
方法從請求參數中解析pageNum
和pageSize
- PageHelper自動改寫SQL添加分頁語句
- 分頁信息存儲在ThreadLocal中
- 查詢結束后自動獲取總記錄數
三、導出查詢
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>
五、查詢功能設計亮點
-
統一查詢邏輯復用
- 分頁查詢和導出查詢共用同一套查詢邏輯
- 避免代碼重復,保證數據一致性
-
靈活的條件組合
SELECT * FROM sys_postWHERE 1=1/* 動態添加條件 */AND post_code LIKE '%DEV%'AND status = '0'ORDER BY post_sort ASC
- 使用MyBatis動態SQL
- 支持多條件自由組合
-
安全的分頁機制
- 最大分頁限制(配置文件設置)
- 防止惡意請求大量數據
-
響應式前端設計
<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>
- 加載狀態提示
- 字典值自動轉換
- 分頁組件與表格聯動
- 完善的權限控制
// 列表查詢權限@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生成,前端負責文件下載。合理的設計可以支持從幾百條到數百萬條數據的導出需求,同時保證系統的穩定性和安全性。