真實業務環境-需求分析思路(二)
用戶管理模塊優化
先聊一下寫這次需求的感想,起初接下這個需求的時候,給我的感覺就是很簡單,并且覺得代碼三天不到就可以寫完,即使是在業務不熟悉的情況下。然后就是經歷了,第三方溝通需求、確定技術方案、熟悉用戶管理涉及到的多個模塊的業務細節、刷數SQL、優化代碼避免出現超時......測試人員給代碼提bug,優化、與第三方確定實現效果上線日期、確定刷數SQL是否可在上線中使用、上線確定等。大概就是這一套下來之后我才知道我有多天真!!! 你們應該能猜到我現在的心理變化,emmmm。不過,這次經歷的好處是,我確實提升了自己的業務理解和代碼能力,而且也驗證了之前學到的知識是有用的,感覺還是挺不錯的。
下面就寫記錄一下用戶管理模塊的實現思路~
需求分析
查詢
(1)查詢條件新增所屬系統,可多選;選中多個時,數據中有任意一個則顯示。
(2)列表增加所屬系統,字段用 ',' 相隔。

添加
(1)所屬系統:必填,多選。
(2)角色:可選角色根據所選系統動態渲染。

修改
(1)所屬系統:必填,多選。
(2)角色:可選角色根據所選系統動態渲染。
(3)用戶省份、地市、所屬系統變更。
用戶省份、地市、所屬系統變更時校驗用戶是否為智聯開發負責人(智聯變成其他系統類型)、工單受理組人員、第一接單人、督辦第一接單人、工單升級人員,根據用戶身份彈窗提示。例:當前用戶已配置對應區域工單受理組,請刪除工單受理組配置后處理。

批量刪除、刪除
刪除用戶時校驗用戶是否為智聯開發負責人、工單受理組人員、第一接單人、督辦第一接單人、工單升級人員,根據用戶身份彈窗提示。例:當前用戶已配置對應區域工單受理組,請刪除工單受理組配置后處理。
用戶參與工單存在在途工單,不允許刪除用戶。提示:當前用戶存在在途工單,不允許刪除!

導出
導出字段加上所屬系統

技術方案
在和三方溝通后確定了第一種技術方案
方案一:直接在t_user表中新增 “所屬系統” 字段
優點:
簡單易實現:直接在現有表中新增字段,不需要創建新的表或復雜的關聯
查詢方便:可以直接通過現有的查詢條件獲取信息
性能較高:在查詢和更新時,性能較高,因為沒有額外的表連接操作
缺點:
擴展性差: 如果將來需要增加更多的系統類型,字段長度限制可能會成為問題。
數據冗余: 如果一個用戶屬于多個系統,需要通過分隔符存儲多個系統類型,這會導致數據冗余和維護困難。
數據一致性問題: 在更新和刪除操作時,可能會因為字符串解析問題導致數據不一致。
方案二:新增t_user_systemType用戶和所屬系統字段中間表
優點:
擴展性強: 可以靈活地增加更多的系統類型,不受字段長度限制。
數據一致性高: 各個系統類型分開存儲,避免了字符串解析問題,保證數據的一致性和完整性。
易于維護: 系統類型的增刪改查操作更加靈活和簡便。
缺點:
復雜性增加: 需要新增表和外鍵,增加了數據模型的復雜性。
性能開銷: 查詢時需要進行表連接操作,可能會影響性能。
實現思路
背景:在之前的版本中也是存在所屬系統字段的,只不過這個所屬系統字段是在角色表中存放,和角色綁定。之前的所屬系統的賦值是通過用戶獲取角色列表,然后拿到角色列表中權限最大的角色,進而拿到最大角色對應的所屬系統字段,這個邏輯明顯不適用在這里。
總體上思考一下需求,要完成這次需求我們要做什么,哪些技術點需要思考,哪些細節需要注意等等。
- 查詢:開發人員(我)和 三方溝通確定需求后,確認直接在t_user中添加所屬系統字段。
- 第一個點就是歷史數據如何保證?原本存在的數據的所屬系統的值如何賦值?(保證歷史數據的準確性之后,其他的才有意義,否則沒有實現的意義)
- 前端后端實現細節,前端傳的所屬系統是String類型,且不同系統之間用逗號隔開,后端存儲所屬系統字段也是。那么查詢就是多對多,這個如何解決?
- 前端所屬系統下拉框中的數據如何獲取?直接從字典中拿所有的數據?
- 新增:
- 新增的對話框中所屬系統和角色之間存在映射關系,不同的所屬系統角色列表就不同
- 修改:由于原本這塊的邏輯有很大的問題,所以這里很多邏輯都要改
- 所屬系統和角色列表有映射關系
- 點擊edit彈出修改的對話框,此時對話框中的所屬系統和角色列表數據獲取都是有問題,那么應該如何獲取?
- 校驗被修改用戶是否是智聯開發負責人(所屬系統從智聯到其他系統時校驗)、第一接單人、督辦第一接單人、工單升級人員、工單受理組。(這里涉及好多個模塊,校驗不只是簡單的校驗,還需要考慮關聯性)
- 在校驗的時候需要調用不同組件服務,這里需要foreign
- 這里涉及到的不同的模塊,比如第一接單人,需要新增刪除第一接單人接口等
- 報錯提示信息應該如何設計?
- 刪除:
- 刪除時校驗被刪除用戶是否存在在途工單以及校驗第一接單人、督辦、升級人員
- 報錯信息應該如何設計?
- 導出
- 導出新增所屬系統字段,這里加個注解就好
- 導出時做一個轉義
以上就是這次需求的簡單構思,構思的時候需要結合代碼結合業務去分析,不然只能是空想,并且也會漏掉很多點。
這里因為我是主后端手,在寫前端的邏輯時相對較慢,所以就先把后端的接口給寫了,先把后端初步思考的做了,之后思路打開之后前端就也完成了。要知道我在寫這個需求的時候,業務也不能算很熟悉,所以要在規定的時間內提測一個測試版本,還是有一定難度,不過,好歹是拿下來了,hhh

