Compare commits

...

2 Commits

Author SHA1 Message Date
e73e0fb6b5 feat(auth): 完善扫码登录状态逻辑,支持绑定手机号和跳转
- 新增响应字段 nextAction、redirectUrl 和 successMessage,用于表示下一步操作
- 扫码登录状态为绑定手机号时,设置 nextAction 为 bind_phone,清空跳转地址
- 扫码已确认且有访问令牌时,设置跳转控制台页面,带成功提示信息
- 其他状态默认保持等待状态,确保扫码流程向前推进
- 更新扫码登录响应模型,优化前端流程控制体验
2026-04-07 01:43:51 +08:00
3c89cbce40 fix(sync): 修复用户同步到 websopy 的数据格式及完整性问题
- 修正同步数据中的 tenant 字段名称为 tenant_id 以匹配 websopy 数据库字段
- 增加 tenantId 值的日志输出便于调试和校验
- 在 wx 官方控制器同步用户时先从数据库重新加载用户数据,保证同步数据完整性
- 若重新加载失败则回退使用当前用户对象进行同步
- 优化用户角色保存后同步操作的可靠性和准确性
2026-04-07 01:38:11 +08:00
5 changed files with 86 additions and 17 deletions

View File

@@ -0,0 +1,17 @@
{
"version": 2,
"sessions": {
"da2fb4dc76a94426a7dfef478f674489": [
{
"expertId": "SeniorDeveloper",
"name": "Will",
"profession": "高级开发工程师",
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/SeniorDeveloper/SeniorDeveloper.png",
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/SeniorDeveloper/SeniorDeveloper_zh.md",
"usedAt": 1775495439006,
"industryId": "all"
}
]
},
"lastUpdated": 1775497010453
}

View File

@@ -0,0 +1,12 @@
## 2026-04-07 工作记录
### 微信扫码登录问题修复
1. 修复了UserSyncService中tenant_id字段名问题从tenantId改为tenant_id
2. 同时发送两种格式的tenant_id字段确保兼容性
3. 修改了WxOfficialController在同步前从数据库重新加载用户对象
4. 添加了详细的调试日志便于问题排查
### 待解决问题
1. websopy侧app_user_cache同步失败tenant_id为null
2. 扫码成功后需跳转到强制绑定手机号页面
3. 注册成功后应跳转到控制台/console

View File

