Spring Boot实现高质量的CRUD-5

曹旭辉  金牌会员 | 2023-6-17 11:37:38 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 859|帖子 859|积分 2577

(续前文)
9、Service实现类代码示例        ​
  1. ​        以用户管理模块为例,展示Service实现类代码。用户管理的Service实现类为UserManServiceImpl。​UserManServiceImpl除了没有deleteItems方法外,具备CRUD的其它常规方法。实际上​UserManService还有其它接口方法,如管理员修改密码,用户修改自身密码,设置用户角色列表,设置用户数据权限等,这些不属于常规CRUD方法,故不在此展示。
复制代码
9.1、类定义及成员属性
  1. ​        UserManServiceImpl的类定义如下:
复制代码
  1. package com.abc.example.service.impl;
  2. import java.io.InputStream;
  3. import java.time.LocalDateTime;
  4. import java.util.ArrayList;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import org.apache.commons.lang3.RandomStringUtils;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.stereotype.Service;
  13. import org.springframework.web.multipart.MultipartFile;
  14. import com.github.pagehelper.PageInfo;
  15. import com.abc.esbcommon.common.impexp.BaseExportObj;
  16. import com.abc.esbcommon.common.impexp.BaseImportObj;
  17. import com.abc.esbcommon.common.impexp.ExcelExportHandler;
  18. import com.abc.esbcommon.common.impexp.ExcelImportHandler;
  19. import com.abc.esbcommon.common.impexp.ImpExpFieldDef;
  20. import com.abc.esbcommon.common.utils.FileUtil;
  21. import com.abc.esbcommon.common.utils.LogUtil;
  22. import com.abc.esbcommon.common.utils.Md5Util;
  23. import com.abc.esbcommon.common.utils.ObjListUtil;
  24. import com.abc.esbcommon.common.utils.ReflectUtil;
  25. import com.abc.esbcommon.common.utils.TimeUtil;
  26. import com.abc.esbcommon.common.utils.Utility;
  27. import com.abc.esbcommon.common.utils.ValidateUtil;
  28. import com.abc.esbcommon.entity.SysParameter;
  29. import com.abc.example.common.constants.Constants;
  30. import com.abc.example.config.UploadConfig;
  31. import com.abc.example.dao.UserDao;
  32. import com.abc.example.entity.Orgnization;
  33. import com.abc.example.entity.User;
  34. import com.abc.example.enumeration.EDeleteFlag;
  35. import com.abc.example.enumeration.EIdType;   
  36. import com.abc.example.enumeration.ESex;
  37. import com.abc.example.enumeration.EUserType;
  38. import com.abc.example.exception.BaseException;
  39. import com.abc.example.exception.ExceptionCodes;
  40. import com.abc.example.service.BaseService;
  41. import com.abc.example.service.DataRightsService;
  42. import com.abc.example.service.IdCheckService;
  43. import com.abc.example.service.SysParameterService;
  44. import com.abc.example.service.TableCodeConfigService;
  45. import com.abc.example.service.UserManService;
  46. /**
  47. * @className        : UserManServiceImpl
  48. * @description        : 用户对象管理服务实现类
  49. * @summary                :
  50. * @history                :
  51. * ------------------------------------------------------------------------------
  52. * date                        version                modifier                remarks
  53. * ------------------------------------------------------------------------------
  54. * 2023/05/17        1.0.0                sheng.zheng                初版
  55. *
  56. */
  57. @SuppressWarnings({ "unchecked", "unused" })
  58. @Service
  59. public class UserManServiceImpl extends BaseService implements UserManService{
  60.         // 用户对象数据访问类对象
  61.         @Autowired
  62.         private UserDao userDao;
  63.     // 文件上传配置类对象
  64.     @Autowired
  65.     private UploadConfig uploadConfig;
  66.     // 对象ID检查服务类对象
  67.         @Autowired
  68.         private IdCheckService ics;   
  69.         // 数据权限服务类对象
  70.         @Autowired
  71.         private DataRightsService drs;
  72.         // 全局ID服务类对象
  73.         @Autowired
  74.         private TableCodeConfigService tccs;
  75.    
  76.     // 系统参数服务类对象
  77.         @Autowired
  78.         private SysParameterService sps;
  79.         // 新增必选字段集
  80.         private String[] mandatoryFieldList = new String[]{"userName","password","userType","orgId"};
  81.         // 修改不可编辑字段集
  82.         private String[] uneditFieldList =  new String[]{"password","salt","deleteFlag"};
  83. }
复制代码
  1. ​        UserManServiceImpl类继承BaseService,实现UserManService接口。BaseService提供参数校验接口、启动分页处理和获取用户账号信息的公共方法(参见上文的8.13.1)。
  2. ​        UserManServiceImpl类成员属性作用说明:
