Spring Boot實現高質量的CRUD-5

来源:https://www.cnblogs.com/alabo1999/archive/2023/06/17/17487241.html
-Advertisement-
Play Games

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


(續前文)

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/ 本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利. 養成良好習慣,好文章隨手頂一下。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 1.Java 發展歷史 ### 由高斯林創建 1995年由甲骨文公司收購併發出第一版本,目前使用最多是Java8 及 Java11 原因是這兩個版本都是長期支持維護的,企業用的也比較多。 # 2.Java的一些特點 > ### 跨平臺性:主要是因為每個平臺都裝有 JVM > ### Java 是 ...
  • #### 1. 跳出/執行下一次迴圈。 ``` {標簽名}: for true { ... for true { ... break/continue {標簽名} //預設不加標簽,則跳出最近一層迴圈。加了標簽可以跳出標簽定義處所在迴圈 } } ``` #### 2. map的使用註意項。 因為ma ...
  • 最近在弄文件上傳、下載、線上預覽時經常需要設置請求標頭或者響應標頭的Content-Type 屬性。所以研究了一下spring支持哪些Content-Type,通過研究MediaTypeFactory.getMediaType的源碼,可以得知spring是將支持的Content-Type 維護在/o... ...
  • # Go 語言之在 gin 框架中使用 zap 日誌庫 ### gin 框架預設使用的是自帶的日誌 #### `gin.Default()`的源碼 Logger(), Recovery() ```go func Default() *Engine { debugPrintWARNINGDefault ...
  • ## 概述 Nginx 是一個高性能的 HTTP 和反向代理伺服器,特點是占用記憶體少,併發能力強 #### 1. 正向代理 如果把區域網外的 Internet 想象成一個巨大的資源庫,則區域網中的客戶端要訪問 Internet,需要通過代理伺服器來訪問,這種訪問就稱為正向代理 ![](https:/ ...
  • # 類和對象 **組成結構** • 構造函數: 在創建對象的時候給屬性賦值 • 成員變數: • 成員方法(函數) • 局部變數 • 代碼塊 ## 構造器 每個類都有一個主構造器,這個構造器和類定義"交織"在一起類名後面的內容就是主構造器,如果參數列表為空的話,()可以省略 scala的類有且僅有一個 ...
  • ## 1.常用命令 `創建項目:django-admin startproject 項目名` `創建APP(進入工程目錄):python manage.py startapp 網站名` `創建庫表(進入工程目錄):python manage.py makemigrations` `執行庫表建立(進入 ...
  • # Go 語言之自定義 zap 日誌 [zap 日誌](https://github.com/uber-go/zap):https://github.com/uber-go/zap ## 一、日誌寫入文件 - `zap.NewProduction`、`zap.NewDevelopment` 是預設配 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...