功能需求:今天在禅道上收到产品经理的新提功能需求:要求保存业务记录数据的前后变更情况。
核心功能点: 业务数据变更记录表、自定义注解、Spring AOP 。
数据库设计:
DROP TABLE IF EXISTS DATA_DEAL_RECORD;
CREATE TABLE DATA_DEAL_RECORD(
ID VARCHAR(64) NOT NULL COMMENT '主键' ,
TID VARCHAR(100) COMMENT '地市代码' ,
REGION_CODE VARCHAR(100) COMMENT '区域编码' ,
DATA_TABLE_NAME VARCHAR(100) COMMENT '数据类型表名' ,
DATA_ORIGIN_ID VARCHAR(100) COMMENT '数据项Id' ,
RECORD_TYPE VARCHAR(10) COMMENT '记录类型' ,
DEAL_TYPE VARCHAR(10) COMMENT '处理类型' ,
YWBH VARCHAR(100) COMMENT '业务编号/主键' ,
YWBJSJ DATETIME COMMENT '业务办结时间' ,
SJCRSJ DATETIME COMMENT '数据存入时间' ,
SOURCE_TYPE VARCHAR(10) COMMENT '来源渠道' ,
DATA_CONTENT VARCHAR(2000) COMMENT '数据内容' ,
ORI_DATA_CONTENT VARCHAR(2000) COMMENT '源数据内容' ,
SYS_CREATE_TIME DATETIME COMMENT '创建时间' ,
SYS_CREATE_ID VARCHAR(32) COMMENT '创建人id' ,
ORG_ID VARCHAR(64) COMMENT '创建者机构id' ,
SYS_CREATE_ORG_TYPE VARCHAR(10) COMMENT '创建者机构类型' ,
SYS_UPDATE_TIME DATETIME COMMENT '修改时间' ,
SYS_UPDATE_ID VARCHAR(32) COMMENT '修改人id' ,
SYS_DELETE_TIME DATETIME COMMENT '删除时间' ,
SYS_DELETE_ID VARCHAR(32) COMMENT '删除人id' ,
IS_DELETE VARCHAR(10) COMMENT '逻辑删除标志' ,
PRIMARY KEY (ID)
) COMMENT = '数据的处理记录';
Java 核心功能代码设计
自定义注解:RecordChange
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
improt com.zzg.common.dao.BaseDao;
import com.zzg.common.enums.DealType
import com.zzg.common.model.po.BasePO;
@Documented
@RetentionPolicy(value =RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RecordChange{
/**
* 操作类型:1 新增, 2 修改
**/
DealType opera();
/**
* dao 接口实现
**/
Class extends BaseDao extends BasePO> mapper();
}
Spring AOP代码:RecordChangeAspectj
@Component
@Aspect
public class RecordChangeAspectj{
public static final Logger LOG = LoggerFactory.getLogger(RecordChangeAspectj.class);
@Resource
private IDataDealRecordDao dataDealRecordDao;
public RecordChangeAspectj() {
}
@Point("annotation(com.zzg.common.annotation.RecordChange)")
public void around(){}
@Around("around()")
public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable{
LOG.info("开始处理@RecordChange注解")
// 获取请求参数
Object obj = getArg(joinPoint);
// 获取RecordChange 注解
RecordChange recordChange = getRecordChange(joinPoint);
if(ObjectUtils.isNull(recordChange)) {
throw new CommonException("缺少RecordChange 注解标签")
}
// 获取操作类型
DealType operate = getOpera(recordChange);
// 获取Dao接口定义
Class dao = getDao(recordChange);
// 上次指定记录变更内容
BasePo oriDataContent = null;
// 请求参数BO 转PO
BasePO basePO = BeanCopierUtil.copy(obj, BasePO.class);
// 通过Spring 容器工具类, 获取Dao接口实现类, 通过Class Dao接口名
BaseDao baseDao = (BaseDao) SpringContextUtil.getBean(dao);
if(ObjectUtils.isNull(baseDao )) {
throw new CommonException("为找到指定Dao接口实现类")
}
// 获取操作业务实体表面
String tableName = baseDao.getTableName();
// 新增
if(operate.equal(DealType.Add)){
addRecord(tableName, DealType.Add.getCode(), SourceType.Two.getCode(), obj, oriDataContent);
}
// 变更
if(operate.equal(DealType.Update)){
String id = basePO.getId();
if(StringUtils.isEmpty(id)){
throw new CommonException("缺少Id 参数")
}
oriDataContent =(BasePO)baseDao.selectById(id);
addRecord(tableName, DealType.Add.getCode(), SourceType.Two.getCode(), obj, oriDataContent);
}
// 继续执行方法请求
return joinPoint.proceed();
}
private Object getArg(ProceedingJoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
if(ObjectUtils.isNull(args) || args.length < 1) {
// 抛出自定义异常
throw new CommonException("请求参数缺失");
}
return args[0];
}
private RecordChange getRecordChange(ProceedingJoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature )signature;
Method method = methodSignature.getMethod();
if(ObjectUtils.nonNull(method )){
return method.getAnnotation(RecordChange.class);
}
return null;
}
private DealType getOpera(RecordChange recordChange) {
return recordChange.opera();
}
private Class getDao(RecordChange recordChange) {
return recordChange.mapper();
}
private void addRecord(String tableName, String dealType, String sourceType, BasePO basePO, Object dataContent, BasePO oriDataContent ){
DataDealRecordPO dataDealRecordPO = new DataDealRecordPO ();
dataDealRecordPO.setDataTableName(tableName);
dataDealRecordPO.setDataOriginId(basePO.getId());
dataDealRecordPO.setRecordType(RecordType.Two.getCode());
dataDealRecordPO.setDealType(dealType);
dataDealRecordPO.setYwbjsj(basePO.getYwbjsj());
dataDealRecordPO.setSjcrsj(basePO.getSjcrsj());
dataDealRecordPO.setSourceType(sourceType);
dataDealRecordPO.setTid(basePO.getTid());
dataDealRecordPO.setRegionCode(basePO.getRegionCode());
if(Objects.nonNull(dataContent)) {
dataDealRecordPO.setDataContent(JacksonUtil.fromObjectToJson(dataContent))
}
if(Objects.nonNull(oriDataContent )) {
dataDealRecordPO.setOriDataContent(JacksonUtil.fromObjectToJson(oriDataContent ))
}
dataDealRecordDao.insert(dataDealRecordPO);
}
}
在业务服务实现类中添加RecordChange 注解,重点关注新增方法和修改方法
在房屋业务服务实现类:HouseServiceImpl.java
@Override
@Transactional(rollbackFor = Exception.class)
@RecordChange(opera = DealType.Update, mapper=IHouseDao.class)
public boolean updateById(HouseBO houseBO){
return houseDao.updateById(BeanCopierUtil.copy(houseBO, HousePO.class));
}
@Override
@Transactional(rollbackFor = Exception.class)
@RecordChange(opera = DealType.Add, mapper=IHouseDao.class)
public String saveGeneratedId(HouseBO houseBO){
return houseDao.insertGeneratedId(BeanCopierUtil.copy(houseBO, HousePO.class));
}