复制代码
  1.         UserDao userDao:用户对象数据访问类对象,用于访问数据库用户表。CRUD操作,与数据库紧密联系,userDao是核心对象。
  2.         UploadConfig uploadConfig:文件上传配置类对象,提供临时路径/tmp,导出Excel文件时,生成临时文件存于临时目录。上传Excel文件,临时文件也存于此目录。
  3.         IdCheckService ics:对象ID检查服务类对象,用于外键对象存在性检查。
  4.         DataRightsService drs:数据权限服务类对象,提供数据权限处理的相关接口方法。
  5.         TableCodeConfigService tccs:全局ID服务类对象,提供生成全局ID的接口方法。
  6.         SysParameterService sps:系统参数服务类对象,在Excel数据导入导出时,对枚举字段的枚举值翻译,需要根据系统参数表的配置记录进行翻译。
  7.         String[] mandatoryFieldList:新增必选字段集,新增对象时,规定哪些字段是必须的。
  8.         String[] uneditFieldList:不可编辑字段集,编辑对象时,规定哪些字段是需要不允许修改的,防止参数注入。
复制代码
9.2、新增对象
  1. ​        新增对象的方法为addItem,下面是新增用户对象的方法:
复制代码
  1.         /**
  2.          * @methodName                : addItem
  3.          * @description                : 新增一个用户对象
  4.          * @remark                    : 参见接口类方法说明
  5.          * @history                        :
  6.          * ------------------------------------------------------------------------------
  7.          * date                        version                modifier                remarks
  8.          * ------------------------------------------------------------------------------
  9.          * 2023/05/17        1.0.0                sheng.zheng                初版
  10.          *
  11.          */
  12.         @Override
  13.         public Map<String,Object> addItem(HttpServletRequest request, User item) {
  14.                 // 输入参数校验
  15.                 checkValidForParams(request, "addItem", item);
  16.         // 检查参照ID的有效性
  17.         Integer orgId = item.getOrgId();
  18.         Orgnization orgnization = (Orgnization)ics.getObjById(EIdType.orgId,orgId);
  19.         // 检查数据权限
  20.         drs.checkUserDrByOrgId(request, orgId);
  21.         // 检查枚举值
  22.         int userType = item.getUserType().intValue();
  23.         EUserType eUserType = EUserType.getTypeByCode(userType);
  24.         int sex = item.getSex().intValue();
  25.         ESex eSex = ESex.getTypeByCode(sex);
  26.         // 检查唯一性
  27.         String userName = item.getUserName();
  28.         String phoneNumber = item.getPhoneNumber();
  29.         String idNo = item.getIdNo();
  30.         String openId = item.getOpenId();
  31.         String woaOpenid = item.getWoaOpenid();
  32.         checkUniqueByUserName(userName);
  33.         checkUniqueByPhoneNumber(phoneNumber);
  34.         checkUniqueByIdNo(idNo);
  35.         checkUniqueByOpenId(openId);
  36.         checkUniqueByWoaOpenid(woaOpenid);
  37.         
  38.         // 业务处理
  39.         LocalDateTime current = LocalDateTime.now();
  40.         String salt = TimeUtil.format(current, "yyyy-MM-dd HH:mm:ss");
  41.         // 明文密码加密
  42.         String password = item.getPassword();
  43.         String encyptPassword = Md5Util.plaintPasswdToDbPasswd(password, salt, Constants.TOKEN_KEY);
  44.         item.setSalt(salt);
  45.         item.setPassword(encyptPassword);
  46.         
  47.         Long userId = 0L;
  48.                 // 获取全局记录ID
  49.                 Long globalRecId = tccs.getTableRecId("exa_users");
  50.         userId = globalRecId;
  51.                 // 获取操作人账号
  52.                 String operatorName = getUserName(request);
  53.                 // 设置信息
  54.                 item.setUserId(userId);
  55.                 item.setOperatorName(operatorName);
  56.                
  57.                 try {
  58.                     // 插入数据
  59.                         userDao.insertItem(item);
  60.                        
  61.                 } catch(Exception e) {
  62.                         LogUtil.error(e);
  63.                         throw new BaseException(ExceptionCodes.ADD_OBJECT_FAILED,e.getMessage());
  64.                 }
  65.                
  66.                 // 构造返回值
  67.                 Map<String,Object> map = new HashMap<String,Object>();
  68.         map.put("userId", userId.toString());
  69.                
  70.                 return map;
  71.         }
复制代码
9.2.1、新增对象的参数校验
  1. ​        首先是参数校验,使用checkValidForParams方法,此方法一般仅对输入参数进行值校验,如字段是否缺失,值类型是否匹配,数据格式是否正确等。
复制代码
  1.         /**
  2.          * @methodName                        : checkValidForParams
  3.          * @description                        : 输入参数校验
  4.          * @param request                : request对象
  5.          * @param methodName        : 方法名称
  6.          * @param params                : 输入参数
  7.          * @history                                :
  8.          * ------------------------------------------------------------------------------
  9.          * date                        version                modifier                remarks
  10.          * ------------------------------------------------------------------------------
  11.          * 2023/05/17        1.0.0                sheng.zheng                初版
  12.          *
  13.          */
  14.         @Override
  15.         public void checkValidForParams(HttpServletRequest request, String methodName, Object params) {
  16.                 switch(methodName) {
  17.                 case "addItem":
  18.                 {
  19.                         User item = (User)params;
  20.                        
  21.                         // 检查项: 必选字段
  22.             ReflectUtil.checkMandatoryFields(item,mandatoryFieldList);
  23.             // 用户名格式校验
  24.             ValidateUtil.loginNameValidator("userName", item.getUserName());
  25.             
  26.             // 手机号码格式校验
  27.             if (!item.getPhoneNumber().isEmpty()) {
  28.                     ValidateUtil.phoneNumberValidator("phoneNumber", item.getPhoneNumber());
  29.             }
  30.             // email格式校验
  31.             if (!item.getEmail().isEmpty()) {
  32.                 ValidateUtil.emailValidator("email", item.getEmail());                   
  33.             }                        
  34.                 }
  35.                 break;
  36.                 // case "editItem":
  37.                 // ...
  38.                 default:
  39.                         break;
  40.                 }
  41.         }
