(續(xù)前文)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-487318.html
9、Service實(shí)現(xiàn)類(lèi)代碼示例 ?
? 以用戶(hù)管理模塊為例,展示Service實(shí)現(xiàn)類(lèi)代碼。用戶(hù)管理的Service實(shí)現(xiàn)類(lèi)為UserManServiceImpl。?UserManServiceImpl除了沒(méi)有deleteItems方法外,具備CRUD的其它常規(guī)方法。實(shí)際上?UserManService還有其它接口方法,如管理員修改密碼,用戶(hù)修改自身密碼,設(shè)置用戶(hù)角色列表,設(shè)置用戶(hù)數(shù)據(jù)權(quán)限等,這些不屬于常規(guī)CRUD方法,故不在此展示。
9.1、類(lèi)定義及成員屬性
? UserManServiceImpl的類(lèi)定義如下:
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 : 用戶(hù)對(duì)象管理服務(wù)實(shí)現(xiàn)類(lèi)
* @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{
// 用戶(hù)對(duì)象數(shù)據(jù)訪(fǎng)問(wèn)類(lèi)對(duì)象
@Autowired
private UserDao userDao;
// 文件上傳配置類(lèi)對(duì)象
@Autowired
private UploadConfig uploadConfig;
// 對(duì)象ID檢查服務(wù)類(lèi)對(duì)象
@Autowired
private IdCheckService ics;
// 數(shù)據(jù)權(quán)限服務(wù)類(lèi)對(duì)象
@Autowired
private DataRightsService drs;
// 全局ID服務(wù)類(lèi)對(duì)象
@Autowired
private TableCodeConfigService tccs;
// 系統(tǒng)參數(shù)服務(wù)類(lèi)對(duì)象
@Autowired
private SysParameterService sps;
// 新增必選字段集
private String[] mandatoryFieldList = new String[]{"userName","password","userType","orgId"};
// 修改不可編輯字段集
private String[] uneditFieldList = new String[]{"password","salt","deleteFlag"};
}
? UserManServiceImpl類(lèi)繼承BaseService,實(shí)現(xiàn)UserManService接口。BaseService提供參數(shù)校驗(yàn)接口、啟動(dòng)分頁(yè)處理和獲取用戶(hù)賬號(hào)信息的公共方法(參見(jiàn)上文的8.13.1)。
? UserManServiceImpl類(lèi)成員屬性作用說(shuō)明:
UserDao userDao:用戶(hù)對(duì)象數(shù)據(jù)訪(fǎng)問(wèn)類(lèi)對(duì)象,用于訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)用戶(hù)表。CRUD操作,與數(shù)據(jù)庫(kù)緊密聯(lián)系,userDao是核心對(duì)象。
UploadConfig uploadConfig:文件上傳配置類(lèi)對(duì)象,提供臨時(shí)路徑/tmp,導(dǎo)出Excel文件時(shí),生成臨時(shí)文件存于臨時(shí)目錄。上傳Excel文件,臨時(shí)文件也存于此目錄。
IdCheckService ics:對(duì)象ID檢查服務(wù)類(lèi)對(duì)象,用于外鍵對(duì)象存在性檢查。
DataRightsService drs:數(shù)據(jù)權(quán)限服務(wù)類(lèi)對(duì)象,提供數(shù)據(jù)權(quán)限處理的相關(guān)接口方法。
TableCodeConfigService tccs:全局ID服務(wù)類(lèi)對(duì)象,提供生成全局ID的接口方法。
SysParameterService sps:系統(tǒng)參數(shù)服務(wù)類(lèi)對(duì)象,在Excel數(shù)據(jù)導(dǎo)入導(dǎo)出時(shí),對(duì)枚舉字段的枚舉值翻譯,需要根據(jù)系統(tǒng)參數(shù)表的配置記錄進(jìn)行翻譯。
String[] mandatoryFieldList:新增必選字段集,新增對(duì)象時(shí),規(guī)定哪些字段是必須的。
String[] uneditFieldList:不可編輯字段集,編輯對(duì)象時(shí),規(guī)定哪些字段是需要不允許修改的,防止參數(shù)注入。
9.2、新增對(duì)象
? 新增對(duì)象的方法為addItem,下面是新增用戶(hù)對(duì)象的方法:
/**
* @methodName : addItem
* @description : 新增一個(gè)用戶(hù)對(duì)象
* @remark : 參見(jiàn)接口類(lèi)方法說(shuō)明
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/17 1.0.0 sheng.zheng 初版
*
*/
@Override
public Map<String,Object> addItem(HttpServletRequest request, User item) {
// 輸入?yún)?shù)校驗(yàn)
checkValidForParams(request, "addItem", item);
// 檢查參照ID的有效性
Integer orgId = item.getOrgId();
Orgnization orgnization = (Orgnization)ics.getObjById(EIdType.orgId,orgId);
// 檢查數(shù)據(jù)權(quán)限
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);
// 業(yè)務(wù)處理
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;
// 獲取操作人賬號(hào)
String operatorName = getUserName(request);
// 設(shè)置信息
item.setUserId(userId);
item.setOperatorName(operatorName);
try {
// 插入數(shù)據(jù)
userDao.insertItem(item);
} catch(Exception e) {
LogUtil.error(e);
throw new BaseException(ExceptionCodes.ADD_OBJECT_FAILED,e.getMessage());
}
// 構(gòu)造返回值
Map<String,Object> map = new HashMap<String,Object>();
map.put("userId", userId.toString());
return map;
}
9.2.1、新增對(duì)象的參數(shù)校驗(yàn)
? 首先是參數(shù)校驗(yàn),使用checkValidForParams方法,此方法一般僅對(duì)輸入?yún)?shù)進(jìn)行值校驗(yàn),如字段是否缺失,值類(lèi)型是否匹配,數(shù)據(jù)格式是否正確等。
/**
* @methodName : checkValidForParams
* @description : 輸入?yún)?shù)校驗(yàn)
* @param request : request對(duì)象
* @param methodName : 方法名稱(chēng)
* @param params : 輸入?yún)?shù)
* @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;
// 檢查項(xiàng): 必選字段
ReflectUtil.checkMandatoryFields(item,mandatoryFieldList);
// 用戶(hù)名格式校驗(yàn)
ValidateUtil.loginNameValidator("userName", item.getUserName());
// 手機(jī)號(hào)碼格式校驗(yàn)
if (!item.getPhoneNumber().isEmpty()) {
ValidateUtil.phoneNumberValidator("phoneNumber", item.getPhoneNumber());
}
// email格式校驗(yàn)
if (!item.getEmail().isEmpty()) {
ValidateUtil.emailValidator("email", item.getEmail());
}
}
break;
// case "editItem":
// ...
default:
break;
}
}
? addItem方法的輸入?yún)?shù)校驗(yàn)。首先是必選字段校驗(yàn),檢查必選字段是否都有值。然后是相關(guān)屬性值的數(shù)據(jù)格式校驗(yàn)。
9.2.1.1、新增對(duì)象的必選字段校驗(yàn)
? 調(diào)用用ReflectUtil工具類(lèi)的checkMandatoryFields,檢查字符串類(lèi)型和整數(shù)的字段,是否有值。
/**
*
* @methodName : checkMandatoryFields
* @description : 檢查必選字段
* @param <T> : 泛型類(lèi)型
* @param item : T類(lèi)型對(duì)象
* @param mandatoryFieldList: 必選字段名數(shù)組
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/26 1.0.0 sheng.zheng 初版
*
*/
public static <T> void checkMandatoryFields(T item,String[] mandatoryFieldList) {
// 獲取對(duì)象item的運(yùn)行時(shí)的類(lèi)
Class<?> clazz = (Class<?>) item.getClass();
String type = "";
String shortType = "";
String error = "";
for(String propName : mandatoryFieldList) {
try {
Field field = clazz.getDeclaredField(propName);
field.setAccessible(true);
// 獲取字段類(lèi)型
type = field.getType().getTypeName();
// 獲取類(lèi)型的短名稱(chēng)
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) {
// 整型類(lèi)型,有效值一般為非0
error += propName + ",";
}
}
break;
case "String":
{
String sVal = oVal.toString();
if (sVal.isEmpty()) {
// 字符串類(lèi)型,有效值一般為非空串
error += propName + ",";
}
}
break;
case "Byte":
case "byte":
// 字節(jié)類(lèi)型,一般用于枚舉值字段,后面使用枚舉值檢查,此處忽略
break;
case "List":
{
List<?> list = (List<?>)oVal;
if (list.size() == 0) {
// 列表類(lèi)型,無(wú)成員
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);
}
}
? 一般情況下,這個(gè)方法可以起作用。但特殊情況,如0值為有效值,-1為無(wú)效值,則會(huì)有誤報(bào)情況。此時(shí),可以使用另一個(gè)方法。
/**
*
* @methodName : checkMandatoryFields
* @description : 檢查必選字段
* @param <T> : 泛型類(lèi)型
* @param item : 參考對(duì)象,屬性字段值使用默認(rèn)值或區(qū)別于有效默認(rèn)值的無(wú)效值
* @param item2 : 被比較對(duì)象
* @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);
}
}
? 這個(gè)方法,增加了參考對(duì)象item,一般使用默認(rèn)值,新對(duì)象為item2,如果新對(duì)象的必選屬性值為參考對(duì)象一致,則認(rèn)為該屬性未賦值。這對(duì)于默認(rèn)值為有效值時(shí),會(huì)有問(wèn)題,此時(shí)調(diào)用方法前先將有效默認(rèn)值設(shè)置為無(wú)效值。
? 如User對(duì)象類(lèi),userType默認(rèn)值為3,是有效值??扇缦路椒ㄕ{(diào)用:
User item = (User)params;
// 檢查項(xiàng): 必選字段
User refItem = new User();
// 0為無(wú)效值
refItem.setUserType((byte)0);
ReflectUtil.checkMandatoryFields(refItem,item,mandatoryFieldList);
? 使用ReflectUtil的mandatoryFieldList的方法,可以大大簡(jiǎn)化代碼。
9.2.1.2、數(shù)據(jù)格式校驗(yàn)
? 某些對(duì)象的某些屬性值,有數(shù)據(jù)格式要求,此時(shí)需要進(jìn)行數(shù)據(jù)格式校驗(yàn)。如用戶(hù)對(duì)象的用戶(hù)名(登錄名),手機(jī)號(hào)碼,email等。這些數(shù)據(jù)格式校驗(yàn),可以累計(jì)起來(lái),開(kāi)發(fā)工具方法,便于其它對(duì)象使用。
? 如下面是登錄名的格式校驗(yàn)方法,支持以字母開(kāi)頭,后續(xù)可以是字母、數(shù)字或"_.-@#%"特殊符號(hào),不支持中文。此校驗(yàn)方法沒(méi)有長(zhǎng)度校驗(yàn),由于各屬性的長(zhǎng)度要求不同,可以另行檢查。
/**
*
* @methodName : checkLoginName
* @description : 檢查登錄名格式是否正確,
* 格式:字母開(kāi)頭,可以支持字母、數(shù)字、以及"_.-@#%"6個(gè)特殊符號(hào)
* @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 : 登錄名稱(chēng)格式校驗(yàn),格式錯(cuò)誤拋出異常
* @param propName : 登錄名稱(chēng)的提示名稱(chēng)
* @param loginName : 登錄名稱(chēng)
* @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);
}
}
? 根據(jù)loginNameValidator核心是checkLoginName,但loginNameValidator方法針對(duì)錯(cuò)誤,直接拋出異常,調(diào)用時(shí)代碼可以更加簡(jiǎn)潔。類(lèi)似的思想,適用于數(shù)據(jù)權(quán)限檢查,參照ID檢查,枚舉值檢查,唯一鍵檢查等。
// 用戶(hù)名格式校驗(yàn)
ValidateUtil.loginNameValidator("userName", item.getUserName());
? 使用checkLoginName方法,則需要如下:
// 用戶(hù)名格式校驗(yàn)
if(!ValidateUtil.checkLoginName(item.getUserName())){
throw new BaseException(ExceptionCodes.DATA_FORMAT_WRONG, "userName:" + item.getUserName());
}
9.2.2、參照ID檢查
? 如果對(duì)象有外鍵,即參照對(duì)象,則外鍵(ID)必須有意義,即參照對(duì)象是存在的。
? 使用集中式的對(duì)象ID檢查服務(wù)類(lèi)IdCheckService,根據(jù)ID類(lèi)型和ID值,獲取對(duì)象。如果類(lèi)型指定的ID值對(duì)象不存在,則拋出異常。
// 檢查參照ID的有效性
Integer orgId = item.getOrgId();
Orgnization orgnization = (Orgnization)ics.getObjById(EIdType.orgId,orgId);
? 至于IdCheckService中,根據(jù)ID獲取對(duì)象方法,可以直接查詢(xún)數(shù)據(jù)庫(kù),也可以通過(guò)緩存,這個(gè)取決于對(duì)象管理。
9.2.3、數(shù)據(jù)權(quán)限檢查
? 如果對(duì)象涉及數(shù)據(jù)權(quán)限,則需要檢查操作者是否有權(quán)新增此對(duì)象。
? 使用數(shù)據(jù)權(quán)限管理類(lèi)DataRightsService的方法,由于對(duì)一個(gè)確定的應(yīng)用,數(shù)據(jù)權(quán)限相關(guān)的字段個(gè)數(shù)是有限的,可以針對(duì)單個(gè)字段開(kāi)發(fā)接口,如orgId是數(shù)據(jù)權(quán)限字段,則可以提供checkUserDrByOrgId方法,代碼如下:
/**
*
* @methodName : checkUserDrByOrgId
* @description : 檢查當(dāng)前用戶(hù)是否對(duì)輸入的組織ID有數(shù)據(jù)權(quán)限
* @param request : request對(duì)象
* @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;
// 獲取賬號(hào)緩存信息
String accountId = accountCacheService.getId(request);
// 獲取用戶(hù)類(lèi)型
Integer userType = (Integer)accountCacheService.getAttribute(accountId,Constants.USER_TYPE);
// 獲取數(shù)據(jù)權(quán)限緩存信息
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()) {
// 如果為系統(tǒng)管理員
bRights = true;
return;
}
}else {
// 如果屬性不存在
throw new BaseException(ExceptionCodes.TOKEN_EXPIRED);
}
// 獲取數(shù)據(jù)權(quán)限
UserDr userDr = null;
bRights = true;
List<UserCustomDr> userCustomDrList = null;
String propName = "orgId";
// 獲取用戶(hù)對(duì)此fieldId的權(quán)限
userDr = fieldDrMap.get(propName);
if (userDr.getDrType().intValue() == EDataRightsType.drtAllE.getCode()) {
// 如果為全部,有權(quán)限
return;
}
if (userDr.getDrType().intValue() == EDataRightsType.drtDefaultE.getCode()) {
// 如果為默認(rèn)權(quán)限,進(jìn)一步檢查下級(jí)對(duì)象
List<Integer> drList = getDefaultDrList(orgId,propName);
boolean bFound = drList.contains(orgId);
if (!bFound) {
bRights = false;
}
}else if (userDr.getDrType().intValue() == EDataRightsType.drtCustomE.getCode()){
// 如果為自定義數(shù)據(jù)權(quán)限
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);
}
}
? 當(dāng)前用戶(hù)的數(shù)據(jù)權(quán)限配置信息,使用key為屬性名的字典Map<String,UserDr>保存到各訪(fǎng)問(wèn)用戶(hù)的賬號(hào)緩存中。根據(jù)request對(duì)象,獲取當(dāng)前操作者的數(shù)據(jù)權(quán)限配置信息。然后根據(jù)配置類(lèi)型,檢查輸入權(quán)限值是否在用戶(hù)許可范圍內(nèi),如果不在,就拋出異常。
? 還可以提供更通用的單屬性數(shù)據(jù)權(quán)限檢查接口方法。
/**
*
* @methodName : checkUserDrByDrId
* @description : 檢查當(dāng)前用戶(hù)是否對(duì)指定權(quán)限字典的輸入值有數(shù)據(jù)權(quán)限
* @param request : request對(duì)象
* @param propName : 權(quán)限屬性名
* @param drId : 權(quán)限屬性值
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2021/05/29 1.0.0 sheng.zheng 初版
*
*/
public void checkUserDrByDrId(HttpServletRequest request,String propName,Object drId);
? 多屬性數(shù)據(jù)權(quán)限檢查接口方法。
/**
*
* @methodName : checkDataRights
* @description : 檢查當(dāng)前用戶(hù)是否對(duì)給定對(duì)象有數(shù)據(jù)權(quán)限
* @param request : request對(duì)象,可從中獲取當(dāng)前用戶(hù)的緩存信息
* @param params : 權(quán)限屬性名與值的字典
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2021/03/13 1.0.0 sheng.zheng 初版
*
*/
public void checkUserDr(HttpServletRequest request,Map<String,Object> params);
9.2.4、枚舉值檢查
? 枚舉類(lèi)型,對(duì)應(yīng)的屬性數(shù)據(jù)類(lèi)型一般是Byte,數(shù)據(jù)庫(kù)使用tinyint。新增對(duì)象時(shí),枚舉字段的值要在枚舉類(lèi)型定義范圍中,否則會(huì)有問(wèn)題。
// 檢查枚舉值
int userType = item.getUserType().intValue();
EUserType eUserType = EUserType.getTypeByCode(userType);
int sex = item.getSex().intValue();
ESex eSex = ESex.getTypeByCode(sex);
? 相關(guān)枚舉類(lèi)型,都提供getTypeByCode方法,實(shí)現(xiàn)枚舉值有效性校驗(yàn)。如:
/**
*
* @methodName : getType
* @description : 根據(jù)code獲取枚舉值
* @param code : code值
* @return : code對(duì)應(yīng)的枚舉值
* @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()) {
// 遍歷每個(gè)枚舉值
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;
}
9.2.5、唯一性檢查
? 如果對(duì)象屬性值有唯一性要求,則需要進(jìn)行唯一性檢查。
// 檢查唯一性
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);
? 相關(guān)唯一性檢查方法,如下(也可以改為public以對(duì)外提供服務(wù)):
/**
*
* @methodName : checkUniqueByUserName
* @description : 檢查userName屬性值的唯一性
* @param userName : 用戶(hù)名
* @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) {
// 如果唯一鍵對(duì)象已存在
throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"userName=" + userName);
}
}
/**
*
* @methodName : checkUniqueByPhoneNumber
* @description : 檢查phoneNumber屬性值的唯一性
* @param phoneNumber : 手機(jī)號(hào)碼
* @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) {
// 如果唯一鍵對(duì)象已存在
throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,
"phoneNumber=" + phoneNumber);
}
}
/**
*
* @methodName : checkUniqueByIdNo
* @description : 檢查idNo屬性值的唯一性
* @param idNo : 身份證號(hào)碼
* @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) {
// 如果唯一鍵對(duì)象已存在
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) {
// 如果唯一鍵對(duì)象已存在
throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"openId=" + openId);
}
}
/**
*
* @methodName : checkUniqueByWoaOpenid
* @description : 檢查woaOpenid屬性值的唯一性
* @param woaOpenid : 微信公眾號(hào)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) {
// 如果唯一鍵對(duì)象已存在
throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"woaOpenid=" + woaOpenid);
}
}
9.2.6、業(yè)務(wù)處理
? 如果新增對(duì)象時(shí),需要一些內(nèi)部處理,則在此處進(jìn)行。如新增用戶(hù)時(shí),需要根據(jù)當(dāng)前時(shí)間生成鹽,然后將管理員輸入的明文密碼轉(zhuǎn)為加鹽Md5簽名密碼。
// 業(yè)務(wù)處理
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);
9.2.7、獲取全局ID
? 為當(dāng)前對(duì)象分配全局ID。
Long userId = 0L;
// 獲取全局記錄ID
Long globalRecId = tccs.getTableRecId("exa_users");
userId = globalRecId;
? 全局ID的獲取使用全局ID服務(wù)類(lèi)對(duì)象TableCodeConfigService,其提供單個(gè)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 : 記錄條數(shù)
* @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是根據(jù)數(shù)據(jù)表名稱(chēng),獲取表ID(這些表是相對(duì)固定的,可以使用緩存字典來(lái)管理)。而getGlobalIdDao的方法就是調(diào)用數(shù)據(jù)庫(kù)的函數(shù)exa_get_global_id,獲取可用ID。
/**
*
* @methodName : getTableRecId
* @description : 獲取表ID的一個(gè)記錄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的多個(gè)記錄ID
* @param tableId : 表ID
* @param count : ID個(gè)數(shù)
* @return : 開(kāi)始的記錄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);
9.2.8、設(shè)置記錄的用戶(hù)賬號(hào)信息
? 從request對(duì)象中獲取賬號(hào)信息,并設(shè)置對(duì)象。
// 獲取操作人賬號(hào)
String operatorName = getUserName(request);
// 設(shè)置信息
item.setUserId(userId);
item.setOperatorName(operatorName);
9.2.9、新增記錄
? 調(diào)用Dao的insertItem方法,新增記錄。
try {
// 插入數(shù)據(jù)
userDao.insertItem(item);
} catch(Exception e) {
LogUtil.error(e);
throw new BaseException(ExceptionCodes.ADD_OBJECT_FAILED,e.getMessage());
}
9.2.10、緩存處理
? 新增用戶(hù)對(duì)象,不涉及緩存處理。
? 如果有的對(duì)象,涉及全集加載,如組織樹(shù),則新增對(duì)象時(shí),組織樹(shù)也會(huì)變化。為了避免無(wú)效加載,使用修改標(biāo)記來(lái)表示集合被修改,獲取全集時(shí),再進(jìn)行加載。這樣,連續(xù)新增對(duì)象時(shí),不會(huì)有無(wú)效加載。緩存涉及全集加載的,新增對(duì)象需設(shè)置修改標(biāo)記。
? 如果緩存不涉及全集的,則無(wú)需處理。如字典類(lèi)緩存,新增時(shí)不必將新對(duì)象加入緩存,獲取時(shí),根據(jù)機(jī)制,緩存中不存在,會(huì)先請(qǐng)求數(shù)據(jù)庫(kù),此時(shí)可以加載到緩存中。
9.2.11、返回值處理
? 新增對(duì)象,如果是系統(tǒng)生成的ID,需要將ID值返回。
// 構(gòu)造返回值
Map<String,Object> map = new HashMap<String,Object>();
map.put("userId", userId.toString());
return map;
? 對(duì)于Long類(lèi)型,由于前端可能損失精度,因此使用字符串類(lèi)型傳遞。
(未完待續(xù)...)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-487318.html
到了這里,關(guān)于Spring Boot實(shí)現(xiàn)高質(zhì)量的CRUD-5的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!