refactor(douyin): 抽取 H5 认证为 capability-douyin 能力包

将 DouyinH5AuthService/DouyinAuthResult 从 payment-common 下沉至 platform-capability,与 capability-wechat/alipay 分层对称;换票失败 i18n 改用 error.douyin.authFailed,并清理 social 死 key。
This commit is contained in:
DaxPay Dev
2026-07-10 15:09:29 +08:00
parent d98b361b8a
commit 6ec1394005
12 changed files with 85 additions and 14 deletions

View File

@@ -110,6 +110,12 @@
<artifactId>capability-alipay</artifactId>
<version>${project.version}</version>
</dependency>
<!-- 抖音开放平台能力(平台级 H5 静默授权调试使用) -->
<dependency>
<groupId>cn.daxpay.open</groupId>
<artifactId>capability-douyin</artifactId>
<version>${project.version}</version>
</dependency>
<!-- 翻译模块 -->
<dependency>
<groupId>cn.daxpay.open</groupId>

View File

@@ -6,6 +6,8 @@ import cn.daxpay.open.payment.unipay.result.assist.AuthUrlResult;
import cn.daxpay.open.platform.capability.alipay.auth.config.AlipayAuthConfig;
import cn.daxpay.open.platform.capability.alipay.auth.result.AlipayAuthResult;
import cn.daxpay.open.platform.capability.alipay.auth.service.AlipayAuthCapability;
import cn.daxpay.open.platform.capability.douyin.auth.result.DouyinAuthResult;
import cn.daxpay.open.platform.capability.douyin.auth.service.DouyinH5AuthService;
import cn.daxpay.open.platform.capability.wechat.auth.result.WechatAuthResult;
import cn.daxpay.open.platform.capability.wechat.auth.result.WechatAuthUrlResult;
import cn.daxpay.open.platform.capability.wechat.auth.service.WechatMpAuthService;
@@ -133,7 +135,7 @@ public class PlatformAuthService {
/// 生成抖音 H5 静默授权链接(平台级, 无商户上下文, 仅调试)
///
/// 读取平台级 [PlatformDouyinH5AuthConfig], 调用 [DouyinH5AuthService] 构造 silent_auth 链接,
/// 读取平台级 [PlatformDouyinH5AuthConfig], 调用 capability-douyin [DouyinH5AuthService] 构造 silent_auth 链接,
/// 回调指向固定的 `/auth/douyin`(抖音要求 redirect_uri 与平台配置完全一致, 不支持 path 段或 query 参数)。
/// 会话标识 authToken 通过 state 参数透传, 回调后从 state 恢复会话。
/// session 标记 `source=platform_douyin`, 认证分发层据此走平台级抖音换票分支([authDouyin])。
@@ -222,7 +224,7 @@ public class PlatformAuthService {
config.getClientKey(), config.getClientSecret(), param.getAuthCode());
if (StrUtil.isBlank(data.getOpenId())) {
// 抖音: 获取用户标识失败
throw new BizInfoException(CommonErrorCode.SYSTEM_ERROR, "error.social.douyinAuthFailed", "openId is blank");
throw new BizInfoException(CommonErrorCode.SYSTEM_ERROR, "error.douyin.authFailed", "openId is blank");
}
AuthResult authResult = new AuthResult()
.setOpenId(data.getOpenId())

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.daxpay.open</groupId>
<artifactId>daxpay-platform-capability</artifactId>
<version>4.0.0-SNAPSHOT</version>
</parent>
<artifactId>capability-douyin</artifactId>
<description>抖音开放平台 H5 认证能力模块</description>
<dependencies>
<!-- web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 核心包 -->
<dependency>
<groupId>cn.daxpay.open</groupId>
<artifactId>daxpay-platform-core</artifactId>
<version>${project.version}</version>
</dependency>
<!-- hutool http (开放平台 HTTP 直连) -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- hutool json (响应解析) -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,16 @@
package cn.daxpay.open.platform.capability.douyin;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
/// # 抖音开放平台能力模块自动配置
///
/// 装配抖音 H5 静默授权服务([DouyinH5AuthService], HTTP 直连无官方 SDK),
/// 供 payment 模块(平台级调试认证)等调用方使用, 不耦合配置存储。
///
@Slf4j
@AutoConfiguration
@ComponentScan
public class DouyinAutoConfiguration {
}

View File

@@ -1,4 +1,4 @@
package cn.daxpay.open.payment.core.assist;
package cn.daxpay.open.platform.capability.douyin.auth.result;
import lombok.Data;
import lombok.experimental.Accessors;

View File

@@ -1,7 +1,7 @@
package cn.daxpay.open.payment.core.assist;
package cn.daxpay.open.platform.capability.douyin.auth.service;
import cn.daxpay.open.platform.core.code.CommonErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.capability.douyin.auth.result.DouyinAuthResult;
import cn.daxpay.open.platform.core.exception.operation.OperationFailException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.HttpRequest;
@@ -21,8 +21,9 @@ import org.springframework.stereotype.Service;
/// 与微信公众号 SDK([WechatMpAuthService]) 不同, 抖音 H5 静默授权仅需简单 HTTP 调用,
/// 不依赖第三方 SDK, 直接使用 Hutool HttpUtil 完成
///
/// 配置来源: 平台级 [cn.daxpay.open.platform.system.entity.config.platform.auth.PlatformDouyinH5AuthConfig]
/// (clientKey / clientSecret)
/// ## 能力边界
/// - 生成静默授权链接 / authCode openId(HTTP 直连, 无官方 SDK)
/// - 不做配置存储 / state / queryCode 缓存(由调用方自行管理)
///
/// 参考文档: https://developer.open-douyin.com/docs/resource/zh-CN/dop/ability/opensdk/user-authorization/get-login-openid
@Slf4j
@@ -41,7 +42,7 @@ public class DouyinH5AuthService {
/// 构造抖音静默授权链接
///
/// 抖音 silent_auth 不支持在 redirect_uri 中携带 query 参数,
/// authToken 通过 path 段传递(`/auth/douyin/{authToken}`), state 用于 CSRF 校验
/// authToken 通过 path 段或 state 传递, state 用于 CSRF 校验
///
/// @param clientKey 抖音开放平台 Client Key
/// @param redirectUri 授权回调地址(不含 query 参数)
@@ -82,20 +83,20 @@ public class DouyinH5AuthService {
JSONObject data = object.getJSONObject("data");
if (data == null) {
// 抖音: 换取用户标识失败: {0}
throw new BizInfoException(CommonErrorCode.SYSTEM_ERROR, "error.social.douyinAuthFailed",
throw new OperationFailException("error.douyin.authFailed",
object.getStr("message", body));
}
// error_code 0 表示业务错误
int errorCode = data.getInt("error_code", -1);
if (errorCode != 0) {
// 抖音: 换取用户标识失败: {0}
throw new BizInfoException(CommonErrorCode.SYSTEM_ERROR, "error.social.douyinAuthFailed",
throw new OperationFailException("error.douyin.authFailed",
data.getStr("description", "error_code=" + errorCode));
}
String openId = data.getStr("open_id");
if (StrUtil.isBlank(openId)) {
// 抖音: 获取用户标识失败
throw new BizInfoException(CommonErrorCode.SYSTEM_ERROR, "error.social.douyinAuthFailed", "openId is blank");
throw new OperationFailException("error.douyin.authFailed", "openId is blank");
}
return new DouyinAuthResult()
.setOpenId(openId)

View File

@@ -0,0 +1 @@
cn.daxpay.open.platform.capability.douyin.DouyinAutoConfiguration

View File

@@ -0,0 +1,3 @@
{
"authFailed": "Douyin: failed to get user identifier: {0}"
}

View File

@@ -0,0 +1,3 @@
{
"authFailed": "抖音: 获取用户标识失败: {0}"
}

View File

@@ -7,7 +7,6 @@
"alipayNotConfigured": "Platform-level Alipay configuration is incomplete, please configure it in 'Third-party Platform Management' first",
"wechatMpNotConfigured": "Platform-level WeChat Official Account configuration is incomplete, please configure it in 'Third-party Platform Management' first",
"douyinH5NotConfigured": "Platform-level Douyin H5 app configuration is incomplete, please configure it in 'Third-party Platform Management' first",
"douyinAuthFailed": "Douyin: failed to get user identifier: {0}",
"weCom": {
"notCorpMember": "Not a member of this enterprise, login is not supported"
},

View File

@@ -7,7 +7,6 @@
"alipayNotConfigured": "平台级支付宝配置不完整,请先在「三方平台管理」中配置",
"wechatMpNotConfigured": "平台级微信公众号配置不完整,请先在「三方平台管理」中配置",
"douyinH5NotConfigured": "平台级抖音 H5 应用配置不完整,请先在「三方平台管理」中配置",
"douyinAuthFailed": "抖音: 获取用户标识失败: {0}",
"weCom": {
"notCorpMember": "非本企业成员,不支持登录"
},

View File

@@ -17,6 +17,7 @@
<module>capability-wechat</module>
<module>capability-social</module>
<module>capability-alipay</module>
<module>capability-douyin</module>
</modules>
<modelVersion>4.0.0</modelVersion>