24 changed files with 617 additions and 46 deletions
@ -0,0 +1,17 @@
|
||||
package com.ruoyi.common.annotation; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.FIELD) |
||||
public @interface PrimaryKey { |
||||
|
||||
|
||||
/** |
||||
* 主键类型,默认是 Long |
||||
*/ |
||||
Class<?> type() default Long.class; |
||||
} |
@ -0,0 +1,57 @@
|
||||
package com.ruoyi.common.utils; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import com.ruoyi.common.annotation.PrimaryKey; |
||||
|
||||
import java.lang.reflect.Field; |
||||
|
||||
public class EntityUtils { |
||||
|
||||
/** |
||||
* 获取实体类的主键值 |
||||
* |
||||
* @param entity 实体对象 |
||||
* @return 主键值 |
||||
*/ |
||||
public static Long getPrimaryKeyValue(Object entity) { |
||||
for (Field field : entity.getClass().getDeclaredFields()) { |
||||
if (field.isAnnotationPresent(PrimaryKey.class)) { |
||||
field.setAccessible(true); |
||||
try { |
||||
return (Long) field.get(entity); // 假设主键类型为 Long
|
||||
} catch (IllegalAccessException e) { |
||||
throw new RuntimeException("无法访问主键字段的值", e); |
||||
} |
||||
} |
||||
} |
||||
throw new RuntimeException("实体类中未找到 @PrimaryKey 标记的字段"); |
||||
} |
||||
|
||||
/** |
||||
* 获取实体类对应的表名 |
||||
* |
||||
* @param entity 实体对象 |
||||
* @return 表名 |
||||
*/ |
||||
public static String getTableName(Object entity) { |
||||
TableName tableNameAnnotation = entity.getClass().getAnnotation(TableName.class); |
||||
return tableNameAnnotation != null ? tableNameAnnotation.value() : entity.getClass().getSimpleName(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 判断实体类是否包含 @PrimaryKey 注解的字段 |
||||
* |
||||
* @param entity 实体对象 |
||||
* @return 是否包含主键注解 |
||||
*/ |
||||
public static boolean hasPrimaryKeyAnnotation(Object entity) { |
||||
for (Field field : entity.getClass().getDeclaredFields()) { |
||||
if (field.isAnnotationPresent(PrimaryKey.class)) { |
||||
return true; // 找到带有 @PrimaryKey 的字段
|
||||
} |
||||
} |
||||
return false; // 未找到
|
||||
} |
||||
} |
||||
|
@ -0,0 +1,118 @@
|
||||
package com.ruoyi.framework.aspectj; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import com.ruoyi.common.core.domain.BaseEntity; |
||||
import com.ruoyi.common.utils.EntityUtils; |
||||
import com.ruoyi.common.utils.SecurityUtils; |
||||
import com.ruoyi.system.domain.SysFieldChangeLog; |
||||
import com.ruoyi.system.service.CommonService; |
||||
import com.ruoyi.system.service.SysFieldChangeLogService; |
||||
import org.aspectj.lang.ProceedingJoinPoint; |
||||
import org.aspectj.lang.annotation.Around; |
||||
import org.aspectj.lang.annotation.Aspect; |
||||
import org.aspectj.lang.annotation.Pointcut; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.lang.reflect.Field; |
||||
import java.util.Date; |
||||
import java.util.Map; |
||||
import java.util.Objects; |
||||
|
||||
@Aspect |
||||
@Component |
||||
public class FieldChangeLogAspect { |
||||
|
||||
@Autowired |
||||
private SysFieldChangeLogService fieldChangeLogService; |
||||
|
||||
@Autowired |
||||
private CommonService commonService; |
||||
|
||||
// 拦截 update 方法
|
||||
@Pointcut("execution(* com.ruoyi.*.service.*.update*(..))") |
||||
public void updatePointCut() {} |
||||
|
||||
@Around("updatePointCut()") |
||||
public Object logFieldChanges(ProceedingJoinPoint point) throws Throwable { |
||||
Object[] args = point.getArgs(); |
||||
if (args.length == 0 || args[0] == null) { |
||||
return point.proceed(); // 跳过无效参数
|
||||
} |
||||
Object updatedEntity = args[0]; |
||||
|
||||
// 判断是否包含 @PrimaryKey 注解
|
||||
if (!EntityUtils.hasPrimaryKeyAnnotation(updatedEntity)) { |
||||
return point.proceed(); // 如果没有主键注解,跳过日志记录
|
||||
} |
||||
|
||||
// 使用工具类获取主键值和表名
|
||||
Long recordId = EntityUtils.getPrimaryKeyValue(updatedEntity); |
||||
String tableName = EntityUtils.getTableName(updatedEntity); |
||||
|
||||
// 动态查询原始记录
|
||||
Map<String, Object> originalRecord = commonService.findById(tableName, recordId); |
||||
|
||||
// 记录字段变更日志
|
||||
saveFieldChangeLogs(originalRecord, updatedEntity, tableName, recordId); |
||||
|
||||
return point.proceed(); // 执行原方法
|
||||
} |
||||
|
||||
|
||||
private void saveFieldChangeLogs(Map<String, Object> original, Object updatedEntity, String tableName, Long recordId) { |
||||
Long userId = SecurityUtils.getLoginUser().getUserId(); |
||||
String username = SecurityUtils.getLoginUser().getUsername(); |
||||
|
||||
for (Field field : updatedEntity.getClass().getDeclaredFields()) { |
||||
field.setAccessible(true); |
||||
|
||||
try { |
||||
String fieldName = field.getName(); |
||||
|
||||
String dbFieldName = toUnderlineCase(fieldName); // 将驼峰命名转换为下划线命名
|
||||
|
||||
|
||||
Object oldValue = original.get(dbFieldName); // 获取原始值
|
||||
Object newValue = field.get(updatedEntity); // 获取新值
|
||||
|
||||
// 如果原值和新值相等,则跳过
|
||||
if ((newValue == null) || (oldValue != null && oldValue.equals(newValue))) { |
||||
continue; |
||||
} |
||||
|
||||
if (!Objects.equals(oldValue, newValue)) { |
||||
SysFieldChangeLog log = new SysFieldChangeLog(); |
||||
log.setTableName(tableName); |
||||
log.setRecordId(recordId); |
||||
log.setFieldName(fieldName); |
||||
log.setOldValue(oldValue != null ? oldValue.toString() : null); |
||||
log.setNewValue(newValue != null ? newValue.toString() : null); |
||||
log.setUserId(userId); |
||||
log.setUsername(username); |
||||
log.setOperationType("修改"); |
||||
log.setCreateTime(new Date()); |
||||
fieldChangeLogService.save(log); |
||||
} |
||||
} catch (IllegalAccessException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 将驼峰命名转换为下划线命名 |
||||
* |
||||
* @param camelCase 驼峰格式字符串(如 contactName) |
||||
* @return 下划线格式字符串(如 contact_name) |
||||
*/ |
||||
public static String toUnderlineCase(String camelCase) { |
||||
if (camelCase == null || camelCase.isEmpty()) { |
||||
return ""; |
||||
} |
||||
// 使用正则表达式匹配驼峰命名的规则
|
||||
return camelCase.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase(); |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,73 @@
|
||||
package com.ruoyi.framework.aspectj; |
||||
|
||||
import com.ruoyi.common.utils.EntityUtils; |
||||
import com.ruoyi.common.utils.SecurityUtils; |
||||
import com.ruoyi.system.domain.SysFieldChangeLog; |
||||
import com.ruoyi.system.service.CommonService; |
||||
import com.ruoyi.system.service.SysFieldChangeLogService; |
||||
import org.aspectj.lang.ProceedingJoinPoint; |
||||
import org.aspectj.lang.annotation.Around; |
||||
import org.aspectj.lang.annotation.Aspect; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.util.Date; |
||||
import java.util.Map; |
||||
@Aspect |
||||
@Component |
||||
public class LogDeleteOperation { |
||||
|
||||
@Autowired |
||||
private CommonService commonService; |
||||
|
||||
@Autowired |
||||
private SysFieldChangeLogService fieldChangeLogService; |
||||
|
||||
@Around("execution(* com.ruoyi.*.service.*.delete*(..))") |
||||
public Object logDeleteOperation(ProceedingJoinPoint point) throws Throwable { |
||||
Object[] args = point.getArgs(); |
||||
if (args.length == 0) { |
||||
return point.proceed(); |
||||
} |
||||
|
||||
Object entityToDelete = args[0]; |
||||
|
||||
// 判断是否包含 @PrimaryKey 注解
|
||||
if (!EntityUtils.hasPrimaryKeyAnnotation(entityToDelete)) { |
||||
return point.proceed(); // 如果没有主键注解,跳过日志记录
|
||||
} |
||||
|
||||
|
||||
// 使用工具类获取主键值和表名
|
||||
Long recordId = EntityUtils.getPrimaryKeyValue(entityToDelete); |
||||
String tableName = EntityUtils.getTableName(entityToDelete ); |
||||
|
||||
// 动态查询记录
|
||||
Map<String, Object> originalRecord = commonService.findById(tableName, recordId); |
||||
|
||||
// 保存删除日志
|
||||
saveDeleteLogs(originalRecord, tableName, recordId); |
||||
|
||||
return point.proceed(); |
||||
} |
||||
|
||||
private void saveDeleteLogs(Map<String, Object> original, String tableName, Long recordId) { |
||||
Long userId = SecurityUtils.getLoginUser().getUserId(); |
||||
String username = SecurityUtils.getLoginUser().getUsername(); |
||||
|
||||
for (Map.Entry<String, Object> entry : original.entrySet()) { |
||||
SysFieldChangeLog log = new SysFieldChangeLog(); |
||||
log.setTableName(tableName); |
||||
log.setRecordId(recordId); |
||||
log.setFieldName(entry.getKey()); |
||||
log.setOldValue(entry.getValue() != null ? entry.getValue().toString() : null); |
||||
log.setNewValue(null); // 删除操作,新值为空
|
||||
log.setUserId(userId); |
||||
log.setUsername(username); |
||||
log.setOperationType("删除"); |
||||
log.setCreateTime(new Date()); |
||||
fieldChangeLogService.save(log); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,66 @@
|
||||
package com.ruoyi.system.domain; |
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity; |
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
|
||||
/** |
||||
* 字段变更日志表 |
||||
* @TableName sys_field_change_log |
||||
*/ |
||||
@Data |
||||
public class SysFieldChangeLog implements Serializable { |
||||
/** |
||||
* 日志ID |
||||
*/ |
||||
private Long id; |
||||
|
||||
/** |
||||
* 表名 |
||||
*/ |
||||
private String tableName; |
||||
|
||||
/** |
||||
* 字段名 |
||||
*/ |
||||
private String fieldName; |
||||
|
||||
/** |
||||
* 修改前值或删除前的值 |
||||
*/ |
||||
private String oldValue; |
||||
|
||||
/** |
||||
* 修改后值(删除时为空) |
||||
*/ |
||||
private String newValue; |
||||
|
||||
/* |
||||
记录的主键值 |
||||
*/ |
||||
private Long recordId; |
||||
|
||||
/** |
||||
* 操作用户ID |
||||
*/ |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 操作用户名 |
||||
*/ |
||||
private String username; |
||||
|
||||
/** |
||||
* 操作时间 |
||||
*/ |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 操作类型 |
||||
*/ |
||||
private String operationType; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,7 @@
|
||||
package com.ruoyi.system.domain; |
||||
|
||||
import lombok.Data; |
||||
|
||||
@Data |
||||
public class SysUserDept { |
||||
} |
@ -0,0 +1,16 @@
|
||||
package com.ruoyi.system.mapper; |
||||
|
||||
import org.apache.ibatis.annotations.Mapper; |
||||
import org.apache.ibatis.annotations.Param; |
||||
import org.apache.ibatis.annotations.Select; |
||||
|
||||
import java.util.Map; |
||||
import java.util.Objects; |
||||
|
||||
@Mapper |
||||
public interface CommonMapper { |
||||
|
||||
@Select("SELECT * FROM `${tableName}` WHERE id = #{id}") |
||||
Map<String, Object> selectById(@Param("tableName") String tableName, @Param("id") Long id); |
||||
|
||||
} |
@ -0,0 +1,20 @@
|
||||
package com.ruoyi.system.mapper; |
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
import com.ruoyi.system.domain.Enterprises; |
||||
import com.ruoyi.system.domain.SysFieldChangeLog; |
||||
import org.apache.ibatis.annotations.Mapper; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 操作日志Mapper接口 |
||||
* |
||||
* @author ruoyi |
||||
* @date 2025-01-08 |
||||
*/ |
||||
@Mapper |
||||
public interface SysFieldChangeLogMapper extends BaseMapper<SysFieldChangeLog> |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,17 @@
|
||||
package com.ruoyi.system.mapper; |
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
import com.ruoyi.system.domain.SysUserDept; |
||||
import org.apache.ibatis.annotations.Mapper; |
||||
|
||||
/** |
||||
* 操作日志Mapper接口 |
||||
* |
||||
* @author ruoyi |
||||
* @date 2025-01-08 |
||||
*/ |
||||
@Mapper |
||||
public interface SysUserDeptMapper extends BaseMapper<SysUserDept> |
||||
{ |
||||
|
||||
} |
@ -0,0 +1,7 @@
|
||||
package com.ruoyi.system.service; |
||||
|
||||
import java.util.Map; |
||||
|
||||
public interface CommonService { |
||||
Map<String, Object> findById(String tableName, Long id); |
||||
} |
@ -0,0 +1,8 @@
|
||||
package com.ruoyi.system.service; |
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
import com.ruoyi.system.domain.SysFieldChangeLog; |
||||
import com.ruoyi.system.domain.SysUserDept; |
||||
|
||||
public interface ISysUserDeptService extends IService<SysUserDept> { |
||||
} |
@ -0,0 +1,7 @@
|
||||
package com.ruoyi.system.service; |
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
import com.ruoyi.system.domain.SysFieldChangeLog; |
||||
|
||||
public interface SysFieldChangeLogService extends IService<SysFieldChangeLog> { |
||||
} |
@ -0,0 +1,21 @@
|
||||
package com.ruoyi.system.service.impl; |
||||
|
||||
import com.ruoyi.system.mapper.CommonMapper; |
||||
import com.ruoyi.system.service.CommonService; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.util.Map; |
||||
|
||||
@Service |
||||
public class CommonServiceImpl implements CommonService |
||||
{ |
||||
|
||||
@Autowired |
||||
private CommonMapper commonMapper; |
||||
|
||||
@Override |
||||
public Map<String, Object> findById(String tableName, Long id) { |
||||
return commonMapper.selectById(tableName, id); |
||||
} |
||||
} |
@ -0,0 +1,37 @@
|
||||
package com.ruoyi.system.service.impl; |
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import com.ruoyi.common.exception.ServiceException; |
||||
import com.ruoyi.system.domain.EnterpriseUsers; |
||||
import com.ruoyi.system.domain.Enterprises; |
||||
import com.ruoyi.system.domain.SysFieldChangeLog; |
||||
import com.ruoyi.system.domain.vo.EnterpriseUserInsertVo; |
||||
import com.ruoyi.system.domain.vo.LabelValueVo; |
||||
import com.ruoyi.system.mapper.EnterpriseUsersMapper; |
||||
import com.ruoyi.system.mapper.EnterprisesMapper; |
||||
import com.ruoyi.system.mapper.SysFieldChangeLogMapper; |
||||
import com.ruoyi.system.service.IEnterprisesService; |
||||
import com.ruoyi.system.service.SysFieldChangeLogService; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.BeanUtils; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Service; |
||||
import org.springframework.transaction.annotation.Transactional; |
||||
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 企业Service业务层处理 |
||||
* |
||||
* @author ruoyi |
||||
* @date 2025-01-08 |
||||
*/ |
||||
@Service |
||||
@Slf4j |
||||
public class SysFieldChangeLogServiceImpl extends ServiceImpl<SysFieldChangeLogMapper, SysFieldChangeLog> implements SysFieldChangeLogService |
||||
{ |
||||
} |
@ -0,0 +1,23 @@
|
||||
package com.ruoyi.system.service.impl; |
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import com.ruoyi.system.domain.SysFieldChangeLog; |
||||
import com.ruoyi.system.domain.SysUserDept; |
||||
import com.ruoyi.system.mapper.SysFieldChangeLogMapper; |
||||
import com.ruoyi.system.mapper.SysUserDeptMapper; |
||||
import com.ruoyi.system.service.ISysUserDeptService; |
||||
import com.ruoyi.system.service.SysFieldChangeLogService; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
/** |
||||
* 企业Service业务层处理 |
||||
* |
||||
* @author ruoyi |
||||
* @date 2025-01-08 |
||||
*/ |
||||
@Service |
||||
@Slf4j |
||||
public class SysUserDeptServiceImpl extends ServiceImpl<SysUserDeptMapper, SysUserDept> implements ISysUserDeptService |
||||
{ |
||||
} |
Loading…
Reference in new issue