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)
    @ResponseBody
    public 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)
    @ResponseBody
    public 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){

        //解析dictValues
        List<Dict> list = getDictList(dictValues);

        //添加字典
        Dict dict = new Dict();
        dict.setName(dictName);
        dict.setCode(dictCode);
        dict.setTips(dictTips);
        dict.setNum(dictNum);//此处传入设置的num
        dict.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();//获取父类id
        for(int i=0; i<list.size(); i++ ){
            item = list.get(i);
            item.setPid(pid);
        }

        //直接调用批量插入语句
        insertBatch(list);

    }

    @Override
    @Transactional
    public void editDict(Integer dictId, String dictCode, String dictName, String dictTips, String dicts,Integer dictNum) {

        //删除之前的字典
        this.delteDict(dictId);

        //添加字典
        insertDict(dictCode, dictName, dictTips, dicts, dictNum);
    }


    @Override
    @Transactional
    public 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/";

    @Autowired
    private 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)
    @ResponseBody
    public 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)
    @ResponseBody
    public 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)
    @ResponseBody
    public 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)
    @ResponseBody
    public Object detail(@PathVariable("dictId") Integer dictId) {
        return dictService.selectById(dictId);
    }



    /**
     * 删除字典记录
     */
    @BussinessLog(value = "删除字典记录", key = "dictId", dict = DictMap.class)
    @RequestMapping(value = "/delete")
    @Permission(Const.ADMIN_NAME)
    @ResponseBody
    public 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 {

    @Resource
    private 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);//此处传入设置的num
        dict.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){

        //解析dictValues
        List<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
    @Transactional
    public 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;

    }



    @Override
    public 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
    @Transactional
    public void delteDict(Integer dictId) {
        //删除这个字典的子词典
        Wrapper<Dict> dictEntityWrapper = new EntityWrapper<>();
        dictEntityWrapper = dictEntityWrapper.eq("pid", dictId);
        dictMapper.delete(dictEntityWrapper);

        //删除这个词典
        dictMapper.deleteById(dictId);
    }

    @Override
    public List<Dict> selectByCode(String code) {
        return this.baseMapper.selectByCode(code);
    }

    @Override
    public 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);
    }

    /**
     * 此处自定义添加需要包装的字段的字典信息
     */
    @Override
    protected 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
     */
    @Override
    public 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
     */
    @Override
    public 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 "";
    }

    /**
     * 获取菜单的名称们(多个)
     */
    @Override
    public 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(), ",");
    }

    /**
     * 获取菜单名称
     */
    @Override
    public String getMenuName(Long menuId) {
        if (ToolUtil.isEmpty(menuId)) {
            return "";
        } else {
            Menu menu = menuMapper.selectById(menuId);
            if (menu == null) {
                return "";
            } else {
                return menu.getName();
            }
        }
    }

    /**
     * 获取菜单名称通过编号
     */
    @Override
    public 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();
            }
        }
    }

    /**
     * 获取字典名称
     */
    @Override
    public String getDictName(Integer dictId) {
        if (ToolUtil.isEmpty(dictId)) {
            return "";
        } else {
            Dict dict = dictMapper.selectById(dictId);
            if (dict == null) {
                return "";
            } else {
                return dict.getName();
            }
        }
    }

    /**
     * 获取通知标题
     */
    @Override
    public String getNoticeTitle(Integer dictId) {
        if (ToolUtil.isEmpty(dictId)) {
            return "";
        } else {
            Notice notice = noticeMapper.selectById(dictId);
            if (notice == null) {
                return "";
            } else {
                return notice.getTitle();
            }
        }
    }


    /**
     * 获取性别名称
     */
    @Override
    public String getSexName(Integer sex) {
        return getDictsByName("性别", sex);
    }


    @Override
    public 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 "";

    }

    @Override
    public 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 "";

    }

    /**
     * 根据字典名称和字典中的值获取对应的名称
     */
    @Override
    public String getDictsByName(String name, Integer val) {

        //判断是否为null
        if(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();
//                }
//            }

        }
    }


    /**
     * 获取用户登录状态
     */
    @Override
    public String getStatusName(Integer status) {
        return ManagerStatus.valueOf(status);
    }

    /**
     * 获取菜单状态
     */
    @Override
    public String getMenuStatusName(Integer status) {
        return MenuStatus.valueOf(status);
    }

    /**
     * 查询字典
     */
    @Override
    public 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;
            }
        }
    }

    /**
     * 获取被缓存的对象(用户删除业务)
     */
    @Override
    public String getCacheObject(String para) {
        return LogObjectHolder.me().get().toString();
    }

    /**
     * 获取子部门id
     */
    @Override
    public 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
     */
    @Override
    public 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二次开发目录

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值