复制代码
  1. ​        addItem方法的输入参数校验。首先是必选字段校验,检查必选字段是否都有值。然后是相关属性值的数据格式校验。
复制代码
9.2.1.1、新增对象的必选字段校验
  1. ​        调用用ReflectUtil工具类的checkMandatoryFields,检查字符串类型和整数的字段,是否有值。
复制代码
  1.         /**
  2.          *
  3.          * @methodName                : checkMandatoryFields
  4.          * @description                : 检查必选字段
  5.          * @param <T>                : 泛型类型
  6.          * @param item                : T类型对象
  7.          * @param mandatoryFieldList: 必选字段名数组
  8.          * @history                :
  9.          * ------------------------------------------------------------------------------
  10.          * date                        version                modifier                remarks                  
  11.          * ------------------------------------------------------------------------------
  12.          * 2023/05/26        1.0.0                sheng.zheng                初版
  13.          *
  14.          */
  15.         public static <T> void checkMandatoryFields(T item,String[] mandatoryFieldList) {
  16.                 // 获取对象item的运行时的类
  17.                 Class<?> clazz = (Class<?>) item.getClass();
  18.                 String type = "";
  19.                 String shortType = "";       
  20.                 String error = "";
  21.                 for(String propName : mandatoryFieldList) {
  22.                         try {
  23.                             Field field = clazz.getDeclaredField(propName);
  24.                             field.setAccessible(true);       
  25.                                 // 获取字段类型
  26.                                 type = field.getType().getTypeName();
  27.                                 // 获取类型的短名称
  28.                                 shortType = getShortTypeName(type);
  29.                            
  30.                                 // 获取属性值
  31.                                 Object oVal = field.get(item);
  32.                                 if (oVal == null) {
  33.                                         // 如果必选字段值为null
  34.                                         error += propName + ",";                                                                                       
  35.                                         continue;
  36.                                 }
  37.                                 switch(shortType) {
  38.                                 case "Integer":
  39.                                 case "int":
  40.                                 {
  41.                                         Integer iVal = Integer.valueOf(oVal.toString());
  42.                                         if (iVal == 0) {
  43.                                                 // 整型类型,有效值一般为非0
  44.                                                 error += propName + ",";
  45.                                         }
  46.                                 }
  47.                                         break;
  48.                                 case "String":
  49.                                 {
  50.                                         String sVal = oVal.toString();
  51.                                         if (sVal.isEmpty()) {
  52.                                                 // 字符串类型,有效值一般为非空串
  53.                                                 error += propName + ",";
  54.                                         }
  55.                                 }
  56.                                         break;
  57.                                 case "Byte":
  58.                                 case "byte":
  59.                                         // 字节类型,一般用于枚举值字段,后面使用枚举值检查,此处忽略
  60.                                         break;       
  61.                                 case "List":
  62.                                 {
  63.                                         List<?> list = (List<?>)oVal;
  64.                                         if (list.size() == 0) {
  65.                                                 // 列表类型,无成员
  66.                                                 error += propName + ",";
  67.                                         }
  68.                                 }
  69.                                         break;
  70.                                 default:
  71.                                         break;
  72.                                 }
  73.                         }catch(Exception e) {
  74.                                 // 非属性字段
  75.                                 if (error.isEmpty()) {
  76.                                         error += propName;                                                                                       
  77.                                 }else {
  78.                                         error += "," + propName;
  79.                                 }
  80.                         }
  81.                 }
  82.                 if (!error.isEmpty()) {
  83.                         error = Utility.trimLeftAndRight(error,"\\,");
  84.                         throw new BaseException(ExceptionCodes.ARGUMENTS_IS_EMPTY,error);
  85.                 }
  86.         }
复制代码
  1. ​        一般情况下,这个方法可以起作用。但特殊情况,如0值为有效值,-1为无效值,则会有误报情况。此时,可以使用另一个方法。
