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