主頁 > 後端開發 > Spring Boot實作高質量的CRUD-5

Spring Boot實作高質量的CRUD-5

2023-06-18 07:39:53 後端開發

(續前文)

9、Service實作類代碼示例 ?

?	以用戶管理模塊為例,展示Service實作類代碼,用戶管理的Service實作類為UserManServiceImpl,?UserManServiceImpl除了沒有deleteItems方法外,具備CRUD的其它常規方法,實際上?UserManService還有其它介面方法,如管理員修改密碼,用戶修改自身密碼,設定用戶角色串列,設定用戶資料權限等,這些不屬于常規CRUD方法,故不在此展示,

9.1、類定義及成員屬性

?	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:不可編輯欄位集,編輯物件時,規定哪些欄位是需要不允許修改的,防止引數注入,

9.2、新增物件

?	新增物件的方法為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;
	}

9.2.1、新增物件的引數校驗

?	首先是引數校驗,使用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方法的輸入引數校驗,首先是必選欄位校驗,檢查必選欄位是否都有值,然后是相關屬性值的資料格式校驗,
9.2.1.1、新增物件的必選欄位校驗
?	呼叫用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的方法,可以大大簡化代碼,
9.2.1.2、資料格式校驗
?	某些物件的某些屬性值,有資料格式要求,此時需要進行資料格式校驗,如用戶物件的用戶名(登錄名),手機號碼,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());
			}

9.2.2、參照ID檢查

?	如果物件有外鍵,即參照物件,則外鍵(ID)必須有意義,即參照物件是存在的,
?	使用集中式的物件ID檢查服務類IdCheckService,根據ID型別和ID值,獲取物件,如果型別指定的ID值物件不存在,則拋出例外,
        // 檢查參照ID的有效性
        Integer orgId = item.getOrgId();
        Orgnization orgnization = (Orgnization)ics.getObjById(EIdType.orgId,orgId);
?	至于IdCheckService中,根據ID獲取物件方法,可以直接查詢資料庫,也可以通過快取,這個取決于物件管理,

9.2.3、資料權限檢查

?	如果物件涉及資料權限,則需要檢查操作者是否有權新增此物件,
?	使用資料權限管理類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); 	

9.2.4、列舉值檢查

?	列舉型別,對應的屬性資料型別一般是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;
	}

9.2.5、唯一性檢查

?	如果物件屬性值有唯一性要求,則需要進行唯一性檢查,
        // 檢查唯一性
        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);            
        }
    }

9.2.6、業務處理

?	如果新增物件時,需要一些內部處理,則在此處進行,如新增用戶時,需要根據當前時間生成鹽,然后將管理員輸入的明文密碼轉為加鹽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); 

9.2.7、獲取全域ID

?	為當前物件分配全域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);

9.2.8、設定記錄的用戶賬號資訊

?	從request物件中獲取賬號資訊,并設定物件,
		// 獲取操作人賬號
		String operatorName = getUserName(request);

		// 設定資訊
		item.setUserId(userId);
		item.setOperatorName(operatorName);

9.2.9、新增記錄

?	呼叫Dao的insertItem方法,新增記錄,
		try {
    		// 插入資料
			userDao.insertItem(item);
			
		} catch(Exception e) {
			LogUtil.error(e);
			throw new BaseException(ExceptionCodes.ADD_OBJECT_FAILED,e.getMessage());
		}

9.2.10、快取處理

?	新增用戶物件,不涉及快取處理,
?	如果有的物件,涉及全集加載,如組織樹,則新增物件時,組織樹也會變化,為了避免無效加載,使用修改標記來表示集合被修改,獲取全集時,再進行加載,這樣,連續新增物件時,不會有無效加載,快取涉及全集加載的,新增物件需設定修改標記,
?	如果快取不涉及全集的,則無需處理,如字典類快取,新增時不必將新物件加入快取,獲取時,根據機制,快取中不存在,會先請求資料庫,此時可以加載到快取中,

9.2.11、回傳值處理

?	新增物件,如果是系統生成的ID,需要將ID值回傳,
		// 構造回傳值
		Map<String,Object> map = new HashMap<String,Object>();
        map.put("userId", userId.toString());
		
		return map;
?	對于Long型別,由于前端可能損失精度,因此使用字串型別傳遞,

(未完待續...)

作者:阿拉伯1999 出處:http://www.cnblogs.com/alabo1999/ 本文著作權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利. 養成良好習慣,好文章隨手頂一下,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/555440.html

標籤:其他