實現過程
歷史數據處理
備份和回退數據這里不多說,歷史數據的處理需要注意一下
根據實現思路中的背景我們知道 所屬系統字段在t_role中存在,并且和角色綁定,所以可以通過用戶身上的角色去拿到所屬系統數據。
比如: AA角色是A系統,BB角色是B系統,CC角色是C系統的
那么當張三用戶有AA、BB角色,那么張三用戶的所屬系統字段就應該是 A,B系統
查詢出 system_type 和 user_id
去重
更新t_user表中的所屬系統字段
#內連接
update table1 join (table2) on ... set ...
# 更新字段數據
UPDATE t_user u
JOIN (
SELECT
u.USER_ID,
GROUP_CONCAT( DISTINCT r.SYSTEM_TYPE ) AS SYSTEM_TYPE
FROM
t_user u
LEFT JOIN t_user_role t ON u.USER_ID = t.USER_ID
LEFT JOIN t_role r ON r.ROLE_ID = t.ROLE_ID
GROUP BY
u.USER_ID
) temp ON u.USER_ID = temp.USER_ID
SET u.SYSTEM_TYPE = temp.SYSTEM_TYPE;
上線和用戶管理這塊相關的所有sql
# 備份表數據
CREATE TABLE t_user_0815 LIKE t_user;
INSERT INTO t_user_0815 ( SELECT * FROM t_user );
# 新增字段
ALTER TABLE t_user ADD COLUMN SYSTEM_TYPE VARCHAR ( 255 ) DEFAULT NULL COMMENT '所屬系統';
# 更新字段數據
UPDATE t_user u
JOIN (
SELECT
u.USER_ID,
GROUP_CONCAT( DISTINCT r.SYSTEM_TYPE ) AS SYSTEM_TYPE
FROM
t_user u
LEFT JOIN t_user_role t ON u.USER_ID = t.USER_ID
LEFT JOIN t_role r ON r.ROLE_ID = t.ROLE_ID
GROUP BY
u.USER_ID
) temp ON u.USER_ID = temp.USER_ID
SET u.SYSTEM_TYPE = temp.SYSTEM_TYPE;
# 新增字段
ALTER TABLE t_user ADD COLUMN IS_VALID BIT(1);
# 更新字段數據
UPDATE t_user SET IS_VALID = 1;
#回退SQL
DROP TABLE t_user;
-- 重新創建 t_user 表并恢復數據
CREATE TABLE t_user LIKE t_user_0815;
INSERT INTO t_user (SELECT * FROM t_user_0815);
下面的具體功能實現,按照前后端,查詢、新增、修改、刪除、導出 這樣的順序去進行適當的記錄
查詢
新增查詢條件 "所屬系統" 、 列表數據顯示增加 "所屬系統" 字段

