Spring Boot实现高质量的CRUD-5

这篇具有很好参考价值的文章主要介绍了Spring Boot实现高质量的CRUD-5。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

(续前文)

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类型,由于前端可能损失精度,因此使用字符串类型传递。

(未完待续...)文章来源地址https://www.toymoban.com/news/detail-487318.html

到了这里,关于Spring Boot实现高质量的CRUD-5的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • AI Code Translator —— 能够实现高质量的自动编程语言转换工具

    https://github.com/mckaywrigley/ai-code-translator AI Code Translator —— 是一款基于大型语言模型的代码翻译工具,同时也是一款颠覆性的编程语言翻译工具,它基于先进的机器学习技术和大规模语料库训练而成,能够实现高质量的自动编程语言转换。 这款工具最大的突破在于翻译速度快且

    2024年02月04日
    浏览(49)
  • 如何写出高质量代码?

    作为一名资深开发人员,写出高质量的代码是我们必须要追求的目标。然而,在实际开发中,我们常常会遇到各种问题。比如,代码的可读性、可维护性、健壮性和灵活性等,这些都会影响代码的质量。那么,究竟如何才能写出高质量的代码呢? 代码结构清晰易懂,能够使代

    2024年02月02日
    浏览(52)
  • 如何写出高质量代码

    一、 前言 编写高质量代码是每一位程序员的追求。高质量的代码可以提高代码可读性、可维护性、可扩展性以及软件运行的性能和稳定性。在这篇文章中,我将分享一些编写高质量代码的特征、编程实践技巧和软件工程方法论。 可读性:好的代码应该能够被维护者轻易地理

    2024年02月02日
    浏览(81)
  • 如何编写高质量代码

    现代软件开发中,代码是构建高质量软件的核心。高质量代码能够提高软件系统的可靠性、可维护性和可扩展性,减少bug的数量和修复时间,提升开发效率和代码可读性,同时有助于团队协作和知识传承共享。 然而,梦想是丰满的,现实是骨感的!软件开发面临诸多挑战。

    2024年02月02日
    浏览(108)
  • 高质量椭圆检测库

    目录 前言 效果展示 检测库 简介 安装库 用法 测试 论文算法步骤简读 1. lsd 检测 2. lsd group 3. 生成初始 ellipse 4. 聚类 椭圆检测是工业中比较常用的一种检测需求。目前常用的基于传统图像处理的椭圆检测方法是霍夫变换,但是霍变换的检测率比较低,很难满足工业场景。而基

    2024年02月07日
    浏览(124)
  • 如何写出高质量的代码

    你是否曾经为自己写的代码而感到懊恼?你是否想过如何才能写出高质量代码?那就不要错过这个话题!在这里,我们可以讨论什么是高质量代码,如何写出高质量代码等问题。无论你是初学者还是资深开发人员,都可以在这个话题下进行分享,汲取灵感和知识,共同提高自

    2023年04月25日
    浏览(122)
  • 网络安全高质量文库

    PeiQI文库 http://api.orchidstudio.cn/ PeiQi文库是一个面对网络安全从业者的知识库,涉及漏洞研究,代码审计,CTF夺旗,红蓝对抗等多个安全方向,用于解决安全信息不聚合,安全资料不易找的难题。帮助网络安全从业者共同构建安全的互联网,快速验证并及时修复相关漏洞,为甲

    2024年02月12日
    浏览(49)
  • 有哪些高质量的自学网站?

    分享32个鲜为人知并且完全免费的高质量自学网站,每个都是堪称神器,让你相见恨晚。 是一个完全免费的综合视频教程网站,非常良心实用。 它提供的视频教程非常丰富并且质量很高,包括:PS 教程、手机摄影教程、Ai 做图教程、Excel 教程、Word 教程、PPT 教程、Pr 视频剪辑

    2024年02月02日
    浏览(124)
  • Visio 转为高质量PDF

    Visio另存为pdf不够清晰怎么办 - - 可以选择先另存为高分辨率的图片( 存的时候分辨率选择打印机或者自定义即可 ),然后转为pdf. 或者用 打印 1 保存为高质量 2 的pdf (本文介绍) 版本:Microsoft Visio 2010 Adobe Acrobat 2018 关键就是设置分辨率,不按照以上流程亦可 一般情况下安装完

    2024年02月04日
    浏览(47)
  • 如何编写高质量的测试计划

    1.1目的 简述本计划的目的,旨在说明各种测试阶段任务、人员分配和时间安排、工作规范等。 测试计划在策略和方法的高度说明如何计划、组织和管理测试项目。测试计划包含足够的信息使测试人员明白项目需要做什么是如何运作的。另外,清晰的文档结构能使任何一个读

    2024年02月16日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包