上一篇:?任務編排工具在之家業務系統中應用探究

下一篇:返回列表

標籤雲
其他(161181) Python(38240) JavaScript(25504) Java(18245) C(15237) 區塊鏈(8271) C#(7972) AI(7469) 爪哇(7425) MySQL(7256) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5875) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4601) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2436) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1984) HtmlCss(1968) 功能(1967) Web開發(1951) C++(1941) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Spring Boot實作高質量的CRUD-5

    (續前文) ## 9、Service實作類代碼示例 ? ? 以用戶管理模塊為例,展示Service實作類代碼。用戶管理的Service實作類為UserManServiceImpl。?UserManServiceImpl除了沒有deleteItems方法外,具備CRUD的其它常規方法。實際上?User ......

    uj5u.com 2023-06-18 07:39:53 more
  • ?任務編排工具在之家業務系統中應用探究

    本文主要介紹在之家廣告業務系統中運用任務編排治理工具的場景及其可以解決的問題,講解任務編排框架的核心要點,以及向大家演示一個任務編排框架的基本結構,讓大家對任務編排工具增強業務開發效率,提高研發質量有更深刻的理解。 ......

    uj5u.com 2023-06-18 07:39:37 more
  • Go 語言之 zap 日志庫簡單使用

    # Go 語言之 zap 日志庫簡單使用 ## 默認的 Go log log:https://pkg.go.dev/log ```go package main import ( "log" "os" ) func init() { log.SetPrefix("LOG: ") // 設定前綴 f, ......

    uj5u.com 2023-06-18 07:39:24 more
  • Scala面向物件

    # 類和物件 **組成結構** ? 建構式: 在創建物件的時候給屬性賦值 ? 成員變數: ? 成員方法(函式) ? 區域變數 ? 代碼塊 ## 構造器 每個類都有一個主構造器,這個構造器和類定義"交織"在一起類名后面的內容就是主構造器,如果引數串列為空的話,()可以省略 scala的類有且僅有一個 ......

    uj5u.com 2023-06-18 07:39:00 more
  • 基于回歸分析的波士頓房價分析

    #基于回歸分析的波士頓房價分析 專案實作步驟: 1.專案結構 2.處理資料 3.處理繪圖 4.對資料進行分析 5.結果展示 一.專案結構 ![image](https://img2023.cnblogs.com/blog/3047082/202306/3047082-2023061722315431 ......

    uj5u.com 2023-06-18 07:38:30 more
  • 通過模仿學會Python爬蟲(一):零基礎上手

    好家伙,爬蟲來了 爬蟲,這玩意,不會怎么辦, 誒,先抄一份作業回來 1.別人的爬蟲 Python爬蟲史上超詳細講解(零基礎入門,老年人都看的懂)_ChenBinBini的博客-CSDN博客 # -*- codeing = utf-8 -*- from bs4 import BeautifulSoup ......

    uj5u.com 2023-06-18 07:38:11 more
  • Python潮流周刊#7:我討厭用 asyncio

    你好,我是貓哥。這里記錄每周值得分享的 Python 及通用技術內容,部分為英文,已在小標題注明。(標題取自其中一則分享,不代表全部內容都是該主題,特此宣告。) 首發于我的博客:[https://pythoncat.top/posts/2023-06-17-weekly7](https://pyth ......

    uj5u.com 2023-06-18 07:32:25 more
  • Django學習筆記

    ## 1.常用命令 `創建專案:django-admin startproject 專案名` `創建APP(進入工程目錄):python manage.py startapp 網站名` `創建庫表(進入工程目錄):python manage.py makemigrations` `執行庫表建立(進入 ......

    uj5u.com 2023-06-18 07:32:06 more
  • 09. centos使用docker方式安裝mysql

    ## 一、創建宿主機物理路徑 新建/mydata/mysql/data、log和conf三個檔案夾 ```bash mkdir -p /mnt/mysql/log mkdir -p /mnt/mysql/data mkdir -p /mnt/mysql/config ``` 或者 ```bash m ......

    uj5u.com 2023-06-18 07:31:43 more
  • Java 注釋及Dos命令

    # Java 注釋、絕對路徑、相對路徑、基本Dos命令 # 1. Java的三種注釋方式 ## 注釋能增加代碼的可讀性,習慣寫注釋能提升我們撰寫代碼的能力 > ### 單行注釋:用//注釋一些代碼提示 > > ### 多行注釋:以/*為開頭 以 */為結束 > > ### 檔案注釋:/* > > # ......

    uj5u.com 2023-06-18 07:30:01 more