实习内容:
1.定时任务与数据补全:基于 XXL-JOB 实现分布式定时任务调度,补全近半年历史操作日志数据,有效解决因网络异常导致的数据缺失问题。
业务场景;集团的4a日志半年内没有同步,这边需要把日志数据同步到集团上
首先先评估每天的数提量,因为我配照的是开发环境,所有的据量不全,让运维的同事宣询一下每天的数据量是多少?差不多是3.6w日志,那么据总量软是180*3.6=650w号数据知道数据的大概范围认后效开始确认调用天数,循环执行、最终根据实际情况,每15天循环一次,然后添加xx1-job注解。从job平台项用,补全数据朴全过程中断怎么办,可以通过Redis记录处理日期,重启后跳过,同时处理期间增加道暂间隔,减少数据库压大
合井数据库文件
//将操作日志数据同步到4A平台上,根据选取的日期,往前推15天,@XxlJob("syncLog4AJobNew")public void JobHandlerNew() throws IOException {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date curTime = null;Date endTime = null;try {curTime = sdf.parse("2024-12-10 00:00:00");endTime = sdf.parse("2025-05-08 22:00:00");} catch (ParseException e) {throw new RuntimeException(e);}// 每次间隔的时间,单位mslong interval = 24 * 60 * 60 * 1000;int num = 1;while (curTime.before(endTime)) {Date nextTime = new Date(curTime.getTime() + interval);log.info("syncLog4AJobNew-4A日志同步任务:"+nextTime);String curFileName = null;BufferedWriter bufferedWriter = null;try {// 读取需要发的日志数据//List<OperateLogDto> logs = opLogService.queryOperateLog4ANew();List<OperateLogDto> logs = opLogService.queryOperateLog4ANew(nextTime);//填加时间范围log.info("syncLog4AJobNew-4A待发送日志查询成功,获取日志信息数为:" + logs.size());if (CollectionUtils.isEmpty(logs)) {XxlJobHelper.log("无需要同步数据.");curTime = nextTime;//修改continue;}// 写入文件流int length = 0;int startId = 0, endId = 0;for (OperateLogDto logDto : logs) {if (StringUtils.isBlank(logDto.getUserCode()))continue;if (curFileName == null)curFileName = logConfig.getResourceCode() + "_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".xml";if (bufferedWriter == null) {bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(ftpConfig.getLocalPath() + curFileName), "UTF-8"));bufferedWriter.write("<?xml version='1.0' encoding='UTF-8' ?><ROOT>");}if (startId == 0)startId = logDto.getOperateLogId();String generateLog = log4AUtil.generateLog(logDto.getToken(), logDto.getOperateLogId().toString(), logDto.getDoneDate(),logDto.getUserCode(), logDto.getOperateTypeName(), logDto.getOperateTypeId(),logDto.getOperateResult(), logDto.getModuleId(), logDto.getModuleName(),logDto.getIp(), logDto.getOperateName());bufferedWriter.write(new String(generateLog.getBytes("UTF-8"), "UTF-8"));endId = logDto.getOperateLogId();// 每个文件控制在5M到10M之间length += generateLog.getBytes(StandardCharsets.UTF_8).length;//当文件长度大于5则传送数据if (length > fileLength) {bufferedWriter.write("</ROOT>");bufferedWriter.flush();// 发送本地文件到4Aboolean ss = this.sendSftp(curFileName);if (ss) {opLogService.updateLog4A(startId, endId);log.info("syncLog4AJobNew-4A日志同步任务成功,上传SFTP成功,传输数据范围为:" + startId + "-" + endId);} else {XxlJobHelper.log("4A日志同步任务失败。上传SFTP失败。");XxlJobHelper.handleFail();return;}// 初始化写入curFileName = null;bufferedWriter = null;length = 0;startId = 0;}}//最后收尾,存在数据没有发送if (bufferedWriter != null) {bufferedWriter.write("</ROOT>");bufferedWriter.flush();// 发送本地文件到4Aboolean ss = this.sendSftp(curFileName);if (ss) {opLogService.updateLog4A(startId, endId);log.info("syncLog4AJobNew-4A日志同步任务成功,上传SFTP成功,传输数据范围为:" + startId + "-" + endId);} else {XxlJobHelper.log("4A日志同步任务失败。上传SFTP失败。");XxlJobHelper.handleFail();}}XxlJobHelper.handleSuccess("4A日志同步任务成功");} catch (Exception e) {log.error("4A日志同步任务失败。" + e);XxlJobHelper.log("4A日志同步任务失败。" + e.getMessage());XxlJobHelper.handleFail();} finally {if (bufferedWriter != null)bufferedWriter.close();}curTime = nextTime;log.info("======SyncLog4AJob.JobHandlerNew()," + num + "," + sdf.format(curTime));num++;}}
2.数据安全与合规控制:通过 SpringAOP 自动脱敏敏感字段,结合 SpringSecurily+ 数据权限注解 实现细粒度访问控制,确保数据隔离与合规性。
aop切面
package com.asiainfo.osm.aop;import com.asiainfo.osm.common.result.ResultObject;
import com.asiainfo.osm.common.result.ResultPage;
import com.asiainfo.osm.common.user.dto.UserDTO;
import com.asiainfo.osm.system.dto.UserExtDTO;
import com.asiainfo.osm.system.dto.UserInfoDTO;
import com.asiainfo.osm.utils.DesensitizationUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.stream.Collectors;/*** 脱敏切面** @author renhx* @createDate 2025/5/9 19:29**/
@Aspect
@Component
public class SensitiveDataAspect {@Around("execution(* com.asiainfo.osm.system.web.UserController.getUsers(..)) || " +"execution(* com.asiainfo.osm.system.web.UserController.getUserInfo(Integer)) ||" +"execution(* com.asiainfo.osm.system.web.UserController.queryUserInfo(Integer))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {Object result = joinPoint.proceed();return processSensitiveData(result);}private Object processSensitiveData(Object obj) {if (obj == null) {return null;}// 人员信息下拉列表出参if (obj instanceof UserInfoDTO) {UserInfoDTO user = (UserInfoDTO) obj;user.setUserName(DesensitizationUtils.chineseName(user.getUserName()));user.setEmail(DesensitizationUtils.email(user.getEmail()));return user;}// 用户详情出参if (obj instanceof UserDTO) {UserDTO user = (UserDTO) obj;user.setUserName(DesensitizationUtils.chineseName(user.getUserName()));user.setPhone(DesensitizationUtils.mobilePhone(user.getPhone()));user.setEmail(DesensitizationUtils.email(user.getEmail()));return user;}// 用户列表出参if (obj instanceof UserExtDTO) {UserExtDTO user = (UserExtDTO) obj;user.setUserName(DesensitizationUtils.chineseName(user.getUserName()));user.setPhone(DesensitizationUtils.mobilePhone(user.getPhone()));user.setEmail(DesensitizationUtils.email(user.getEmail()));return user;}if (obj instanceof List) {List<?> list = (List<?>) obj;return list.stream().map(this::processSensitiveData).collect(Collectors.toList());}if (obj instanceof ResultPage) {ResultPage<?> resultPage = (ResultPage<?>) obj;List<?> pageData = resultPage.getPageData().stream().map(this::processSensitiveData).collect(Collectors.toList());return ResultPage.instance(resultPage.getTotal(),pageData);}if (obj instanceof ResultObject) {ResultObject<?> resultObject = (ResultObject<?>) obj;Object data = processSensitiveData(resultObject.getData());ResultObject<Object> resultObjectNew = new ResultObject<>();resultObjectNew.setCode(resultObject.getCode());resultObjectNew.setName(resultObject.getName());resultObjectNew.setMessage(resultObject.getMessage());resultObjectNew.setData(data);return resultObjectNew;}return obj;}
}
脱敏规则
package com.asiainfo.osm.utils;import org.apache.commons.lang.StringUtils;/*** 脱敏工具** @author renhx* @createDate 2025/5/10 8:19**/
public class DesensitizationUtils {// 姓名脱敏 (两字: 张* | 三字: 张*三 | 多字: 张**...三)public static String chineseName(String fullName) {if (StringUtils.isBlank(fullName)) {return "";}int length = fullName.length();if (length == 1) {return fullName; // 单字名不做处理}if (length == 2) {// 两字名: 张*return fullName.charAt(0) + "*";}if (length == 3) {// 三字名: 张*三return fullName.charAt(0) + "*" + fullName.charAt(2);}// 多字名: 张**...三return fullName.charAt(0) +StringUtils.repeat("*", length - 2) +fullName.charAt(length - 1);}// 手机号脱敏 (保留前3后4)public static String mobilePhone(String phone) {if (StringUtils.isBlank(phone)) {return "";}return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");}// 邮箱脱敏 (保留@前3位和域名)public static String email(String email) {if (StringUtils.isBlank(email)) {return "";}int index = email.indexOf("@");if (index <= 1) {return email;}String head = "";if (index <= 3) {head = email.substring(0,index);} else {head = email.substring(0,3);}return head + "*********" + email.substring(index);}
}
越权规则