复制代码
  1.         /**
  2.          *
  3.          * @methodName                : checkMandatoryFields
  4.          * @description                : 检查必选字段
  5.          * @param <T>                : 泛型类型
  6.          * @param item                : 参考对象,属性字段值使用默认值或区别于有效默认值的无效值
  7.          * @param item2                : 被比较对象
  8.          * @param mandatoryFieldList: 必须字段属性名列表
  9.          * @history                :
  10.          * ------------------------------------------------------------------------------
  11.          * date                        version                modifier                remarks                  
  12.          * ------------------------------------------------------------------------------
  13.          * 2023/06/17        1.0.0                sheng.zheng                初版
  14.          *
  15.          */
  16.         public static <T> void checkMandatoryFields(T item,T item2,
  17.                         String[] mandatoryFieldList) {
  18.                 Class<?> clazz = (Class<?>) item.getClass();
  19.                 String error = "";
  20.                 for(String propName : mandatoryFieldList) {
  21.                         try {
  22.                             Field field = clazz.getDeclaredField(propName);
  23.                             field.setAccessible(true);                                   
  24.                                 // 获取属性值
  25.                                 Object oVal = field.get(item);
  26.                             field.setAccessible(true);                                   
  27.                                 // 获取属性值
  28.                                 Object oVal2 = field.get(item2);
  29.                                 if (oVal2 == null) {
  30.                                         // 新值为null
  31.                                         error += propName + ",";
  32.                                         continue;
  33.                                 }
  34.                                 if (oVal != null) {
  35.                                         if (oVal.equals(oVal2)) {
  36.                                                 // 如果值相等
  37.                                                 error += propName + ",";
  38.                                         }
  39.                                 }
  40.                                
  41.                         }catch(Exception e) {
  42.                                 // 非属性字段
  43.                                 if (error.isEmpty()) {
  44.                                         error += propName;                                                                                       
  45.                                 }else {
  46.                                         error += "," + propName;
  47.                                 }
  48.                         }
  49.                 }
  50.                 if (!error.isEmpty()) {
  51.                         error = Utility.trimLeftAndRight(error,"\\,");
  52.                         throw new BaseException(ExceptionCodes.ARGUMENTS_IS_EMPTY,error);
  53.                 }
  54.         }
复制代码
  1. ​        这个方法,增加了参考对象item,一般使用默认值,新对象为item2,如果新对象的必选属性值为参考对象一致,则认为该属性未赋值。这对于默认值为有效值时,会有问题,此时调用方法前先将有效默认值设置为无效值。
  2. ​        如User对象类,userType默认值为3,是有效值。可如下方法调用:
复制代码
  1.                 User item = (User)params;
  2.                 // 检查项: 必选字段
  3.                 User refItem = new User();
  4.                 // 0为无效值
  5.                 refItem.setUserType((byte)0);                       
  6.         ReflectUtil.checkMandatoryFields(refItem,item,mandatoryFieldList);
复制代码
  1. ​        使用ReflectUtil的mandatoryFieldList的方法,可以大大简化代码。
复制代码
9.2.1.2、数据格式校验
  1. ​        某些对象的某些属性值,有数据格式要求,此时需要进行数据格式校验。如用户对象的用户名(登录名),手机号码,email等。这些数据格式校验,可以累计起来,开发工具方法,便于其它对象使用。
  2. ​        如下面是登录名的格式校验方法,支持以字母开头,后续可以是字母、数字或"_.-@#%"特殊符号,不支持中文。此校验方法没有长度校验,由于各属性的长度要求不同,可以另行检查。
复制代码
  1.         /**
  2.          *
  3.          * @methodName                : checkLoginName
  4.          * @description                : 检查登录名格式是否正确,
  5.          *         格式:字母开头,可以支持字母、数字、以及"_.-@#%"6个特殊符号
  6.          * @param loginName        : 登录名
  7.          * @return                :
  8.          * @history                :
  9.          * ------------------------------------------------------------------------------
  10.          * date                        version                modifier                remarks                  
  11.          * ------------------------------------------------------------------------------
  12.          * 2023/06/16        1.0.0                sheng.zheng                初版
  13.          *
  14.          */
  15.         public static boolean checkLoginName(String loginName) {
  16.                 String pattern = "^[a-zA-Z]([a-zA-Z0-9_.\\-@#%]*)$";
  17.                 boolean bRet = Pattern.matches(pattern,loginName);
  18.                 return bRet;
  19.         }
  20.        
  21.         /**
  22.          *
  23.          * @methodName                : loginNameValidator
  24.          * @description                : 登录名称格式校验,格式错误抛出异常
  25.          * @param propName        : 登录名称的提示名称
  26.          * @param loginName        : 登录名称
  27.          * @history                :
  28.          * ------------------------------------------------------------------------------
  29.          * date                        version                modifier                remarks                  
  30.          * ------------------------------------------------------------------------------
  31.          * 2023/06/16        1.0.0                sheng.zheng                初版
  32.          *
  33.          */
  34.         public static void loginNameValidator(String propName,String loginName) {
  35.                 boolean bRet = checkLoginName(loginName);
  36.                 if (!bRet) {
  37.                         throw new BaseException(ExceptionCodes.DATA_FORMAT_WRONG, propName + ":" + loginName);
  38.                 }
  39.         }
复制代码
  1. ​        根据loginNameValidator核心是checkLoginName,但loginNameValidator方法针对错误,直接抛出异常,调用时代码可以更加简洁。类似的思想,适用于数据权限检查,参照ID检查,枚举值检查,唯一键检查等。
