(續前文)
以使用者管理模組為例,展示Service實現類程式碼。使用者管理的Service實現類為UserManServiceImpl。UserManServiceImpl除了沒有deleteItems方法外,具備CRUD的其它常規方法。實際上UserManService還有其它介面方法,如管理員修改密碼,使用者修改自身密碼,設定使用者角色列表,設定使用者資料許可權等,這些不屬於常規CRUD方法,故不在此展示。
UserManServiceImpl的類定義如下:
package com.abc.example.service.impl;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.github.pagehelper.PageInfo;
import com.abc.esbcommon.common.impexp.BaseExportObj;
import com.abc.esbcommon.common.impexp.BaseImportObj;
import com.abc.esbcommon.common.impexp.ExcelExportHandler;
import com.abc.esbcommon.common.impexp.ExcelImportHandler;
import com.abc.esbcommon.common.impexp.ImpExpFieldDef;
import com.abc.esbcommon.common.utils.FileUtil;
import com.abc.esbcommon.common.utils.LogUtil;
import com.abc.esbcommon.common.utils.Md5Util;
import com.abc.esbcommon.common.utils.ObjListUtil;
import com.abc.esbcommon.common.utils.ReflectUtil;
import com.abc.esbcommon.common.utils.TimeUtil;
import com.abc.esbcommon.common.utils.Utility;
import com.abc.esbcommon.common.utils.ValidateUtil;
import com.abc.esbcommon.entity.SysParameter;
import com.abc.example.common.constants.Constants;
import com.abc.example.config.UploadConfig;
import com.abc.example.dao.UserDao;
import com.abc.example.entity.Orgnization;
import com.abc.example.entity.User;
import com.abc.example.enumeration.EDeleteFlag;
import com.abc.example.enumeration.EIdType;
import com.abc.example.enumeration.ESex;
import com.abc.example.enumeration.EUserType;
import com.abc.example.exception.BaseException;
import com.abc.example.exception.ExceptionCodes;
import com.abc.example.service.BaseService;
import com.abc.example.service.DataRightsService;
import com.abc.example.service.IdCheckService;
import com.abc.example.service.SysParameterService;
import com.abc.example.service.TableCodeConfigService;
import com.abc.example.service.UserManService;
/**
* @className : UserManServiceImpl
* @description : 使用者物件管理服務實現類
* @summary :
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/17 1.0.0 sheng.zheng 初版
*
*/
@SuppressWarnings({ "unchecked", "unused" })
@Service
public class UserManServiceImpl extends BaseService implements UserManService{
// 使用者物件資料存取類物件
@Autowired
private UserDao userDao;
// 檔案上傳設定類物件
@Autowired
private UploadConfig uploadConfig;
// 物件ID檢查服務類物件
@Autowired
private IdCheckService ics;
// 資料許可權服務類物件
@Autowired
private DataRightsService drs;
// 全域性ID服務類物件
@Autowired
private TableCodeConfigService tccs;
// 系統引數服務類物件
@Autowired
private SysParameterService sps;
// 新增必選欄位集
private String[] mandatoryFieldList = new String[]{"userName","password","userType","orgId"};
// 修改不可編輯欄位集
private String[] uneditFieldList = new String[]{"password","salt","deleteFlag"};
}
UserManServiceImpl類繼承BaseService,實現UserManService介面。BaseService提供引數校驗介面、啟動分頁處理和獲取使用者賬號資訊的公共方法(參見上文的8.13.1)。
UserManServiceImpl類成員屬性作用說明:
UserDao userDao:使用者物件資料存取類物件,用於存取資料庫使用者表。CRUD操作,與資料庫緊密聯絡,userDao是核心物件。
UploadConfig uploadConfig:檔案上傳設定類物件,提供臨時路徑/tmp,匯出Excel檔案時,生成臨時檔案存於臨時目錄。上傳Excel檔案,臨時檔案也存於此目錄。
IdCheckService ics:物件ID檢查服務類物件,用於外來鍵物件存在性檢查。
DataRightsService drs:資料許可權服務類物件,提供資料許可權處理的相關介面方法。
TableCodeConfigService tccs:全域性ID服務類物件,提供生成全域性ID的介面方法。
SysParameterService sps:系統引數服務類物件,在Excel資料匯入匯出時,對列舉欄位的列舉值翻譯,需要根據系統參數列的設定記錄進行翻譯。
String[] mandatoryFieldList:新增必選欄位集,新增物件時,規定哪些欄位是必須的。
String[] uneditFieldList:不可編輯欄位集,編輯物件時,規定哪些欄位是需要不允許修改的,防止引數注入。
新增物件的方法為addItem,下面是新增使用者物件的方法:
/**
* @methodName : addItem
* @description : 新增一個使用者物件
* @remark : 參見介面類方法說明
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/17 1.0.0 sheng.zheng 初版
*
*/
@Override
public Map<String,Object> addItem(HttpServletRequest request, User item) {
// 輸入引數校驗
checkValidForParams(request, "addItem", item);
// 檢查參照ID的有效性
Integer orgId = item.getOrgId();
Orgnization orgnization = (Orgnization)ics.getObjById(EIdType.orgId,orgId);
// 檢查資料許可權
drs.checkUserDrByOrgId(request, orgId);
// 檢查列舉值
int userType = item.getUserType().intValue();
EUserType eUserType = EUserType.getTypeByCode(userType);
int sex = item.getSex().intValue();
ESex eSex = ESex.getTypeByCode(sex);
// 檢查唯一性
String userName = item.getUserName();
String phoneNumber = item.getPhoneNumber();
String idNo = item.getIdNo();
String openId = item.getOpenId();
String woaOpenid = item.getWoaOpenid();
checkUniqueByUserName(userName);
checkUniqueByPhoneNumber(phoneNumber);
checkUniqueByIdNo(idNo);
checkUniqueByOpenId(openId);
checkUniqueByWoaOpenid(woaOpenid);
// 業務處理
LocalDateTime current = LocalDateTime.now();
String salt = TimeUtil.format(current, "yyyy-MM-dd HH:mm:ss");
// 明文密碼加密
String password = item.getPassword();
String encyptPassword = Md5Util.plaintPasswdToDbPasswd(password, salt, Constants.TOKEN_KEY);
item.setSalt(salt);
item.setPassword(encyptPassword);
Long userId = 0L;
// 獲取全域性記錄ID
Long globalRecId = tccs.getTableRecId("exa_users");
userId = globalRecId;
// 獲取操作人賬號
String operatorName = getUserName(request);
// 設定資訊
item.setUserId(userId);
item.setOperatorName(operatorName);
try {
// 插入資料
userDao.insertItem(item);
} catch(Exception e) {
LogUtil.error(e);
throw new BaseException(ExceptionCodes.ADD_OBJECT_FAILED,e.getMessage());
}
// 構造返回值
Map<String,Object> map = new HashMap<String,Object>();
map.put("userId", userId.toString());
return map;
}
首先是引數校驗,使用checkValidForParams方法,此方法一般僅對輸入引數進行值校驗,如欄位是否缺失,值型別是否匹配,資料格式是否正確等。
/**
* @methodName : checkValidForParams
* @description : 輸入引數校驗
* @param request : request物件
* @param methodName : 方法名稱
* @param params : 輸入引數
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/17 1.0.0 sheng.zheng 初版
*
*/
@Override
public void checkValidForParams(HttpServletRequest request, String methodName, Object params) {
switch(methodName) {
case "addItem":
{
User item = (User)params;
// 檢查項: 必選欄位
ReflectUtil.checkMandatoryFields(item,mandatoryFieldList);
// 使用者名稱格式校驗
ValidateUtil.loginNameValidator("userName", item.getUserName());
// 手機號碼格式校驗
if (!item.getPhoneNumber().isEmpty()) {
ValidateUtil.phoneNumberValidator("phoneNumber", item.getPhoneNumber());
}
// email格式校驗
if (!item.getEmail().isEmpty()) {
ValidateUtil.emailValidator("email", item.getEmail());
}
}
break;
// case "editItem":
// ...
default:
break;
}
}
addItem方法的輸入引數校驗。首先是必選欄位校驗,檢查必選欄位是否都有值。然後是相關屬性值的資料格式校驗。
呼叫用ReflectUtil工具類的checkMandatoryFields,檢查字串型別和整數的欄位,是否有值。
/**
*
* @methodName : checkMandatoryFields
* @description : 檢查必選欄位
* @param <T> : 泛型型別
* @param item : T型別物件
* @param mandatoryFieldList: 必選欄位名陣列
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/26 1.0.0 sheng.zheng 初版
*
*/
public static <T> void checkMandatoryFields(T item,String[] mandatoryFieldList) {
// 獲取物件item的執行時的類
Class<?> clazz = (Class<?>) item.getClass();
String type = "";
String shortType = "";
String error = "";
for(String propName : mandatoryFieldList) {
try {
Field field = clazz.getDeclaredField(propName);
field.setAccessible(true);
// 獲取欄位型別
type = field.getType().getTypeName();
// 獲取型別的短名稱
shortType = getShortTypeName(type);
// 獲取屬性值
Object oVal = field.get(item);
if (oVal == null) {
// 如果必選欄位值為null
error += propName + ",";
continue;
}
switch(shortType) {
case "Integer":
case "int":
{
Integer iVal = Integer.valueOf(oVal.toString());
if (iVal == 0) {
// 整型型別,有效值一般為非0
error += propName + ",";
}
}
break;
case "String":
{
String sVal = oVal.toString();
if (sVal.isEmpty()) {
// 字串型別,有效值一般為非空串
error += propName + ",";
}
}
break;
case "Byte":
case "byte":
// 位元組型別,一般用於列舉值欄位,後面使用列舉值檢查,此處忽略
break;
case "List":
{
List<?> list = (List<?>)oVal;
if (list.size() == 0) {
// 列表型別,無成員
error += propName + ",";
}
}
break;
default:
break;
}
}catch(Exception e) {
// 非屬性欄位
if (error.isEmpty()) {
error += propName;
}else {
error += "," + propName;
}
}
}
if (!error.isEmpty()) {
error = Utility.trimLeftAndRight(error,"\\,");
throw new BaseException(ExceptionCodes.ARGUMENTS_IS_EMPTY,error);
}
}
一般情況下,這個方法可以起作用。但特殊情況,如0值為有效值,-1為無效值,則會有誤報情況。此時,可以使用另一個方法。
/**
*
* @methodName : checkMandatoryFields
* @description : 檢查必選欄位
* @param <T> : 泛型型別
* @param item : 參考物件,屬性欄位值使用預設值或區別於有效預設值的無效值
* @param item2 : 被比較物件
* @param mandatoryFieldList: 必須欄位屬性名列表
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/06/17 1.0.0 sheng.zheng 初版
*
*/
public static <T> void checkMandatoryFields(T item,T item2,
String[] mandatoryFieldList) {
Class<?> clazz = (Class<?>) item.getClass();
String error = "";
for(String propName : mandatoryFieldList) {
try {
Field field = clazz.getDeclaredField(propName);
field.setAccessible(true);
// 獲取屬性值
Object oVal = field.get(item);
field.setAccessible(true);
// 獲取屬性值
Object oVal2 = field.get(item2);
if (oVal2 == null) {
// 新值為null
error += propName + ",";
continue;
}
if (oVal != null) {
if (oVal.equals(oVal2)) {
// 如果值相等
error += propName + ",";
}
}
}catch(Exception e) {
// 非屬性欄位
if (error.isEmpty()) {
error += propName;
}else {
error += "," + propName;
}
}
}
if (!error.isEmpty()) {
error = Utility.trimLeftAndRight(error,"\\,");
throw new BaseException(ExceptionCodes.ARGUMENTS_IS_EMPTY,error);
}
}
這個方法,增加了參考物件item,一般使用預設值,新物件為item2,如果新物件的必選屬性值為參考物件一致,則認為該屬性未賦值。這對於預設值為有效值時,會有問題,此時呼叫方法前先將有效預設值設定為無效值。
如User物件類,userType預設值為3,是有效值。可如下方法呼叫:
User item = (User)params;
// 檢查項: 必選欄位
User refItem = new User();
// 0為無效值
refItem.setUserType((byte)0);
ReflectUtil.checkMandatoryFields(refItem,item,mandatoryFieldList);
使用ReflectUtil的mandatoryFieldList的方法,可以大大簡化程式碼。
某些物件的某些屬性值,有資料格式要求,此時需要進行資料格式校驗。如使用者物件的使用者名稱(登入名),手機號碼,email等。這些資料格式校驗,可以累計起來,開發工具方法,便於其它物件使用。
如下面是登入名的格式校驗方法,支援以字母開頭,後續可以是字母、數位或"_.-@#%"特殊符號,不支援中文。此校驗方法沒有長度校驗,由於各屬性的長度要求不同,可以另行檢查。
/**
*
* @methodName : checkLoginName
* @description : 檢查登入名格式是否正確,
* 格式:字母開頭,可以支援字母、數位、以及"_.-@#%"6個特殊符號
* @param loginName : 登入名
* @return :
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/06/16 1.0.0 sheng.zheng 初版
*
*/
public static boolean checkLoginName(String loginName) {
String pattern = "^[a-zA-Z]([a-zA-Z0-9_.\\-@#%]*)$";
boolean bRet = Pattern.matches(pattern,loginName);
return bRet;
}
/**
*
* @methodName : loginNameValidator
* @description : 登入名稱格式校驗,格式錯誤丟擲異常
* @param propName : 登入名稱的提示名稱
* @param loginName : 登入名稱
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/06/16 1.0.0 sheng.zheng 初版
*
*/
public static void loginNameValidator(String propName,String loginName) {
boolean bRet = checkLoginName(loginName);
if (!bRet) {
throw new BaseException(ExceptionCodes.DATA_FORMAT_WRONG, propName + ":" + loginName);
}
}
根據loginNameValidator核心是checkLoginName,但loginNameValidator方法針對錯誤,直接丟擲異常,呼叫時程式碼可以更加簡潔。類似的思想,適用於資料許可權檢查,參照ID檢查,列舉值檢查,唯一鍵檢查等。
// 使用者名稱格式校驗
ValidateUtil.loginNameValidator("userName", item.getUserName());
使用checkLoginName方法,則需要如下:
// 使用者名稱格式校驗
if(!ValidateUtil.checkLoginName(item.getUserName())){
throw new BaseException(ExceptionCodes.DATA_FORMAT_WRONG, "userName:" + item.getUserName());
}
如果物件有外來鍵,即參照物件,則外來鍵(ID)必須有意義,即參照物件是存在的。
使用集中式的物件ID檢查服務類IdCheckService,根據ID型別和ID值,獲取物件。如果型別指定的ID值物件不存在,則丟擲異常。
// 檢查參照ID的有效性
Integer orgId = item.getOrgId();
Orgnization orgnization = (Orgnization)ics.getObjById(EIdType.orgId,orgId);
至於IdCheckService中,根據ID獲取物件方法,可以直接查詢資料庫,也可以通過快取,這個取決於物件管理。
如果物件涉及資料許可權,則需要檢查操作者是否有權新增此物件。
使用資料許可權管理類DataRightsService的方法,由於對一個確定的應用,資料許可權相關的欄位個數是有限的,可以針對單個欄位開發介面,如orgId是資料許可權欄位,則可以提供checkUserDrByOrgId方法,程式碼如下:
/**
*
* @methodName : checkUserDrByOrgId
* @description : 檢查當前使用者是否對輸入的組織ID有資料許可權
* @param request : request物件
* @param orgId : 組織ID
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2021/05/29 1.0.0 sheng.zheng 初版
*
*/
@SuppressWarnings("unchecked")
@Override
public void checkUserDrByOrgId(HttpServletRequest request,Integer orgId) {
boolean bRights = false;
// 獲取賬號快取資訊
String accountId = accountCacheService.getId(request);
// 獲取使用者型別
Integer userType = (Integer)accountCacheService.getAttribute(accountId,Constants.USER_TYPE);
// 獲取資料許可權快取資訊
Map<String,UserDr> fieldDrMap = null;
fieldDrMap = (Map<String,UserDr>)accountCacheService.getAttribute(accountId, Constants.DR_MAP);
if (userType != null || fieldDrMap == null) {
if (userType == EUserType.utAdminE.getCode()) {
// 如果為系統管理員
bRights = true;
return;
}
}else {
// 如果屬性不存在
throw new BaseException(ExceptionCodes.TOKEN_EXPIRED);
}
// 獲取資料許可權
UserDr userDr = null;
bRights = true;
List<UserCustomDr> userCustomDrList = null;
String propName = "orgId";
// 獲取使用者對此fieldId的許可權
userDr = fieldDrMap.get(propName);
if (userDr.getDrType().intValue() == EDataRightsType.drtAllE.getCode()) {
// 如果為全部,有許可權
return;
}
if (userDr.getDrType().intValue() == EDataRightsType.drtDefaultE.getCode()) {
// 如果為預設許可權,進一步檢查下級物件
List<Integer> drList = getDefaultDrList(orgId,propName);
boolean bFound = drList.contains(orgId);
if (!bFound) {
bRights = false;
}
}else if (userDr.getDrType().intValue() == EDataRightsType.drtCustomE.getCode()){
// 如果為自定義資料許可權
List<Integer> orgIdList = null;
if (userCustomDrList == null) {
// 如果自定義列表為空,則獲取
Long userId = (Long)accountCacheService.getAttribute(accountId,Constants.USER_ID);
userCustomDrList = getUserCustomDrList(userId,propName);
orgIdList = getUserCustomFieldList(userCustomDrList,propName);
if (orgIdList != null) {
boolean bFound = orgIdList.contains(orgId);
if (!bFound) {
bRights = false;
}
}
}
}
if (bRights == false) {
throw new BaseException(ExceptionCodes.ACCESS_FORBIDDEN);
}
}
當前使用者的資料許可權設定資訊,使用key為屬性名的字典Map<String,UserDr>儲存到各存取使用者的賬號快取中。根據request物件,獲取當前操作者的資料許可權設定資訊。然後根據設定型別,檢查輸入許可權值是否在使用者許可範圍內,如果不在,就丟擲異常。
還可以提供更通用的單屬性資料許可權檢查介面方法。
/**
*
* @methodName : checkUserDrByDrId
* @description : 檢查當前使用者是否對指定許可權字典的輸入值有資料許可權
* @param request : request物件
* @param propName : 許可權屬性名
* @param drId : 許可權屬性值
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2021/05/29 1.0.0 sheng.zheng 初版
*
*/
public void checkUserDrByDrId(HttpServletRequest request,String propName,Object drId);
多屬性資料許可權檢查介面方法。
/**
*
* @methodName : checkDataRights
* @description : 檢查當前使用者是否對給定物件有資料許可權
* @param request : request物件,可從中獲取當前使用者的快取資訊
* @param params : 許可權屬性名與值的字典
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2021/03/13 1.0.0 sheng.zheng 初版
*
*/
public void checkUserDr(HttpServletRequest request,Map<String,Object> params);
列舉型別,對應的屬性資料型別一般是Byte,資料庫使用tinyint。新增物件時,列舉欄位的值要在列舉型別定義範圍中,否則會有問題。
// 檢查列舉值
int userType = item.getUserType().intValue();
EUserType eUserType = EUserType.getTypeByCode(userType);
int sex = item.getSex().intValue();
ESex eSex = ESex.getTypeByCode(sex);
相關列舉型別,都提供getTypeByCode方法,實現列舉值有效性校驗。如:
/**
*
* @methodName : getType
* @description : 根據code獲取列舉值
* @param code : code值
* @return : code對應的列舉值
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/17 1.0.0 sheng.zheng 初版
*
*/
public static EUserType getType(int code) {
// 返回值變數
EUserType eRet = null;
for (EUserType item : values()) {
// 遍歷每個列舉值
if (code == item.getCode()) {
// code匹配
eRet = item;
break;
}
}
return eRet;
}
// 檢查並獲取指定code的列舉值
public static EUserType getTypeByCode(int code) {
EUserType item = getType(code);
if (item == null) {
throw new BaseException(ExceptionCodes.INVALID_ENUM_VALUE,"EUserType with code="+code);
}
return item;
}
如果物件屬性值有唯一性要求,則需要進行唯一性檢查。
// 檢查唯一性
String userName = item.getUserName();
String phoneNumber = item.getPhoneNumber();
String idNo = item.getIdNo();
String openId = item.getOpenId();
String woaOpenid = item.getWoaOpenid();
checkUniqueByUserName(userName);
checkUniqueByPhoneNumber(phoneNumber);
checkUniqueByIdNo(idNo);
checkUniqueByOpenId(openId);
checkUniqueByWoaOpenid(woaOpenid);
相關唯一性檢查方法,如下(也可以改為public以對外提供服務):
/**
*
* @methodName : checkUniqueByUserName
* @description : 檢查userName屬性值的唯一性
* @param userName : 使用者名稱
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/17 1.0.0 sheng.zheng 初版
*
*/
private void checkUniqueByUserName(String userName) {
User item = userDao.selectItemByUserName(userName);
if (item != null) {
// 如果唯一鍵物件已存在
throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"userName=" + userName);
}
}
/**
*
* @methodName : checkUniqueByPhoneNumber
* @description : 檢查phoneNumber屬性值的唯一性
* @param phoneNumber : 手機號碼
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/17 1.0.0 sheng.zheng 初版
*
*/
private void checkUniqueByPhoneNumber(String phoneNumber) {
if (phoneNumber.equals("")) {
// 如果為例外值
return;
}
User item = userDao.selectItemByPhoneNumber(phoneNumber);
if (item != null) {
// 如果唯一鍵物件已存在
throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,
"phoneNumber=" + phoneNumber);
}
}
/**
*
* @methodName : checkUniqueByIdNo
* @description : 檢查idNo屬性值的唯一性
* @param idNo : 身份證號碼
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/17 1.0.0 sheng.zheng 初版
*
*/
private void checkUniqueByIdNo(String idNo) {
if (idNo.equals("")) {
// 如果為例外值
return;
}
User item = userDao.selectItemByIdNo(idNo);
if (item != null) {
// 如果唯一鍵物件已存在
throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"idNo=" + idNo);
}
}
/**
*
* @methodName : checkUniqueByOpenId
* @description : 檢查openId屬性值的唯一性
* @param openId : 微信小程式的openid
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/17 1.0.0 sheng.zheng 初版
*
*/
private void checkUniqueByOpenId(String openId) {
if (openId.equals("")) {
// 如果為例外值
return;
}
User item = userDao.selectItemByOpenId(openId);
if (item != null) {
// 如果唯一鍵物件已存在
throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"openId=" + openId);
}
}
/**
*
* @methodName : checkUniqueByWoaOpenid
* @description : 檢查woaOpenid屬性值的唯一性
* @param woaOpenid : 微信公眾號openid
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2023/05/17 1.0.0 sheng.zheng 初版
*
*/
private void checkUniqueByWoaOpenid(String woaOpenid) {
if (woaOpenid.equals("")) {
// 如果為例外值
return;
}
User item = userDao.selectItemByWoaOpenid(woaOpenid);
if (item != null) {
// 如果唯一鍵物件已存在
throw new BaseException(ExceptionCodes.OBJECT_ALREADY_EXISTS,"woaOpenid=" + woaOpenid);
}
}
如果新增物件時,需要一些內部處理,則在此處進行。如新增使用者時,需要根據當前時間生成鹽,然後將管理員輸入的明文密碼轉為加鹽Md5簽名密碼。
// 業務處理
LocalDateTime current = LocalDateTime.now();
String salt = TimeUtil.format(current, "yyyy-MM-dd HH:mm:ss");
// 明文密碼加密
String password = item.getPassword();
String encyptPassword = Md5Util.plaintPasswdToDbPasswd(password, salt, Constants.TOKEN_KEY);
item.setSalt(salt);
item.setPassword(encyptPassword);
為當前物件分配全域性ID。
Long userId = 0L;
// 獲取全域性記錄ID
Long globalRecId = tccs.getTableRecId("exa_users");
userId = globalRecId;
全域性ID的獲取使用全域性ID服務類物件TableCodeConfigService,其提供單個ID和批次ID的獲取介面。
/**
*
* @methodName : getTableRecId
* @description : 獲取指定表名的一條記錄ID
* @param tableName : 表名
* @return : 記錄ID
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2021/01/01 1.0.0 sheng.zheng 初版
*
*/
@Override
public Long getTableRecId(String tableName) {
int tableId = getTableId(tableName);
Long recId = getGlobalIdDao.getTableRecId(tableId);
return recId;
}
/**
*
* @methodName : getTableRecIds
* @description : 獲取指定表名的多條記錄ID
* @param tableName : 表名
* @param recCount : 記錄條數
* @return : 第一條記錄ID
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2021/01/01 1.0.0 sheng.zheng 初版
*
*/
@Override
public Long getTableRecIds(String tableName,int recCount) {
int tableId = getTableId(tableName);
Long recId = getGlobalIdDao.getTableRecIds(tableId,recCount);
return recId;
}
getTableId是根據資料表名稱,獲取表ID(這些表是相對固定的,可以使用快取字典來管理)。而getGlobalIdDao的方法就是呼叫資料庫的函數exa_get_global_id,獲取可用ID。
/**
*
* @methodName : getTableRecId
* @description : 獲取表ID的一個記錄ID
* @param tableId : 表ID
* @return : 記錄ID
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2021/01/01 1.0.0 sheng.zheng 初版
*
*/
@Select("SELECT exa_get_global_id(#{tableId}, 1)")
Long getTableRecId(@Param("tableId") Integer tableId);
/**
*
* @methodName : getTableRecIds
* @description : 獲取表ID的多個記錄ID
* @param tableId : 表ID
* @param count : ID個數
* @return : 開始的記錄ID
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2021/01/01 1.0.0 sheng.zheng 初版
*
*/
@Select("SELECT exa_get_global_id(#{tableId}, #{count})")
Long getTableRecIds(@Param("tableId") Integer tableId, @Param("count") Integer count);
從request物件中獲取賬號資訊,並設定物件。
// 獲取操作人賬號
String operatorName = getUserName(request);
// 設定資訊
item.setUserId(userId);
item.setOperatorName(operatorName);
呼叫Dao的insertItem方法,新增記錄。
try {
// 插入資料
userDao.insertItem(item);
} catch(Exception e) {
LogUtil.error(e);
throw new BaseException(ExceptionCodes.ADD_OBJECT_FAILED,e.getMessage());
}
新增使用者物件,不涉及快取處理。
如果有的物件,涉及全集載入,如組織樹,則新增物件時,組織樹也會變化。為了避免無效載入,使用修改標記來表示集合被修改,獲取全集時,再進行載入。這樣,連續新增物件時,不會有無效載入。快取涉及全集載入的,新增物件需設定修改標記。
如果快取不涉及全集的,則無需處理。如字典類快取,新增時不必將新物件加入快取,獲取時,根據機制,快取中不存在,會先請求資料庫,此時可以載入到快取中。
新增物件,如果是系統生成的ID,需要將ID值返回。
// 構造返回值
Map<String,Object> map = new HashMap<String,Object>();
map.put("userId", userId.toString());
return map;
對於Long型別,由於前端可能損失精度,因此使用字串型別傳遞。
(未完待續...)