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