复制代码
  1.             // 用户名格式校验
  2.             ValidateUtil.loginNameValidator("userName", item.getUserName());
复制代码
  1. ​        使用checkLoginName方法,则需要如下:
复制代码
  1.             // 用户名格式校验
  2.             if(!ValidateUtil.checkLoginName(item.getUserName())){
  3.                                 throw new BaseException(ExceptionCodes.DATA_FORMAT_WRONG, "userName:" + item.getUserName());
  4.                         }
复制代码
9.2.2、参照ID检查
  1. ​        如果对象有外键,即参照对象,则外键(ID)必须有意义,即参照对象是存在的。
  2. ​        使用集中式的对象ID检查服务类IdCheckService,根据ID类型和ID值,获取对象。如果类型指定的ID值对象不存在,则抛出异常。
复制代码
  1.         // 检查参照ID的有效性
  2.         Integer orgId = item.getOrgId();
  3.         Orgnization orgnization = (Orgnization)ics.getObjById(EIdType.orgId,orgId);
复制代码
  1. ​        至于IdCheckService中,根据ID获取对象方法,可以直接查询数据库,也可以通过缓存,这个取决于对象管理。
复制代码
9.2.3、数据权限检查
  1. ​        如果对象涉及数据权限,则需要检查操作者是否有权新增此对象。
  2. ​        使用数据权限管理类DataRightsService的方法,由于对一个确定的应用,数据权限相关的字段个数是有限的,可以针对单个字段开发接口,如orgId是数据权限字段,则可以提供checkUserDrByOrgId方法,代码如下:
复制代码
  1.         /**
  2.          *
  3.          * @methodName                : checkUserDrByOrgId
  4.          * @description                : 检查当前用户是否对输入的组织ID有数据权限
  5.          * @param request        : request对象
  6.          * @param orgId                : 组织ID
  7.          * @history                :
  8.          * ------------------------------------------------------------------------------
  9.          * date                        version                modifier                remarks                  
  10.          * ------------------------------------------------------------------------------
  11.          * 2021/05/29        1.0.0                sheng.zheng                初版
  12.          *
  13.          */
  14.         @SuppressWarnings("unchecked")
  15.         @Override
  16.         public void checkUserDrByOrgId(HttpServletRequest request,Integer orgId) {
  17.                 boolean bRights = false;
  18.                
  19.                 // 获取账号缓存信息
  20.                 String accountId = accountCacheService.getId(request);
  21.                 // 获取用户类型
  22.                 Integer userType = (Integer)accountCacheService.getAttribute(accountId,Constants.USER_TYPE);
  23.                 // 获取数据权限缓存信息
  24.                 Map<String,UserDr> fieldDrMap = null;
  25.                 fieldDrMap = (Map<String,UserDr>)accountCacheService.getAttribute(accountId, Constants.DR_MAP);
  26.                 if (userType != null || fieldDrMap == null) {
  27.                         if (userType == EUserType.utAdminE.getCode()) {
  28.                                 // 如果为系统管理员
  29.                                 bRights = true;
  30.                                 return;
  31.                         }
  32.                 }else {
  33.                         // 如果属性不存在
  34.                         throw new BaseException(ExceptionCodes.TOKEN_EXPIRED);                               
  35.                 }
  36.                                
  37.                 // 获取数据权限
  38.                 UserDr userDr = null;
  39.                 bRights = true;
  40.                 List<UserCustomDr> userCustomDrList = null;
  41.                 String propName = "orgId";
  42.                        
  43.                 // 获取用户对此fieldId的权限
  44.                 userDr = fieldDrMap.get(propName);
  45.                 if (userDr.getDrType().intValue() == EDataRightsType.drtAllE.getCode()) {
  46.                         // 如果为全部,有权限
  47.                         return;
  48.                 }
  49.                 if (userDr.getDrType().intValue() == EDataRightsType.drtDefaultE.getCode()) {
  50.                         // 如果为默认权限,进一步检查下级对象
  51.                         List<Integer> drList = getDefaultDrList(orgId,propName);
  52.                         boolean bFound = drList.contains(orgId);
  53.                         if (!bFound) {
  54.                                 bRights = false;
  55.                         }
  56.                 }else if (userDr.getDrType().intValue() == EDataRightsType.drtCustomE.getCode()){
  57.                         // 如果为自定义数据权限
  58.                         List<Integer> orgIdList = null;
  59.                         if (userCustomDrList == null) {
  60.                                 // 如果自定义列表为空,则获取
  61.                                 Long userId = (Long)accountCacheService.getAttribute(accountId,Constants.USER_ID);
  62.                                 userCustomDrList = getUserCustomDrList(userId,propName);
  63.                                 orgIdList = getUserCustomFieldList(userCustomDrList,propName);
  64.                                 if (orgIdList != null) {
  65.                                         boolean bFound = orgIdList.contains(orgId);
  66.                                         if (!bFound) {
  67.                                                 bRights = false;
  68.                                         }                                       
  69.                                 }                                       
  70.                         }
  71.                 }                       
  72.                 if (bRights == false) {
  73.                         throw new BaseException(ExceptionCodes.ACCESS_FORBIDDEN);
  74.                 }               
  75.         }
