diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocCallbackController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocCallbackController.java new file mode 100644 index 0000000..6d517ed --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocCallbackController.java @@ -0,0 +1,152 @@ +package com.ruoyi.web.controller.jarvis; + +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.jarvis.service.ITencentDocService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 腾讯文档OAuth回调控制器(备用路径) + * 用于处理更简单的回调路径,避免前端路由拦截 + * + * @author system + */ +@RestController +@RequestMapping("/tendoc-callback") +public class TencentDocCallbackController extends BaseController { + + private static final Logger log = LoggerFactory.getLogger(TencentDocCallbackController.class); + + @Autowired + private ITencentDocService tencentDocService; + + @Autowired + private com.ruoyi.jarvis.service.ITencentDocTokenService tencentDocTokenService; + + /** + * OAuth回调 - 通过授权码获取访问令牌 + * 路径:/tendoc-callback + * 注意:在腾讯文档开放平台只需配置域名:jarvis.van333.cn(不能包含路径) + * 授权URL中的redirect_uri参数会自动使用配置中的完整URL:https://jarvis.van333.cn/tendoc-callback + */ + @Anonymous + @GetMapping(produces = MediaType.TEXT_HTML_VALUE) + public String oauthCallback(@RequestParam(value = "code", required = false) String code, + @RequestParam(value = "state", required = false) String state, + @RequestParam(value = "error", required = false) String error, + @RequestParam(value = "error_description", required = false) String errorDescription) { + try { + // 处理授权错误 + if (error != null) { + log.error("腾讯文档授权失败 - error: {}, error_description: {}", error, errorDescription); + String errorMsg = errorDescription != null ? errorDescription : error; + return generateCallbackHtml(false, "授权失败: " + errorMsg, null); + } + + // 验证授权码 + if (code == null || code.trim().isEmpty()) { + log.error("授权码为空"); + return generateCallbackHtml(false, "授权码不能为空", null); + } + + log.info("收到腾讯文档授权回调(备用路径)- code: {}, state: {}", code, state); + + // 使用授权码换取access_token + com.alibaba.fastjson2.JSONObject tokenInfo = tencentDocService.getAccessTokenByCode(code); + + // 验证返回的token信息 + if (tokenInfo == null || !tokenInfo.containsKey("access_token")) { + log.error("获取访问令牌失败 - 响应数据: {}", tokenInfo); + return generateCallbackHtml(false, "获取访问令牌失败,响应数据格式不正确", null); + } + + String accessToken = tokenInfo.getString("access_token"); + String refreshToken = tokenInfo.getString("refresh_token"); + Integer expiresIn = tokenInfo.getIntValue("expires_in"); + + log.info("成功获取访问令牌 - access_token: {}", accessToken); + + // 自动保存token到后端 + try { + if (tencentDocTokenService instanceof com.ruoyi.jarvis.service.impl.TencentDocTokenServiceImpl) { + ((com.ruoyi.jarvis.service.impl.TencentDocTokenServiceImpl) tencentDocTokenService) + .setToken(accessToken, refreshToken, expiresIn); + log.info("访问令牌已自动保存到后端缓存"); + } + } catch (Exception e) { + log.error("保存访问令牌失败", e); + return generateCallbackHtml(false, "保存访问令牌失败: " + e.getMessage(), null); + } + + return generateCallbackHtml(true, "授权成功,访问令牌已自动保存", null); + } catch (Exception e) { + log.error("OAuth回调处理失败", e); + return generateCallbackHtml(false, "授权失败: " + e.getMessage(), null); + } + } + + /** + * 生成回调HTML页面 + */ + private String generateCallbackHtml(boolean success, String message, Object data) { + StringBuilder html = new StringBuilder(); + html.append(""); + html.append(""); + html.append(""); + html.append(""); + html.append(""); + html.append("腾讯文档授权").append(success ? "成功" : "失败").append(""); + html.append(""); + html.append(""); + html.append(""); + html.append("
"); + if (success) { + html.append("
"); + html.append("

授权成功

"); + } else { + html.append("
"); + html.append("

授权失败

"); + } + html.append("
").append(message).append("
"); + html.append("

窗口将在3秒后自动关闭...

"); + html.append("
"); + html.append(""); + html.append(""); + html.append(""); + return html.toString(); + } +} + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java index 5176fd3..ee96e19 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java @@ -45,6 +45,15 @@ public class TencentDocController extends BaseController { /** Redis key前缀,用于存储上次处理的最大行数 */ private static final String LAST_PROCESSED_ROW_KEY_PREFIX = "tendoc:last_row:"; + /** + * 测试回调接口是否可访问(用于调试) + */ + @Anonymous + @GetMapping("/test-callback") + public AjaxResult testCallback() { + return AjaxResult.success("回调接口测试成功,路由配置正确"); + } + /** * 获取授权URL(用于手动授权,获取token后通过setToken接口保存) */ diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index a8db9a4..624a3b2 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -193,8 +193,8 @@ tencent: app-id: 90aa0b70e7704c2abd2a42695d5144a4 # 应用密钥(需要在腾讯文档开放平台申请) app-secret: G8ZdSWcoViIawygo7JSolE86PL32UO0O - # 授权回调地址(需要在腾讯文档开放平台配置,必须使用HTTPS) - redirect-uri: https://jarvis.van333.cn/jarvis/tendoc/oauth/callback + # 授权回调地址(需要在腾讯文档开放平台配置域名:jarvis.van333.cn,这里使用完整URL) + redirect-uri: https://jarvis.van333.cn/tendoc-callback # API基础地址 api-base-url: https://docs.qq.com/open/v1 # OAuth授权地址 diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 1f12da2..826277a 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -192,8 +192,8 @@ tencent: app-id: 90aa0b70e7704c2abd2a42695d5144a4 # 应用密钥(需要在腾讯文档开放平台申请) app-secret: G8ZdSWcoViIawygo7JSolE86PL32UO0O - # 授权回调地址(需要在腾讯文档开放平台配置,必须使用HTTPS) - redirect-uri: https://jarvis.van333.cn/jarvis/tendoc/oauth/callback + # 授权回调地址(需要在腾讯文档开放平台配置域名:jarvis.van333.cn,这里使用完整URL) + redirect-uri: https://jarvis.van333.cn/tendoc-callback # API基础地址 api-base-url: https://docs.qq.com/open/v1 # OAuth授权地址 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index 0b7434b..003c8b4 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -116,6 +116,8 @@ public class SecurityConfig .antMatchers("/public/**").permitAll() // 腾讯文档OAuth回调接口,允许匿名访问 .antMatchers("/jarvis/tendoc/oauth/callback").permitAll() + // 腾讯文档OAuth回调接口(备用路径),允许匿名访问 + .antMatchers("/tendoc-callback").permitAll() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()