feat(user): 添加用户数据同步到 websopy 功能
- 新增 websopyUrl 配置项用于指定同步服务地址 - 创建 UserSyncService 服务实现用户数据同步逻辑 - 在 UserController 中添加用户新增时同步到 websopy 的功能 - 在 WxLoginController 和 WxOfficialController 中集成用户同步 - 实现单个用户同步和批量用户同步的功能 - 添加同步状态日志记录和错误处理机制
This commit is contained in:
@@ -92,6 +92,11 @@ public class ConfigProperties {
|
|||||||
*/
|
*/
|
||||||
private String serverUrl;
|
private String serverUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* websopy 服务地址(用于同步用户数据)
|
||||||
|
*/
|
||||||
|
private String websopyUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 阿里云存储 OSS
|
* 阿里云存储 OSS
|
||||||
* Endpoint
|
* Endpoint
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ public class UserController extends BaseController {
|
|||||||
private ConfigProperties configProperties;
|
private ConfigProperties configProperties;
|
||||||
@Resource
|
@Resource
|
||||||
private LoginRecordService loginRecordService;
|
private LoginRecordService loginRecordService;
|
||||||
|
@Resource
|
||||||
|
private UserSyncService userSyncService;
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:auth:user')")
|
@PreAuthorize("hasAuthority('sys:auth:user')")
|
||||||
@Operation(summary = "分页查询用户")
|
@Operation(summary = "分页查询用户")
|
||||||
@@ -142,6 +144,8 @@ public class UserController extends BaseController {
|
|||||||
return fail("该手机号码已存在");
|
return fail("该手机号码已存在");
|
||||||
}
|
}
|
||||||
if (userService.saveUser(user)) {
|
if (userService.saveUser(user)) {
|
||||||
|
// 同步到 websopy
|
||||||
|
userSyncService.syncUserToWebsopy(user);
|
||||||
return success("添加成功", user.getUserId());
|
return success("添加成功", user.getUserId());
|
||||||
}
|
}
|
||||||
return fail("添加失败");
|
return fail("添加失败");
|
||||||
@@ -159,6 +163,8 @@ public class UserController extends BaseController {
|
|||||||
return fail("该手机号码已存在");
|
return fail("该手机号码已存在");
|
||||||
}
|
}
|
||||||
if (userService.saveUser(user)) {
|
if (userService.saveUser(user)) {
|
||||||
|
// 同步到 websopy
|
||||||
|
userSyncService.syncUserToWebsopy(user);
|
||||||
return success("添加成功", user.getUserId());
|
return success("添加成功", user.getUserId());
|
||||||
}
|
}
|
||||||
return fail("添加失败");
|
return fail("添加失败");
|
||||||
@@ -181,6 +187,8 @@ public class UserController extends BaseController {
|
|||||||
userList.removeIf(d -> phoneCollect.containsKey(d.getPhone()));
|
userList.removeIf(d -> phoneCollect.containsKey(d.getPhone()));
|
||||||
|
|
||||||
if (userService.saveBatch(userList)) {
|
if (userService.saveBatch(userList)) {
|
||||||
|
// 同步到 websopy(异步处理,不阻塞响应)
|
||||||
|
userList.forEach(user -> userSyncService.syncUserToWebsopy(user));
|
||||||
return success("添加成功");
|
return success("添加成功");
|
||||||
}
|
}
|
||||||
return fail("添加失败");
|
return fail("添加失败");
|
||||||
@@ -201,6 +209,8 @@ public class UserController extends BaseController {
|
|||||||
userParam.setLimit(500L);
|
userParam.setLimit(500L);
|
||||||
final PageResult<User> result = userService.pageRel(userParam);
|
final PageResult<User> result = userService.pageRel(userParam);
|
||||||
final Set<Integer> collect = result.getList().stream().map(User::getUserId).collect(Collectors.toSet());
|
final Set<Integer> collect = result.getList().stream().map(User::getUserId).collect(Collectors.toSet());
|
||||||
|
// 同步到 websopy(异步处理,不阻塞响应)
|
||||||
|
userList.forEach(user -> userSyncService.syncUserToWebsopy(user));
|
||||||
return success("添加成功", collect);
|
return success("添加成功", collect);
|
||||||
}
|
}
|
||||||
return fail("添加失败");
|
return fail("添加失败");
|
||||||
@@ -511,6 +521,7 @@ public class UserController extends BaseController {
|
|||||||
// 保存新用户(逐个保存以处理角色关系)
|
// 保存新用户(逐个保存以处理角色关系)
|
||||||
int successCount = 0;
|
int successCount = 0;
|
||||||
List<String> failedUsers = new ArrayList<>();
|
List<String> failedUsers = new ArrayList<>();
|
||||||
|
List<User> successUsers = new ArrayList<>();
|
||||||
|
|
||||||
for (User user : users) {
|
for (User user : users) {
|
||||||
try {
|
try {
|
||||||
@@ -524,6 +535,7 @@ public class UserController extends BaseController {
|
|||||||
userRoleService.saveBatch(user.getUserId(), roleIds);
|
userRoleService.saveBatch(user.getUserId(), roleIds);
|
||||||
}
|
}
|
||||||
successCount++;
|
successCount++;
|
||||||
|
successUsers.add(user);
|
||||||
} else {
|
} else {
|
||||||
failedUsers.add(user.getUsername());
|
failedUsers.add(user.getUsername());
|
||||||
}
|
}
|
||||||
@@ -534,6 +546,11 @@ public class UserController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 同步成功导入的用户到 websopy
|
||||||
|
for (User successUser : successUsers) {
|
||||||
|
userSyncService.syncUserToWebsopy(successUser);
|
||||||
|
}
|
||||||
|
|
||||||
// 构建返回消息
|
// 构建返回消息
|
||||||
StringBuilder message = new StringBuilder();
|
StringBuilder message = new StringBuilder();
|
||||||
if (successCount > 0) {
|
if (successCount > 0) {
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ public class WxLoginController extends BaseController {
|
|||||||
private ConfigProperties config;
|
private ConfigProperties config;
|
||||||
@Resource
|
@Resource
|
||||||
private UserRefereeService userRefereeService;
|
private UserRefereeService userRefereeService;
|
||||||
|
@Resource
|
||||||
|
private UserSyncService userSyncService;
|
||||||
|
|
||||||
public WxLoginController(StringRedisTemplate redisTemplate) {
|
public WxLoginController(StringRedisTemplate redisTemplate) {
|
||||||
this.redisTemplate = redisTemplate;
|
this.redisTemplate = redisTemplate;
|
||||||
@@ -473,6 +475,8 @@ public class WxLoginController extends BaseController {
|
|||||||
userRole.setTenantId(addUser.getTenantId());
|
userRole.setTenantId(addUser.getTenantId());
|
||||||
userRole.setRoleId(addUser.getRoleId());
|
userRole.setRoleId(addUser.getRoleId());
|
||||||
userRoleService.save(userRole);
|
userRoleService.save(userRole);
|
||||||
|
// 同步到 websopy
|
||||||
|
userSyncService.syncUserToWebsopy(addUser);
|
||||||
}
|
}
|
||||||
// 绑定关系
|
// 绑定关系
|
||||||
if (userParam.getSceneType() != null && userParam.getSceneType().equals("save_referee") && userParam.getRefereeId() != null && userParam.getRefereeId() != 0) {
|
if (userParam.getSceneType() != null && userParam.getSceneType().equals("save_referee") && userParam.getRefereeId() != null && userParam.getRefereeId() != 0) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.gxwebsoft.common.system.service.RoleService;
|
|||||||
import com.gxwebsoft.common.system.service.UserOauthService;
|
import com.gxwebsoft.common.system.service.UserOauthService;
|
||||||
import com.gxwebsoft.common.system.service.UserRoleService;
|
import com.gxwebsoft.common.system.service.UserRoleService;
|
||||||
import com.gxwebsoft.common.system.service.UserService;
|
import com.gxwebsoft.common.system.service.UserService;
|
||||||
|
import com.gxwebsoft.common.system.service.UserSyncService;
|
||||||
import com.gxwebsoft.common.system.vo.WxOfficialButton;
|
import com.gxwebsoft.common.system.vo.WxOfficialButton;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -69,6 +70,8 @@ public class WxOfficialController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private UserRoleService userRoleService;
|
private UserRoleService userRoleService;
|
||||||
@Resource
|
@Resource
|
||||||
|
private UserSyncService userSyncService;
|
||||||
|
@Resource
|
||||||
private UserOauthService userOauthService;
|
private UserOauthService userOauthService;
|
||||||
@Resource
|
@Resource
|
||||||
private RedisUtil redisUtil;
|
private RedisUtil redisUtil;
|
||||||
@@ -155,6 +158,8 @@ public class WxOfficialController extends BaseController {
|
|||||||
userRole.setTenantId(user.getTenantId());
|
userRole.setTenantId(user.getTenantId());
|
||||||
userRole.setRoleId(user.getRoleId());
|
userRole.setRoleId(user.getRoleId());
|
||||||
userRoleService.save(userRole);
|
userRoleService.save(userRole);
|
||||||
|
// 同步到 websopy
|
||||||
|
userSyncService.syncUserToWebsopy(user);
|
||||||
}
|
}
|
||||||
System.out.println("新微信公众号用户 = " + userId);
|
System.out.println("新微信公众号用户 = " + userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package com.gxwebsoft.common.system.service;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||||
|
import com.gxwebsoft.common.core.utils.HttpUtils;
|
||||||
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户同步服务(同步到 websopy)
|
||||||
|
*
|
||||||
|
* @author WebSoft
|
||||||
|
* @since 2026-04-04
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class UserSyncService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ConfigProperties configProperties;
|
||||||
|
|
||||||
|
private String websopyBaseUrl;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
websopyBaseUrl = configProperties.getWebsopyUrl();
|
||||||
|
if (websopyBaseUrl == null || websopyBaseUrl.isEmpty()) {
|
||||||
|
log.warn("websopyUrl 未配置,用户同步功能将不可用");
|
||||||
|
} else {
|
||||||
|
log.info("用户同步服务初始化完成,websopy地址: {}", websopyBaseUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步单个用户到 websopy
|
||||||
|
*
|
||||||
|
* @param user 用户信息
|
||||||
|
*/
|
||||||
|
public void syncUserToWebsopy(User user) {
|
||||||
|
if (websopyBaseUrl == null || websopyBaseUrl.isEmpty()) {
|
||||||
|
log.warn("websopyUrl 未配置,跳过用户同步: userId={}", user.getUserId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user == null || user.getUserId() == null) {
|
||||||
|
log.warn("用户信息为空,跳过同步");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 构建请求体
|
||||||
|
JSONObject userCache = new JSONObject();
|
||||||
|
userCache.put("userId", user.getUserId());
|
||||||
|
userCache.put("username", user.getUsername());
|
||||||
|
userCache.put("nickname", user.getNickname());
|
||||||
|
userCache.put("avatar", user.getAvatar());
|
||||||
|
userCache.put("phone", user.getPhone());
|
||||||
|
userCache.put("status", user.getStatus());
|
||||||
|
userCache.put("updateTime", System.currentTimeMillis());
|
||||||
|
|
||||||
|
String url = websopyBaseUrl + "/api/app/user-sync/single";
|
||||||
|
String body = userCache.toJSONString();
|
||||||
|
|
||||||
|
log.info("同步用户到 websopy: userId={}, url={}", user.getUserId(), url);
|
||||||
|
|
||||||
|
// 发送 HTTP POST 请求
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Content-Type", "application/json");
|
||||||
|
|
||||||
|
HttpResponse response = HttpUtils.doPost(url, "", "POST", headers, null, body);
|
||||||
|
String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
log.debug("websopy 响应: {}", responseBody);
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
JSONObject result = JSON.parseObject(responseBody);
|
||||||
|
if (result != null && result.getIntValue("code") == 0) {
|
||||||
|
log.info("用户同步成功: userId={}", user.getUserId());
|
||||||
|
} else {
|
||||||
|
String message = result != null ? result.getString("message") : "未知错误";
|
||||||
|
log.error("用户同步失败: userId={}, message={}", user.getUserId(), message);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("用户同步异常: userId={}, error={}", user.getUserId(), e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新 websopy 端的用户缓存
|
||||||
|
* 只传 userId,websopy 端会通过 API 回查获取完整信息
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
*/
|
||||||
|
public void refreshUserCache(Integer userId) {
|
||||||
|
if (websopyBaseUrl == null || websopyBaseUrl.isEmpty()) {
|
||||||
|
log.warn("websopyUrl 未配置,跳过刷新缓存: userId={}", userId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userId == null) {
|
||||||
|
log.warn("userId 为空,跳过刷新");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String url = websopyBaseUrl + "/api/app/user-sync/refresh/" + userId;
|
||||||
|
|
||||||
|
log.info("刷新用户缓存: userId={}, url={}", userId, url);
|
||||||
|
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Content-Type", "application/json");
|
||||||
|
|
||||||
|
HttpResponse response = HttpUtils.doPost(url, "", "POST", headers, null, "");
|
||||||
|
String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
JSONObject result = JSON.parseObject(responseBody);
|
||||||
|
if (result != null && result.getIntValue("code") == 0) {
|
||||||
|
log.info("刷新缓存成功: userId={}", userId);
|
||||||
|
} else {
|
||||||
|
String message = result != null ? result.getString("message") : "未知错误";
|
||||||
|
log.error("刷新缓存失败: userId={}, message={}", userId, message);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("刷新缓存异常: userId={}, error={}", userId, e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user