前端直接復用Vue和Element UI組件的頁面代碼,但是"所屬系統" 字段是新增的,所以該字段的數據獲取需要調后端接口
具體的語法含義可以去 element ui 官網查看 組件 | Element
# 查詢條件中的所屬系統
<el-select v-model="queryParams.systemType" clearable filterable multiple placeholder="所屬系統" class="filter-item search-item">
<el-option
v-for="item in systemTypeList"
:key="item.dictValue"
:label="item.dictName"
:value="item.dictValue"
/>
</el-select>
# 列表數據中的所屬系統
<el-table-column label="所屬系統" align="center" min-width="100px" show-overflow-tooltip>
<template slot-scope="scope">
<span>{{ translateSystemType(scope.row.systemType) }}</span>
</template>
</el-table-column>
下面是所屬系統數據的獲取
在獲取所屬系統數據時,需要特別注意對后端返回的系統類型字段進行轉換。后端返回的數據通常是一個逗號分隔的字符串,比如 "energy,zhuti,test",需要將其轉換為對應的中文名稱字符串。為實現這一轉換,首先將字符串拆分為數組,再通過匹配系統類型列表,將每個英文代碼轉換為相應的中文名稱,并最終將這些名稱組合成一個完整的中文字符串。
// 請求參數對象,包括類型、用戶名、地區、省市、時間范圍、角色ID和所屬系統字段
queryParams: {
typename: '', // 類型名稱
username: '', // 用戶名
province: '', // 省份
city: '', // 城市
timeRange: '', // 時間范圍
roleId: [], // 角色ID數組
systemType: null // 所屬系統字段,初始值為空
},
systemTypeList: [], // 初始化系統類型列表
// 初始化 systemTypeList 數據的方法
initSystemTypeList() {
// 調用 getUserInfo 方法獲取當前用戶信息
getUserInfo(this.currentUser.userId).then(res => {
// 判斷獲取的用戶系統類型數據是否為空
if (res.data.data.systemType !== '') {
// 如果不為空,將系統類型字符串轉為數組
const systemTypeArray = res.data.data.systemType.split(',');
// 獲取系統類型列表
this.getSystemTypeList(systemTypeArray);
} else {
// 如果為空,顯示錯誤信息
this.$message.error("字典數據獲取為空或者失敗");
}
});
},
// 根據系統類型獲取對應的系統類型列表 --- 比如 'energy' 變成 dictValue:'energy' 和 dictName:'能源'
getSystemTypeList(systemType) {
// 遍歷系統類型數組
systemType.forEach(systemType => {
// 如果系統類型為 'energy' 且未存在于 systemTypeList 中,則將其添加
if (systemType === 'energy') {
if (!this.systemTypeList.find(item => item.dictValue === 'energy')) {
this.systemTypeList.push({ dictValue: systemType, dictName: '能源' });
}
}
// 如果系統類型為 'yunguan' 且未存在于 systemTypeList 中,則將其添加
if (systemType === 'yunguan') {
if (!this.systemTypeList.find(item => item.dictValue === 'yunguan')) {
this.systemTypeList.push({ dictValue: systemType, dictName: '行拓(智聯)' });
}
}
// 如果系統類型為 'yunyingshang' 且未存在于 systemTypeList 中,則將其添加
if (systemType === 'yunyingshang') {
if (!this.systemTypeList.find(item => item.dictValue === 'yunyingshang')) {
this.systemTypeList.push({ dictValue: systemType, dictName: '主體' });
}
}
});
},
// 將系統類型從英文代碼翻譯為對應的中文名稱
translateSystemType(systemType) {
if (!systemType) return ''; // 如果系統類型為空,返回空字符串
// 將系統類型字符串按逗號分隔,并逐個查找對應的中文名稱
return systemType
.split(',')
.map(dictValue => {
const item = this.systemTypeList.find(item => item.dictValue === dictValue);
return item ? item.dictName : ''; // 找到對應的 dictCode 后返回 dictName,否則返回空字符串
})
.filter(name => name !== '') // 過濾掉空字符串
.join(','); // 將結果連接成一個字符串
}
查詢按鈕,當我們點擊查詢時觸發search方法
<el-button class="filter-item" type="primary" @click="search">
{{ $t('table.search') }}
</el-button>
跳轉search方法,在search方法中調用后端接口("/user/list")獲取列表信息
// 請求參數對象,包括類型、用戶名、地區、省市、時間范圍、角色ID和所屬系統字段
queryParams: {
typename: '', // 類型名稱
username: '', // 用戶名
province: '', // 省份
city: '', // 城市
timeRange: '', // 時間范圍
roleId: [], // 角色ID數組
systemType: null // 所屬系統字段,初始值為空
},
search() {
this.fetch({
...this.queryParams,
...this.sort
})
},
后端
后端User實體類中添加三個字段
為什么在實體類中添加systemType字段之后還需要添加systemTypeList、noSystemTypeList?
在上述技術方案中,選擇在 t_user 表中新增一個字段來存儲用戶所屬系統,而不是通過創建單獨的表來維護用戶與系統類型的關系。因此,在查詢時需要處理多對多的關系。例如,查詢條件為 "systemA,systemB",而數據庫中的字段可能存儲的是 "systemA,systemC,systemB"。
兩種解決方案(使用的第二種)
- 排列組合方案
由于系統類型數量有限,并且未來擴展的可能性不大,可以將系統類型(如"systemA"和"systemB")進行排列組合,生成所有可能的組合形式。例如,將三個系統類型映射為0, 1, 2,然后生成組合如01, 02, 12, 012,這些組合分別代表不同的系統類型組合。該方案的優點是可以實現精準查詢,但隨著系統類型的增加,組合數量和查詢的復雜性也會增加。 - 包含與排除方案
該方案使用systemTypeList和noSystemTypeList兩個列表,分別表示需要包含和需要排除的系統類型。在查詢時,通過檢查數據庫中所屬系統字段是否包含systemTypeList中的類型,并且不包含noSystemTypeList中的類型,從而篩選出符合條件的數據。這種方式的優點是靈活性更高,能夠適應更復雜的查詢場景。
/**
* 所屬系統
*/
@TableField("SYSTEM_TYPE")
@ExcelField(value = "所屬系統", required = true)
private String systemType;
/**
* 查詢條件:系統類型多選
*/
@TableField(exist = false)
private List<String> systemTypeList;
/**
* 查詢條件:當前登錄用戶不包含的系統類型列表
*/
@TableField(exist = false)
private List<String> noSystemTypeList;
SQL部分邏輯
<if test="user.systemTypeList != null and user.systemTypeList.size() > 0">
AND (
<foreach item="type" collection="user.systemTypeList" separator="or">
FIND_IN_SET(#{type}, u.system_type)
</foreach>
)
</if>
<if test="user.noSystemTypeList != null and user.noSystemTypeList.size() > 0">
AND
<foreach item="type" collection="user.noSystemTypeList" separator="and">
u.system_type not like CONCAT('%',#{type},'%')
</foreach>
</if>
新增
前端優化直接參考修改處
修改
前端
角色列表數據根據所屬系統和當前登錄用戶的最大角色權限動態獲取展示

父組件中引入子組件對話框(父子組件之間的交互)
當我們點擊修改按鈕后,isVisible屬性置為true,彈出修改對話框,并將用戶數據賦值到子組件的屬性中
<user-edit
ref="edit"
:dialog-visible="dialog.isVisible"
:title="dialog.title"
@success="editSuccess"
@close="editClose"
/>
editClose() {
# 關閉對話框
this.dialog.isVisible = false
},
editSuccess() {
#修改成功后,查詢頁面列表數據
this.search()
}
在下面的edit方法中,賦值角色、部門信息,并且調用子組件中的setUser用戶給屬性賦值
edit(row) {
let roleId = []
if (row.roleId && typeof row.roleId === 'string') {
roleId = row.roleId.split(',')
#給row本行數據中的roleId賦值
row.roleId = roleId
}
this.$get(`system/user/${row.userId}`).then((r) => {
#部門ids賦值
row.deptIds = r.data.data
#用戶信息賦值
this.$refs.edit.setUser(row)
this.$refs.edit.setSubFlag(false)
this.dialog.title = this.$t('common.edit')
this.dialog.isVisible = true
})
},
子組件(修改對話框)
在子組件中要想在改變所屬系統字段時,角色列表展示信息更新,只需要在設置一個@change="systemTypeChange"方法,該方法參數是systemType,然后調后端接口獲取橘色列表信息
<el-select v-model="user.systemType" value="" multiple placeholder="" style="width:100%" @change="systemTypeChange">
后端
修改后端接口實現思路:
1.更改省份、地市時,需校驗用戶是否是第一接單人、督辦第一接單人、工單升級人員,如果是則直接回顯提示信息,如果不是則修改成功
2.禁用賬號狀態時,需校驗用戶是否是第一接單人、督辦第一接單人、工單升級人員、工單受理組人員、用戶在途工單(草稿,待評價,待處理等工單)
3.修改所屬系統時,需校驗所屬系統是否是從智聯 -> 其他系統,如果是則還需要校驗智聯開發負責人,如果不是則只需校驗第一接單人、督辦第一接單人、工單升級人員
4.刪除用戶時,需要校驗用戶是否是第一接單人、督辦第一接單人、工單升級人員、工單受理組人員、用戶在途工單(草稿,待評價,待處理等工單)
起初將這些校驗邏輯寫在了 update 接口中,但在本地運行時,接口經常會超時。下面我只展示最終的結果供大家參考。中間的過程就不詳細贅述了,以免干擾大家
之前的文章中就說了,寫需求首先就是要把思路理清,這樣做才不會白費功夫
根據上面的校驗內容可以把第一接單人、督辦第一接單人、工單升級人員提在一個遠程接口中復用,參數類型有userId,systemType,province,city
/**
* 判斷被修改用戶是否是第一接單人、督辦人、升級人員
* @param userId
* @param systemType
* @param province
* @param city
* @return
*/
@GetMapping("/***/checkOtherBusiness")
public String checkOtherBusiness(@RequestParam("userId") String userId,
@RequestParam(value = "systemType",required = false) String systemType,
@RequestParam(value = "province",required = false) String province,
@RequestParam(value = "city",required = false) String city);
userId 參數是必填項,用于指定需要校驗的用戶,后面的參數可根據用戶修改內容選擇性填寫。比如修改省份時,就傳省份參數其他參數為空。
校驗工單受理組和在途工單校驗寫了兩個接口
/**
* 遠程調用 查詢用戶在途工單數量
* @param userId
* @return
*/
@GetMapping("/***/getJoinCount")
public Integer getJoinCount(@RequestParam(value = "userId",required = false) String userId);
/**
* 根據userId在工單受理組中查詢用戶
* @param userId
* @return
*/
@GetMapping("/***/queryUserByAllProvince")
public List<ProEmployeeVo> queryUserByAllProvince(@RequestParam("userId") String userId);
接著就是主邏輯應該如何寫
- 如果用戶的修改是禁用賬號,那么此時需要進行最全面的校驗。若禁用狀態的校驗已經通過,則不再需要對省份、地市、所屬系統等變更進行額外校驗。因此,我設置了一個 boolean 值來控制這個邏輯。
- 省份或地市變更時,需要考慮省份變更是否影響地市的校驗。由于鐵塔總部在省份中,當省份變更為鐵塔時,不需要進行額外校驗。由于之前業務邏輯的漏洞,導致一個用戶可以在不同省份擁有工單和身份。如果省份從北京變為上海,則只需要校驗除上海以外的工單和身份。地市修改的邏輯相似,例如從北京朝陽區變為北京城區,則只需要校驗除城區以外的身份和工單。
- 所屬系統變更時,需要考慮系統是否從包含智聯變為不包含智聯,如果是這樣,則需要校驗用戶在智聯開發負責人角色中的身份。如果所屬系統從智聯、能源變為智聯、主體,則需要校驗用戶在能源系統下是否仍有身份和工單。因為對于被修改的用戶,新增系統通常不會有影響,而減少系統則可能帶來影響,因此系統變更時,需要校驗原本有但現在沒有的系統。
- 校驗信息的返回和打印,最初我使用了
StringBuilder,但考慮到省份、地市和所屬系統都可能發生變更,調用的接口可能導致消息中出現重復信息。因此,我最終使用了HashSet來處理。 - 而且這里由于用戶新增的涉及到的模塊中的邏輯也需要更新
下面接口方法就是校驗第一接單人、督辦、升級,返回的String可能就是 "第一接單人、第一督辦接單人、工單升級人員"

下面抽出來部分代碼用于展示錯誤信息打印邏輯
// 定義返回結果的Msg
Set<String> errMsgSet = new HashSet<>();
String result = problemApiClient.checkOtherBusiness(String.valueOf(oldUser.getUserId()), null, null, null);
if (StringUtils.isNotBlank(result)) errMsgSet.addAll(Arrays.asList(result.split("、")));
// 檢查用戶是否有未處理的工單
joinCount = problemApiClient.getJoinCount(String.valueOf(user.getUserId()));
// 查詢當前用戶是否在工單受理組中
employeeVos = problemApiClient.queryUserByAllProvince(user.getUserId().toString());
if(employeeVos.size() > 0){
errMsgSet.add("工單受理組人員");
}
StringBuilder resultErrMsg = new StringBuilder();
// 構建最終的錯誤信息并拋出異常
if (!errMsgSet.isEmpty() && joinCount > 0) {
resultErrMsg.append("當前用戶為").append(String.join("、", errMsgSet)).append(",并且存在").append(joinCount).append("個在途工單,無法進行修改操作。");
} else if (!errMsgSet.isEmpty()) {
resultErrMsg.append("當前用戶為").append(String.join("、", errMsgSet)).append(",無法進行修改操作。");
} else if (joinCount > 0) {
resultErrMsg.append("當前用戶存在 ").append(joinCount).append(" 個在途工單,無法進行修改操作。");
}
if(StringUtils.isNotBlank(resultErrMsg)) throw new FebsRuntimeException(resultErrMsg.toString().trim());
修改邏輯的總結,因為是需求寫完之后寫的筆記,所以其實在寫的過程中一些比較好的點可能在寫的時候沒考慮到,所以只記錄了部分邏輯
刪除
這里可類比修改,調的接口都一樣,只是錯誤打印的內容需要更改
導出
加注解,然后就是導出的時候做了轉義
// 所屬系統字段轉義
List<SystemUser> users = this.baseMapper.exportUserDetail(user);
users.forEach(SystemUser::translateSystemType);
public void translateSystemType(){
if(StringUtils.isNotEmpty(this.systemType)){
this.systemType = Arrays.stream(this.systemType.split(","))
.map(systemType -> {
switch (systemType){
case "energy":
return "能源";
case "yunguan":
return "智聯";
case "yunyingshang":
return "主體";
default:
return systemType;
}
})
.collect(Collectors.joining(","));
}
}
需求測試
測試人員測試,提bug,然后開發人員修改代碼
版本上線
和三方溝通確認功能,確認上線時間,上線版本的需求
知識總結
這里把接口調用寫在JS文件中調用,不要直接寫在代碼中
好處:
首先,最大的好處就是代碼復用,這點毋庸置疑。其次,分離業務邏輯代碼和接口調用邏輯有助于解耦合,使代碼結構更加清晰。再者,考慮到后期的維護性,如果接口調用需要修改,只需要在一個地方進行調整,而不必逐一修改所有調用該接口的代碼。

http請求的嚴格區分
get、post、put、delete?_刪除用get還是post-CSDN博客
因為再寫刪除第一接單人的時候用的是GET請求,這里是不好的
所有 http 請求,一律用 POST,在業務功能的實現是沒有問題的.
post,get,put,delete 是標準, 大家都遵循這樣的規則. 這樣的api對于它人來說一目了然, get就是獲取數據, post就是提交數據, put就是更新數據, delete就做刪除操作. 如果一律使用post對一個項目組的內部人員來說是沒有問題的, 但是對于對外公開的接口就讓調用者摸不著頭腦了。
以遵循 RFC-2616 所定義的協議的方式顯式地使用 HTTP 方法,建立創建、檢索、更新和刪除(CRUD:Create, Retrieve, Update and Delete)操作與 HTTP 方法之間的一對一映射:
- 若要在服務器上創建資源,應該使用 POST 方法;
- 若要檢索某個資源,應該使用 GET 方法;
- 若要更改資源狀態或對其進行更新,應該使用 PUT 方法;
- 若要刪除某個資源,應該使用 DELETE 方法。
@RequestParam和@PathVariable
請求參數和路徑參數
相同點與區別
@RequestParam和@PathVariable都能夠完成類似的功能——因為本質上,它們都是用戶的輸入,只不過輸入的部分不同,一個在URL路徑部分,另一個在參數部分。要訪問一篇博客文章,這兩種URL設計都是可以的:
- 通過@PathVariable,例如/blogs/1
- 通過@RequestParam,例如blogs?blogId=1
那么究竟應該選擇哪一種呢?建議:
1、當URL指向的是某一具體業務資源(或資源列表),例如博客,用戶時,使用@PathVariable
2、當URL需要對資源或者資源列表進行過濾,篩選時,用@RequestParam
例如我們會這樣設計URL:
- /blogs/
- /blogs?state=publish而不是/blogs/state/publish來表示處于發布狀態的博客文章
更多用法
一旦我們在方法中定義了@RequestParam變量,如果訪問的URL中不帶有相應的參數,就會拋出異常——這是顯然的,Spring嘗試幫我們進行綁定,然而沒有成功。但有的時候,參數確實不一定永遠都存在,這時我們可以通過定義required屬性:
@RequestParam(value = "id", required = false)
當然,在參數不存在的情況下,可能希望變量有一個默認值:
@RequestParam(value = "id", required = false, defaultValue = "0")
前端父子組件之間的交互
這里父組件引入子組件
<user-edit
ref="edit"
:dialog-visible="dialog.isVisible"
:title="dialog.title"
@success="editSuccess"
@close="editClose"
/>
<user-view
ref="view"
:dialog-visible="userViewVisible"
@close="viewClose"
/>
import UserEdit from './Edit'
import UserView from './View'
ref="edit":ref 屬性用于給組件實例一個引用名稱。通過 this.$refs.edit 可以在父組件中訪問這個 user-edit 組件實例。
:dialog-visible="dialog.isVisible":dialog-visible 是一個屬性綁定,dialog.isVisible 是父組件中的一個布爾值,控制 user-edit 組件中的對話框是否可見。使用了 : 來動態綁定父組件中的 dialog.isVisible 數據。
:title="dialog.title":類似地,title 也是一個屬性綁定,傳遞的是 dialog.title,用于設置 user-edit 組件中的標題。
@success="editSuccess":@ 是 Vue.js 事件綁定的簡寫形式。這個綁定表示,當 user-edit 組件觸發 success 事件時,父組件的 editSuccess 方法會被調用。
@close="editClose":同樣,當 user-edit 組件觸發 close 事件時,父組件的 editClose 方法會被調用。
典型場景
- 打開編輯對話框:用戶在頁面中點擊“編輯”按鈕,父組件將
dialog.isVisible設置為true,從而顯示<user-edit>組件的對話框。 - 成功保存:在
<user-edit>組件中,用戶成功保存信息時,組件會觸發success事件,父組件的editSuccess方法被調用,可能會刷新用戶列表或進行其他邏輯處理。 - 關閉對話框:無論是
<user-edit>還是<user-view>,當用戶關閉對話框時,組件會觸發close事件,父組件會相應地將對話框的可見狀態設置為false。
這里是如何獲取row一行數據的
row 參數是通過 el-table 組件的 slot-scope 傳遞給插槽內的模板的
好吧什么是插槽?

對象展開語法
...val 表示將對象 val 的所有屬性逐一展開到新的對象中。
{ ...val } 創建了一個新對象,這個新對象包含了 val 對象的所有屬性和對應的值。

前端store的學習

Arrays.asList返回的List無法add和remove
分析源碼就知道了為什么了,這里我會寫一篇新的
關于StringBuilder 出現null的問題
在寫需求時出現了一個奇怪的報錯,當problemApiClient調用checkOtherBusiness方法返回的String對象為空時,也就是null
然后調用roleErrMsg.append(result) 的時候會讓這個StringBuilder對象中存儲值 "null" ,導致不為空
我這里復現失敗
https://blog.csdn.net/Mikeoperfect/article/details/106739567

本文來自博客園,作者:Liberty碼農志,轉載請注明原文鏈接:http://www.rzrgm.cn/zhiliu/p/18372225

浙公網安備 33010602011771號