使用者管理模組最佳化
先聊一下寫這次需求的感想,起初接下這個需求的時候,給我的感覺就是很簡單,並且覺得程式碼三天不到就可以寫完,即使是在業務不熟悉的情況下。然後就是經歷了,第三方溝通需求、確定技術方案、熟悉使用者管理涉及到的多個模組的業務細節、刷數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
![image-20240815163027854](https://img2023.cnblogs.com/blog/2794716/202408/2794716-20240815163028870-1905577234.png)
去重
![image-20240815163156417](https://img2023.cnblogs.com/blog/2794716/202408/2794716-20240815163156943-1694094008.png)
更新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的學習
vuex之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