Guns二次开发(四):重构字典管理模块
?
關于博客中使用的Guns版本問題請先閱讀? ?Guns二次開發目錄? ? ? ?
? ? ? ?Guns中的字段管理是一個很不錯的功能,我其實也是第一次發現還能這么玩,因為以前一直使用的方法是:后端直接將數據庫保存的狀態值返回給前端,然后在文檔中加上字段不同值的解釋,前端針對這個字段做不同的 if else 判斷來解釋其含義,并回顯到頁面中。某種意義上來講,這其實是后端開發人員將自己的工作量拋給前端開發人員,以此減輕自己的工作量。但是對于mvc項目下的后臺管理系統,此時前端代碼和后端代碼都是由Java開發包圓了,于是后端拋出去的球又回到了自己手中,在這種情況下,字典管理便應運而生了。當然了,以上都是我個人的想法,大家當個段子看便好。
? ? ? ?現在切入正題。上一篇博客中,我們一起修改了字典管理的性別字段顯示錯誤的問題,相信大家對字典管理的都有了一定的認識。今天我決定再加大挑戰的難度,我準備重構這個字典功能。之所以重構,并非本人閑的蛋疼,而是字典管理這個模塊的確存在一定的設計缺陷。
?
1、保證字典名稱的唯一性
? ? ? 需求: 準確點描述就是,在sys_dict 表中,對于pid為0的記錄,要保證name字段的唯一性(也就是保證字典名稱的唯一性)。對于pid不為0的同級別記錄(即pid相等但pid不為0的記錄),要保證name和code字段的唯一性。
?
更為形象的表述:
以上圖中的A、B兩組數據為例來說明,A、B兩組數據代表兩個字典,
(1)pid為0的記錄的name字段代表的是字典名稱,code字段表示表中的字段名;
(2)pid不為0的記錄的name字段代表的就是字段值的釋義,code字段代表的是字段值。
?
要求:
(1)當pid都為0時,此時必須保證name字段的唯一性,而code字段反而可以重復;
(2)當pid不為0,但相等時,比如pid是53,此時A組數據的id為54和55的記錄就是同級記錄,此時既要保證name字段的唯一性,也要保證code字段的唯一性。
(3)當pid不同時,比如id為54和129的兩條記錄,因為不屬于同一組,所以不管是name重復還是code重復都是允許的。
?
?
而現在的情況是:
?
?
?
?
?
所以,需要修改添加字典和修改字典接口的邏輯,保證字典名稱(即pid=0的記錄的name字段)的唯一性,之所以要保證字典名稱的唯一性,而不是code字段的唯一性,是因為實際開發中,不同表中出現相同字段的情況是時常發生的,有可能我商品分類表中有一個字段status,然后我商品表中也有一個status字段,如果此時這兩個字段都要做字典,此時如何保證唯一性?一開始我想到要不要在字典表中新增一個代表表名的字段來用于保證記錄唯一性,但后來一想,這反而增加修改的成本,我們直接在給name字段命名的時候規范一下,其實也正好可以達到這個效果。比如商品表的status字段的字典命名為【商品狀態】,商品分類表的status字段的字典命名為【商品分類狀態】,這樣既保證了name的唯一性,又不用修改表結構,而且反而還更具可讀性。
?
下面把修改后的代碼貼出來,要修改的接口有兩個,一個是添加接口,另一個是修改接口,另外我將公共的添加方法抽取出來了。下面只粘貼需要修改的部分代碼,方便閱讀,文章最后會有完整的代碼:
?
DictController.java
/*** 新增字典** @param dictValues 格式例如 "1:啟用;2:禁用;3:凍結"*/@BussinessLog(value = "添加字典記錄", key = "dictName,dictValues", dict = DictMap.class)@RequestMapping(value = "/add")@Permission(Const.ADMIN_NAME)@ResponseBodypublic Object add(@RequestParam(required = false,defaultValue = "0",value = "dictNum")Integer dictNum,String dictCode, String dictTips, String dictName, String dictValues) {if (ToolUtil.isOneEmpty(dictCode, dictValues,dictName)) {throw new ServiceException(BizExceptionEnum.REQUEST_NULL);}this.dictService.addDict(dictCode, dictName, dictTips, dictValues,dictNum);return SUCCESS_TIP;}/*** 修改字典*/@BussinessLog(value = "修改字典", key = "dictName,dictValues", dict = DictMap.class)@RequestMapping(value = "/update")@Permission(Const.ADMIN_NAME)@ResponseBodypublic Object update(@RequestParam(required = false,defaultValue = "0",value = "dictNum")Integer dictNum,Integer dictId, String dictCode, String dictName, String dictTips, String dictValues) {if (ToolUtil.isOneEmpty(dictId, dictCode, dictName, dictValues)) {throw new ServiceException(BizExceptionEnum.REQUEST_NULL);}dictService.editDict(dictId, dictCode, dictName, dictTips, dictValues,dictNum);return SUCCESS_TIP;}?
DictServiceImpl.java
@Override@Transactional//為了保證事務一致性,必須加上這個注解public void addDict(String dictCode, String dictName, String dictTips, String dictValues, Integer dictNum) {//添加字典insertDict(dictCode, dictName, dictTips, dictValues, dictNum);}/*** 公共的添加字典業務*/private void insertDict(String dictCode, String dictName, String dictTips, String dictValues, Integer dictNum){//解析dictValuesList<Dict> list = getDictList(dictValues);//添加字典Dict dict = new Dict();dict.setName(dictName);dict.setCode(dictCode);dict.setTips(dictTips);dict.setNum(dictNum);//此處傳入設置的numdict.setPid(0);//執行字典名的添加操作int count = dictMapper.insert(dict);if(count==0) {//FAIL_ADD_RECORD(500,"數據庫中新增數據失敗"),throw new ServiceException(BizExceptionEnum.FAIL_ADD_RECORD);}//因為要保證字典名的唯一性,所以需要再查詢一遍,判斷是否沖突Wrapper<Dict> wrapper = new EntityWrapper<>();wrapper.eq("pid",0).eq("name",dictName);count = dictMapper.selectCount(wrapper);if(count>1){//DICT_NAME_EXISTED(400,"字典名稱已經存在"),throw new ServiceException(BizExceptionEnum.DICT_NAME_EXISTED);}//添加字典條目Dict item = null;Integer pid = dict.getId();//獲取父類idfor(int i=0; i<list.size(); i++ ){item = list.get(i);item.setPid(pid);}//直接調用批量插入語句insertBatch(list);}@Override@Transactionalpublic void editDict(Integer dictId, String dictCode, String dictName, String dictTips, String dicts,Integer dictNum) {//刪除之前的字典this.delteDict(dictId);//添加字典insertDict(dictCode, dictName, dictTips, dicts, dictNum);}@Override@Transactionalpublic void delteDict(Integer dictId) {//刪除這個字典的子詞典Wrapper<Dict> dictEntityWrapper = new EntityWrapper<>();dictEntityWrapper = dictEntityWrapper.eq("pid", dictId);dictMapper.delete(dictEntityWrapper);//刪除這個詞典dictMapper.deleteById(dictId);}/*** 解析一個組合字符串(例如: "1:啟用;2:禁用;3:凍結" 這樣的字符串)* 本方法是將字符串直接轉換成對應的Dict實體類集合,* 如果前端傳入參數的方式錯誤* 或者用戶輸入的參數不合法,此處都會拋出異常,終止程序*/private List<Dict> getDictList(String mutiString) {List<Dict> list = new ArrayList<>();//判斷分隔后是否為空數組List<String> split = StringUtil.split(mutiString,";");if(split.isEmpty()){ // ERROR_ITEM_EMPTY(500, "字典詳情不能為空"),throw new ServiceException(BizExceptionEnum.ERROR_ITEM_EMPTY);}//用來封裝已經存在的code參數,如果有重復的值,說明前端傳入的值錯誤了,//此時也應該拋出異常Set<String> codeSet = new HashSet<>();Set<String> nameSet = new HashSet<>();Dict dict = null;List<String> attrs = null;String attr = null;for (String item : split) {dict = new Dict();//如果數組長度不為3,說明前端的傳入數據錯誤,此時請求應終止attrs = StringUtil.split(item,":");if(attrs.size() !=3){//DICT_PARAMS_ILLEGAL(400,"請填寫完整的字典詳情信息"),throw new ServiceException(BizExceptionEnum.DICT_PARAMS_ILLEGAL);}//保證code值的唯一attr = attrs.get(0);if(codeSet.contains(attr)){throw new ServiceException(400,"值【"+attr+"】重復");}codeSet.add(attr);dict.setCode(attr);//為了字典的可讀性,我這邊也要保證name字段的一致attr = attrs.get(1);if(nameSet.contains(attr)){throw new ServiceException(400,"名稱【"+attr+"】重復");}nameSet.add(attr);dict.setName(attr);//保存排序編號if(!NumberUtils.isDigits(attrs.get(2))){//ERROR_NUM_TYPE(500, "字典詳情序號只能是數字"),throw new ServiceException(BizExceptionEnum.DICT_MUST_BE_NUMBER);}dict.setNum(new Integer(attrs.get(2)));list.add(dict);}return list;}?
?
?
?
修改好代碼后,重新運行項目,測試效果:
測試添加重復的字典名稱:
?
測試添加相同釋義的記錄:
?
添加列表顯示的字段,提高可讀性:
?
?
?
?
?
2、修改編輯字典的內部邏輯
? ? ? ?不知道大家還記不記得,上一篇博客?Guns二次開發(三):解決用戶性別字典顯示錯誤?中,我們在解決用戶性別字典顯示錯誤的時候,有讀到這么一段源碼:
?
性別字典的名稱在代碼中被寫死了,也就是我要想正常使用【性別】這個字典的值,就必須保證性別字典的名稱是字符串“性別”二字:
?
只有這樣,性別字典才能正常使用。
?
一旦我們修改了【性別】字典的名稱,如下圖:
?
那么,性別字典就會失效,如下圖:
?
?
?
要想讓性別字典生效,只能去改動代碼中的字典名稱,但這樣一來,字典功能就顯得沒有價值了。還有一種方法就是在修改字典的時候,前端禁用字典名稱字段所在的文本框:
?
? ? ? ?但是這樣一來,就降低了用戶體驗,因為有時候,用戶修改字典名稱的需求還是很大的,不能因為設計者的無能而降低了用戶的體驗。所以,給字典定義一個不變且唯一的字段也就變得很有必要,一說到唯一且不變,我們應該很下意識就能想到數據表的主鍵id,主鍵id因為是自增的,所以能保證唯一,又因為是主鍵,所以更新操作通常都不會改變它。不過呢,只是正常情況下如此,我們只要去查看字典修改接口 /dict/update 的內部實現邏輯,就會發現它的邏輯是:先刪除掉原來的字典,然后再重新添加一遍。如此一來,修改之后,主鍵id也會跟著改變,如下圖源碼:
?
? ? ? ?看到這里,內心是不是有一種無法言明的難受感,事實上,這樣寫代碼是不行的。因為用戶(不管是后臺客服還是前臺用戶)的刪除操作,都不能使用邏輯刪除,除了DBA(數據庫管理員)外,任何人都不應該有刪除數據庫數據的權限,因為大數據時代來了,大數據時代,沒有無效的數據,只有無用的大數據開發者。當然這些都只是我個人的理解,言歸正傳,鑒于前面的情況,我決定還是修改后端的修改字典接口來達到我們的目的。
? ? ? ?我原本是想將物理刪除修改為邏輯刪除(sys_dict表中加個狀態字段),但是考慮到這樣修改的成本太大,而且我的時間也有限,自己項目的進度也很緊,所以最終還是以 “盡量減少改動原代碼”的原則,做出如下的修改思路:
? ? ? ?我在編輯的時候,對于字典名稱那條記錄,不做刪除操作,而做修改操作,這樣一來,字典名稱所在記錄的id就是唯一且一直有效的,然后字典條目(字典釋義)的記錄(即pid不為0的記錄)則依舊是物理刪除后再重新添加。接著是字典包裝那一段,我不再使用字典名稱作為定位的標志,而是改為使用字典名稱記錄的id字段,如此一來,即便我將定位標志寫死了,后面無論我如何修改字典名稱,我的字典都能正常使用。
前面的話都太抽象,讓我換個形象點的說法:?
?
?
?
?
?
?
?
?
3、源碼
?
?
?
?
?
?
?
?
(1)DictController.java
/*** Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)* <p>* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at* <p>* http://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/ package cn.stylefeng.guns.modular.system.controller;import cn.stylefeng.guns.core.common.annotion.BussinessLog; import cn.stylefeng.guns.core.common.annotion.Permission; import cn.stylefeng.guns.core.common.constant.Const; import cn.stylefeng.guns.core.common.constant.dictmap.DictMap; import cn.stylefeng.guns.core.common.constant.factory.ConstantFactory; import cn.stylefeng.guns.core.common.exception.BizExceptionEnum; import cn.stylefeng.guns.core.log.LogObjectHolder; import cn.stylefeng.guns.modular.system.model.Dict; import cn.stylefeng.guns.modular.system.service.IDictService; import cn.stylefeng.guns.modular.system.warpper.DictWarpper; import cn.stylefeng.roses.core.base.controller.BaseController; import cn.stylefeng.roses.core.util.ToolUtil; import cn.stylefeng.roses.kernel.model.exception.ServiceException; import com.baomidou.mybatisplus.mapper.EntityWrapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*;import java.util.List; import java.util.Map;/*** 字典控制器** @author fengshuonan* @Date 2017年4月26日 12:55:31*/ @Controller @RequestMapping("/dict") public class DictController extends BaseController {private String PREFIX = "/system/dict/";@Autowiredprivate IDictService dictService;/*** 跳轉到字典管理首頁*/@RequestMapping("")public String index() {return PREFIX + "dict.html";}/*** 跳轉到添加字典*/@RequestMapping("/dict_add")public String deptAdd() {return PREFIX + "dict_add.html";}/*** 跳轉到修改字典*/@Permission(Const.ADMIN_NAME)@RequestMapping("/dict_edit/{dictId}")public String deptUpdate(@PathVariable Integer dictId, Model model) {Dict dict = dictService.selectById(dictId);model.addAttribute("dict", dict);List<Dict> subDicts = dictService.selectList(new EntityWrapper<Dict>().eq("pid", dictId));model.addAttribute("subDicts", subDicts);LogObjectHolder.me().set(dict);return PREFIX + "dict_edit.html";}/*** 新增字典** @param dictValues 格式例如 "1:啟用;2:禁用;3:凍結"*/@BussinessLog(value = "添加字典記錄", key = "dictName,dictValues", dict = DictMap.class)@RequestMapping(value = "/add")@Permission(Const.ADMIN_NAME)@ResponseBodypublic Object add(@RequestParam(required = false,defaultValue = "0",value = "dictNum")Integer dictNum,String dictCode, String dictTips, String dictName, String dictValues) {if (ToolUtil.isOneEmpty(dictCode, dictValues,dictName)) {throw new ServiceException(BizExceptionEnum.REQUEST_NULL);}this.dictService.addDict(dictCode, dictName, dictTips, dictValues,dictNum);return SUCCESS_TIP;}/*** 修改字典*/@BussinessLog(value = "修改字典", key = "dictName,dictValues", dict = DictMap.class)@RequestMapping(value = "/update")@Permission(Const.ADMIN_NAME)@ResponseBodypublic Object update(@RequestParam(required = false,defaultValue = "0",value = "dictNum")Integer dictNum,Integer dictId, String dictCode, String dictName, String dictTips, String dictValues) {if (ToolUtil.isOneEmpty(dictId, dictCode, dictName, dictValues)) {throw new ServiceException(BizExceptionEnum.REQUEST_NULL);}dictService.editDict(dictId, dictCode, dictName, dictTips, dictValues,dictNum);return SUCCESS_TIP;}/*** 獲取所有字典列表*/@RequestMapping(value = "/list")@Permission(Const.ADMIN_NAME)@ResponseBodypublic Object list(String condition) {List<Map<String, Object>> list = this.dictService.list(condition);return super.warpObject(new DictWarpper(list));}/*** 字典詳情*/@RequestMapping(value = "/detail/{dictId}")@Permission(Const.ADMIN_NAME)@ResponseBodypublic Object detail(@PathVariable("dictId") Integer dictId) {return dictService.selectById(dictId);}/*** 刪除字典記錄*/@BussinessLog(value = "刪除字典記錄", key = "dictId", dict = DictMap.class)@RequestMapping(value = "/delete")@Permission(Const.ADMIN_NAME)@ResponseBodypublic Object delete(@RequestParam Integer dictId) {//緩存被刪除的名稱LogObjectHolder.me().set(ConstantFactory.me().getDictName(dictId));this.dictService.delteDict(dictId);return SUCCESS_TIP;}}?
(2)DictServiceImpl.java
/*** Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)* <p>* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at* <p>* http://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/ package cn.stylefeng.guns.modular.system.service.impl;import cn.stylefeng.guns.core.common.exception.BizExceptionEnum; import cn.stylefeng.guns.elephish.utils.StringUtil; import cn.stylefeng.guns.modular.system.dao.DictMapper; import cn.stylefeng.guns.modular.system.model.Dict; import cn.stylefeng.guns.modular.system.service.IDictService; import cn.stylefeng.roses.kernel.model.exception.ServiceException; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.mapper.Wrapper; import com.baomidou.mybatisplus.service.impl.ServiceImpl; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource; import java.util.*;/*** 字典服務** @author fengshuonan* @Date 2018/10/15 下午11:39*/ @Service public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements IDictService {@Resourceprivate DictMapper dictMapper;@Override@Transactional//為了保證事務一致性,必須加上這個注解public void addDict(String dictCode, String dictName, String dictTips, String dictValues, Integer dictNum) {//添加字典Dict dict = new Dict();dict.setName(dictName);dict.setCode(dictCode);dict.setTips(dictTips);dict.setNum(dictNum);//此處傳入設置的numdict.setPid(0);//執行字典名的添加操作int count = dictMapper.insert(dict);if(count==0) {//FAIL_ADD_RECORD(500,"數據庫中新增數據失敗"),throw new ServiceException(BizExceptionEnum.FAIL_ADD_RECORD);}//因為要保證字典名的唯一性,所以需要再查詢一遍,判斷是否沖突Wrapper<Dict> wrapper = new EntityWrapper<>();wrapper.eq("pid",0).eq("name",dictName);count = dictMapper.selectCount(wrapper);if(count>1){//DICT_NAME_EXISTED(400,"字典名稱已經存在"),throw new ServiceException(BizExceptionEnum.DICT_NAME_EXISTED);}//添加字典條目addChildrenDict(dictValues,dict.getId());}/*** 公共的添加字典條目*/private void addChildrenDict( String dictValues, Integer pid){//解析dictValuesList<Dict> list = getDictList(dictValues);//添加字典條目Dict item = null;for(int i=0; i<list.size(); i++ ){item = list.get(i);item.setPid(pid);}//直接調用批量插入語句insertBatch(list);}@Override@Transactionalpublic void editDict(Integer dictId, String dictCode, String dictName, String dictTips, String dicts,Integer dictNum) {//更新當前這個字典名稱Dict dict = new Dict();dict.setName(dictName);dict.setCode(dictCode);dict.setTips(dictTips);dict.setNum(dictNum);dict.setId(dictId);dictMapper.updateById(dict);//刪除這個字典的所有子字典Wrapper<Dict> wrapper = new EntityWrapper<>();wrapper.eq("pid", dictId);dictMapper.delete(wrapper);//添加新的字典條目addChildrenDict(dicts,dictId);}/*** 解析一個組合字符串(例如: "1:啟用;2:禁用;3:凍結" 這樣的字符串)* 本方法是將字符串直接轉換成對應的Dict實體類集合,* 如果前端傳入參數的方式錯誤* 或者用戶輸入的參數不合法,此處都會拋出異常,終止程序*/private List<Dict> getDictList(String mutiString) {List<Dict> list = new ArrayList<>();//判斷分隔后是否為空數組List<String> split = StringUtil.split(mutiString,";");if(split.isEmpty()){ // ERROR_ITEM_EMPTY(500, "字典詳情不能為空"),throw new ServiceException(BizExceptionEnum.ERROR_ITEM_EMPTY);}//用來封裝已經存在的code參數,如果有重復的值,說明前端傳入的值錯誤了,//此時也應該拋出異常Set<String> codeSet = new HashSet<>();Set<String> nameSet = new HashSet<>();Dict dict = null;List<String> attrs = null;String attr = null;for (String item : split) {dict = new Dict();//如果數組長度不為3,說明前端的傳入數據錯誤,此時請求應終止attrs = StringUtil.split(item,":");if(attrs.size() !=3){//DICT_PARAMS_ILLEGAL(400,"請填寫完整的字典詳情信息"),throw new ServiceException(BizExceptionEnum.DICT_PARAMS_ILLEGAL);}//保證code值的唯一attr = attrs.get(0);if(codeSet.contains(attr)){throw new ServiceException(400,"值【"+attr+"】重復");}codeSet.add(attr);dict.setCode(attr);//為了字典的可讀性,我這邊也要保證name字段的一致attr = attrs.get(1);if(nameSet.contains(attr)){throw new ServiceException(400,"名稱【"+attr+"】重復");}nameSet.add(attr);dict.setName(attr);//保存排序編號if(!NumberUtils.isDigits(attrs.get(2))){//ERROR_NUM_TYPE(500, "字典詳情序號只能是數字"),throw new ServiceException(BizExceptionEnum.DICT_MUST_BE_NUMBER);}dict.setNum(new Integer(attrs.get(2)));list.add(dict);}return list;}@Overridepublic List<Map<String, Object>> list(String conditiion) {Wrapper<Dict> wrapper = new EntityWrapper<>();if(StringUtils.isNoneBlank(conditiion)){wrapper.like("name",conditiion.trim());}//此處添加按num字段順序排序wrapper.eq("pid",0).orderBy("num",true);return dictMapper.selectMaps(wrapper);}@Override@Transactionalpublic void delteDict(Integer dictId) {//刪除這個字典的子詞典Wrapper<Dict> dictEntityWrapper = new EntityWrapper<>();dictEntityWrapper = dictEntityWrapper.eq("pid", dictId);dictMapper.delete(dictEntityWrapper);//刪除這個詞典dictMapper.deleteById(dictId);}@Overridepublic List<Dict> selectByCode(String code) {return this.baseMapper.selectByCode(code);}@Overridepublic List<Dict> selectByParentCode(String code) {return this.baseMapper.selectByParentCode(code);}}?
(3)UserWarpper.java
/*** Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)* <p>* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at* <p>* http://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/ package cn.stylefeng.guns.modular.system.warpper;import cn.stylefeng.guns.core.common.constant.factory.ConstantFactory; import cn.stylefeng.guns.elephish.constants.WrapperDictNameConstant; import cn.stylefeng.roses.core.base.warpper.BaseControllerWrapper; import cn.stylefeng.roses.kernel.model.page.PageResult; import com.baomidou.mybatisplus.plugins.Page;import java.util.List; import java.util.Map;/*** 用戶管理的包裝類** @author fengshuonan* @date 2017年2月13日 下午10:47:03*/ public class UserWarpper extends BaseControllerWrapper {public UserWarpper(Map<String, Object> single) {super(single);}public UserWarpper(List<Map<String, Object>> multi) {super(multi);}public UserWarpper(Page<Map<String, Object>> page) {super(page);}public UserWarpper(PageResult<Map<String, Object>> pageResult) {super(pageResult);}/*** 此處自定義添加需要包裝的字段的字典信息*/@Overrideprotected void wrapTheMap(Map<String, Object> map) {//這段代碼表示,我需要在返回值中添加一個屬性 sexName,// 這個seName是字段sex對應的字典解釋信息,比如如果sex=1,那么sexName=男 // map.put("sexName", ConstantFactory.me().getSexName( (Integer)map.get("sex")));map.put("sexName", ConstantFactory.me().getDictById(WrapperDictNameConstant.SYS_USER_SEX_ID,map.get("sex").toString()));map.put("roleName", ConstantFactory.me().getRoleName((String) map.get("roleid")));map.put("deptName", ConstantFactory.me().getDeptName((Integer) map.get("deptid")));map.put("statusName", ConstantFactory.me().getStatusName((Integer) map.get("status")));}}?
?
(4)ConstantFactory.java
/*** Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)* <p>* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at* <p>* http://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/ package cn.stylefeng.guns.core.common.constant.factory;import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; import cn.stylefeng.guns.core.common.constant.cache.Cache; import cn.stylefeng.guns.core.common.constant.cache.CacheKey; import cn.stylefeng.guns.core.common.constant.state.ManagerStatus; import cn.stylefeng.guns.core.common.constant.state.MenuStatus; import cn.stylefeng.guns.core.log.LogObjectHolder; import cn.stylefeng.guns.modular.system.dao.*; import cn.stylefeng.guns.modular.system.model.*; import cn.stylefeng.roses.core.util.SpringContextHolder; import cn.stylefeng.roses.core.util.ToolUtil; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.mapper.Wrapper; import lombok.val; import org.apache.commons.lang3.StringUtils; import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component;import java.util.ArrayList; import java.util.List;/*** 常量的生產工廠** @author fengshuonan* @date 2017年2月13日 下午10:55:21*/ @Component @DependsOn("springContextHolder") public class ConstantFactory implements IConstantFactory {private RoleMapper roleMapper = SpringContextHolder.getBean(RoleMapper.class);private DeptMapper deptMapper = SpringContextHolder.getBean(DeptMapper.class);private DictMapper dictMapper = SpringContextHolder.getBean(DictMapper.class);private UserMapper userMapper = SpringContextHolder.getBean(UserMapper.class);private MenuMapper menuMapper = SpringContextHolder.getBean(MenuMapper.class);private NoticeMapper noticeMapper = SpringContextHolder.getBean(NoticeMapper.class);public static IConstantFactory me() {return SpringContextHolder.getBean("constantFactory");}/*** 根據用戶id獲取用戶名稱** @author stylefeng* @Date 2017/5/9 23:41*/@Overridepublic String getUserNameById(Integer userId) {User user = userMapper.selectById(userId);if (user != null) {return user.getName();} else {return "--";}}/*** 根據用戶id獲取用戶賬號** @author stylefeng* @date 2017年5月16日21:55:371*/@Overridepublic String getUserAccountById(Integer userId) {User user = userMapper.selectById(userId);if (user != null) {return user.getAccount();} else {return "--";}}/*** 通過角色ids獲取角色名稱*/@Override@Cacheable(value = Cache.CONSTANT, key = "'" + CacheKey.ROLES_NAME + "'+#roleIds")public String getRoleName(String roleIds) {if (ToolUtil.isEmpty(roleIds)) {return "";}Integer[] roles = Convert.toIntArray(roleIds);StringBuilder sb = new StringBuilder();for (int role : roles) {Role roleObj = roleMapper.selectById(role);if (ToolUtil.isNotEmpty(roleObj) && ToolUtil.isNotEmpty(roleObj.getName())) {sb.append(roleObj.getName()).append(",");}}return StrUtil.removeSuffix(sb.toString(), ",");}/*** 通過角色id獲取角色名稱*/@Override@Cacheable(value = Cache.CONSTANT, key = "'" + CacheKey.SINGLE_ROLE_NAME + "'+#roleId")public String getSingleRoleName(Integer roleId) {if (0 == roleId) {return "--";}Role roleObj = roleMapper.selectById(roleId);if (ToolUtil.isNotEmpty(roleObj) && ToolUtil.isNotEmpty(roleObj.getName())) {return roleObj.getName();}return "";}/*** 通過角色id獲取角色英文名稱*/@Override@Cacheable(value = Cache.CONSTANT, key = "'" + CacheKey.SINGLE_ROLE_TIP + "'+#roleId")public String getSingleRoleTip(Integer roleId) {if (0 == roleId) {return "--";}Role roleObj = roleMapper.selectById(roleId);if (ToolUtil.isNotEmpty(roleObj) && ToolUtil.isNotEmpty(roleObj.getName())) {return roleObj.getTips();}return "";}/*** 獲取部門名稱*/@Override@Cacheable(value = Cache.CONSTANT, key = "'" + CacheKey.DEPT_NAME + "'+#deptId")public String getDeptName(Integer deptId) {Dept dept = deptMapper.selectById(deptId);if (ToolUtil.isNotEmpty(dept) && ToolUtil.isNotEmpty(dept.getFullname())) {return dept.getFullname();}return "";}/*** 獲取菜單的名稱們(多個)*/@Overridepublic String getMenuNames(String menuIds) {Integer[] menus = Convert.toIntArray(menuIds);StringBuilder sb = new StringBuilder();for (int menu : menus) {Menu menuObj = menuMapper.selectById(menu);if (ToolUtil.isNotEmpty(menuObj) && ToolUtil.isNotEmpty(menuObj.getName())) {sb.append(menuObj.getName()).append(",");}}return StrUtil.removeSuffix(sb.toString(), ",");}/*** 獲取菜單名稱*/@Overridepublic String getMenuName(Long menuId) {if (ToolUtil.isEmpty(menuId)) {return "";} else {Menu menu = menuMapper.selectById(menuId);if (menu == null) {return "";} else {return menu.getName();}}}/*** 獲取菜單名稱通過編號*/@Overridepublic String getMenuNameByCode(String code) {if (ToolUtil.isEmpty(code)) {return "";} else {Menu param = new Menu();param.setCode(code);Menu menu = menuMapper.selectOne(param);if (menu == null) {return "";} else {return menu.getName();}}}/*** 獲取字典名稱*/@Overridepublic String getDictName(Integer dictId) {if (ToolUtil.isEmpty(dictId)) {return "";} else {Dict dict = dictMapper.selectById(dictId);if (dict == null) {return "";} else {return dict.getName();}}}/*** 獲取通知標題*/@Overridepublic String getNoticeTitle(Integer dictId) {if (ToolUtil.isEmpty(dictId)) {return "";} else {Notice notice = noticeMapper.selectById(dictId);if (notice == null) {return "";} else {return notice.getTitle();}}}/*** 獲取性別名稱*/@Overridepublic String getSexName(Integer sex) {return getDictsByName("性別", sex);}@Overridepublic String getDictByNameAndCode(String name, String code) {/*** 判斷是否*/if (StringUtils.isBlank(name) || StringUtils.isBlank(code)) {return "";}//通過name字段查詢父類字典Dict temp = new Dict();temp.setName(name);temp.setPid(0);Dict dict = dictMapper.selectOne(temp);if(dict==null){return "";}Wrapper<Dict> wrapper = new EntityWrapper<>();//遍歷操作太過耗費內存資源,不如直接查結果wrapper.setSqlSelect("name")//提高封裝效率,只查一個字段.eq("pid", dict.getId()).eq("code", code.trim());//理論上只會查詢到一條字段,但是為了安全起見,還是查詢集合,并且只取第一條記錄List<Dict> dicts = dictMapper.selectList(wrapper);//mybatis-plus返回的list不會為null,如果沒有查到結果,會返回空集合if (!dicts.isEmpty()) {return dicts.get(0).getName();}return "";}@Overridepublic String getDictById(Integer id, String code) {//判斷是否合法參數if(id==null || StringUtils.isBlank(code)){return "";}Wrapper<Dict> wrapper = new EntityWrapper<>();//遍歷操作太過耗費內存資源,不如直接查結果wrapper.setSqlSelect("name")//提高查詢后的封裝效率,只查一個字段.eq("pid", id).eq("code", code.trim());//理論上只會查詢到一條字段,但是為了安全起見,還是查詢集合,并且只取第一條記錄List<Dict> dicts = dictMapper.selectList(wrapper);//mybatis-plus返回的list不會為null,如果沒有查到結果,會返回空集合if(!dicts.isEmpty()){return dicts.get(0).getName();}return "";}/*** 根據字典名稱和字典中的值獲取對應的名稱*/@Overridepublic String getDictsByName(String name, Integer val) {//判斷是否為nullif(StringUtils.isBlank(name) ||val == null){return "";}//通過name字段查詢父類字典Dict temp = new Dict();temp.setName(name);Dict dict = dictMapper.selectOne(temp);if (dict == null) {return "";} else {Wrapper<Dict> wrapper = new EntityWrapper<>();//遍歷操作太過耗費內存資源,不如直接查結果wrapper.eq("pid", dict.getId()).eq("code",val.toString());//注意,要轉換成字符串后再傳入參數//有可能會查詢到多條記錄,因為添加的時候沒有做code的唯一判斷List<Dict> dicts = dictMapper.selectList(wrapper);//mybatis-plus返回的list不會為null,如果沒有查到結果,會返回空集合if(!dicts.isEmpty()){return dicts.get(0).getName();}return "";// wrapper = wrapper.eq("pid", dict.getId()); // List<Dict> dicts = dictMapper.selectList(wrapper); // for (Dict item : dicts) { // if (item.getNum() != null && item.getNum().equals(val)) { // return item.getName(); // } // }}}/*** 獲取用戶登錄狀態*/@Overridepublic String getStatusName(Integer status) {return ManagerStatus.valueOf(status);}/*** 獲取菜單狀態*/@Overridepublic String getMenuStatusName(Integer status) {return MenuStatus.valueOf(status);}/*** 查詢字典*/@Overridepublic List<Dict> findInDict(Integer id) {if (ToolUtil.isEmpty(id)) {return null;} else {EntityWrapper<Dict> wrapper = new EntityWrapper<>();List<Dict> dicts = dictMapper.selectList(wrapper.eq("pid", id));if (dicts == null || dicts.size() == 0) {return null;} else {return dicts;}}}/*** 獲取被緩存的對象(用戶刪除業務)*/@Overridepublic String getCacheObject(String para) {return LogObjectHolder.me().get().toString();}/*** 獲取子部門id*/@Overridepublic List<Integer> getSubDeptId(Integer deptid) {Wrapper<Dept> wrapper = new EntityWrapper<>();wrapper = wrapper.like("pids", "%[" + deptid + "]%");List<Dept> depts = this.deptMapper.selectList(wrapper);ArrayList<Integer> deptids = new ArrayList<>();if (depts != null && depts.size() > 0) {for (Dept dept : depts) {deptids.add(dept.getId());}}return deptids;}/*** 獲取所有父部門id*/@Overridepublic List<Integer> getParentDeptIds(Integer deptid) {Dept dept = deptMapper.selectById(deptid);String pids = dept.getPids();String[] split = pids.split(",");ArrayList<Integer> parentDeptIds = new ArrayList<>();for (String s : split) {parentDeptIds.add(Integer.valueOf(StrUtil.removeSuffix(StrUtil.removePrefix(s, "["), "]")));}return parentDeptIds;}}?
?
(5)IConstantFactory.java
/*** Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)* <p>* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at* <p>* http://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/ package cn.stylefeng.guns.core.common.constant.factory;import cn.stylefeng.guns.modular.system.model.Dict; import io.swagger.models.auth.In;import java.util.List;/*** 常量生產工廠的接口** @author fengshuonan* @date 2017-06-14 21:12*/ public interface IConstantFactory {/*** 根據用戶id獲取用戶名稱** @author stylefeng* @Date 2017/5/9 23:41*/String getUserNameById(Integer userId);/*** 根據用戶id獲取用戶賬號** @author stylefeng* @date 2017年5月16日21:55:371*/String getUserAccountById(Integer userId);/*** 通過角色ids獲取角色名稱*/String getRoleName(String roleIds);/*** 通過角色id獲取角色名稱*/String getSingleRoleName(Integer roleId);/*** 通過角色id獲取角色英文名稱*/String getSingleRoleTip(Integer roleId);/*** 獲取部門名稱*/String getDeptName(Integer deptId);/*** 獲取菜單的名稱們(多個)*/String getMenuNames(String menuIds);/*** 獲取菜單名稱*/String getMenuName(Long menuId);/*** 獲取菜單名稱通過編號*/String getMenuNameByCode(String code);/*** 獲取字典名稱*/String getDictName(Integer dictId);/*** 獲取通知標題*/String getNoticeTitle(Integer dictId);/*** 根據字典名稱和字典中的值獲取對應的名稱*/String getDictsByName(String name, Integer val); // String getDictsByName(String name, String val);/*** 根據字典名稱和字典中的值獲取對應的名稱*/String getDictByNameAndCode(String name,String code);/*** 根據字典父類id和對應的子類字典的code獲取對應的子類字典名稱* @param id 字典的父類id* @param code 字典的子類id* @return*/String getDictById(Integer id,String code);/*** 獲取性別 */String getSexName(Integer sex); // String getSexName(String sex);/*** 獲取用戶登錄狀態*/String getStatusName(Integer status);/*** 獲取菜單狀態*/String getMenuStatusName(Integer status);/*** 查詢字典*/List<Dict> findInDict(Integer id);/*** 獲取被緩存的對象(用戶刪除業務)*/String getCacheObject(String para);/*** 獲取子部門id*/List<Integer> getSubDeptId(Integer deptid);/*** 獲取所有父部門id*/List<Integer> getParentDeptIds(Integer deptid);}?
(6)WrapperDictNameConstant.java
package cn.stylefeng.guns.elephish.constants;/**** 字典包裝類的字典名稱常量** Created by hqq on 2020/4/15.*/ public interface WrapperDictNameConstant {/*** sys_user 表中 sex 字段對應的字典名稱 的ID*/int SYS_USER_SEX_ID = 127;}?
(7)StringUtil.java
package cn.stylefeng.guns.elephish.utils;import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.velocity.runtime.directive.Foreach;import java.util.ArrayList; import java.util.List;/*** 自定義的字符串工具類* Created by hqq on 2020/4/15.*/ public class StringUtil {/*** 自定義字符串split方法,并去除每一個元素的首尾空格* 如果傳入的參數是null或空串,容量為0的字符串集合* 如果分隔后的結果為null或空數組,返回容量為0的集合,防止調用者空指針異常** 示例如:split("aa, bb ,c c," , ",");* 得到結果: ["aa","bb","c c"]** @param mutiString 需要進行分割的字符串* @param separatorChars 分割標志* @return*/public static List<String> split(String mutiString,final String separatorChars){//定義一個list封裝最后的結果List<String> result = new ArrayList<>();if(StringUtils.isBlank(mutiString)){return result;}//使用性能更好的StringUtils.split方法String[] split = StringUtils.split(mutiString,separatorChars);if(split==null || split.length<1){return result;}//去除首尾空格for (int i = 0; i<split.length; i++) {if(StringUtils.isBlank(split[i])){continue;}result.add(split[i].trim());}return result;}}?
?
至此,分享結束!
?
? ? ? ? 寫了這么多,目的只有一個,就是通過帶領大家修改guns源碼來加深對guns的理解,以此幫助大家更高效的對Guns進行二次開發,我們對guns上手的越快,那我們就能更多的將時間花在我們自己的業務上。另外,有些人可能會抗拒這種修改,我想說的是,? ultimate v2.5 版本的Guns,也對字典模塊進行了較大的改變,所以,就連guns作者也在進步,因為不進步,就只能被社會淘汰,哪怕你曾經很牛逼!
?
該系列更多文章請前往?Guns二次開發目錄
?
總結
以上是生活随笔為你收集整理的Guns二次开发(四):重构字典管理模块的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 护理业务查房PPT模板
- 下一篇: java篇:自学Java到底需要多久呢?