@@ -37,6 +37,15 @@ public class QrLoginStatusResponse {
@Schema(description = "状态提示信息") @Schema(description = "状态提示信息")
private String message; private String message;
@Schema(description = "下一步操作bind_phone-绑定手机号, redirect-跳转, login-直接登录")
private String nextAction;
@Schema(description = "跳转URL当nextAction为redirect时使用")
private String redirectUrl;
@Schema(description = "成功消息")
private String successMessage;
public QrLoginStatusResponse(String status, String accessToken, User userInfo, Long expiresIn, Integer tenantId) { public QrLoginStatusResponse(String status, String accessToken, User userInfo, Long expiresIn, Integer tenantId) {
this.status = status; this.status = status;
this.accessToken = accessToken; this.accessToken = accessToken;

View File

@@ -442,6 +442,18 @@ public class QrLoginServiceImpl implements QrLoginService {
|| QR_LOGIN_STATUS_BIND_PHONE.equals(qrLoginData.getStatus())); || QR_LOGIN_STATUS_BIND_PHONE.equals(qrLoginData.getStatus()));
response.setMessage(qrLoginData.getMessage()); response.setMessage(qrLoginData.getMessage());
// 设置下一步操作逻辑
if (QR_LOGIN_STATUS_BIND_PHONE.equals(qrLoginData.getStatus()) || Boolean.TRUE.equals(qrLoginData.getNeedBindPhone())) {
response.setNextAction("bind_phone");
response.setRedirectUrl(null);
} else if (QR_LOGIN_STATUS_CONFIRMED.equals(qrLoginData.getStatus()) && StrUtil.isNotBlank(qrLoginData.getAccessToken())) {
response.setNextAction("redirect");
response.setRedirectUrl("/console");
response.setSuccessMessage("登录成功,即将跳转到控制台");
} else {
response.setNextAction("wait");
}
if (qrLoginData.getUserId() != null) { if (qrLoginData.getUserId() != null) {
try { try {
User user = userService.getAllByUserId(String.valueOf(qrLoginData.getUserId())); User user = userService.getAllByUserId(String.valueOf(qrLoginData.getUserId()));

View File

@@ -27,6 +27,22 @@ import java.util.Map;
@Service @Service
public class UserSyncService { public class UserSyncService {
/**
* 转义JSON字符串中的特殊字符
*/
private String escapeJson(String str) {
if (str == null) {
return "";
}
return str.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\b", "\\b")
.replace("\f", "\\f")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}
@Resource @Resource
private ConfigProperties configProperties; private ConfigProperties configProperties;
@@ -55,29 +71,32 @@ public class UserSyncService {
} }
try { try {
// 构建请求体 // 构建请求体 - 使用手动JSON构建确保字段正确
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", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// tenantId 可能为 null但在 websopy 端是必填字段,至少传 0
// 注意websopy 端数据库字段是 tenant_id下划线不是 tenantId驼峰
Integer tenantIdValue = user.getTenantId() != null ? user.getTenantId() : 0; Integer tenantIdValue = user.getTenantId() != null ? user.getTenantId() : 0;
userCache.put("tenant_id", tenantIdValue);
// 构建JSON字符串确保tenant_id字段存在且不为null
StringBuilder jsonBuilder = new StringBuilder();
jsonBuilder.append("{");
jsonBuilder.append("\"userId\":").append(user.getUserId()).append(",");
jsonBuilder.append("\"username\":\"").append(escapeJson(user.getUsername())).append("\",");
jsonBuilder.append("\"nickname\":\"").append(escapeJson(user.getNickname())).append("\",");
jsonBuilder.append("\"avatar\":\"").append(escapeJson(user.getAvatar())).append("\",");
jsonBuilder.append("\"phone\":\"").append(escapeJson(user.getPhone())).append("\",");
jsonBuilder.append("\"status\":").append(user.getStatus()).append(",");
jsonBuilder.append("\"updateTime\":\"").append(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("\",");
jsonBuilder.append("\"tenantId\":").append(tenantIdValue).append(","); // 驼峰格式
jsonBuilder.append("\"tenant_id\":").append(tenantIdValue); // 下划线格式
jsonBuilder.append("}");
String url = websopyBaseUrl + "/api/app/user-sync/single"; String url = websopyBaseUrl + "/api/app/user-sync/single";
String body = userCache.toJSONString(); String body = jsonBuilder.toString();
log.info("同步用户到 websopy: userId={}, username={}, nickname={}, phone={}, tenantId={}, url={}", log.info("同步用户到 websopy: userId={}, username={}, nickname={}, phone={}, tenantId={}, url={}",
user.getUserId(), user.getUsername(), user.getNickname(), user.getPhone(), user.getTenantId(), url); user.getUserId(), user.getUsername(), user.getNickname(), user.getPhone(), user.getTenantId(), url);
log.debug("同步用户请求体: {}", body); log.info("同步用户请求体JSON: {}", body); // 改为info级别以便查看
// 额外日志tenantId 值检查 // 额外日志tenantId 值检查
log.debug("tenantId检查 - 原始值: {}, 转换后值: {}, 请求体中tenant_id字段: {}", log.debug("tenantId检查 - 原始值: {}, 转换后值: {}",
user.getTenantId(), tenantIdValue, userCache.get("tenant_id")); user.getTenantId(), tenantIdValue);
// 发送 HTTP POST 请求 // 发送 HTTP POST 请求
Map<String, String> headers = new HashMap<>(); Map<String, String> headers = new HashMap<>();