复制代码
  1. ​        当前用户的数据权限配置信息,使用key为属性名的字典Map<String,UserDr>保存到各访问用户的账号缓存中。根据request对象,获取当前操作者的数据权限配置信息。然后根据配置类型,检查输入权限值是否在用户许可范围内,如果不在,就抛出异常。
  2. ​        还可以提供更通用的单属性数据权限检查接口方法。
复制代码
  1.         /**
  2.          *
  3.          * @methodName                : checkUserDrByDrId
  4.          * @description                : 检查当前用户是否对指定权限字典的输入值有数据权限
  5.          * @param request        : request对象
  6.          * @param propName        : 权限属性名
  7.          * @param drId                : 权限属性值
  8.          * @history                :
  9.          * ------------------------------------------------------------------------------
  10.          * date                        version                modifier                remarks                  
  11.          * ------------------------------------------------------------------------------
  12.          * 2021/05/29        1.0.0                sheng.zheng                初版
  13.          *
  14.          */
  15.         public void checkUserDrByDrId(HttpServletRequest request,String propName,Object drId);        
复制代码
  1. ​        多属性数据权限检查接口方法。
复制代码
  1.         /**
  2.          *
  3.          * @methodName                : checkDataRights
  4.          * @description                : 检查当前用户是否对给定对象有数据权限
  5.          * @param request        : request对象,可从中获取当前用户的缓存信息
  6.          * @param params        : 权限属性名与值的字典
  7.          * @history                :
  8.          * ------------------------------------------------------------------------------
  9.          * date                        version                modifier                remarks                  
  10.          * ------------------------------------------------------------------------------
  11.          * 2021/03/13        1.0.0                sheng.zheng                初版
  12.          *
  13.          */
  14.         public void checkUserDr(HttpServletRequest request,Map<String,Object> params);        
复制代码
9.2.4、枚举值检查
  1. ​        枚举类型,对应的属性数据类型一般是Byte,数据库使用tinyint。新增对象时,枚举字段的值要在枚举类型定义范围中,否则会有问题。
复制代码
  1.         // 检查枚举值
  2.         int userType = item.getUserType().intValue();
  3.         EUserType eUserType = EUserType.getTypeByCode(userType);
  4.         int sex = item.getSex().intValue();
  5.         ESex eSex = ESex.getTypeByCode(sex);       
复制代码
  1. ​        相关枚举类型,都提供getTypeByCode方法,实现枚举值有效性校验。如:
复制代码
  1.         /**
  2.          *
  3.          * @methodName        : getType
  4.          * @description        : 根据code获取枚举值
  5.          * @param code        : code值
  6.          * @return                : code对应的枚举值
  7.          * @history                :
  8.          * ------------------------------------------------------------------------------
  9.          * date                        version                modifier                remarks                  
  10.          * ------------------------------------------------------------------------------
  11.          * 2023/05/17        1.0.0                sheng.zheng                初版
  12.          *
  13.          */
  14.         public static EUserType getType(int code) {
  15.                 // 返回值变量
  16.                 EUserType eRet = null;
  17.                
  18.                 for (EUserType item : values()) {
  19.                         // 遍历每个枚举值
  20.                         if (code == item.getCode()) {
  21.                                 // code匹配
  22.                                 eRet = item;
  23.                                 break;
  24.                         }
  25.                 }
  26.                
  27.                 return eRet;
  28.         }
  29.         // 检查并获取指定code的枚举值
  30.         public static EUserType getTypeByCode(int code) {
  31.                 EUserType item = getType(code);
  32.                 if (item == null) {
  33.                         throw new BaseException(ExceptionCodes.INVALID_ENUM_VALUE,"EUserType with code="+code);
  34.                 }
  35.                
  36.                 return item;
  37.         }
复制代码
9.2.5、唯一性检查
  1. ​        如果对象属性值有唯一性要求,则需要进行唯一性检查。
复制代码
  1.         // 检查唯一性
  2.         String userName = item.getUserName();
  3.         String phoneNumber = item.getPhoneNumber();
  4.         String idNo = item.getIdNo();
  5.         String openId = item.getOpenId();
  6.         String woaOpenid = item.getWoaOpenid();
  7.         checkUniqueByUserName(userName);
  8.         checkUniqueByPhoneNumber(phoneNumber);
  9.         checkUniqueByIdNo(idNo);
  10.         checkUniqueByOpenId(openId);
  11.         checkUniqueByWoaOpenid(woaOpenid);       
复制代码
  1. ​        相关唯一性检查方法,如下(也可以改为public以对外提供服务):
