feat(用户同步): 添加手动同步AB98用户信息功能
新增手动同步AB98用户信息接口和定时任务服务 移除Ab98ApiUtil中的main方法测试代码 修正用户地址字段映射为idCardAddress 添加/manual/**路径到安全白名单 更新测试用例中的身份证号参数
This commit is contained in:
parent
a5fc5201aa
commit
60f3595d0d
|
|
@ -0,0 +1,47 @@
|
|||
package com.agileboot.admin.controller.job;
|
||||
|
||||
import com.agileboot.admin.customize.service.job.SyncAb98UserJob;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.exception.ApiException;
|
||||
import com.agileboot.common.exception.error.ErrorCode;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 手动执行任务控制器
|
||||
* 用于手动触发定时任务,方便调试和即时执行
|
||||
* 该接口无需权限验证,路径为 /manual/** 已在SecurityConfig中配置为permitAll
|
||||
*/
|
||||
@Tag(name = "手动执行任务API", description = "手动触发定时任务执行")
|
||||
@RestController
|
||||
@RequestMapping("/manual/job")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ManualJobController {
|
||||
|
||||
private final SyncAb98UserJob syncAb98UserJob;
|
||||
|
||||
/**
|
||||
* 手动同步AB98用户信息
|
||||
* 对应定时任务:@Scheduled(cron = "0 0 2 * * ?")
|
||||
* 从汇邦云平台拉取最新用户信息进行对比更新
|
||||
*/
|
||||
@Operation(summary = "手动同步AB98用户信息", description = "从汇邦云平台同步AB98用户信息到本地数据库")
|
||||
@PostMapping("/sync-ab98-user")
|
||||
public ResponseDTO<String> syncAb98UserInfo() {
|
||||
try {
|
||||
log.info("开始手动执行AB98用户信息同步任务...");
|
||||
syncAb98UserJob.syncAb98UserInfo();
|
||||
log.info("手动执行AB98用户信息同步任务完成");
|
||||
return ResponseDTO.ok("AB98用户信息同步任务执行成功");
|
||||
} catch (Exception e) {
|
||||
log.error("手动执行AB98用户信息同步任务失败", e);
|
||||
return ResponseDTO.fail(new ApiException(ErrorCode.Internal.INTERNAL_ERROR, "AB98用户信息同步失败:" + e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ public class SecurityConfig {
|
|||
.antMatchers("/login", "/register", "/captchaImage", "/api/**", "/file/**").anonymous()
|
||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js",
|
||||
"/profile/**").permitAll()
|
||||
.antMatchers("/qywx/**", "/test/**", "/monitor/**", "/getQyUserinfo", "/getConfig").permitAll()
|
||||
.antMatchers("/manual/**", "/qywx/**", "/test/**", "/monitor/**", "/getQyUserinfo", "/getConfig").permitAll()
|
||||
// TODO this is danger.
|
||||
.antMatchers("/swagger-ui.html").anonymous()
|
||||
.antMatchers("/swagger-resources/**").anonymous()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,187 @@
|
|||
package com.agileboot.admin.customize.service.job;
|
||||
|
||||
import com.agileboot.domain.ab98.api.Ab98ApiUtil;
|
||||
import com.agileboot.domain.ab98.api.Ab98UserDto;
|
||||
import com.agileboot.domain.ab98.user.Ab98UserApplicationService;
|
||||
import com.agileboot.domain.ab98.user.command.UpdateAb98UserCommand;
|
||||
import com.agileboot.domain.ab98.user.db.Ab98UserEntity;
|
||||
import com.agileboot.domain.ab98.user.db.Ab98UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 同步ab98用户信息定时任务
|
||||
* 定时从汇邦云平台拉取用户信息,与本地数据库中的用户信息进行对比,
|
||||
* 如有差异则更新本地用户信息
|
||||
*
|
||||
* @author your-name
|
||||
* @since 2025-01-01
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SyncAb98UserJob {
|
||||
|
||||
private final Ab98UserService ab98UserService;
|
||||
|
||||
private final Ab98UserApplicationService ab98UserApplicationService;
|
||||
|
||||
private static final String APP_NAME = "wxshop";
|
||||
private static final String APP_SECRET = "34164e41f0c6694be6bbbba0dc50c14a";
|
||||
|
||||
/**
|
||||
* 定时任务:同步ab98用户信息
|
||||
* 每天凌晨2点执行,获取所有本地ab98用户信息,
|
||||
* 从汇邦云平台拉取最新用户信息进行对比更新
|
||||
*/
|
||||
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
|
||||
public void syncAb98UserInfo() {
|
||||
log.info("开始执行ab98用户信息同步任务...");
|
||||
|
||||
try {
|
||||
// 1. 获取所有本地ab98用户(只查询有身份证号的用户)
|
||||
List<Ab98UserEntity> localUserList = ab98UserService.selectAll();
|
||||
|
||||
if (localUserList == null || localUserList.isEmpty()) {
|
||||
log.info("本地数据库中未找到ab98用户,跳过同步任务.");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("本地数据库共有 {} 个ab98用户需要同步.", localUserList.size());
|
||||
|
||||
int successCount = 0;
|
||||
int skipCount = 0;
|
||||
int errorCount = 0;
|
||||
|
||||
// 2. 遍历每个用户,查询汇邦云平台的最新信息
|
||||
for (Ab98UserEntity localUser : localUserList) {
|
||||
try {
|
||||
// 跳过没有身份证号的用户(可能是测试数据或异常数据)
|
||||
if (StringUtils.isBlank(localUser.getIdnum())) {
|
||||
log.debug("用户[{}]身份证号为空,跳过同步.", localUser.getAb98UserId());
|
||||
skipCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 从汇邦云平台拉取用户信息
|
||||
Ab98UserDto remoteUserDto = Ab98ApiUtil.pullUserInfoByIdnum(
|
||||
APP_NAME,
|
||||
APP_SECRET,
|
||||
localUser.getIdnum()
|
||||
);
|
||||
|
||||
// 如果平台没有此用户信息,跳过
|
||||
if (remoteUserDto == null) {
|
||||
log.debug("汇邦云平台未找到身份证号[{}]对应的用户,跳过同步.", localUser.getIdnum());
|
||||
skipCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 对比并更新用户信息
|
||||
boolean isUpdated = compareAndUpdateUser(localUser, remoteUserDto);
|
||||
if (isUpdated) {
|
||||
successCount++;
|
||||
log.info("用户[{}]信息已更新,身份证号: {}", localUser.getAb98UserId(), localUser.getIdnum());
|
||||
} else {
|
||||
skipCount++;
|
||||
log.debug("用户[{}]信息无变化,跳过更新.", localUser.getAb98UserId());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
errorCount++;
|
||||
log.error("同步用户[{}]信息时发生错误,身份证号: {}", localUser.getAb98UserId(), localUser.getIdnum(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("ab98用户信息同步任务执行完成. 总数: {}, 成功: {}, 跳过: {}, 错误: {}",
|
||||
localUserList.size(), successCount, skipCount, errorCount);
|
||||
|
||||
} catch (Exception globalException) {
|
||||
// 记录整个定时任务的全局错误
|
||||
log.error("执行ab98用户信息同步任务时发生全局错误:", globalException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对比并更新用户信息
|
||||
*
|
||||
* @param localUser 本地用户实体
|
||||
* @param remoteUserDto 汇邦云平台用户信息
|
||||
* @return 是否进行了更新
|
||||
*/
|
||||
private boolean compareAndUpdateUser(Ab98UserEntity localUser, Ab98UserDto remoteUserDto) {
|
||||
boolean isUpdated = false;
|
||||
|
||||
// 对比需要更新的字段
|
||||
UpdateAb98UserCommand updateCommand = new UpdateAb98UserCommand();
|
||||
updateCommand.setAb98UserId(localUser.getAb98UserId());
|
||||
|
||||
// 对比并更新姓名
|
||||
if (StringUtils.isNotBlank(remoteUserDto.getRealName())
|
||||
&& !StringUtils.equals(remoteUserDto.getRealName(), localUser.getName())) {
|
||||
updateCommand.setName(remoteUserDto.getRealName());
|
||||
isUpdated = true;
|
||||
}
|
||||
|
||||
// 对比并更新手机号
|
||||
if (StringUtils.isNotBlank(remoteUserDto.getPhone())
|
||||
&& !StringUtils.equals(remoteUserDto.getPhone(), localUser.getTel())) {
|
||||
updateCommand.setTel(remoteUserDto.getPhone());
|
||||
isUpdated = true;
|
||||
}
|
||||
|
||||
// 对比并更新性别
|
||||
if (StringUtils.isNotBlank(remoteUserDto.getSex())
|
||||
&& !StringUtils.equals(remoteUserDto.getSex(), localUser.getSex())) {
|
||||
updateCommand.setSex(remoteUserDto.getSex());
|
||||
isUpdated = true;
|
||||
}
|
||||
|
||||
// 对比并更新身份证正面
|
||||
if (StringUtils.isNotBlank(remoteUserDto.getIdCardFront())
|
||||
&& !StringUtils.equals(remoteUserDto.getIdCardFront(), localUser.getIdcardFront())) {
|
||||
updateCommand.setIdcardFront(remoteUserDto.getIdCardFront());
|
||||
isUpdated = true;
|
||||
}
|
||||
|
||||
// 对比并更新身份证背面
|
||||
if (StringUtils.isNotBlank(remoteUserDto.getIdCardBack())
|
||||
&& !StringUtils.equals(remoteUserDto.getIdCardBack(), localUser.getIdcardBack())) {
|
||||
updateCommand.setIdcardBack(remoteUserDto.getIdCardBack());
|
||||
isUpdated = true;
|
||||
}
|
||||
|
||||
// 对比并更新人脸照片
|
||||
if (StringUtils.isNotBlank(remoteUserDto.getFacePicture())
|
||||
&& !StringUtils.equals(remoteUserDto.getFacePicture(), localUser.getFaceImg())) {
|
||||
updateCommand.setFaceImg(remoteUserDto.getFacePicture());
|
||||
isUpdated = true;
|
||||
}
|
||||
|
||||
// 对比并更新地址
|
||||
if (StringUtils.isNotBlank(remoteUserDto.getIdCardAddress())
|
||||
&& !StringUtils.equals(remoteUserDto.getIdCardAddress(), localUser.getAddress())) {
|
||||
updateCommand.setAddress(remoteUserDto.getIdCardAddress());
|
||||
isUpdated = true;
|
||||
}
|
||||
|
||||
// 如果有字段需要更新,则执行更新
|
||||
if (isUpdated) {
|
||||
try {
|
||||
ab98UserApplicationService.updateUser(updateCommand);
|
||||
log.debug("用户[{}]信息更新成功", localUser.getAb98UserId());
|
||||
} catch (Exception e) {
|
||||
log.error("用户[{}]信息更新失败", localUser.getAb98UserId(), e);
|
||||
throw e; // 抛出异常,触发事务回滚
|
||||
}
|
||||
}
|
||||
|
||||
return isUpdated;
|
||||
}
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ public class CabinetControllerTest {
|
|||
|
||||
@Test
|
||||
public void testPullUserInfoByIdnum() {
|
||||
String IdNum = "452526196206215829";
|
||||
String IdNum = "";
|
||||
Ab98UserDto ab98UserDto = Ab98ApiUtil.pullUserInfoByIdnum("wxshop", "34164e41f0c6694be6bbbba0dc50c14a", IdNum);
|
||||
log.info("ab98UserDto:{}", JSONUtil.toJsonStr(ab98UserDto));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,11 +208,6 @@ public class Ab98ApiUtil {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Ab98UserDto userDto = pullUserInfoByIdnum("wxshop", "34164e41f0c6694be6bbbba0dc50c14a", "450981199505186050");
|
||||
log.info("拉取用户信息: {}", JSONUtil.toJsonStr(userDto));
|
||||
}
|
||||
|
||||
// 基础响应对象
|
||||
@Data
|
||||
public static class BaseResponse {
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ public class Ab98UserApplicationService {
|
|||
addAb98UserCommand.setIdcardFront(ab98UserDto.getIdCardFront());
|
||||
addAb98UserCommand.setIdcardBack(ab98UserDto.getIdCardBack());
|
||||
addAb98UserCommand.setFaceImg(ab98UserDto.getFacePicture());
|
||||
addAb98UserCommand.setAddress(ab98UserDto.getAddress());
|
||||
addAb98UserCommand.setAddress(ab98UserDto.getIdCardAddress());
|
||||
addAb98UserCommand.setRegistered(true);
|
||||
addAb98UserCommand.initBaseEntity();
|
||||
return addAb98UserCommand;
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ public class WxUserApplicationService {
|
|||
addCommand.setIdcardFront(ab98UserDto.getIdCardFront());
|
||||
addCommand.setIdcardBack(ab98UserDto.getIdCardBack());
|
||||
addCommand.setFaceImg(ab98UserDto.getFacePicture());
|
||||
addCommand.setAddress(ab98UserDto.getAddress());
|
||||
addCommand.setAddress(ab98UserDto.getIdCardAddress());
|
||||
addCommand.setRegistered(true);
|
||||
addCommand.initBaseEntity();
|
||||
|
||||
|
|
@ -345,7 +345,7 @@ public class WxUserApplicationService {
|
|||
updateCommand.setIdcardFront(ab98UserDto.getIdCardFront());
|
||||
updateCommand.setIdcardBack(ab98UserDto.getIdCardBack());
|
||||
updateCommand.setFaceImg(ab98UserDto.getFacePicture());
|
||||
updateCommand.setAddress(ab98UserDto.getAddress());
|
||||
updateCommand.setAddress(ab98UserDto.getIdCardAddress());
|
||||
updateCommand.setRegistered(true);
|
||||
ab98UserApplicationService.updateUser(updateCommand);
|
||||
ab98UserEntity = existingUser;
|
||||
|
|
|
|||
Loading…
Reference in New Issue