From e4d903483f6046a70616aaf648f59ff2518c921e Mon Sep 17 00:00:00 2001 From: "925116093-qq.com" <925116093@qq.com> Date: Tue, 10 Jun 2025 09:09:49 +0800 Subject: [PATCH] 202506100909 --- pom.xml | 17 - ruoyi-auth-common/pom.xml | 33 - .../ruoyi/auth/common/domain/OauthUser.java | 293 ------ .../common/enums/OauthVerificationUse.java | 59 -- .../auth/common/mapper/OauthUserMapper.java | 112 -- .../common/service/IOauthUserService.java | 102 -- .../service/OauthVerificationCodeService.java | 15 - .../ruoyi/auth/common/service/TfaService.java | 73 -- .../service/impl/OauthUserServiceImpl.java | 149 --- .../auth/common/utils/RandomCodeUtil.java | 28 - .../mapper/common/OauthUserMapper.xml | 169 --- .../core/controller/BaseController.java | 3 + ruoyi-oauth-wx/pom.xml | 34 - .../oauth/wx/constant/WxMiniAppConstant.java | 42 - .../oauth/wx/constant/WxPubConstant.java | 41 - .../wx/controller/WxLoginController.java | 69 -- .../Impl/WxMiniAppLoginServiceImpl.java | 76 -- .../service/Impl/WxPubLoginServiceImpl.java | 77 -- .../oauth/wx/service/WxLoginService.java | 34 - ...itional-spring-configuration-metadata.json | 20 - ruoyi-system/pom.xml | 7 - .../system/controller/AppletController.java | 236 ++++- .../controllerUtil/AppletLoginUtil.java | 374 +++++++ .../ruoyi/system/controllerUtil/PageUtil.java | 155 +++ .../system/controllerUtil/WechatApiUtil.java | 977 ++++++++++++++++-- .../java/com/ruoyi/system/domain/Users.java | 32 + 26 files changed, 1640 insertions(+), 1587 deletions(-) delete mode 100644 ruoyi-auth-common/pom.xml delete mode 100644 ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/domain/OauthUser.java delete mode 100644 ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/enums/OauthVerificationUse.java delete mode 100644 ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/mapper/OauthUserMapper.java delete mode 100644 ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/IOauthUserService.java delete mode 100644 ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/OauthVerificationCodeService.java delete mode 100644 ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/TfaService.java delete mode 100644 ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/impl/OauthUserServiceImpl.java delete mode 100644 ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/utils/RandomCodeUtil.java delete mode 100644 ruoyi-auth-common/src/main/resources/mapper/common/OauthUserMapper.xml delete mode 100644 ruoyi-oauth-wx/pom.xml delete mode 100644 ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/constant/WxMiniAppConstant.java delete mode 100644 ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/constant/WxPubConstant.java delete mode 100644 ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/controller/WxLoginController.java delete mode 100644 ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/Impl/WxMiniAppLoginServiceImpl.java delete mode 100644 ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/Impl/WxPubLoginServiceImpl.java delete mode 100644 ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/WxLoginService.java delete mode 100644 ruoyi-oauth-wx/src/main/resources/META-INF/additional-spring-configuration-metadata.json create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/AppletLoginUtil.java create mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/PageUtil.java diff --git a/pom.xml b/pom.xml index d8a3637..70b8af9 100644 --- a/pom.xml +++ b/pom.xml @@ -197,21 +197,6 @@ ruoyi-generator ${ruoyi.version} - - - - com.ruoyi - ruoyi-oauth-wx - ${ruoyi.version} - - - - - com.ruoyi - ruoyi-auth-common - ${ruoyi.version} - - com.ruoyi @@ -271,8 +256,6 @@ ruoyi-quartz ruoyi-generator ruoyi-common - ruoyi-oauth-wx - ruoyi-auth-common pom diff --git a/ruoyi-auth-common/pom.xml b/ruoyi-auth-common/pom.xml deleted file mode 100644 index 5c0c528..0000000 --- a/ruoyi-auth-common/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - hfrj - com.ruoyi - 3.8.9 - - 4.0.0 - - ruoyi-auth-common - - - system系统模块 - - - - - - - com.ruoyi - ruoyi-framework - - - - - org.apache.httpcomponents - httpclient - - - - - \ No newline at end of file diff --git a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/domain/OauthUser.java b/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/domain/OauthUser.java deleted file mode 100644 index 650b514..0000000 --- a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/domain/OauthUser.java +++ /dev/null @@ -1,293 +0,0 @@ -package com.ruoyi.auth.common.domain; - -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -import com.ruoyi.common.annotation.Excel; -import com.ruoyi.common.core.domain.BaseEntity; - -import io.swagger.v3.oas.annotations.media.Schema; - -/** - * 第三方认证对象 oauth_user - * - * @author Dftre - * @date 2024-01-18 - */ -@Schema(description = "第三方认证对象") -public class OauthUser extends BaseEntity { - private static final long serialVersionUID = 1L; - - /** 主键 */ - @Schema(title = "主键") - private Long id; - - /** 第三方系统的唯一ID,详细解释请参考:名词解释 */ - @Schema(title = "第三方系统的唯一ID,详细解释请参考:名词解释") - @Excel(name = "第三方系统的唯一ID,详细解释请参考:名词解释") - private String uuid; - - /** 用户ID */ - @Schema(title = "用户ID") - @Excel(name = "用户ID") - private Long userId; - - /** - * 第三方用户来源,可选值:GITHUB、GITEE、QQ,更多请参考:AuthDefaultSource.java(opens new window) - */ - @Schema(title = "第三方用户来源,可选值:GITHUB、GITEE、QQ,更多请参考:AuthDefaultSource.java(opens new window)") - @Excel(name = "第三方用户来源,可选值:GITHUB、GITEE、QQ,更多请参考:AuthDefaultSource.java(opens new window)") - private String source; - - /** 用户的授权令牌 */ - @Schema(title = "用户的授权令牌") - @Excel(name = "用户的授权令牌") - private String accessToken; - - /** 第三方用户的授权令牌的有效期,部分平台可能没有 */ - @Schema(title = "第三方用户的授权令牌的有效期,部分平台可能没有") - @Excel(name = "第三方用户的授权令牌的有效期,部分平台可能没有") - private Long expireIn; - - /** 刷新令牌,部分平台可能没有 */ - @Schema(title = "刷新令牌,部分平台可能没有") - @Excel(name = "刷新令牌,部分平台可能没有") - private String refreshToken; - - /** 第三方用户的 open id,部分平台可能没有 */ - @Schema(title = "第三方用户的 open id,部分平台可能没有") - @Excel(name = "第三方用户的 open id,部分平台可能没有") - private String openId; - - /** 第三方用户的 ID,部分平台可能没有 */ - @Schema(title = "第三方用户的 ID,部分平台可能没有") - @Excel(name = "第三方用户的 ID,部分平台可能没有") - private String uid; - - /** 个别平台的授权信息,部分平台可能没有 */ - @Schema(title = "个别平台的授权信息,部分平台可能没有") - @Excel(name = "个别平台的授权信息,部分平台可能没有") - private String accessCode; - - /** 第三方用户的 union id,部分平台可能没有 */ - @Schema(title = "第三方用户的 union id,部分平台可能没有") - @Excel(name = "第三方用户的 union id,部分平台可能没有") - private String unionId; - - /** 第三方用户授予的权限,部分平台可能没有 */ - @Schema(title = "第三方用户授予的权限,部分平台可能没有") - @Excel(name = "第三方用户授予的权限,部分平台可能没有") - private String scope; - - /** 个别平台的授权信息,部分平台可能没有 */ - @Schema(title = "个别平台的授权信息,部分平台可能没有") - @Excel(name = "个别平台的授权信息,部分平台可能没有") - private String tokenType; - - /** id token,部分平台可能没有 */ - @Schema(title = "id token,部分平台可能没有") - @Excel(name = "id token,部分平台可能没有") - private String idToken; - - /** 小米平台用户的附带属性,部分平台可能没有 */ - @Schema(title = "小米平台用户的附带属性,部分平台可能没有") - @Excel(name = "小米平台用户的附带属性,部分平台可能没有") - private String macAlgorithm; - - /** 小米平台用户的附带属性,部分平台可能没有 */ - @Schema(title = "小米平台用户的附带属性,部分平台可能没有") - @Excel(name = "小米平台用户的附带属性,部分平台可能没有") - private String macKey; - - /** 用户的授权code,部分平台可能没有 */ - @Schema(title = "用户的授权code,部分平台可能没有") - @Excel(name = "用户的授权code,部分平台可能没有") - private String code; - - /** Twitter平台用户的附带属性,部分平台可能没有 */ - @Schema(title = "Twitter平台用户的附带属性,部分平台可能没有") - @Excel(name = "Twitter平台用户的附带属性,部分平台可能没有") - private String oauthToken; - - /** Twitter平台用户的附带属性,部分平台可能没有 */ - @Schema(title = "Twitter平台用户的附带属性,部分平台可能没有") - @Excel(name = "Twitter平台用户的附带属性,部分平台可能没有") - private String oauthTokenSecret; - - public void setId(Long id) { - this.id = id; - } - - public Long getId() { - return id; - } - - public void setUuid(String uuid) { - this.uuid = uuid; - } - - public String getUuid() { - return uuid; - } - - public void setUserId(Long userId) { - this.userId = userId; - } - - public Long getUserId() { - return userId; - } - - public void setSource(String source) { - this.source = source; - } - - public String getSource() { - return source; - } - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - public String getAccessToken() { - return accessToken; - } - - public void setExpireIn(Long expireIn) { - this.expireIn = expireIn; - } - - public Long getExpireIn() { - return expireIn; - } - - public void setRefreshToken(String refreshToken) { - this.refreshToken = refreshToken; - } - - public String getRefreshToken() { - return refreshToken; - } - - public void setOpenId(String openId) { - this.openId = openId; - } - - public String getOpenId() { - return openId; - } - - public void setUid(String uid) { - this.uid = uid; - } - - public String getUid() { - return uid; - } - - public void setAccessCode(String accessCode) { - this.accessCode = accessCode; - } - - public String getAccessCode() { - return accessCode; - } - - public void setUnionId(String unionId) { - this.unionId = unionId; - } - - public String getUnionId() { - return unionId; - } - - public void setScope(String scope) { - this.scope = scope; - } - - public String getScope() { - return scope; - } - - public void setTokenType(String tokenType) { - this.tokenType = tokenType; - } - - public String getTokenType() { - return tokenType; - } - - public void setIdToken(String idToken) { - this.idToken = idToken; - } - - public String getIdToken() { - return idToken; - } - - public void setMacAlgorithm(String macAlgorithm) { - this.macAlgorithm = macAlgorithm; - } - - public String getMacAlgorithm() { - return macAlgorithm; - } - - public void setMacKey(String macKey) { - this.macKey = macKey; - } - - public String getMacKey() { - return macKey; - } - - public void setCode(String code) { - this.code = code; - } - - public String getCode() { - return code; - } - - public void setOauthToken(String oauthToken) { - this.oauthToken = oauthToken; - } - - public String getOauthToken() { - return oauthToken; - } - - public void setOauthTokenSecret(String oauthTokenSecret) { - this.oauthTokenSecret = oauthTokenSecret; - } - - public String getOauthTokenSecret() { - return oauthTokenSecret; - } - - @Override - public String toString() { - return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) - .append("id", getId()) - .append("uuid", getUuid()) - .append("userId", getUserId()) - .append("source", getSource()) - .append("accessToken", getAccessToken()) - .append("expireIn", getExpireIn()) - .append("refreshToken", getRefreshToken()) - .append("openId", getOpenId()) - .append("uid", getUid()) - .append("accessCode", getAccessCode()) - .append("unionId", getUnionId()) - .append("scope", getScope()) - .append("tokenType", getTokenType()) - .append("idToken", getIdToken()) - .append("macAlgorithm", getMacAlgorithm()) - .append("macKey", getMacKey()) - .append("code", getCode()) - .append("oauthToken", getOauthToken()) - .append("oauthTokenSecret", getOauthTokenSecret()) - .toString(); - } -} diff --git a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/enums/OauthVerificationUse.java b/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/enums/OauthVerificationUse.java deleted file mode 100644 index 6b44b6c..0000000 --- a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/enums/OauthVerificationUse.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.ruoyi.auth.common.enums; - -public enum OauthVerificationUse { - - /** 用于登录 */ - LOGIN("登录", "login"), - /** 用于注册 */ - REGISTER("注册", "register"), - /** 用于禁用 */ - DISABLE("禁用", "disable"), - /** 用于重置信息 */ - RESET("重置", "reset"), - /** 用于绑定信息 */ - BIND("绑定", "bind"), - /** 其他用途 */ - OTHER("其他", "other"); - - private String name; - private String value; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public static OauthVerificationUse getByValue(String value) { - for (OauthVerificationUse use : OauthVerificationUse.values()) { - if (use.getValue().equals(value)) { - return use; - } - } - return null; - } - - public static OauthVerificationUse getByName(String name) { - for (OauthVerificationUse use : OauthVerificationUse.values()) { - if (use.getName().equals(name)) { - return use; - } - } - return null; - } - - private OauthVerificationUse(String name, String value) { - this.name = name; - this.value = value; - } -} diff --git a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/mapper/OauthUserMapper.java b/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/mapper/OauthUserMapper.java deleted file mode 100644 index 656630e..0000000 --- a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/mapper/OauthUserMapper.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.ruoyi.auth.common.mapper; - -import java.util.List; - -import org.apache.ibatis.annotations.Param; - -import com.ruoyi.auth.common.domain.OauthUser; - -/** - * 第三方认证Mapper接口 - * - * @author Dftre - * @date 2024-01-18 - */ -public interface OauthUserMapper { - /** - * 查询第三方认证 - * - * @param id 第三方认证主键 - * @return 第三方认证 - */ - public OauthUser selectOauthUserById(Long id); - - public OauthUser selectOauthUserByUserId(Long userId); - - /** - * 查询第三方认证 - * 钉钉、抖音:uuid 为用户的 unionid - * 微信公众平台登录、京东、酷家乐、美团:uuid 为用户的 openId - * 微信开放平台登录、QQ:uuid 为用户的 openId,平台支持获取unionid, unionid 在 AuthToken - * 中(如果支持),在登录完成后,可以通过 response.getData().getToken().getUnionId() 获取 - * Google:uuid 为用户的 sub,sub为Google的所有账户体系中用户唯一的身份标识符,详见:OpenID Connect - * - * @param uuid - * @return - */ - public OauthUser selectOauthUserByUUID(String uuid); - - /** - * 查询第三方认证列表 - * - * @param oauthUser 第三方认证 - * @return 第三方认证集合 - */ - public List selectOauthUserList(OauthUser oauthUser); - - /** - * 新增第三方认证 - * - * @param oauthUser 第三方认证 - * @return 结果 - */ - public int insertOauthUser(OauthUser oauthUser); - - /** - * 修改第三方认证 - * - * @param oauthUser 第三方认证 - * @return 结果 - */ - public int updateOauthUser(OauthUser oauthUser); - - /** - * 删除第三方认证 - * - * @param id 第三方认证主键 - * @return 结果 - */ - public int deleteOauthUserById(Long id); - - /** - * 批量删除第三方认证 - * - * @param ids 需要删除的数据主键集合 - * @return 结果 - */ - public int deleteOauthUserByIds(Long[] ids); - - /** - * 校验source平台是否绑定 - * - * @param userId 用户编号 - * @param source 绑定平台 - * @return 结果 - */ - public int checkAuthUser(@Param("userId") Long userId, @Param("source") String source); - - /** - * 校验用户名称是否唯一 - * - * @param userName 用户名称 - * @return 结果 - */ - public int checkUserNameUnique(String userName); - - /** - * 校验手机号码是否唯一 - * - * @param phonenumber 手机号码 - * @return 结果 - */ - public int checkPhoneUnique(String phonenumber); - - /** - * 校验email是否唯一 - * - * @param email 用户邮箱 - * @return 结果 - */ - public int checkEmailUnique(String email); - -} diff --git a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/IOauthUserService.java b/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/IOauthUserService.java deleted file mode 100644 index bc01edf..0000000 --- a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/IOauthUserService.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.ruoyi.auth.common.service; - -import java.util.List; - -import com.ruoyi.auth.common.domain.OauthUser; -import com.ruoyi.common.core.domain.entity.SysUser; - -/** - * 第三方认证Service接口 - * - * @author Dftre - * @date 2024-01-18 - */ -public interface IOauthUserService { - /** - * 查询第三方认证 - * - * @param id 第三方认证主键 - * @return 第三方认证 - */ - public OauthUser selectOauthUserById(Long id); - - public OauthUser selectOauthUserByUUID(String uuid); - - public OauthUser selectOauthUserByUserId(Long userId); - - public SysUser selectSysUserByUUID(String uuid); - - /** - * 查询第三方认证列表 - * - * @param oauthUser 第三方认证 - * @return 第三方认证集合 - */ - public List selectOauthUserList(OauthUser oauthUser); - - /** - * 新增第三方认证 - * - * @param oauthUser 第三方认证 - * @return 结果 - */ - public int insertOauthUser(OauthUser oauthUser); - - /** - * 修改第三方认证 - * - * @param oauthUser 第三方认证 - * @return 结果 - */ - public int updateOauthUser(OauthUser oauthUser); - - /** - * 批量删除第三方认证 - * - * @param ids 需要删除的第三方认证主键集合 - * @return 结果 - */ - public int deleteOauthUserByIds(Long[] ids); - - /** - * 删除第三方认证信息 - * - * @param id 第三方认证主键 - * @return 结果 - */ - public int deleteOauthUserById(Long id); - - /** - * 校验source平台是否绑定 - * - * @param userId 用户编号 - * @param source 绑定平台 - * @return 结果 - */ - public boolean checkAuthUser(Long userId, String source); - - /** - * 校验用户名称是否唯一 - * - * @param userName 用户名称 - * @return 结果 - */ - public boolean checkUserNameUnique(String userName); - - /** - * 校验手机号码是否唯一 - * - * @param phonenumber 手机号码 - * @return 结果 - */ - public boolean checkPhoneUnique(String phonenumber); - - /** - * 校验email是否唯一 - * - * @param email 用户邮箱 - * @return 结果 - */ - public boolean checkEmailUnique(String email); - -} diff --git a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/OauthVerificationCodeService.java b/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/OauthVerificationCodeService.java deleted file mode 100644 index 8fa22f0..0000000 --- a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/OauthVerificationCodeService.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.ruoyi.auth.common.service; - -import com.ruoyi.auth.common.enums.OauthVerificationUse; - -/** - * code认证方式接口 - * - * @author zlh - * @date 2024-04-16 - */ -public interface OauthVerificationCodeService { - public boolean sendCode(String o, String code,OauthVerificationUse use) throws Exception; - public boolean checkCode(String o, String code,OauthVerificationUse use) throws Exception; - -} diff --git a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/TfaService.java b/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/TfaService.java deleted file mode 100644 index 92d92dd..0000000 --- a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/TfaService.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.ruoyi.auth.common.service; - -import com.ruoyi.common.core.domain.model.LoginBody; -import com.ruoyi.common.core.domain.model.RegisterBody; - -/** - * 双因素认证(TFA)操作的服务接口。 - * 该接口提供处理TFA绑定、注册和登录流程的方法, - * 包括它们的验证步骤。 - * - *

- * 双因素认证通过要求用户提供两种不同的认证因素, - * 为认证过程增加了额外的安全层。 - *

- */ -public interface TfaService { - /** - * 启动将TFA方法绑定到用户账户的流程。 - * - * @param loginBody 包含TFA绑定所需数据的登录信息 - */ - public void doBind(LoginBody loginBody); - - /** - * 使用验证码或其他确认方式验证TFA绑定流程。 - * - * @param loginBody 包含验证数据的登录信息 - */ - public void doBindVerify(LoginBody loginBody); - - /** - * 处理包含TFA设置的注册流程。 - * - * @param registerBody 包含用户详情和TFA设置的注册信息 - */ - public void doRegister(RegisterBody registerBody); - - /** - * 验证包含TFA设置的注册流程。 - * - * @param registerBody 包含验证数据的注册信息 - */ - public void doRegisterVerify(RegisterBody registerBody); - - /** - * 启动TFA登录流程的第一步。 - * - * @param loginBody 包含用户凭证的登录信息 - */ - public void doLogin(LoginBody loginBody, boolean autoRegister); - - /** - * 验证TFA登录流程的第二步并完成认证。 - * - * @param loginBody 包含TFA验证码的登录信息 - * @return 已认证会话的字符串令牌或会话标识符 - */ - public String doLoginVerify(LoginBody loginBody, boolean autoRegister); - - /** - * 启动TFA重置流程的第一步。 - * - * @param registerBody 包含用户凭证的注册信息 - */ - public void doReset(RegisterBody registerBody); - - /** - * 验证TFA重置流程的第二步并完成重置。 - * - * @param registerBody 包含TFA验证码的注册信息 - */ - public void doResetVerify(RegisterBody registerBody); -} diff --git a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/impl/OauthUserServiceImpl.java b/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/impl/OauthUserServiceImpl.java deleted file mode 100644 index 4d4ebc0..0000000 --- a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/service/impl/OauthUserServiceImpl.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.ruoyi.auth.common.service.impl; - -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import com.ruoyi.auth.common.domain.OauthUser; -import com.ruoyi.auth.common.mapper.OauthUserMapper; -import com.ruoyi.auth.common.service.IOauthUserService; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.system.mapper.SysUserMapper; - -/** - * 第三方认证Service业务层处理 - * - * @author Dftre - * @date 2024-01-18 - */ -@Service -public class OauthUserServiceImpl implements IOauthUserService { - @Autowired - private OauthUserMapper oauthUserMapper; - - @Autowired - private SysUserMapper sysUserMapper; - - /** - * 查询第三方认证 - * - * @param id 第三方认证主键 - * @return 第三方认证 - */ - @Override - public OauthUser selectOauthUserById(Long id) { - return oauthUserMapper.selectOauthUserById(id); - } - - @Override - public OauthUser selectOauthUserByUUID(String uuid) { - return oauthUserMapper.selectOauthUserByUUID(uuid); - } - - @Override - public OauthUser selectOauthUserByUserId(Long userId) { - return oauthUserMapper.selectOauthUserByUserId(userId); - } - - /** - * 查询第三方认证列表 - * - * @param oauthUser 第三方认证 - * @return 第三方认证 - */ - @Override - public List selectOauthUserList(OauthUser oauthUser) { - return oauthUserMapper.selectOauthUserList(oauthUser); - } - - /** - * 新增第三方认证 - * - * @param oauthUser 第三方认证 - * @return 结果 - */ - @Override - public int insertOauthUser(OauthUser oauthUser) { - return oauthUserMapper.insertOauthUser(oauthUser); - } - - /** - * 修改第三方认证 - * - * @param oauthUser 第三方认证 - * @return 结果 - */ - @Override - public int updateOauthUser(OauthUser oauthUser) { - return oauthUserMapper.updateOauthUser(oauthUser); - } - - /** - * 批量删除第三方认证 - * - * @param ids 需要删除的第三方认证主键 - * @return 结果 - */ - @Override - public int deleteOauthUserByIds(Long[] ids) { - return oauthUserMapper.deleteOauthUserByIds(ids); - } - - /** - * 删除第三方认证信息 - * - * @param id 第三方认证主键 - * @return 结果 - */ - @Override - public int deleteOauthUserById(Long id) { - return oauthUserMapper.deleteOauthUserById(id); - } - - public SysUser selectSysUserByUUID(String uuid) { - OauthUser oauthUser = oauthUserMapper.selectOauthUserByUUID(uuid); - return sysUserMapper.selectUserById(oauthUser.getUserId()); - } - - /** - * 校验source平台是否绑定 - * - * @param userId 用户编号 - * @param source 绑定平台 - * @return 结果 - */ - public boolean checkAuthUser(Long userId, String source) { - return oauthUserMapper.checkAuthUser(userId, source) > 0; - }; - - /** - * 校验用户名称是否唯一 - * - * @param userName 用户名称 - * @return 结果 - */ - public boolean checkUserNameUnique(String userName) { - return oauthUserMapper.checkUserNameUnique(userName) > 0; - }; - - /** - * 校验手机号码是否唯一 - * - * @param phonenumber 手机号码 - * @return 结果 - */ - public boolean checkPhoneUnique(String phonenumber) { - return oauthUserMapper.checkPhoneUnique(phonenumber) > 0; - }; - - /** - * 校验email是否唯一 - * - * @param email 用户邮箱 - * @return 结果 - */ - public boolean checkEmailUnique(String email) { - return oauthUserMapper.checkEmailUnique(email) > 0; - }; -} diff --git a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/utils/RandomCodeUtil.java b/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/utils/RandomCodeUtil.java deleted file mode 100644 index 098e9c4..0000000 --- a/ruoyi-auth-common/src/main/java/com/ruoyi/auth/common/utils/RandomCodeUtil.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.ruoyi.auth.common.utils; - -import java.security.SecureRandom; - -public class RandomCodeUtil { - - public static String randomString(String characters, int length) { - StringBuilder result = new StringBuilder(); - SecureRandom random = new SecureRandom(); - - for (int i = 0; i < length; i++) { - int index = random.nextInt(characters.length()); - result.append(characters.charAt(index)); - } - - return result.toString(); - } - - public static String numberCode(int length) { - String characters = "0123456789"; - return randomString(characters, length); - } - - public static String code(int length) { - String characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - return randomString(characters, length); - } -} diff --git a/ruoyi-auth-common/src/main/resources/mapper/common/OauthUserMapper.xml b/ruoyi-auth-common/src/main/resources/mapper/common/OauthUserMapper.xml deleted file mode 100644 index c409d09..0000000 --- a/ruoyi-auth-common/src/main/resources/mapper/common/OauthUserMapper.xml +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - select id, uuid, user_id, source, access_token, expire_in, refresh_token, open_id, uid, access_code, union_id, scope, token_type, id_token, mac_algorithm, mac_key, code, oauth_token, oauth_token_secret from oauth_user - - - - - - - - - - - - - - - - - - - - insert into oauth_user - - id, - uuid, - user_id, - source, - access_token, - expire_in, - refresh_token, - open_id, - uid, - access_code, - union_id, - scope, - token_type, - id_token, - mac_algorithm, - mac_key, - code, - oauth_token, - oauth_token_secret, - - - #{id}, - #{uuid}, - #{userId}, - #{source}, - #{accessToken}, - #{expireIn}, - #{refreshToken}, - #{openId}, - #{uid}, - #{accessCode}, - #{unionId}, - #{scope}, - #{tokenType}, - #{idToken}, - #{macAlgorithm}, - #{macKey}, - #{code}, - #{oauthToken}, - #{oauthTokenSecret}, - - - - - update oauth_user - - uuid = #{uuid}, - user_id = #{userId}, - source = #{source}, - access_token = #{accessToken}, - expire_in = #{expireIn}, - refresh_token = #{refreshToken}, - open_id = #{openId}, - uid = #{uid}, - access_code = #{accessCode}, - union_id = #{unionId}, - scope = #{scope}, - token_type = #{tokenType}, - id_token = #{idToken}, - mac_algorithm = #{macAlgorithm}, - mac_key = #{macKey}, - code = #{code}, - oauth_token = #{oauthToken}, - oauth_token_secret = #{oauthTokenSecret}, - - where oauth_user.id = #{id} - - - - delete from oauth_user where id = #{id} - - - - delete from oauth_user where id in - - #{id} - - - \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java index a685e06..f789dd7 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java @@ -90,6 +90,9 @@ public class BaseController return rspData; } + + + /** * 返回成功 */ diff --git a/ruoyi-oauth-wx/pom.xml b/ruoyi-oauth-wx/pom.xml deleted file mode 100644 index 047724a..0000000 --- a/ruoyi-oauth-wx/pom.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - hfrj - com.ruoyi - 3.8.9 - - 4.0.0 - - ruoyi-oauth-wx - - - 微信认证模块 - - - - - - - com.ruoyi - ruoyi-auth-common - - - - com.ruoyi - ruoyi-common - - - - - - \ No newline at end of file diff --git a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/constant/WxMiniAppConstant.java b/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/constant/WxMiniAppConstant.java deleted file mode 100644 index 7dc9433..0000000 --- a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/constant/WxMiniAppConstant.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.ruoyi.oauth.wx.constant; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class WxMiniAppConstant { - @Value("${oauth.wx.miniapp.appId}") - private String appId; - - @Value("${oauth.wx.miniapp.appSecret}") - private String appSecret; - - @Value("${oauth.wx.miniapp.url}") - private String url; - - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getAppId() { - return appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } - - public String getAppSecret() { - return appSecret; - } - - public void setAppSecret(String appSecret) { - this.appSecret = appSecret; - } - -} diff --git a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/constant/WxPubConstant.java b/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/constant/WxPubConstant.java deleted file mode 100644 index ae7cda4..0000000 --- a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/constant/WxPubConstant.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.ruoyi.oauth.wx.constant; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class WxPubConstant { - @Value("${oauth.wx.pub.appId}") - private String appId; - - @Value("${oauth.wx.pub.appSecret}") - private String appSecret; - - @Value("${oauth.wx.pub.url}") - private String url; - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getAppId() { - return appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } - - public String getAppSecret() { - return appSecret; - } - - public void setAppSecret(String appSecret) { - this.appSecret = appSecret; - } - -} diff --git a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/controller/WxLoginController.java b/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/controller/WxLoginController.java deleted file mode 100644 index 22adeda..0000000 --- a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/controller/WxLoginController.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.ruoyi.oauth.wx.controller; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import com.ruoyi.auth.common.domain.OauthUser; -import com.ruoyi.auth.common.service.IOauthUserService; -import com.ruoyi.common.annotation.Anonymous; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.core.controller.BaseController; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.oauth.wx.service.Impl.WxMiniAppLoginServiceImpl; -import com.ruoyi.oauth.wx.service.Impl.WxPubLoginServiceImpl; - -@RestController -@RequestMapping("/oauth/wx") -public class WxLoginController extends BaseController { - - @Autowired - private IOauthUserService oauthUserService; - - @Autowired - private WxMiniAppLoginServiceImpl wxMiniAppLoginServiceImpl; - - @Autowired - private WxPubLoginServiceImpl wxPubLoginServiceImpl; - - @Anonymous - @PostMapping("/login/{source}/{code}") - public AjaxResult loginMiniApp(@PathVariable("source") String source, @PathVariable("code") String code) { - String token = null; - AjaxResult ajax = AjaxResult.success(); - if ("miniapp".equals(source)) { - token = wxMiniAppLoginServiceImpl.doLogin(code); - } else if ("pub".equals(source)) { - token = wxPubLoginServiceImpl.doLogin(code); - } else { - return error("错误的登录方式"); - } - ajax.put(Constants.TOKEN, token); - return ajax; - } - - @PostMapping("/register/{source}/{code}") - public AjaxResult register(@PathVariable("source") String source, @PathVariable("code") String code) { - OauthUser oauthUser = oauthUserService.selectOauthUserByUserId(getUserId()); - if (oauthUser != null) { - return error("不可以重复绑定"); - } else { - String msg = ""; - oauthUser = new OauthUser(); - oauthUser.setUserId(getUserId()); - oauthUser.setCode(code); - if ("miniapp".equals(source)) { - msg = wxMiniAppLoginServiceImpl.doRegister(oauthUser); - } else if ("pub".equals(source)) { - msg = wxPubLoginServiceImpl.doRegister(oauthUser); - } else { - return error("错误的注册方式"); - } - return StringUtils.isEmpty(msg) ? success() : error(msg); - } - } - -} diff --git a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/Impl/WxMiniAppLoginServiceImpl.java b/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/Impl/WxMiniAppLoginServiceImpl.java deleted file mode 100644 index ebea34e..0000000 --- a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/Impl/WxMiniAppLoginServiceImpl.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.ruoyi.oauth.wx.service.Impl; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import com.alibaba.fastjson2.JSONObject; -import com.ruoyi.auth.common.domain.OauthUser; -import com.ruoyi.auth.common.service.IOauthUserService; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.web.service.TokenService; -import com.ruoyi.framework.web.service.UserDetailsServiceImpl; -import com.ruoyi.oauth.wx.constant.WxMiniAppConstant; -import com.ruoyi.oauth.wx.service.WxLoginService; -import com.ruoyi.system.service.ISysUserService; - -@Service -public class WxMiniAppLoginServiceImpl implements WxLoginService { - @Autowired - private WxMiniAppConstant wxAppConstant; - - @Autowired - private TokenService tokenService; - - @Autowired - private UserDetailsServiceImpl userDetailsServiceImpl; - - @Autowired - private ISysUserService userService; - - @Autowired - private IOauthUserService oauthUserService; - - @Override - public String doLogin(String code) { - String openid = doAuth( - wxAppConstant.getUrl(), - wxAppConstant.getAppId(), - wxAppConstant.getAppSecret(), - code).getString("openid"); - OauthUser selectOauthUser = oauthUserService.selectOauthUserByUUID(openid); - if (selectOauthUser == null) { - return null; - } - SysUser sysUser = userService.selectUserById(selectOauthUser.getUserId()); - if (sysUser == null) { - throw new ServiceException("该微信未绑定用户"); - } - LoginUser loginUser = (LoginUser) userDetailsServiceImpl.createLoginUser(sysUser); - return tokenService.createToken(loginUser); - } - - @Override - public String doRegister(OauthUser oauthUser) { - if (StringUtils.isEmpty(oauthUser.getCode())) { - return "没有凭证"; - } - if (oauthUser.getUserId() == null) { - return "请先注册账号"; - } - JSONObject doAuth = doAuth( - wxAppConstant.getUrl(), - wxAppConstant.getAppId(), - wxAppConstant.getAppSecret(), - oauthUser.getCode()); - oauthUser.setOpenId(doAuth.getString("openid")); - oauthUser.setUuid(doAuth.getString("openid")); - oauthUser.setSource("WXMiniApp"); - oauthUser.setAccessToken(doAuth.getString("sessionKey")); - oauthUserService.insertOauthUser(oauthUser); - return ""; - } - -} diff --git a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/Impl/WxPubLoginServiceImpl.java b/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/Impl/WxPubLoginServiceImpl.java deleted file mode 100644 index 350fee1..0000000 --- a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/Impl/WxPubLoginServiceImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.ruoyi.oauth.wx.service.Impl; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import com.alibaba.fastjson2.JSONObject; -import com.ruoyi.auth.common.domain.OauthUser; -import com.ruoyi.auth.common.service.IOauthUserService; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.domain.model.LoginUser; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.web.service.TokenService; -import com.ruoyi.framework.web.service.UserDetailsServiceImpl; -import com.ruoyi.oauth.wx.constant.WxPubConstant; -import com.ruoyi.oauth.wx.service.WxLoginService; -import com.ruoyi.system.service.ISysUserService; - -@Service -public class WxPubLoginServiceImpl implements WxLoginService { - - @Autowired - private WxPubConstant wxH5Constant; - - @Autowired - private TokenService tokenService; - - @Autowired - private UserDetailsServiceImpl userDetailsServiceImpl; - - @Autowired - private ISysUserService userService; - - @Autowired - private IOauthUserService oauthUserService; - - @Override - public String doLogin(String code) { - String openid = doAuth( - wxH5Constant.getUrl(), - wxH5Constant.getAppId(), - wxH5Constant.getAppSecret(), - code).getString("openid"); - OauthUser selectOauthUser = oauthUserService.selectOauthUserByUUID(openid); - if (selectOauthUser == null) { - return null; - } - SysUser sysUser = userService.selectUserById(selectOauthUser.getUserId()); - if (sysUser == null) { - throw new ServiceException("该微信未绑定用户"); - } - LoginUser loginUser = (LoginUser) userDetailsServiceImpl.createLoginUser(sysUser); - return tokenService.createToken(loginUser); - } - - @Override - public String doRegister(OauthUser oauthUser) { - if (StringUtils.isEmpty(oauthUser.getCode())) { - return "没有凭证"; - } - if (oauthUser.getUserId() == null) { - return "请先注册账号"; - } - JSONObject doAuth = doAuth( - wxH5Constant.getUrl(), - wxH5Constant.getAppId(), - wxH5Constant.getAppSecret(), - oauthUser.getCode()); - oauthUser.setOpenId(doAuth.getString("openid")); - oauthUser.setUuid(doAuth.getString("openid")); - oauthUser.setSource("WXPub"); - oauthUser.setAccessToken(doAuth.getString("sessionKey")); - oauthUserService.insertOauthUser(oauthUser); - return ""; - } - -} diff --git a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/WxLoginService.java b/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/WxLoginService.java deleted file mode 100644 index 08b9455..0000000 --- a/ruoyi-oauth-wx/src/main/java/com/ruoyi/oauth/wx/service/WxLoginService.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.ruoyi.oauth.wx.service; - -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; -import com.ruoyi.auth.common.domain.OauthUser; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.http.HttpClientUtil; - -public interface WxLoginService { - - public String doLogin(String code); - - public String doRegister(OauthUser oauthUser); - - public default JSONObject doAuth(String url, String appid, String secret, String code) { - StringBuilder builder = new StringBuilder(url); - builder.append("?appid=").append(appid) - .append("&secret=").append(secret) - .append("&js_code=").append(code) - .append("&grant_type=").append("authorization_code"); - String getMessageUrl = builder.toString(); - String result = HttpClientUtil.sendHttpGet(getMessageUrl); - JSONObject jsonObject = JSON.parseObject(result); - if (jsonObject.containsKey("openid")) { - String openid = jsonObject.getString("openid"); - String sessionKey = jsonObject.getString("session_key"); - System.out.println("openid:" + openid); - System.out.println("sessionKey:" + sessionKey); - return jsonObject; - } else { - throw new ServiceException(jsonObject.getString("errmsg"), jsonObject.getIntValue("errcode")); - } - } -} diff --git a/ruoyi-oauth-wx/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/ruoyi-oauth-wx/src/main/resources/META-INF/additional-spring-configuration-metadata.json deleted file mode 100644 index e001d72..0000000 --- a/ruoyi-oauth-wx/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "properties": [ - { - "name": "oauth.wx.miniapp.app-id", - "type": "java.lang.String", - "description": "wx73d0202b3c8a6d68" - }, - { - "name": "oauth.wx.miniapp.app-secret", - "type": "java.lang.String", - "description": "c0871da0ca140930420c695147f3694b" - }, - { - "name": "oauth.wx.miniapp.url", - "type": "java.lang.String", - "description": "微信小程序认证地址" - } - - ] -} \ No newline at end of file diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml index 7666837..fe76162 100644 --- a/ruoyi-system/pom.xml +++ b/ruoyi-system/pom.xml @@ -24,13 +24,6 @@
- - - com.ruoyi - ruoyi-oauth-wx - - - com.qiniu diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/AppletController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/AppletController.java index 762098d..0eb40ff 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/AppletController.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/AppletController.java @@ -1,13 +1,18 @@ package com.ruoyi.system.controller; import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.system.ControllerUtil.WechatApiUtil; import com.ruoyi.system.domain.*; import com.ruoyi.system.service.*; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; @@ -16,8 +21,12 @@ import java.util.Map; import org.springframework.web.bind.annotation.RequestBody; import com.ruoyi.system.ControllerUtil.AppletControllerUtil; +import com.ruoyi.system.ControllerUtil.AppletLoginUtil; +import com.ruoyi.system.ControllerUtil.PageUtil; import static com.ruoyi.common.core.domain.AjaxResult.error; import static com.ruoyi.common.core.domain.AjaxResult.success; +import static com.ruoyi.common.utils.PageUtils.startPage; +import com.github.pagehelper.PageHelper; /** * 小程序控制器 @@ -35,7 +44,7 @@ import static com.ruoyi.common.core.domain.AjaxResult.success; * @version 1.0 */ @RestController -public class AppletController { +public class AppletController extends BaseController { @Autowired private IServiceCateService serviceCateService; @@ -47,6 +56,8 @@ public class AppletController { private IAdvImgService advImgService; @Autowired private IServiceGoodsService serviceGoodsService; + @Autowired + private IUserAddressService userAddressService; /** * 获取服务分类列表 * @@ -127,6 +138,113 @@ public class AppletController { } } + + + + @GetMapping(value = "/api/public/get/config") + public AjaxResult getconfig(HttpServletRequest request) { + SiteConfig configQuery = new SiteConfig(); + configQuery.setName("config_one"); + List list = siteConfigService.selectSiteConfigList(configQuery); + return success(list.get(0).getValue()); + } + + + + + /** + * 查询用户收货地址列表 + * + * @param limit 每页显示数量 + * @param page 页码 + * @param request HTTP请求对象 + * @return 分页地址列表 + * + * 请求参数: + * - limit: 每页显示数量,默认15 + * - page: 页码,默认1 + * + * 返回格式: + * { + * "code": 200, + * "msg": "OK", + * "data": { + * "current_page": 1, + * "data": [...], + * "from": 1, + * "last_page": 1, + * "next_page_url": null, + * "per_page": "15", + * "prev_page_url": null, + * "to": 1, + * "total": 1 + * } + * } + */ + @GetMapping("/api/user/address/list") + public AjaxResult getaddresslist( + @RequestParam(value = "limit", defaultValue = "15") int limit, + @RequestParam(value = "page", defaultValue = "1") int page, + HttpServletRequest request) { + try { + // 1. 验证分页参数 + Map pageValidation = PageUtil.validatePageParams(page, limit); + if (!(Boolean) pageValidation.get("valid")) { + return error((String) pageValidation.get("message")); + } + + // 2. 验证用户登录状态 + String token = request.getHeader("token"); + Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); + if (!(Boolean) userValidation.get("valid")) { + return error("用户未登录或token无效"); + } + + // 3. 获取用户信息 + Users user = (Users) userValidation.get("user"); + if (user == null) { + return error("用户信息获取失败"); + } + + // 4. 设置分页参数 + PageHelper.startPage(page, limit); + + // 5. 查询用户地址列表 + UserAddress userAddressQuery = new UserAddress(); + userAddressQuery.setUid(user.getId()); + List addressList = userAddressService.selectUserAddressList(userAddressQuery); + + // 6. 获取分页信息并构建响应 + TableDataInfo tableDataInfo = getDataTable(addressList); + + // 7. 构建符合要求的分页响应格式 + Map pageData = PageUtil.buildPageResponse(tableDataInfo, page, limit); + + return success(pageData); + + } catch (Exception e) { + System.err.println("查询用户地址列表异常:" + e.getMessage()); + return error("查询地址列表失败:" + e.getMessage()); + } + } + + + + + @PostMapping(value = "/api/user/login") + public AjaxResult getuserlogin(HttpServletRequest request) { + String token=request.getHeader("token"); + Users users=usersService.selectUsersByRememberToken(token); + if (users!=null){ + users.setRemember_token(users.getRememberToken()); + return success(users); + }else{ + return error("用户不存在"); + } + + } + + /** * 获取服务商品列表 * 前端参数格式:{cate_id: 18, keywords:"dfffff"} @@ -267,13 +385,14 @@ public class AppletController { /** * 微信用户登录接口 * - * @param params 请求参数(包含openid) + * @param params 请求参数 * @param request HTTP请求对象 * @return 登录结果 * * 请求参数格式: * { - * "openid": "微信用户openid" + * "usercode": "微信小程序登录code", + * "code": "手机号授权code" * } * * 返回数据格式: @@ -281,50 +400,59 @@ public class AppletController { * "code": 200, * "msg": "操作成功", * "data": { - * "success": true, - * "token": "用户token", - * "userInfo": {...}, - * "isNewUser": true/false, - * "message": "登录成功" + * "user": { + * "id": 用户ID, + * "phone": "手机号", + * "name": "用户名", + * "openid": "微信openid", + * "remember_token": "用户token", + * "avatar": "头像地址", + * "isNewUser": true/false + * } * } * } * - * 接口说明: - * - 验证微信openid的有效性 - * - 首次登录自动创建用户记录 - * - 再次登录更新用户信息和token - * - 生成用户唯一身份token - * - 支持用户信息同步更新 + * 登录流程: + * 1. 验证请求参数 + * 2. 通过usercode获取微信openid + * 3. 检查用户是否已存在(通过openid) + * 4. 如果用户存在,直接返回用户信息 + * 5. 如果用户不存在,获取手机号并创建/更新用户 */ - @PostMapping(value = "/api/wechat/login") + @PostMapping(value = "/api/user/phone/login") public AjaxResult wechatLogin(@RequestBody Map params, HttpServletRequest request) { - try { - // 1. 参数验证 - if (params == null || !params.containsKey("openid")) { - return error("请求参数不能为空,需要提供openid"); - } - - String openid = (String) params.get("openid"); - if (openid == null || openid.trim().isEmpty()) { - return error("openid不能为空"); - } - - // 2. 调用登录方法 - Map loginResult = AppletControllerUtil.wechatUserLogin(openid.trim(), usersService); - - // 3. 判断登录结果 - boolean success = (Boolean) loginResult.get("success"); - if (success) { - return success(loginResult); - } else { - return error((String) loginResult.get("message")); - } - - } catch (Exception e) { - return error("微信登录失败:" + e.getMessage()); - } + // 使用AppletLoginUtil执行完整的微信登录流程 + return AppletLoginUtil.executeWechatLogin(params, usersService); } + + + @GetMapping(value = "/api/user/info") + public AjaxResult getUserByPhone(HttpServletRequest request) { + String token=request.getHeader("token"); + Map order_num=new HashMap<>(); + Map goods_order_num=new HashMap<>(); + Users users=usersService.selectUsersByRememberToken(token); + if (users!=null){ + users.setRemember_token(users.getRememberToken()); + order_num.put("pending_accept",2); + order_num.put("pending_service",0); + order_num.put("in_service",0); + order_num.put("other_status",0); + users.setOrder_num(order_num); + goods_order_num.put("pending_accept",0); + goods_order_num.put("pending_service",3); + goods_order_num.put("in_service",0); + goods_order_num.put("other_status",0); + users.setGoods_order_num(goods_order_num); + return success(users); + }else{ + return error("用户不存在"); + } + + } + + /** * 验证用户token接口 * @@ -357,21 +485,17 @@ public class AppletController { public AjaxResult validateToken(HttpServletRequest request) { try { // 1. 获取token - String token = request.getHeader("token"); - if (token == null || token.trim().isEmpty()) { - // 尝试从Authorization头获取 - String authHeader = request.getHeader("Authorization"); - if (authHeader != null && authHeader.startsWith("Bearer ")) { - token = authHeader.substring(7); - } - } + String token = AppletLoginUtil.extractToken( + request.getHeader("Authorization"), + request.getHeader("token") + ); - if (token == null || token.trim().isEmpty()) { + if (token == null) { return error("未提供token,请先登录"); } // 2. 验证token - Map validateResult = AppletControllerUtil.validateUserToken(token.trim(), usersService); + Map validateResult = AppletLoginUtil.validateUserToken(token, usersService); // 3. 返回验证结果 boolean valid = (Boolean) validateResult.get("valid"); @@ -392,7 +516,7 @@ public class AppletController { /** * 获取服务商品详细信息 * - * @param id 商品ID + * @param params 商品ID * @param request HTTP请求对象 * @return 商品详细信息 * @@ -455,7 +579,7 @@ public class AppletController { try { // 1. 验证用户登录状态 String token = request.getHeader("token"); - Map userValidation = AppletControllerUtil.validateUserToken(token, usersService); + Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); if (!(Boolean) userValidation.get("valid")) { return error("用户未登录或token无效"); } @@ -488,7 +612,7 @@ public class AppletController { try { // 1. 验证用户登录状态 String token = request.getHeader("token"); - Map userValidation = AppletControllerUtil.validateUserToken(token, usersService); + Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); if (!(Boolean) userValidation.get("valid")) { return error("用户未登录或token无效"); } @@ -532,7 +656,7 @@ public class AppletController { try { // 1. 验证用户登录状态 String token = request.getHeader("token"); - Map userValidation = AppletControllerUtil.validateUserToken(token, usersService); + Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); if (!(Boolean) userValidation.get("valid")) { return error("用户未登录或token无效"); } @@ -612,7 +736,7 @@ public class AppletController { try { // 1. 验证用户登录状态 String token = request.getHeader("token"); - Map userValidation = AppletControllerUtil.validateUserToken(token, usersService); + Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); if (!(Boolean) userValidation.get("valid")) { return error("用户未登录或token无效"); } @@ -653,7 +777,7 @@ public class AppletController { try { // 1. 验证用户登录状态(这里可能需要管理员权限) String token = request.getHeader("token"); - Map userValidation = AppletControllerUtil.validateUserToken(token, usersService); + Map userValidation = AppletLoginUtil.validateUserToken(token, usersService); if (!(Boolean) userValidation.get("valid")) { return error("用户未登录或token无效"); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/AppletLoginUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/AppletLoginUtil.java new file mode 100644 index 0000000..54ae475 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/AppletLoginUtil.java @@ -0,0 +1,374 @@ +package com.ruoyi.system.ControllerUtil; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.system.domain.Users; +import com.ruoyi.system.service.IUsersService; + +import java.util.HashMap; +import java.util.Map; + +import static com.ruoyi.common.core.domain.AjaxResult.error; +import static com.ruoyi.common.core.domain.AjaxResult.success; + +/** + * 小程序登录工具类 + * + * 提供微信小程序登录相关的工具方法 + * 主要功能: + * 1. 参数验证和提取 + * 2. 微信openid获取 + * 3. 用户手机号获取 + * 4. 用户创建和更新 + * 5. 登录响应构建 + * + * @author Mr. Zhang Pan + * @date 2025-01-03 + * @version 1.0 + */ +public class AppletLoginUtil { + + /** + * 执行微信登录完整流程 + * + * @param params 登录参数 + * @param usersService 用户服务 + * @return 登录结果 + */ + public static AjaxResult executeWechatLogin(Map params, IUsersService usersService) { + try { + // 1. 参数验证 + String jsCode = validateAndExtractParam(params, "usercode", "微信登录code不能为空"); + String phoneCode = validateAndExtractParam(params, "code", "手机号授权code不能为空"); + + // 2. 获取微信openid + String openid = getWechatOpenid(jsCode); + if (openid == null) { + return error("获取微信openid失败,请重试"); + } + + // 3. 检查用户是否已存在 + Users existingUser = usersService.selectUsersByOpenid(openid); + if (existingUser != null) { + existingUser.setRemember_token(existingUser.getRememberToken()); + return buildSuccessResponse(existingUser, false); + } + + // 4. 新用户处理:获取手机号并创建用户 + String phoneNumber = getPhoneNumber(phoneCode); + if (phoneNumber == null) { + return error("获取手机号失败,请重新授权"); + } + + // 5. 创建或更新用户 + Users user = createOrUpdateUser(openid, phoneNumber, usersService); + user.setRemember_token(user.getRememberToken()); + return buildSuccessResponse(user, true); + + } catch (IllegalArgumentException e) { + return error(e.getMessage()); + } catch (Exception e) { + System.err.println("微信登录异常:" + e.getMessage()); + e.printStackTrace(); + return error("微信登录失败,请稍后重试"); + } + } + + /** + * 验证并提取请求参数 + * + * @param params 参数Map + * @param key 参数键 + * @param errorMessage 错误信息 + * @return 参数值 + * @throws IllegalArgumentException 参数无效时抛出 + */ + public static String validateAndExtractParam(Map params, String key, String errorMessage) { + Object value = params.get(key); + if (value == null || value.toString().trim().isEmpty()) { + throw new IllegalArgumentException(errorMessage); + } + return value.toString().trim(); + } + + /** + * 获取微信openid + * + * @param jsCode 微信小程序登录code + * @return openid或null + */ + public static String getWechatOpenid(String jsCode) { + try { + Map openidResult = WechatApiUtil.getWechatUserOpenidInfo(jsCode); + + if (!(Boolean) openidResult.get("success")) { + System.err.println("获取openid失败:" + openidResult.get("errorMsg")); + return null; + } + + String openid = (String) openidResult.get("openid"); + if (openid == null || openid.trim().isEmpty()) { + System.err.println("微信API返回的openid为空"); + return null; + } + + System.out.println("成功获取微信openid:" + openid.substring(0, 8) + "..."); + return openid; + + } catch (Exception e) { + System.err.println("获取微信openid异常:" + e.getMessage()); + return null; + } + } + + /** + * 获取用户手机号 + * + * @param phoneCode 手机号授权code + * @return 手机号或null + */ + public static String getPhoneNumber(String phoneCode) { + try { + Map phoneResult = WechatApiUtil.getPhoneNumberByCode(phoneCode); + + if (!(Boolean) phoneResult.get("success")) { + System.err.println("获取手机号失败:" + phoneResult.get("errorMsg")); + return null; + } + + Map phoneInfo = (Map) phoneResult.get("phone_info"); + if (phoneInfo == null) { + System.err.println("微信API返回的手机号信息为空"); + return null; + } + + String phoneNumber = (String) phoneInfo.get("phoneNumber"); + if (phoneNumber == null || phoneNumber.trim().isEmpty()) { + System.err.println("微信API返回的手机号为空"); + return null; + } + + System.out.println("成功获取用户手机号:" + phoneNumber.substring(0, 3) + "****" + phoneNumber.substring(7)); + return phoneNumber; + + } catch (Exception e) { + System.err.println("获取用户手机号异常:" + e.getMessage()); + return null; + } + } + + /** + * 创建或更新用户 + * + * @param openid 微信openid + * @param phoneNumber 手机号 + * @param usersService 用户服务 + * @return 用户对象 + * @throws Exception 操作失败时抛出 + */ + public static Users createOrUpdateUser(String openid, String phoneNumber, IUsersService usersService) throws Exception { + // 1. 生成用户token + String token = WechatApiUtil.generateUserToken(openid); + if (token == null || token.trim().isEmpty()) { + throw new Exception("生成用户token失败"); + } + + // 2. 检查手机号是否已存在 + Users existingUserByPhone = usersService.selectUsersByPhone(phoneNumber); + + if (existingUserByPhone != null) { + // 3. 更新已有用户的openid和token + return updateExistingUser(existingUserByPhone, openid, token, usersService); + } else { + // 4. 创建新用户 + return createNewUser(openid, phoneNumber, token, usersService); + } + } + + /** + * 更新已有用户信息 + * + * @param user 已有用户 + * @param openid 微信openid + * @param token 新token + * @param usersService 用户服务 + * @return 更新后的用户 + * @throws Exception 更新失败时抛出 + */ + public static Users updateExistingUser(Users user, String openid, String token, IUsersService usersService) throws Exception { + try { + user.setOpenid(openid); + user.setRememberToken(token); + + int updateResult = usersService.updateUsers(user); + if (updateResult <= 0) { + throw new Exception("更新用户信息失败"); + } + + System.out.println("成功更新用户信息,用户ID:" + user.getId()); + return user; + + } catch (Exception e) { + System.err.println("更新用户信息异常:" + e.getMessage()); + throw new Exception("更新用户信息失败:" + e.getMessage()); + } + } + + /** + * 创建新用户 + * + * @param openid 微信openid + * @param phoneNumber 手机号 + * @param token 用户token + * @param usersService 用户服务 + * @return 新创建的用户 + * @throws Exception 创建失败时抛出 + */ + public static Users createNewUser(String openid, String phoneNumber, String token, IUsersService usersService) throws Exception { + try { + Users newUser = new Users(); + newUser.setPhone(phoneNumber); + newUser.setRememberToken(token); + newUser.setName("微信用户"); + newUser.setOpenid(openid); + newUser.setAvatar("/default/user_avatar.jpeg"); + newUser.setType("1"); + newUser.setStatus(1); + newUser.setIsWork(0); + + int insertResult = usersService.insertUsers(newUser); + if (insertResult <= 0) { + throw new Exception("创建用户记录失败"); + } + + System.out.println("成功创建新用户,手机号:" + phoneNumber.substring(0, 3) + "****" + phoneNumber.substring(7)); + return newUser; + + } catch (Exception e) { + System.err.println("创建新用户异常:" + e.getMessage()); + throw new Exception("创建用户失败:" + e.getMessage()); + } + } + + /** + * 构建成功响应 + * + * @param user 用户对象 + * @param isNewUser 是否为新用户 + * @return 响应结果 + */ + public static AjaxResult buildSuccessResponse(Users user, boolean isNewUser) { + try { + // 设置返回字段 + user.setRemember_token(user.getRememberToken()); + + // 构建响应数据 + Map responseData = new HashMap<>(); + responseData.put("user", user); + responseData.put("isNewUser", isNewUser); + responseData.put("loginTime", System.currentTimeMillis()); + responseData.put("message", isNewUser ? "注册成功" : "登录成功"); + + System.out.println((isNewUser ? "用户注册成功" : "用户登录成功") + ",用户ID:" + user.getId()); + return success(responseData); + + } catch (Exception e) { + System.err.println("构建响应数据异常:" + e.getMessage()); + return error("登录成功但返回数据异常"); + } + } + + /** + * 验证用户token并获取用户信息 + * + * @param token 用户token + * @param usersService 用户服务 + * @return 验证结果 + */ + public static Map validateUserToken(String token, IUsersService usersService) { + Map result = new HashMap<>(); + + try { + if (token == null || token.trim().isEmpty()) { + result.put("valid", false); + result.put("message", "token不能为空"); + return result; + } + + // 验证token格式 + if (!WechatApiUtil.isValidTokenFormat(token.trim())) { + result.put("valid", false); + result.put("message", "token格式无效"); + return result; + } + + // 查询用户信息 + Users user = usersService.selectUsersByRememberToken(token.trim()); + if (user == null) { + result.put("valid", false); + result.put("message", "token无效或用户不存在"); + return result; + } + + // 检查用户状态 + if (user.getStatus() == null || user.getStatus() != 1) { + result.put("valid", false); + result.put("message", "用户账号已被禁用"); + return result; + } + + // 设置返回字段并过滤敏感信息 + user.setRemember_token(user.getRememberToken()); + + result.put("valid", true); + result.put("user", user); + result.put("message", "token验证成功"); + + } catch (Exception e) { + result.put("valid", false); + result.put("message", "token验证异常:" + e.getMessage()); + System.err.println("验证用户token异常:" + e.getMessage()); + } + + return result; + } + + /** + * 获取并验证请求头中的token + * + * @param authHeader Authorization请求头 + * @param tokenHeader token请求头 + * @return 提取的token或null + */ + public static String extractToken(String authHeader, String tokenHeader) { + String token = tokenHeader; + + // 如果token头为空,尝试从Authorization头获取 + if (token == null || token.trim().isEmpty()) { + if (authHeader != null && authHeader.startsWith("Bearer ")) { + token = authHeader.substring(7); + } + } + + return (token != null && !token.trim().isEmpty()) ? token.trim() : null; + } + + /** + * 构建登录失败响应 + * + * @param message 失败信息 + * @return 失败响应 + */ + public static AjaxResult buildFailureResponse(String message) { + return error("登录失败:" + message); + } + + /** + * 构建参数错误响应 + * + * @param paramName 参数名 + * @return 错误响应 + */ + public static AjaxResult buildParamErrorResponse(String paramName) { + return error("参数错误:" + paramName + " 不能为空或格式不正确"); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/PageUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/PageUtil.java new file mode 100644 index 0000000..653d201 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/PageUtil.java @@ -0,0 +1,155 @@ +package com.ruoyi.system.ControllerUtil; + +import com.ruoyi.common.core.page.TableDataInfo; + +import java.util.HashMap; +import java.util.Map; + +/** + * 分页工具类 + * + * 提供小程序端分页相关的工具方法 + * 主要功能: + * 1. 构建标准分页响应格式 + * 2. 分页参数计算 + * 3. 分页URL构建 + * + * @author Mr. Zhang Pan + * @date 2025-01-03 + * @version 1.0 + */ +public class PageUtil { + + /** + * 构建小程序端标准分页响应数据 + * + * @param tableDataInfo 分页表格数据 + * @param currentPage 当前页码 + * @param perPage 每页数量 + * @return 分页响应数据 + * + * 返回格式: + * { + * "current_page": 1, + * "data": [...], + * "from": 1, + * "last_page": 1, + * "next_page_url": "?page=2&limit=15", + * "per_page": "15", + * "prev_page_url": null, + * "to": 15, + * "total": 25 + * } + */ + public static Map buildPageResponse(TableDataInfo tableDataInfo, int currentPage, int perPage) { + Map pageData = new HashMap<>(); + + // 计算分页信息 + long total = tableDataInfo.getTotal(); + int lastPage = (int) Math.ceil((double) total / perPage); + int from = total > 0 ? (currentPage - 1) * perPage + 1 : 0; + int to = (int) Math.min(currentPage * perPage, total); + + // 构建分页URL(这里简化处理,实际项目中可以根据需要构建完整URL) + String nextPageUrl = (currentPage < lastPage) ? "?page=" + (currentPage + 1) + "&limit=" + perPage : null; + String prevPageUrl = (currentPage > 1) ? "?page=" + (currentPage - 1) + "&limit=" + perPage : null; + + // 设置分页数据 + pageData.put("current_page", currentPage); + pageData.put("data", tableDataInfo.getRows()); + pageData.put("from", from); + pageData.put("last_page", lastPage); + pageData.put("next_page_url", nextPageUrl); + pageData.put("per_page", String.valueOf(perPage)); + pageData.put("prev_page_url", prevPageUrl); + pageData.put("to", to); + pageData.put("total", total); + + return pageData; + } + + /** + * 构建简单的分页响应数据(不包含URL) + * + * @param tableDataInfo 分页表格数据 + * @param currentPage 当前页码 + * @param perPage 每页数量 + * @return 简单分页响应数据 + */ + public static Map buildSimplePageResponse(TableDataInfo tableDataInfo, int currentPage, int perPage) { + Map pageData = new HashMap<>(); + + // 计算分页信息 + long total = tableDataInfo.getTotal(); + int lastPage = (int) Math.ceil((double) total / perPage); + int from = total > 0 ? (currentPage - 1) * perPage + 1 : 0; + int to = (int) Math.min(currentPage * perPage, total); + + // 设置分页数据(不包含URL) + pageData.put("current_page", currentPage); + pageData.put("data", tableDataInfo.getRows()); + pageData.put("from", from); + pageData.put("last_page", lastPage); + pageData.put("per_page", String.valueOf(perPage)); + pageData.put("to", to); + pageData.put("total", total); + + return pageData; + } + + /** + * 计算总页数 + * + * @param total 总记录数 + * @param perPage 每页数量 + * @return 总页数 + */ + public static int calculateTotalPages(long total, int perPage) { + return (int) Math.ceil((double) total / perPage); + } + + /** + * 计算偏移量 + * + * @param page 页码 + * @param limit 每页数量 + * @return 偏移量 + */ + public static int calculateOffset(int page, int limit) { + return (page - 1) * limit; + } + + /** + * 验证分页参数 + * + * @param page 页码 + * @param limit 每页数量 + * @return 验证结果 + */ + public static Map validatePageParams(int page, int limit) { + Map result = new HashMap<>(); + + if (page < 1) { + result.put("valid", false); + result.put("message", "页码不能小于1"); + return result; + } + + if (limit < 1) { + result.put("valid", false); + result.put("message", "每页数量不能小于1"); + return result; + } + + if (limit > 100) { + result.put("valid", false); + result.put("message", "每页数量不能超过100"); + return result; + } + + result.put("valid", true); + result.put("page", page); + result.put("limit", limit); + return result; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WechatApiUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WechatApiUtil.java index c7c4d6f..a7286c8 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WechatApiUtil.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/WechatApiUtil.java @@ -7,10 +7,16 @@ import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; +import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; /** * 微信API工具类 @@ -21,6 +27,7 @@ import java.util.UUID; * 2. 用户token生成和管理 * 3. 微信API调用封装 * 4. 用户信息处理 + * 5. access_token缓存管理 * * @author Mr. Zhang Pan * @date 2025-01-03 @@ -35,13 +42,20 @@ public class WechatApiUtil { private static final String WECHAT_APPID = "wx73d0202b3c8a6d68"; // 微信小程序AppID private static final String WECHAT_SECRET = "c0871da0ca140930420c695147f3694b"; // 微信小程序Secret private static final String WECHAT_API_BASE_URL = "https://api.weixin.qq.com"; // 微信API基础URL - + /** * Token配置常量 */ private static final String TOKEN_PREFIX = "WECHAT_USER_"; // Token前缀 private static final long TOKEN_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000; // Token过期时间(7天) + /** + * access_token缓存相关 + */ + private static final Map ACCESS_TOKEN_CACHE = new ConcurrentHashMap<>(); + private static final String CACHE_KEY_ACCESS_TOKEN = "access_token"; + private static final String CACHE_KEY_EXPIRE_TIME = "expire_time"; + /** * RestTemplate实例,用于HTTP请求 */ @@ -107,6 +121,83 @@ public class WechatApiUtil { return result; } + /** + * 通过js_code获取微信用户的openid和session_key + * + * @param js_code 微信小程序登录时获取的code + * @return 包含openid和session_key的结果Map + * + * 返回数据结构: + * { + * "success": true/false, // 是否成功 + * "openid": "用户openid", // 用户openid + * "session_key": "会话密钥", // 会话密钥 + * "unionid": "用户unionid", // 用户unionid(如果有) + * "errorMsg": "错误信息" // 错误信息(失败时返回) + * } + */ + public static Map getWechatUserOpenidInfo(String js_code) { + Map result = new HashMap<>(); + + try { + // 1. 参数验证 + if (js_code == null || js_code.trim().isEmpty()) { + result.put("success", false); + result.put("errorMsg", "js_code不能为空"); + return result; + } + + // 2. 构建请求URL - 调用微信jscode2session接口 + String url = WECHAT_API_BASE_URL + "/sns/jscode2session" + + "?grant_type=authorization_code" + + "&appid=" + WECHAT_APPID + + "&js_code=" + js_code.trim() + + "&secret=" + WECHAT_SECRET; + + System.out.println("请求微信API: " + url); + + // 3. 发起HTTP请求 + ResponseEntity response = restTemplate.getForEntity(url, String.class); + System.out.println("微信API响应: " + response.getBody()); + + // 4. 处理响应结果 + if (response.getStatusCode().is2xxSuccessful()) { + JSONObject jsonResponse = JSONObject.parseObject(response.getBody()); + + // 检查是否有错误 + if (jsonResponse.containsKey("errcode")) { + result.put("success", false); + result.put("errorMsg", "微信API返回错误:" + + jsonResponse.getString("errmsg") + + " (错误码:" + jsonResponse.getString("errcode") + ")"); + } else if (jsonResponse.containsKey("openid")) { + // 成功获取用户信息 + result.put("success", true); + result.put("openid", jsonResponse.getString("openid")); + result.put("session_key", jsonResponse.getString("session_key")); + + // unionid是可选的,只有在满足一定条件时才会返回 + if (jsonResponse.containsKey("unionid")) { + result.put("unionid", jsonResponse.getString("unionid")); + } + } else { + result.put("success", false); + result.put("errorMsg", "微信API返回数据格式异常"); + } + } else { + result.put("success", false); + result.put("errorMsg", "HTTP请求失败,状态码:" + response.getStatusCode()); + } + + } catch (Exception e) { + result.put("success", false); + result.put("errorMsg", "获取openid时发生异常:" + e.getMessage()); + e.printStackTrace(); + } + + return result; + } + /** * 检查openid格式是否正确 * @@ -175,76 +266,240 @@ public class WechatApiUtil { } /** - * 生成用户token + * 获取微信小程序全局唯一后台接口调用凭据access_token + * + * @return access_token信息Map + * + * 返回数据结构: + * { + * "success": true/false, // 是否成功 + * "access_token": "访问令牌", // 访问令牌 + * "expires_in": 7200, // 凭证有效时间(秒) + * "errorMsg": "错误信息" // 错误信息(失败时返回) + * } + * + * 注意: + * 1. access_token的有效期通常为2小时 + * 2. 建议将access_token缓存起来,避免频繁调用 + * 3. 重复获取将导致上次获取的access_token失效 + */ + public static Map getAccessToken() { + Map result = new HashMap<>(); + + try { + // 1. 构建请求URL + String url = WECHAT_API_BASE_URL + "/cgi-bin/token" + + "?grant_type=client_credential" + + "&appid=" + WECHAT_APPID + + "&secret=" + WECHAT_SECRET; + + System.out.println("获取access_token请求: " + url); + + // 2. 发起HTTP请求 + ResponseEntity response = restTemplate.getForEntity(url, String.class); + System.out.println("access_token响应: " + response.getBody()); + + // 3. 处理响应结果 + if (response.getStatusCode().is2xxSuccessful()) { + JSONObject jsonResponse = JSONObject.parseObject(response.getBody()); + + if (jsonResponse.containsKey("access_token")) { + // 成功获取access_token + result.put("success", true); + result.put("access_token", jsonResponse.getString("access_token")); + result.put("expires_in", jsonResponse.getInteger("expires_in")); + } else { + // 获取失败 + result.put("success", false); + result.put("errorMsg", "获取access_token失败:" + + jsonResponse.getString("errmsg") + + " (错误码:" + jsonResponse.getString("errcode") + ")"); + } + } else { + result.put("success", false); + result.put("errorMsg", "HTTP请求失败,状态码:" + response.getStatusCode()); + } + + } catch (Exception e) { + result.put("success", false); + result.put("errorMsg", "获取access_token异常:" + e.getMessage()); + e.printStackTrace(); + } + + return result; + } + + /** + * 生成业务系统用户token(基于微信openid) * * @param openid 微信用户openid + * @param sessionKey 微信会话密钥(可选) * @return 生成的token字符串 * * Token生成规则: - * 1. 使用UUID + openid + 时间戳生成基础字符串 - * 2. 通过MD5加密生成32位token - * 3. 添加前缀标识 - * 4. 确保token的唯一性 + * 1. 使用UUID + openid + sessionKey + 时间戳生成基础字符串 + * 2. 通过MD5加密生成32位字符串 + * 3. 转换为Base62编码(包含大小写字母和数字) + * 4. 截取固定长度确保token格式一致 * - * Token格式:WECHAT_USER_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + * Token格式:40位大小写字母和数字组合,如:WPbgh6AvhsEm18AOsrlbbjctA9DjsPUChFKeM6FG */ - public static String generateUserToken(String openid) { + public static String generateUserToken(String openid, String sessionKey) { try { - // 1. 构建基础字符串:UUID + openid + 当前时间戳 + // 1. 构建基础字符串 String baseString = UUID.randomUUID().toString().replace("-", "") + - openid + - System.currentTimeMillis(); + (openid != null ? openid : "") + + (sessionKey != null ? sessionKey : "") + + System.currentTimeMillis() + + Math.random(); // 增加随机性 - // 2. 使用MD5加密生成32位token + // 2. 使用MD5加密生成32位字符串 MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] digest = md5.digest(baseString.getBytes("UTF-8")); - // 3. 将字节数组转换为16进制字符串 - StringBuilder token = new StringBuilder(); - for (byte b : digest) { - token.append(String.format("%02x", b)); + // 3. 将字节数组转换为Base62格式 + String token = bytesToBase62(digest); + + // 4. 确保token长度为40位 + if (token.length() < 40) { + // 如果长度不足,补充随机字符 + token = token + generateRandomBase62String(40 - token.length()); + } else if (token.length() > 40) { + // 如果长度超出,截取前40位 + token = token.substring(0, 40); } - // 4. 添加前缀并返回 - return TOKEN_PREFIX + token.toString(); + return token; } catch (Exception e) { - // 如果加密失败,使用UUID作为备用方案 - return TOKEN_PREFIX + UUID.randomUUID().toString().replace("-", ""); + e.printStackTrace(); + // 如果加密失败,使用备用方案生成40位随机字符串 + return generateRandomBase62String(40); } } /** - * 验证token格式是否正确 + * 将字节数组转换为Base62编码字符串 + * + * @param bytes 字节数组 + * @return Base62编码字符串 + * + * Base62字符集:0-9, a-z, A-Z(共62个字符) + */ + private static String bytesToBase62(byte[] bytes) { + // Base62字符集 + String base62Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + StringBuilder result = new StringBuilder(); + + // 将字节数组转换为正数 + java.math.BigInteger bigInt = new java.math.BigInteger(1, bytes); + java.math.BigInteger base = java.math.BigInteger.valueOf(62); + + // 转换为Base62 + while (bigInt.compareTo(java.math.BigInteger.ZERO) > 0) { + int remainder = bigInt.remainder(base).intValue(); + result.insert(0, base62Chars.charAt(remainder)); + bigInt = bigInt.divide(base); + } + + return result.toString(); + } + + /** + * 生成指定长度的随机Base62字符串 + * + * @param length 字符串长度 + * @return 随机Base62字符串 + */ + private static String generateRandomBase62String(int length) { + String base62Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + StringBuilder result = new StringBuilder(); + + java.util.Random random = new java.util.Random(); + for (int i = 0; i < length; i++) { + result.append(base62Chars.charAt(random.nextInt(base62Chars.length()))); + } + + return result.toString(); + } + + /** + * 验证token格式是否正确(新格式) * * @param token 用户token * @return 格式是否正确 * * 验证规则: * - 不能为空 - * - 必须以指定前缀开头 - * - 长度符合要求 - * - 只包含字母和数字 + * - 长度必须为40位 + * - 只包含数字、小写字母、大写字母 */ public static boolean isValidTokenFormat(String token) { if (token == null || token.isEmpty()) { return false; } - // 检查前缀 - if (!token.startsWith(TOKEN_PREFIX)) { + // 检查长度(40位) + if (token.length() != 40) { return false; } - // 检查长度(前缀 + 32位MD5) - int expectedLength = TOKEN_PREFIX.length() + 32; - if (token.length() != expectedLength) { - return false; + // 检查字符集(只允许数字、小写字母、大写字母) + return token.matches("^[0-9a-zA-Z]{40}$"); + } + + /** + * 生成高强度token(增强版) + * + * @param openid 微信用户openid + * @param sessionKey 微信会话密钥 + * @param additionalSalt 额外的盐值(可选) + * @return 生成的高强度token + * + * 特点: + * 1. 使用多重哈希 + * 2. 添加时间戳和随机数 + * 3. 确保唯一性和安全性 + */ + public static String generateSecureUserToken(String openid, String sessionKey, String additionalSalt) { + try { + // 1. 构建复杂的基础字符串 + String baseString = String.join(":", + UUID.randomUUID().toString(), + openid != null ? openid : "", + sessionKey != null ? sessionKey : "", + additionalSalt != null ? additionalSalt : "", + String.valueOf(System.currentTimeMillis()), + String.valueOf(System.nanoTime()), + String.valueOf(Math.random()) + ); + + // 2. 第一次MD5哈希 + MessageDigest md5 = MessageDigest.getInstance("MD5"); + byte[] firstHash = md5.digest(baseString.getBytes("UTF-8")); + + // 3. 第二次SHA-1哈希(增强安全性) + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + byte[] secondHash = sha1.digest(firstHash); + + // 4. 转换为Base62格式 + String token = bytesToBase62(secondHash); + + // 5. 确保token长度为40位 + if (token.length() < 40) { + token = token + generateRandomBase62String(40 - token.length()); + } else if (token.length() > 40) { + token = token.substring(0, 40); + } + + return token; + + } catch (Exception e) { + e.printStackTrace(); + // 备用方案 + return generateRandomBase62String(40); } - - // 检查token部分只包含字母和数字 - String tokenPart = token.substring(TOKEN_PREFIX.length()); - return tokenPart.matches("^[a-zA-Z0-9]+$"); } /** @@ -279,53 +534,6 @@ public class WechatApiUtil { return response; } - /** - * 获取access_token(如果需要调用微信API) - * - * @return access_token信息 - * - * 注意:实际项目中需要: - * 1. 调用微信获取access_token接口 - * 2. 缓存access_token避免频繁调用 - * 3. 处理access_token过期刷新 - */ - public static Map getAccessToken() { - Map result = new HashMap<>(); - - try { - // 构建请求URL - String url = WECHAT_API_BASE_URL + "/cgi-bin/token" + - "?grant_type=client_credential" + - "&appid=" + WECHAT_APPID + - "&secret=" + WECHAT_SECRET; - - // 发起HTTP请求 - ResponseEntity response = restTemplate.getForEntity(url, String.class); - - if (response.getStatusCode().is2xxSuccessful()) { - JSONObject jsonResponse = JSONObject.parseObject(response.getBody()); - - if (jsonResponse.containsKey("access_token")) { - result.put("success", true); - result.put("access_token", jsonResponse.getString("access_token")); - result.put("expires_in", jsonResponse.getInteger("expires_in")); - } else { - result.put("success", false); - result.put("errorMsg", "获取access_token失败:" + jsonResponse.getString("errmsg")); - } - } else { - result.put("success", false); - result.put("errorMsg", "HTTP请求失败,状态码:" + response.getStatusCode()); - } - - } catch (Exception e) { - result.put("success", false); - result.put("errorMsg", "获取access_token异常:" + e.getMessage()); - } - - return result; - } - /** * 解析token获取用户标识 * @@ -359,4 +567,611 @@ public class WechatApiUtil { return result; } + + /** + * 获取带缓存的access_token + * + * @return access_token信息Map + * + * 功能特点: + * 1. 自动缓存access_token + * 2. 检查缓存有效期 + * 3. 过期自动刷新 + * 4. 线程安全 + */ + public static Map getCachedAccessToken() { + Map result = new HashMap<>(); + + try { + // 1. 检查缓存中是否有有效的access_token + if (isAccessTokenCacheValid()) { + result.put("success", true); + result.put("access_token", ACCESS_TOKEN_CACHE.get(CACHE_KEY_ACCESS_TOKEN)); + result.put("expires_in", getRemainingExpireTime()); + result.put("fromCache", true); + return result; + } + + // 2. 缓存无效,重新获取access_token + Map tokenResult = getAccessToken(); + + if ((Boolean) tokenResult.get("success")) { + // 3. 获取成功,更新缓存 + String accessToken = (String) tokenResult.get("access_token"); + Integer expiresIn = (Integer) tokenResult.get("expires_in"); + + updateAccessTokenCache(accessToken, expiresIn); + + result.put("success", true); + result.put("access_token", accessToken); + result.put("expires_in", expiresIn); + result.put("fromCache", false); + } else { + // 4. 获取失败 + result = tokenResult; + } + + } catch (Exception e) { + result.put("success", false); + result.put("errorMsg", "获取缓存access_token异常:" + e.getMessage()); + e.printStackTrace(); + } + + return result; + } + + /** + * 完整的微信小程序登录流程 + * + * @param js_code 微信小程序登录code + * @return 登录结果Map + * + * 流程说明: + * 1. 通过js_code获取openid和session_key + * 2. 验证openid有效性 + * 3. 生成业务系统token + * 4. 构建登录响应数据 + * + * 返回数据结构: + * { + * "success": true/false, + * "openid": "用户openid", + * "token": "业务系统token", + * "session_key": "微信会话密钥", + * "userInfo": {...}, + * "loginTime": 登录时间戳, + * "expireTime": token过期时间戳, + * "errorMsg": "错误信息" + * } + */ + public static Map wechatLogin(String js_code) { + Map result = new HashMap<>(); + + try { + // 1. 获取openid和session_key + Map openidResult = getWechatUserOpenidInfo(js_code); + + if (!(Boolean) openidResult.get("success")) { + result.put("success", false); + result.put("errorMsg", "获取openid失败:" + openidResult.get("errorMsg")); + return result; + } + + String openid = (String) openidResult.get("openid"); + String sessionKey = (String) openidResult.get("session_key"); + String unionid = (String) openidResult.get("unionid"); + + // 2. 验证openid + Map validateResult = validateOpenid(openid); + if (!(Boolean) validateResult.get("valid")) { + result.put("success", false); + result.put("errorMsg", "openid验证失败:" + validateResult.get("errorMsg")); + return result; + } + + // 3. 生成业务系统token + String userToken = generateUserToken(openid, sessionKey); + + // 4. 构建用户信息 + Map userInfo = new HashMap<>(); + userInfo.put("openid", openid); + userInfo.put("session_key", sessionKey); + if (unionid != null) { + userInfo.put("unionid", unionid); + } + + // 5. 构建登录响应 + Map loginResponse = buildLoginResponse(openid, userToken, userInfo); + loginResponse.put("success", true); + loginResponse.put("session_key", sessionKey); + + result = loginResponse; + + } catch (Exception e) { + result.put("success", false); + result.put("errorMsg", "微信登录流程异常:" + e.getMessage()); + e.printStackTrace(); + } + + return result; + } + + /** + * 检查access_token缓存是否有效 + * + * @return 缓存是否有效 + */ + private static boolean isAccessTokenCacheValid() { + if (ACCESS_TOKEN_CACHE.isEmpty()) { + return false; + } + + Object expireTimeObj = ACCESS_TOKEN_CACHE.get(CACHE_KEY_EXPIRE_TIME); + if (expireTimeObj == null) { + return false; + } + + long expireTime = (Long) expireTimeObj; + long currentTime = System.currentTimeMillis(); + + // 提前5分钟刷新,避免在使用时过期 + return currentTime < (expireTime - 5 * 60 * 1000); + } + + /** + * 更新access_token缓存 + * + * @param accessToken access_token值 + * @param expiresIn 有效期(秒) + */ + private static void updateAccessTokenCache(String accessToken, Integer expiresIn) { + long currentTime = System.currentTimeMillis(); + long expireTime = currentTime + (expiresIn * 1000L); + + ACCESS_TOKEN_CACHE.put(CACHE_KEY_ACCESS_TOKEN, accessToken); + ACCESS_TOKEN_CACHE.put(CACHE_KEY_EXPIRE_TIME, expireTime); + + System.out.println("access_token缓存已更新,过期时间:" + new java.util.Date(expireTime)); + } + + /** + * 获取缓存中access_token的剩余有效时间(秒) + * + * @return 剩余有效时间 + */ + private static long getRemainingExpireTime() { + Object expireTimeObj = ACCESS_TOKEN_CACHE.get(CACHE_KEY_EXPIRE_TIME); + if (expireTimeObj == null) { + return 0; + } + + long expireTime = (Long) expireTimeObj; + long currentTime = System.currentTimeMillis(); + + return Math.max(0, (expireTime - currentTime) / 1000); + } + + /** + * 清除access_token缓存 + */ + public static void clearAccessTokenCache() { + ACCESS_TOKEN_CACHE.clear(); + System.out.println("access_token缓存已清除"); + } + + /** + * 生成用户token(重载方法,保持向后兼容) + * + * @param openid 微信用户openid + * @return 生成的token字符串 + */ + public static String generateUserToken(String openid) { + return generateUserToken(openid, null); + } + + /** + * 获取配置信息(用于调试) + * + * @return 配置信息Map + */ + public static Map getConfigInfo() { + Map config = new HashMap<>(); + config.put("appid", WECHAT_APPID); + config.put("apiBaseUrl", WECHAT_API_BASE_URL); + config.put("tokenPrefix", TOKEN_PREFIX); + config.put("tokenExpireTime", TOKEN_EXPIRE_TIME); + config.put("cacheValid", isAccessTokenCacheValid()); + config.put("remainingExpireTime", getRemainingExpireTime()); + return config; + } + + /** + * 获取用户手机号 + * + * @param encryptedData 加密的敏感数据 + * @param iv 加密算法的初始向量 + * @param sessionKey 会话密钥 + * @return 解密结果Map + * + * 使用说明: + * 1. 前端调用wx.getPhoneNumber获取encryptedData和iv + * 2. 后端使用sessionKey解密数据 + * 3. 解密成功后返回手机号等信息 + * + * 返回数据结构: + * { + * "success": true/false, // 是否成功 + * "phoneNumber": "手机号", // 用户手机号 + * "purePhoneNumber": "纯手机号", // 没有区号的手机号 + * "countryCode": "国家码", // 国家码 + * "watermark": {...}, // 数据水印信息 + * "errorMsg": "错误信息" // 错误信息(失败时返回) + * } + */ + public static Map getPhoneNumber(String encryptedData, String iv, String sessionKey) { + Map result = new HashMap<>(); + + try { + // 1. 参数验证 + if (encryptedData == null || encryptedData.trim().isEmpty()) { + result.put("success", false); + result.put("errorMsg", "encryptedData不能为空"); + return result; + } + + if (iv == null || iv.trim().isEmpty()) { + result.put("success", false); + result.put("errorMsg", "iv不能为空"); + return result; + } + + if (sessionKey == null || sessionKey.trim().isEmpty()) { + result.put("success", false); + result.put("errorMsg", "sessionKey不能为空"); + return result; + } + + // 2. 解密数据 + String decryptedData = decryptData(encryptedData.trim(), iv.trim(), sessionKey.trim()); + + if (decryptedData == null) { + result.put("success", false); + result.put("errorMsg", "数据解密失败"); + return result; + } + + // 3. 解析解密后的JSON数据 + JSONObject phoneInfo = JSONObject.parseObject(decryptedData); + + // 4. 验证数据完整性 + if (!phoneInfo.containsKey("phoneNumber")) { + result.put("success", false); + result.put("errorMsg", "解密数据中未找到手机号信息"); + return result; + } + + // 5. 验证水印信息(确保数据来源正确) + if (phoneInfo.containsKey("watermark")) { + JSONObject watermark = phoneInfo.getJSONObject("watermark"); + String appid = watermark.getString("appid"); + if (!WECHAT_APPID.equals(appid)) { + result.put("success", false); + result.put("errorMsg", "数据水印验证失败,appid不匹配"); + return result; + } + } + + // 6. 构建返回结果 + result.put("success", true); + result.put("phoneNumber", phoneInfo.getString("phoneNumber")); + result.put("purePhoneNumber", phoneInfo.getString("purePhoneNumber")); + result.put("countryCode", phoneInfo.getString("countryCode")); + + if (phoneInfo.containsKey("watermark")) { + result.put("watermark", phoneInfo.getJSONObject("watermark")); + } + + System.out.println("手机号解密成功:" + phoneInfo.getString("phoneNumber")); + + } catch (Exception e) { + result.put("success", false); + result.put("errorMsg", "获取手机号异常:" + e.getMessage()); + e.printStackTrace(); + } + + return result; + } + + /** + * 通过code获取手机号(新版API方式) + * + * @param code 手机号获取凭证 + * @return 手机号信息Map + * + * 注意:这是微信新推出的获取手机号方式,需要access_token + * 适用于微信小程序基础库2.21.2以上版本 + * + * 返回数据结构: + * { + * "success": true/false, // 是否成功 + * "phone_info": { // 手机号信息 + * "phoneNumber": "手机号", + * "purePhoneNumber": "纯手机号", + * "countryCode": "国家码" + * }, + * "errorMsg": "错误信息" // 错误信息(失败时返回) + * } + */ + public static Map getPhoneNumberByCode(String code) { + Map result = new HashMap<>(); + + try { + // 1. 参数验证 + if (code == null || code.trim().isEmpty()) { + result.put("success", false); + result.put("errorMsg", "code不能为空"); + return result; + } + + // 2. 获取access_token + Map tokenResult = getCachedAccessToken(); + if (!(Boolean) tokenResult.get("success")) { + result.put("success", false); + result.put("errorMsg", "获取access_token失败:" + tokenResult.get("errorMsg")); + return result; + } + + String accessToken = (String) tokenResult.get("access_token"); + + // 3. 构建请求参数 + Map requestBody = new HashMap<>(); + requestBody.put("code", code.trim()); + + // 4. 构建请求URL + String url = WECHAT_API_BASE_URL + "/wxa/business/getuserphonenumber?access_token=" + accessToken; + + // 5. 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.set("Content-Type", "application/json"); + + // 6. 创建请求实体 + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + System.out.println("请求微信手机号API: " + url); + System.out.println("请求参数: " + requestBody); + + // 7. 发起POST请求 + ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + System.out.println("微信手机号API响应: " + response.getBody()); + + // 8. 处理响应结果 + if (response.getStatusCode().is2xxSuccessful()) { + JSONObject jsonResponse = JSONObject.parseObject(response.getBody()); + + if (jsonResponse.getInteger("errcode") == 0) { + // 成功获取手机号 + result.put("success", true); + result.put("phone_info", jsonResponse.getJSONObject("phone_info")); + } else { + // 获取失败 + result.put("success", false); + result.put("errorMsg", "微信API返回错误:" + + jsonResponse.getString("errmsg") + + " (错误码:" + jsonResponse.getInteger("errcode") + ")"); + } + } else { + result.put("success", false); + result.put("errorMsg", "HTTP请求失败,状态码:" + response.getStatusCode()); + } + + } catch (Exception e) { + result.put("success", false); + result.put("errorMsg", "通过code获取手机号异常:" + e.getMessage()); + e.printStackTrace(); + } + + return result; + } + + /** + * AES解密数据 + * + * @param encryptedData 加密的数据 + * @param iv 初始向量 + * @param sessionKey 会话密钥 + * @return 解密后的数据 + * + * 微信小程序数据解密算法: + * 1. 对称解密算法:AES-128-CBC + * 2. 数据组织:AES( sessionKey, iv, encryptedData ) + * 3. 去除PKCS#7填充 + */ + private static String decryptData(String encryptedData, String iv, String sessionKey) { + try { + // 1. Base64解码 + byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData); + byte[] ivBytes = Base64.getDecoder().decode(iv); + byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey); + + // 2. 创建AES密钥 + SecretKeySpec secretKeySpec = new SecretKeySpec(sessionKeyBytes, "AES"); + + // 3. 创建初始向量 + IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes); + + // 4. 创建加密器 + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); + + // 5. 执行解密 + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + + // 6. 转换为字符串 + String decryptedData = new String(decryptedBytes, StandardCharsets.UTF_8); + + System.out.println("数据解密成功"); + return decryptedData; + + } catch (Exception e) { + System.err.println("数据解密失败:" + e.getMessage()); + e.printStackTrace(); + return null; + } + } + + /** + * 验证数据签名(可选的安全校验) + * + * @param rawData 原始数据 + * @param signature 数据签名 + * @param sessionKey 会话密钥 + * @return 签名是否有效 + */ + public static boolean verifySignature(String rawData, String signature, String sessionKey) { + try { + // 1. 构建待签名字符串 + String signStr = rawData + sessionKey; + + // 2. 计算SHA1哈希 + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + byte[] hashBytes = sha1.digest(signStr.getBytes(StandardCharsets.UTF_8)); + + // 3. 转换为16进制字符串 + StringBuilder hexString = new StringBuilder(); + for (byte b : hashBytes) { + hexString.append(String.format("%02x", b)); + } + + // 4. 比较签名 + return hexString.toString().equals(signature); + + } catch (Exception e) { + System.err.println("签名验证失败:" + e.getMessage()); + e.printStackTrace(); + return false; + } + } + + /** + * 完整的获取手机号流程(推荐使用) + * + * @param encryptedData 加密的敏感数据 + * @param iv 加密算法的初始向量 + * @param sessionKey 会话密钥 + * @param openid 用户openid(用于日志记录) + * @return 获取结果Map + * + * 这个方法整合了手机号获取的完整流程,包括: + * 1. 参数验证 + * 2. 数据解密 + * 3. 结果验证 + * 4. 日志记录 + */ + public static Map getUserPhoneNumber(String encryptedData, String iv, String sessionKey, String openid) { + Map result = new HashMap<>(); + + try { + System.out.println("开始获取用户手机号,openid: " + openid); + + // 1. 获取手机号 + Map phoneResult = getPhoneNumber(encryptedData, iv, sessionKey); + + if ((Boolean) phoneResult.get("success")) { + String phoneNumber = (String) phoneResult.get("phoneNumber"); + + // 2. 记录成功日志 + System.out.println("用户手机号获取成功 - openid: " + openid + ", phone: " + + phoneNumber.substring(0, 3) + "****" + phoneNumber.substring(7)); + + // 3. 构建返回结果 + result.put("success", true); + result.put("openid", openid); + result.put("phoneNumber", phoneNumber); + result.put("purePhoneNumber", phoneResult.get("purePhoneNumber")); + result.put("countryCode", phoneResult.get("countryCode")); + result.put("timestamp", System.currentTimeMillis()); + + } else { + // 4. 记录失败日志 + System.err.println("用户手机号获取失败 - openid: " + openid + ", error: " + phoneResult.get("errorMsg")); + + result.put("success", false); + result.put("openid", openid); + result.put("errorMsg", phoneResult.get("errorMsg")); + } + + } catch (Exception e) { + result.put("success", false); + result.put("errorMsg", "获取手机号流程异常:" + e.getMessage()); + e.printStackTrace(); + } + + return result; + } + + /** + * 测试token生成(用于调试和验证) + * + * @return 测试结果Map + */ + public static Map testTokenGeneration() { + Map result = new HashMap<>(); + + try { + // 测试参数 + String testOpenid = "oL1Fu5K6V8BYO2V5q4fK4HB7VdIo"; + String testSessionKey = "test_session_key_123456"; + + // 生成多个token进行测试 + String token1 = generateUserToken(testOpenid, testSessionKey); + String token2 = generateUserToken(testOpenid, null); + String token3 = generateUserToken(null, testSessionKey); + String token4 = generateSecureUserToken(testOpenid, testSessionKey, "extra_salt"); + String token5 = generateRandomBase62String(40); + + // 验证token格式 + boolean valid1 = isValidTokenFormat(token1); + boolean valid2 = isValidTokenFormat(token2); + boolean valid3 = isValidTokenFormat(token3); + boolean valid4 = isValidTokenFormat(token4); + boolean valid5 = isValidTokenFormat(token5); + + // 构建测试结果 + Map tokens = new HashMap<>(); + tokens.put("token1", token1); + tokens.put("token2", token2); + tokens.put("token3", token3); + tokens.put("secureToken", token4); + tokens.put("randomToken", token5); + + Map validations = new HashMap<>(); + validations.put("token1_valid", valid1); + validations.put("token2_valid", valid2); + validations.put("token3_valid", valid3); + validations.put("secureToken_valid", valid4); + validations.put("randomToken_valid", valid5); + + result.put("success", true); + result.put("tokens", tokens); + result.put("validations", validations); + result.put("allValid", valid1 && valid2 && valid3 && valid4 && valid5); + result.put("message", "Token生成测试完成"); + + // 打印测试结果 + System.out.println("=== Token生成测试结果 ==="); + System.out.println("Token1 (基础): " + token1 + " (长度: " + token1.length() + ", 有效: " + valid1 + ")"); + System.out.println("Token2 (无SessionKey): " + token2 + " (长度: " + token2.length() + ", 有效: " + valid2 + ")"); + System.out.println("Token3 (无OpenID): " + token3 + " (长度: " + token3.length() + ", 有效: " + valid3 + ")"); + System.out.println("Token4 (高强度): " + token4 + " (长度: " + token4.length() + ", 有效: " + valid4 + ")"); + System.out.println("Token5 (随机): " + token5 + " (长度: " + token5.length() + ", 有效: " + valid5 + ")"); + System.out.println("所有Token格式: " + (valid1 && valid2 && valid3 && valid4 && valid5 ? "✓ 正确" : "✗ 错误")); + + } catch (Exception e) { + result.put("success", false); + result.put("error", e.getMessage()); + e.printStackTrace(); + } + + return result; + } } \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/Users.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/Users.java index 17822d5..41eb299 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/Users.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/Users.java @@ -2,6 +2,8 @@ package com.ruoyi.system.domain; import java.math.BigDecimal; import java.util.Date; +import java.util.Map; + import com.fasterxml.jackson.annotation.JsonFormat; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -150,6 +152,12 @@ public class Users extends BaseEntity private BigDecimal marginMax; private BigDecimal commissionMin; private BigDecimal commissionMax; + private String remember_token; + + private Map order_num; + private Map goods_order_num; + + public void setId(Long id) { @@ -507,6 +515,30 @@ public class Users extends BaseEntity this.isWork = isWork; } + public String getRemember_token() { + return remember_token; + } + + public void setRemember_token(String remember_token) { + this.remember_token = remember_token; + } + + public Map getOrder_num() { + return order_num; + } + + public void setOrder_num(Map order_num) { + this.order_num = order_num; + } + + public Map getGoods_order_num() { + return goods_order_num; + } + + public void setGoods_order_num(Map goods_order_num) { + this.goods_order_num = goods_order_num; + } + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)