复制代码
  1.         /**
  2.          *
  3.          * @methodName                : checkUniqueByUserName
  4.          * @description            : 检查userName属性值的唯一性
  5.      * @param userName        : 用户名
  6.          * @history                    :
  7.          * ------------------------------------------------------------------------------
  8.          * date                        version                modifier                remarks                  
  9.          * ------------------------------------------------------------------------------
  10.          * 2023/05/17        1.0.0                sheng.zheng                初版
  11.          *
  12.          */
  13.     private void checkUniqueByUserName(String userName) {
  14.         User item = userDao.selectItemByUserName(userName);
  15.         if (item != null) {
  16.             // 如果唯一键对象已存在
  17.             throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"userName=" + userName);            
  18.         }
  19.     }
  20.         /**
  21.          *
  22.          * @methodName                : checkUniqueByPhoneNumber
  23.          * @description            : 检查phoneNumber属性值的唯一性
  24.      * @param phoneNumber        : 手机号码
  25.          * @history                    :
  26.          * ------------------------------------------------------------------------------
  27.          * date                        version                modifier                remarks                  
  28.          * ------------------------------------------------------------------------------
  29.          * 2023/05/17        1.0.0                sheng.zheng                初版
  30.          *
  31.          */
  32.     private void checkUniqueByPhoneNumber(String phoneNumber) {
  33.         if (phoneNumber.equals("")) {
  34.             // 如果为例外值
  35.             return;
  36.         }
  37.         User item = userDao.selectItemByPhoneNumber(phoneNumber);
  38.         if (item != null) {
  39.             // 如果唯一键对象已存在
  40.             throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,
  41.                     "phoneNumber=" + phoneNumber);            
  42.         }
  43.     }
  44.         /**
  45.          *
  46.          * @methodName                : checkUniqueByIdNo
  47.          * @description            : 检查idNo属性值的唯一性
  48.      * @param idNo                : 身份证号码
  49.          * @history                    :
  50.          * ------------------------------------------------------------------------------
  51.          * date                        version                modifier                remarks                  
  52.          * ------------------------------------------------------------------------------
  53.          * 2023/05/17        1.0.0                sheng.zheng                初版
  54.          *
  55.          */
  56.     private void checkUniqueByIdNo(String idNo) {
  57.         if (idNo.equals("")) {
  58.             // 如果为例外值
  59.             return;
  60.         }
  61.         User item = userDao.selectItemByIdNo(idNo);
  62.         if (item != null) {
  63.             // 如果唯一键对象已存在
  64.             throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"idNo=" + idNo);            
  65.         }
  66.     }
  67.         /**
  68.          *
  69.          * @methodName                : checkUniqueByOpenId
  70.          * @description            : 检查openId属性值的唯一性
  71.      * @param openId        : 微信小程序的openid
  72.          * @history                    :
  73.          * ------------------------------------------------------------------------------
  74.          * date                        version                modifier                remarks                  
  75.          * ------------------------------------------------------------------------------
  76.          * 2023/05/17        1.0.0                sheng.zheng                初版
  77.          *
  78.          */
  79.     private void checkUniqueByOpenId(String openId) {
  80.         if (openId.equals("")) {
  81.             // 如果为例外值
  82.             return;
  83.         }
  84.         User item = userDao.selectItemByOpenId(openId);
  85.         if (item != null) {
  86.             // 如果唯一键对象已存在
  87.             throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"openId=" + openId);            
  88.         }
  89.     }
  90.         /**
  91.          *
  92.          * @methodName                : checkUniqueByWoaOpenid
  93.          * @description            : 检查woaOpenid属性值的唯一性
  94.      * @param woaOpenid        : 微信公众号openid
  95.          * @history                    :
  96.          * ------------------------------------------------------------------------------
  97.          * date                        version                modifier                remarks                  
  98.          * ------------------------------------------------------------------------------
  99.          * 2023/05/17        1.0.0                sheng.zheng                初版
  100.          *
  101.          */
  102.     private void checkUniqueByWoaOpenid(String woaOpenid) {
  103.         if (woaOpenid.equals("")) {
  104.             // 如果为例外值
  105.             return;
  106.         }
  107.         User item = userDao.selectItemByWoaOpenid(woaOpenid);
  108.         if (item != null) {
  109.             // 如果唯一键对象已存在
  110.             throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"woaOpenid=" + woaOpenid);            
  111.         }
  112.     }
复制代码
9.2.6、业务处理
  1. ​        如果新增对象时,需要一些内部处理,则在此处进行。如新增用户时,需要根据当前时间生成盐,然后将管理员输入的明文密码转为加盐Md5签名密码。
复制代码
  1.         // 业务处理
  2.         LocalDateTime current = LocalDateTime.now();
  3.         String salt = TimeUtil.format(current, "yyyy-MM-dd HH:mm:ss");
  4.         // 明文密码加密
  5.         String password = item.getPassword();
  6.         String encyptPassword = Md5Util.plaintPasswdToDbPasswd(password, salt, Constants.TOKEN_KEY);
  7.         item.setSalt(salt);
  8.         item.setPassword(encyptPassword);
复制代码
9.2.7、获取全局ID
  1. ​        为当前对象分配全局ID。
复制代码
  1.         Long userId = 0L;
  2.                 // 获取全局记录ID
  3.                 Long globalRecId = tccs.getTableRecId("exa_users");
  4.         userId = globalRecId;
复制代码
  1. ​        全局ID的获取使用全局ID服务类对象TableCodeConfigService,其提供单个ID和批量ID的获取接口。
