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