复制代码
  1.         /**
  2.          *
  3.          * @methodName                : getTableRecId
  4.          * @description                : 获取指定表名的一条记录ID
  5.          * @param tableName        : 表名
  6.          * @return                        : 记录ID
  7.          * @history                :
  8.          * ------------------------------------------------------------------------------
  9.          * date                        version                modifier                remarks                  
  10.          * ------------------------------------------------------------------------------
  11.          * 2021/01/01        1.0.0                sheng.zheng                初版
  12.          *
  13.          */
  14.         @Override
  15.         public Long getTableRecId(String tableName) {
  16.                 int tableId = getTableId(tableName);
  17.                 Long recId = getGlobalIdDao.getTableRecId(tableId);
  18.                 return recId;
  19.         }
  20.        
  21.         /**
  22.          *
  23.          * @methodName                : getTableRecIds
  24.          * @description                : 获取指定表名的多条记录ID
  25.          * @param tableName        : 表名
  26.          * @param recCount        : 记录条数
  27.          * @return                        : 第一条记录ID
  28.          * @history                :
  29.          * ------------------------------------------------------------------------------
  30.          * date                        version                modifier                remarks                  
  31.          * ------------------------------------------------------------------------------
  32.          * 2021/01/01        1.0.0                sheng.zheng                初版
  33.          *
  34.          */
  35.         @Override
  36.         public Long getTableRecIds(String tableName,int recCount) {
  37.                 int tableId = getTableId(tableName);
  38.                 Long recId = getGlobalIdDao.getTableRecIds(tableId,recCount);
  39.                 return recId;               
  40.         }
复制代码
  1. ​        getTableId是根据数据表名称,获取表ID(这些表是相对固定的,可以使用缓存字典来管理)。而getGlobalIdDao的方法就是调用数据库的函数exa_get_global_id,获取可用ID。
复制代码
  1.         /**
  2.          *
  3.          * @methodName                : getTableRecId
  4.          * @description                : 获取表ID的一个记录ID
  5.          * @param tableId        : 表ID
  6.          * @return                        : 记录ID
  7.          * @history                :
  8.          * ------------------------------------------------------------------------------
  9.          * date                        version                modifier                remarks                  
  10.          * ------------------------------------------------------------------------------
  11.          * 2021/01/01        1.0.0                sheng.zheng                初版
  12.          *
  13.          */
  14.         @Select("SELECT exa_get_global_id(#{tableId}, 1)")
  15.         Long getTableRecId(@Param("tableId") Integer tableId);
  16.        
  17.         /**
  18.          *
  19.          * @methodName                : getTableRecIds
  20.          * @description                : 获取表ID的多个记录ID
  21.          * @param tableId        : 表ID
  22.          * @param count                : ID个数
  23.          * @return                        : 开始的记录ID
  24.          * @history                :
  25.          * ------------------------------------------------------------------------------
  26.          * date                        version                modifier                remarks                  
  27.          * ------------------------------------------------------------------------------
  28.          * 2021/01/01        1.0.0                sheng.zheng                初版
  29.          *
  30.          */
  31.         @Select("SELECT exa_get_global_id(#{tableId}, #{count})")
  32.         Long getTableRecIds(@Param("tableId") Integer tableId, @Param("count") Integer count);
复制代码
9.2.8、设置记录的用户账号信息
  1. ​        从request对象中获取账号信息,并设置对象。
复制代码
  1.                 // 获取操作人账号
  2.                 String operatorName = getUserName(request);
  3.                 // 设置信息
  4.                 item.setUserId(userId);
  5.                 item.setOperatorName(operatorName);
复制代码
9.2.9、新增记录
  1. ​        调用Dao的insertItem方法,新增记录。
复制代码
  1.                 try {
  2.                     // 插入数据
  3.                         userDao.insertItem(item);
  4.                        
  5.                 } catch(Exception e) {
  6.                         LogUtil.error(e);
  7.                         throw new BaseException(ExceptionCodes.ADD_OBJECT_FAILED,e.getMessage());
  8.                 }
复制代码
9.2.10、缓存处理
  1. ​        新增用户对象,不涉及缓存处理。
  2. ​        如果有的对象,涉及全集加载,如组织树,则新增对象时,组织树也会变化。为了避免无效加载,使用修改标记来表示集合被修改,获取全集时,再进行加载。这样,连续新增对象时,不会有无效加载。缓存涉及全集加载的,新增对象需设置修改标记。
  3. ​        如果缓存不涉及全集的,则无需处理。如字典类缓存,新增时不必将新对象加入缓存,获取时,根据机制,缓存中不存在,会先请求数据库,此时可以加载到缓存中。
复制代码
9.2.11、返回值处理
  1. ​        新增对象,如果是系统生成的ID,需要将ID值返回。
复制代码
  1.                 // 构造返回值
  2.                 Map<String,Object> map = new HashMap<String,Object>();
  3.         map.put("userId", userId.toString());
  4.                
  5.                 return map;
复制代码
  1. ​        对于Long类型,由于前端可能损失精度,因此使用字符串类型传递。
复制代码
(未完待续...)
        出处:http://www.cnblogs.com/alabo1999/        本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.        养成良好习惯,好文章随手顶一下。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曹旭辉

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表