diff --git a/ruoyi-client/pom.xml b/ruoyi-client/pom.xml new file mode 100644 index 0000000..3ffb319 --- /dev/null +++ b/ruoyi-client/pom.xml @@ -0,0 +1,34 @@ + + + + ruoyi + com.ruoyi + 3.9.0 + + 4.0.0 + + ruoyi-client + + + 用户端服务模块,支持第三方登录(微信、支付宝、抖音) + + + + + + + com.ruoyi + ruoyi-common + + + + + com.ruoyi + ruoyi-system + + + + + diff --git a/ruoyi-client/src/main/java/com/ruoyi/client/controller/ThirdPartyLoginController.java b/ruoyi-client/src/main/java/com/ruoyi/client/controller/ThirdPartyLoginController.java new file mode 100644 index 0000000..7a282d1 --- /dev/null +++ b/ruoyi-client/src/main/java/com/ruoyi/client/controller/ThirdPartyLoginController.java @@ -0,0 +1,184 @@ +package com.ruoyi.client.controller; + +import com.ruoyi.system.domain.thirdparty.LoginRequest; +import com.ruoyi.system.domain.thirdparty.LoginResponse; +import com.ruoyi.system.service.thirdparty.ThirdPartyLoginService; +import com.ruoyi.system.service.thirdparty.impl.WeChatLoginServiceImpl; +import com.ruoyi.system.service.thirdparty.impl.AlipayLoginServiceImpl; +import com.ruoyi.system.service.thirdparty.impl.DouyinLoginServiceImpl; +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * 第三方登录Controller + */ +@RestController +@RequestMapping("/client/login") +public class ThirdPartyLoginController extends BaseController +{ + @Autowired + private WeChatLoginServiceImpl weChatLoginService; + + @Autowired + private AlipayLoginServiceImpl alipayLoginService; + + @Autowired + private DouyinLoginServiceImpl douyinLoginService; + + /** + * 统一第三方登录接口 + */ + @Anonymous + @PostMapping("/thirdParty") + public AjaxResult thirdPartyLogin(@RequestBody LoginRequest request) + { + try + { + ThirdPartyLoginService loginService = getLoginService(request.getLoginType()); + + if (loginService == null) + { + return AjaxResult.error("不支持的登录类型"); + } + + LoginResponse response = loginService.login(request); + + Map result = new HashMap<>(); + result.put("userId", response.getUserId()); + result.put("username", response.getUsername()); + result.put("nickname", response.getNickname()); + result.put("avatar", response.getAvatar()); + result.put("token", response.getToken()); + result.put("userLevel", response.getUserLevel()); + result.put("coin", response.getCoin()); + result.put("diamond", response.getDiamond()); + result.put("isNewUser", response.getIsNewUser()); + + return AjaxResult.success(result); + } + catch (Exception e) + { + logger.error("第三方登录失败", e); + return AjaxResult.error("登录失败:" + e.getMessage()); + } + } + + /** + * 微信登录 + */ + @Anonymous + @PostMapping("/wechat") + public AjaxResult wechatLogin(@RequestBody LoginRequest request) + { + try + { + LoginResponse response = weChatLoginService.login(request); + + Map result = new HashMap<>(); + result.put("userId", response.getUserId()); + result.put("username", response.getUsername()); + result.put("nickname", response.getNickname()); + result.put("avatar", response.getAvatar()); + result.put("token", response.getToken()); + result.put("userLevel", response.getUserLevel()); + result.put("coin", response.getCoin()); + result.put("diamond", response.getDiamond()); + result.put("isNewUser", response.getIsNewUser()); + + return AjaxResult.success(result); + } + catch (Exception e) + { + logger.error("微信登录失败", e); + return AjaxResult.error("登录失败:" + e.getMessage()); + } + } + + /** + * 支付宝登录 + */ + @Anonymous + @PostMapping("/alipay") + public AjaxResult alipayLogin(@RequestBody LoginRequest request) + { + try + { + LoginResponse response = alipayLoginService.login(request); + + Map result = new HashMap<>(); + result.put("userId", response.getUserId()); + result.put("username", response.getUsername()); + result.put("nickname", response.getNickname()); + result.put("avatar", response.getAvatar()); + result.put("token", response.getToken()); + result.put("userLevel", response.getUserLevel()); + result.put("coin", response.getCoin()); + result.put("diamond", response.getDiamond()); + result.put("isNewUser", response.getIsNewUser()); + + return AjaxResult.success(result); + } + catch (Exception e) + { + logger.error("支付宝登录失败", e); + return AjaxResult.error("登录失败:" + e.getMessage()); + } + } + + /** + * 抖音登录 + */ + @Anonymous + @PostMapping("/douyin") + public AjaxResult douyinLogin(@RequestBody LoginRequest request) + { + try + { + LoginResponse response = douyinLoginService.login(request); + + Map result = new HashMap<>(); + result.put("userId", response.getUserId()); + result.put("username", response.getUsername()); + result.put("nickname", response.getNickname()); + result.put("avatar", response.getAvatar()); + result.put("token", response.getToken()); + result.put("userLevel", response.getUserLevel()); + result.put("coin", response.getCoin()); + result.put("diamond", response.getDiamond()); + result.put("isNewUser", response.getIsNewUser()); + + return AjaxResult.success(result); + } + catch (Exception e) + { + logger.error("抖音登录失败", e); + return AjaxResult.error("登录失败:" + e.getMessage()); + } + } + + /** + * 根据登录类型获取对应的服务 + */ + private ThirdPartyLoginService getLoginService(String loginType) + { + if ("wechat".equalsIgnoreCase(loginType)) + { + return weChatLoginService; + } + else if ("alipay".equalsIgnoreCase(loginType)) + { + return alipayLoginService; + } + else if ("douyin".equalsIgnoreCase(loginType)) + { + return douyinLoginService; + } + return null; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/WxMaConfiguration.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/WxMaConfiguration.java new file mode 100644 index 0000000..ad3b64e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/WxMaConfiguration.java @@ -0,0 +1,34 @@ +package com.ruoyi.system.config; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 微信小程序配置 + */ +@Configuration +public class WxMaConfiguration +{ + @Value("${wx.miniapp.appid}") + private String appId; + + @Value("${wx.miniapp.secret}") + private String secret; + + @Bean + public WxMaService wxMaService() + { + WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); + config.setAppid(appId); + config.setSecret(secret); + + WxMaServiceImpl service = new WxMaServiceImpl(); + service.setWxMaConfig(config); + + return service; + } +} \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IFateSkillsService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IFateSkillsService.java new file mode 100644 index 0000000..8c92333 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IFateSkillsService.java @@ -0,0 +1,62 @@ +package com.ruoyi.system.service; + +import com.ruoyi.system.domain.FateSkills; + +import java.util.List; + +/** + * 技能基础Service接口 + * + * @author ruoyi + * @date 2025-01-18 + */ +public interface IFateSkillsService +{ + /** + * 查询技能基础 + * + * @param skillId 技能基础主键 + * @return 技能基础 + */ + public FateSkills selectFateSkillsBySkillId(Long skillId); + + /** + * 查询技能基础列表 + * + * @param fateSkills 技能基础 + * @return 技能基础集合 + */ + public List selectFateSkillsList(FateSkills fateSkills); + + /** + * 新增技能基础 + * + * @param fateSkills 技能基础 + * @return 结果 + */ + public int insertFateSkills(FateSkills fateSkills); + + /** + * 修改技能基础 + * + * @param fateSkills 技能基础 + * @return 结果 + */ + public int updateFateSkills(FateSkills fateSkills); + + /** + * 批量删除技能基础 + * + * @param skillIds 需要删除的技能基础主键集合 + * @return 结果 + */ + public int deleteFateSkillsBySkillIds(Long[] skillIds); + + /** + * 删除技能基础信息 + * + * @param skillId 技能基础主键 + * @return 结果 + */ + public int deleteFateSkillsBySkillId(Long skillId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IFateUserInfoService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IFateUserInfoService.java new file mode 100644 index 0000000..af0bada --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IFateUserInfoService.java @@ -0,0 +1,61 @@ +package com.ruoyi.system.service; + +import com.ruoyi.system.domain.FateUserInfo; +import java.util.List; + +/** + * 用户信息Service接口 + * + * @author ruoyi + * @date 2025-01-18 + */ +public interface IFateUserInfoService +{ + /** + * 查询用户信息 + * + * @param userId 用户信息主键 + * @return 用户信息 + */ + public FateUserInfo selectFateUserInfoByUserId(Long userId); + + /** + * 查询用户信息列表 + * + * @param fateUserInfo 用户信息 + * @return 用户信息集合 + */ + public List selectFateUserInfoList(FateUserInfo fateUserInfo); + + /** + * 新增用户信息 + * + * @param fateUserInfo 用户信息 + * @return 结果 + */ + public int insertFateUserInfo(FateUserInfo fateUserInfo); + + /** + * 修改用户信息 + * + * @param fateUserInfo 用户信息 + * @return 结果 + */ + public int updateFateUserInfo(FateUserInfo fateUserInfo); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户信息主键集合 + * @return 结果 + */ + public int deleteFateUserInfoByUserIds(Long[] userIds); + + /** + * 删除用户信息信息 + * + * @param userId 用户信息主键 + * @return 结果 + */ + public int deleteFateUserInfoByUserId(Long userIdId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FateSkillsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FateSkillsServiceImpl.java new file mode 100644 index 0000000..dd76834 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FateSkillsServiceImpl.java @@ -0,0 +1,102 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; + +import com.ruoyi.system.domain.FateSkills; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.mapper.FateSkillsMapper; +import com.ruoyi.system.service.IFateSkillsService; + +/** + * 技能基础Service业务层处理 + * + * @author ruoyi + * @date 2025-01-18 + */ +@Service +public class FateSkillsServiceImpl implements IFateSkillsService +{ + @Autowired + private FateSkillsMapper fateSkillsMapper; + + /** + * 查询技能基础 + * + * @param skillId 技能基础主键 + * @return 技能基础 + */ + @Override + public FateSkills selectFateSkillsBySkillId(Long skillId) + { + FateSkills fateSkills = fateSkillsMapper.selectFateSkillsBySkillId(skillId); + if (fateSkills != null) { + fateSkills.setSkillTypeDesc(fateSkills.getSkillTypeDesc()); + } + return fateSkills; + } + + /** + * 查询技能基础列表 + * + * @param fateSkills 技能基础 + * @return 技能基础 + */ + @Override + public List selectFateSkillsList(FateSkills fateSkills) + { + List list = fateSkillsMapper.selectFateSkillsList(fateSkills); + for (FateSkills skill : list) { + skill.setSkillTypeDesc(skill.getSkillTypeDesc()); + } + return list; + } + + /** + * 新增技能基础 + * + * @param fateSkills 技能基础 + * @return 结果 + */ + @Override + public int insertFateSkills(FateSkills fateSkills) + { + return fateSkillsMapper.insertFateSkills(fateSkills); + } + + /** + * 修改技能基础 + * + * @param fateSkills 技能基础 + * @return 结果 + */ + @Override + public int updateFateSkills(FateSkills fateSkills) + { + return fateSkillsMapper.updateFateSkills(fateSkills); + } + + /** + * 批量删除技能基础 + * + * @param skillIds 需要删除的技能基础主键 + * @return 结果 + */ + @Override + public int deleteFateSkillsBySkillIds(Long[] skillIds) + { + return fateSkillsMapper.deleteFateSkillsBySkillIds(skillIds); + } + + /** + * 删除技能基础信息 + * + * @param skillId 技能基础主键 + * @return 结果 + */ + @Override + public int deleteFateSkillsBySkillId(Long skillId) + { + return fateSkillsMapper.deleteFateSkillsBySkillId(skillId); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FateUserInfoServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FateUserInfoServiceImpl.java new file mode 100644 index 0000000..397be90 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FateUserInfoServiceImpl.java @@ -0,0 +1,104 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; + +import com.ruoyi.system.domain.FateUserInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.mapper.FateUserInfoMapper; +import com.ruoyi.system.service.IFateUserInfoService; + +/** + * 用户信息Service业务层处理 + * + * @author ruoyi + * @date 2025-01-18 + */ +@Service +public class FateUserInfoServiceImpl implements IFateUserInfoService +{ + @Autowired + private FateUserInfoMapper fateUserInfoMapper; + + /** + * 查询用户信息 + * + * @param userId 用户信息主键 + * @return 用户信息 + */ + @Override + public FateUserInfo selectFateUserInfoByUserId(Long userId) + { + FateUserInfo fateUserInfo = fateUserInfoMapper.selectFateUserInfoByUserId(userId); + if (fateUserInfo != null) { + fateUserInfo.setGenderDesc(fateUserInfo.getGenderDesc()); + fateUserInfo.setStatusDesc(fateUserInfo.getStatusDesc()); + } + return fateUserInfo; + } + + /** + * 查询用户信息列表 + * + * @param fateUserInfo 用户信息 + * @return 用户信息 + */ + @Override + public List selectFateUserInfoList(FateUserInfo fateUserInfo) + { + List list = fateUserInfoMapper.selectFateUserInfoList(fateUserInfo); + for (FateUserInfo userInfo : list) { + userInfo.setGenderDesc(userInfo.getGenderDesc()); + userInfo.setStatusDesc(userInfo.getStatusDesc()); + } + return list; + } + + /** + * 新增用户信息 + * + * @param fateUserInfo 用户信息 + * @return 结果 + */ + @Override + public int insertFateUserInfo(FateUserInfo fateUserInfo) + { + return fateUserInfoMapper.insertFateUserInfo(fateUserInfo); + } + + /** + * 修改用户信息 + * + * @param fateUserInfo 用户信息 + * @return 结果 + */ + @Override + public int updateFateUserInfo(FateUserInfo fateUserInfo) + { + return fateUserInfoMapper.updateFateUserInfo(fateUserInfo); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户信息主键 + * @return 结果 + */ + @Override + public int deleteFateUserInfoByUserIds(Long[] userIds) + { + return fateUserInfoMapper.deleteFateUserInfoByUserIds(userIds); + } + + /** + * 删除用户信息信息 + * + * @param userId 用户信息主键 + * @return 结果 + */ + @Override + public int deleteFateUserInfoByUserId(Long userIdId) + { + return fateUserInfoMapper.deleteFateUserInfoByUserId(userIdId); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/ThirdPartyLoginService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/ThirdPartyLoginService.java new file mode 100644 index 0000000..5b4e679 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/ThirdPartyLoginService.java @@ -0,0 +1,18 @@ +package com.ruoyi.system.service.thirdparty; + +import com.ruoyi.system.domain.thirdparty.LoginRequest; +import com.ruoyi.system.domain.thirdparty.LoginResponse; + +/** + * 第三方登录服务接口 + */ +public interface ThirdPartyLoginService +{ + /** + * 第三方登录 + * + * @param request 登录请求 + * @return 登录响应 + */ + LoginResponse login(LoginRequest request); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/impl/AlipayLoginServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/impl/AlipayLoginServiceImpl.java new file mode 100644 index 0000000..1d42224 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/impl/AlipayLoginServiceImpl.java @@ -0,0 +1,193 @@ +package com.ruoyi.system.service.thirdparty.impl; + +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayClient; +import com.alipay.api.DefaultAlipayClient; +import com.alipay.api.request.AlipaySystemOauthTokenRequest; +import com.alipay.api.request.AlipayUserInfoShareRequest; +import com.alipay.api.response.AlipaySystemOauthTokenResponse; +import com.alipay.api.response.AlipayUserInfoShareResponse; +import com.ruoyi.system.domain.thirdparty.LoginRequest; +import com.ruoyi.system.domain.thirdparty.LoginResponse; +import com.ruoyi.system.service.thirdparty.ThirdPartyLoginService; +import com.ruoyi.system.domain.FateUserAlipay; +import com.ruoyi.system.mapper.FateUserAlipayMapper; +import com.ruoyi.system.domain.FateUserInfo; +import com.ruoyi.system.mapper.FateUserInfoMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + * 支付宝登录服务实现 + */ +@Service +public class AlipayLoginServiceImpl implements ThirdPartyLoginService +{ + @Value("${alipay.app-id}") + private String appId; + + @Value("${alipay.private-key}") + private String privateKey; + + @Value("${alipay.alipay-public-key}") + private String alipayPublicKey; + + @Value("${alipay.server-url}") + private String serverUrl; + + @Value("${alipay.charset}") + private String charset; + + @Value("${alipay.sign-type}") + private String signType; + + @Autowired + private FateUserAlipayMapper fateUserAlipayMapper; + + @Autowired + private FateUserInfoMapper fateUserInfoMapper; + + @Override + public LoginResponse login(LoginRequest request) + { + LoginResponse response = new LoginResponse(); + + try + { + // 1. 创建支付宝客户端 + AlipayClient alipayClient = new DefaultAlipayClient( + serverUrl, + appId, + privateKey, + "json", + charset, + alipayPublicKey, + signType + ); + + // 2. 通过auth_code获取access_token和user_id + AlipaySystemOauthTokenRequest oauthTokenRequest = new AlipaySystemOauthTokenRequest(); + oauthTokenRequest.setGrantType("authorization_code"); + oauthTokenRequest.setCode(request.getCode()); + oauthTokenRequest.setRefreshToken("201208134b203fe6c014"); + + AlipaySystemOauthTokenResponse oauthTokenResponse = alipayClient.execute(oauthTokenRequest); + + if (!oauthTokenResponse.isSuccess()) + { + throw new RuntimeException("支付宝授权失败:" + oauthTokenResponse.getSubMsg()); + } + + String accessToken = oauthTokenResponse.getAccessToken(); + String alipayUserId = oauthTokenResponse.getUserId(); + + // 3. 查询是否已绑定支付宝 + FateUserAlipay alipayInfo = fateUserAlipayMapper.selectByAlipayUserId(alipayUserId); + + FateUserInfo userInfo; + boolean isNewUser = false; + + if (alipayInfo == null) + { + // 4. 新用户,创建用户信息 + userInfo = new FateUserInfo(); + userInfo.setUsername("alipay_" + alipayUserId.substring(0, 8)); + userInfo.setNickname("支付宝用户"); + userInfo.setGender(0); + userInfo.setUserLevel(1); + userInfo.setUserExp(0L); + userInfo.setCoin(1000L); + userInfo.setDiamond(100L); + userInfo.setStatus(1); + userInfo.setCreatedAt(new Date()); + userInfo.setUpdatedAt(new Date()); + + // 插入用户信息 + fateUserInfoMapper.insertFateUserInfo(userInfo); + isNewUser = true; + + // 5. 创建支付宝绑定信息 + FateUserAlipay newAlipayInfo = new FateUserAlipay(); + newAlipayInfo.setUserId(userInfo.getUserId()); + newAlipayInfo.setAlipayUserId(alipayUserId); + newAlipayInfo.setAccessToken(accessToken); + newAlipayInfo.setExpiresIn(Integer.valueOf(oauthTokenResponse.getExpiresIn())); + newAlipayInfo.setGender("未知"); + newAlipayInfo.setCreatedAt(new Date()); + newAlipayInfo.setUpdatedAt(new Date()); + fateUserAlipayMapper.insertFateUserAlipay(newAlipayInfo); + + alipayInfo = newAlipayInfo; + } + else + { + // 6. 已存在用户,更新access_token + alipayInfo.setAccessToken(accessToken); + alipayInfo.setExpiresIn(Integer.valueOf(oauthTokenResponse.getExpiresIn())); + alipayInfo.setUpdatedAt(new Date()); + fateUserAlipayMapper.updateFateUserAlipay(alipayInfo); + + userInfo = fateUserInfoMapper.selectFateUserInfoByUserId(alipayInfo.getUserId()); + } + + // 7. 获取支付宝用户信息 + AlipayUserInfoShareRequest userInfoRequest = new AlipayUserInfoShareRequest(); + AlipayUserInfoShareResponse userInfoResponse = alipayClient.execute(userInfoRequest, accessToken); + + if (userInfoResponse.isSuccess()) + { + // 更新支付宝信息 + alipayInfo.setNickname(userInfoResponse.getNickName()); + alipayInfo.setAvatar(userInfoResponse.getAvatar()); + alipayInfo.setGender(userInfoResponse.getGender()); + alipayInfo.setProvince(userInfoResponse.getProvince()); + alipayInfo.setCity(userInfoResponse.getCity()); + alipayInfo.setUpdatedAt(new Date()); + fateUserAlipayMapper.updateFateUserAlipay(alipayInfo); + + // 更新用户基本信息 + userInfo.setNickname(userInfoResponse.getNickName()); + userInfo.setAvatar(userInfoResponse.getAvatar()); + if (userInfoResponse.getProvince() != null) + { + userInfo.setProvince(userInfoResponse.getProvince()); + } + if (userInfoResponse.getCity() != null) + { + userInfo.setCity(userInfoResponse.getCity()); + } + fateUserInfoMapper.updateFateUserInfo(userInfo); + } + + // 8. 构建响应 + response.setUserId(userInfo.getUserId()); + response.setUsername(userInfo.getUsername()); + response.setNickname(userInfo.getNickname()); + response.setAvatar(userInfo.getAvatar()); + response.setToken(generateToken(userInfo.getUserId())); + response.setUserLevel(userInfo.getUserLevel()); + response.setCoin(userInfo.getCoin()); + response.setDiamond(userInfo.getDiamond()); + response.setIsNewUser(isNewUser); + + return response; + } + catch (AlipayApiException e) + { + e.printStackTrace(); + throw new RuntimeException("支付宝登录失败:" + e.getErrMsg()); + } + } + + /** + * 生成令牌 + */ + private String generateToken(Long userId) + { + // TODO: 实现JWT或自定义token生成逻辑 + return "token_" + userId + "_" + System.currentTimeMillis(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/impl/DouyinLoginServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/impl/DouyinLoginServiceImpl.java new file mode 100644 index 0000000..2849381 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/impl/DouyinLoginServiceImpl.java @@ -0,0 +1,196 @@ +package com.ruoyi.system.service.thirdparty.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.system.domain.thirdparty.LoginRequest; +import com.ruoyi.system.domain.thirdparty.LoginResponse; +import com.ruoyi.system.service.thirdparty.ThirdPartyLoginService; +import com.ruoyi.system.domain.FateUserDouyin; +import com.ruoyi.system.mapper.FateUserDouyinMapper; +import com.ruoyi.system.domain.FateUserInfo; +import com.ruoyi.system.mapper.FateUserInfoMapper; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + * 抖音登录服务实现 + */ +@Service +public class DouyinLoginServiceImpl implements ThirdPartyLoginService +{ + @Value("${douyin.app-id}") + private String appId; + + @Value("${douyin.app-secret}") + private String appSecret; + + @Value("${douyin.access-token-url}") + private String accessTokenUrl; + + @Value("${douyin.user-info-url}") + private String userInfoUrl; + + @Autowired + private FateUserDouyinMapper fateUserDouyinMapper; + + @Autowired + private FateUserInfoMapper fateUserInfoMapper; + + @Override + public LoginResponse login(LoginRequest request) + { + LoginResponse response = new LoginResponse(); + + try + { + // 1. 通过code获取access_token + String tokenUrl = accessTokenUrl + "?client_key=" + appId + + "&client_secret=" + appSecret + "&code=" + request.getCode() + + "&grant_type=authorization_code"; + + String tokenResponse = httpPost(tokenUrl, ""); + JSONObject tokenJson = JSON.parseObject(tokenResponse); + + if (tokenJson.containsKey("error")) + { + throw new RuntimeException("抖音授权失败:" + tokenJson.getString("error_description")); + } + + String accessToken = tokenJson.getString("access_token"); + String openid = tokenJson.getString("open_id"); + String unionid = tokenJson.getString("union_id"); + + // 2. 查询是否已绑定抖音 + FateUserDouyin douyinInfo = fateUserDouyinMapper.selectByOpenid(openid); + + FateUserInfo userInfo; + boolean isNewUser = false; + + if (douyinInfo == null) + { + // 3. 新用户,创建用户信息 + userInfo = new FateUserInfo(); + userInfo.setUsername("douyin_" + openid.substring(0, 8)); + userInfo.setNickname("抖音用户"); + userInfo.setGender(0); + userInfo.setUserLevel(1); + userInfo.setUserExp(0L); + userInfo.setCoin(1000L); + userInfo.setDiamond(100L); + userInfo.setStatus(1); + userInfo.setCreatedAt(new Date()); + userInfo.setUpdatedAt(new Date()); + + // 插入用户信息 + fateUserInfoMapper.insertFateUserInfo(userInfo); + isNewUser = true; + + // 4. 创建抖音绑定信息 + FateUserDouyin newDouyinInfo = new FateUserDouyin(); + newDouyinInfo.setUserId(userInfo.getUserId()); + newDouyinInfo.setOpenid(openid); + newDouyinInfo.setUnionid(unionid); + newDouyinInfo.setAccessToken(accessToken); + newDouyinInfo.setExpiresIn(tokenJson.getInteger("expires_in")); + newDouyinInfo.setGender(0); + newDouyinInfo.setCreatedAt(new Date()); + newDouyinInfo.setUpdatedAt(new Date()); + fateUserDouyinMapper.insertFateUserDouyin(newDouyinInfo); + + douyinInfo = newDouyinInfo; + } + else + { + // 5. 已存在用户,更新access_token + douyinInfo.setAccessToken(accessToken); + douyinInfo.setExpiresIn(tokenJson.getInteger("expires_in")); + douyinInfo.setUpdatedAt(new Date()); + fateUserDouyinMapper.updateFateUserDouyin(douyinInfo); + + userInfo = fateUserInfoMapper.selectFateUserInfoByUserId(douyinInfo.getUserId()); + } + + // 6. 获取抖音用户信息 + String infoUrl = userInfoUrl + "?access_token=" + accessToken + "&open_id=" + openid; + String infoResponse = httpPost(infoUrl, ""); + JSONObject infoJson = JSON.parseObject(infoResponse); + + if (infoJson != null && infoJson.containsKey("data")) + { + JSONObject data = infoJson.getJSONObject("data"); + + // 更新抖音信息 + douyinInfo.setNickname(data.getString("nickname")); + douyinInfo.setAvatar(data.getString("avatar")); + douyinInfo.setGender(data.getInteger("gender")); + douyinInfo.setCountry(data.getString("country")); + douyinInfo.setProvince(data.getString("province")); + douyinInfo.setCity(data.getString("city")); + douyinInfo.setUpdatedAt(new Date()); + fateUserDouyinMapper.updateFateUserDouyin(douyinInfo); + + // 更新用户基本信息 + userInfo.setNickname(data.getString("nickname")); + userInfo.setAvatar(data.getString("avatar")); + userInfo.setGender(data.getInteger("gender")); + if (data.getString("province") != null) + { + userInfo.setProvince(data.getString("province")); + } + if (data.getString("city") != null) + { + userInfo.setCity(data.getString("city")); + } + fateUserInfoMapper.updateFateUserInfo(userInfo); + } + + // 7. 构建响应 + response.setUserId(userInfo.getUserId()); + response.setUsername(userInfo.getUsername()); + response.setNickname(userInfo.getNickname()); + response.setAvatar(userInfo.getAvatar()); + response.setToken(generateToken(userInfo.getUserId())); + response.setUserLevel(userInfo.getUserLevel()); + response.setCoin(userInfo.getCoin()); + response.setDiamond(userInfo.getDiamond()); + response.setIsNewUser(isNewUser); + + return response; + } + catch (Exception e) + { + e.printStackTrace(); + throw new RuntimeException("抖音登录失败:" + e.getMessage()); + } + } + + /** + * HTTP POST请求 + */ + private String httpPost(String url, String body) throws Exception + { + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(url); + httpPost.setHeader("Content-Type", "application/json"); + httpPost.setEntity(new StringEntity(body, "UTF-8")); + + return EntityUtils.toString(httpClient.execute(httpPost).getEntity(), "UTF-8"); + } + + /** + * 生成令牌 + */ + private String generateToken(Long userId) + { + // TODO: 实现JWT或自定义token生成逻辑 + return "token_" + userId + "_" + System.currentTimeMillis(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/impl/WeChatLoginServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/impl/WeChatLoginServiceImpl.java new file mode 100644 index 0000000..6b90ba0 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/thirdparty/impl/WeChatLoginServiceImpl.java @@ -0,0 +1,156 @@ +package com.ruoyi.system.service.thirdparty.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.FateUserInfo; +import com.ruoyi.system.domain.FateUserWechat; +import com.ruoyi.system.domain.thirdparty.LoginRequest; +import com.ruoyi.system.domain.thirdparty.LoginResponse; +import com.ruoyi.system.mapper.FateUserInfoMapper; +import com.ruoyi.system.mapper.FateUserWechatMapper; +import com.ruoyi.system.service.thirdparty.ThirdPartyLoginService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + * 微信登录服务实现 + */ +@Service +public class WeChatLoginServiceImpl implements ThirdPartyLoginService +{ + @Autowired + private WxMaService wxMaService; + + @Autowired + private FateUserWechatMapper fateUserWechatMapper; + + @Autowired + private FateUserInfoMapper fateUserInfoMapper; + + @Override + public LoginResponse login(LoginRequest request) + { + LoginResponse response = new LoginResponse(); + + try + { + // 1. 通过code获取session信息 + WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(request.getCode()); + String openid = sessionInfo.getOpenid(); + String sessionKey = sessionInfo.getSessionKey(); + String unionid = sessionInfo.getUnionid(); + + // 2. 查询是否已绑定微信 + FateUserWechat wechatInfo = fateUserWechatMapper.selectByOpenid(openid); + + FateUserInfo userInfo; + boolean isNewUser = false; + + if (wechatInfo == null) + { + // 3. 新用户,创建用户信息 + userInfo = new FateUserInfo(); + userInfo.setUsername("wx_" + openid.substring(0, 8)); + userInfo.setNickname("微信用户"); + userInfo.setGender(0); + userInfo.setUserLevel(1); + userInfo.setUserExp(0L); + userInfo.setCoin(1000L); + userInfo.setDiamond(100L); + userInfo.setStatus(1); + userInfo.setCreatedAt(new Date()); + userInfo.setUpdatedAt(new Date()); + + // 插入用户信息 + fateUserInfoMapper.insertFateUserInfo(userInfo); + isNewUser = true; + + // 4. 创建微信绑定信息 + FateUserWechat newWechatInfo = new FateUserWechat(); + newWechatInfo.setUserId(userInfo.getUserId()); + newWechatInfo.setOpenid(openid); + newWechatInfo.setUnionid(unionid); + newWechatInfo.setSessionKey(sessionKey); + newWechatInfo.setGender(0); + newWechatInfo.setCreatedAt(new Date()); + newWechatInfo.setUpdatedAt(new Date()); + fateUserWechatMapper.insertFateUserWechat(newWechatInfo); + + wechatInfo = newWechatInfo; + } + else + { + // 5. 已存在用户,更新session_key + wechatInfo.setSessionKey(sessionKey); + wechatInfo.setUpdatedAt(new Date()); + fateUserWechatMapper.updateFateUserWechat(wechatInfo); + + userInfo = fateUserInfoMapper.selectFateUserInfoByUserId(wechatInfo.getUserId()); + } + + // 6. 解密用户信息(如果提供了encryptedData和iv) + if (StringUtils.isNotEmpty(request.getEncryptedData()) && StringUtils.isNotEmpty(request.getIv())) + { + WxMaUserInfo wxUserInfo = wxMaService.getUserService().getUserInfo(sessionKey, request.getEncryptedData(), request.getIv()); + if (wxUserInfo != null) + { + // 更新微信信息 + wechatInfo.setNickname(wxUserInfo.getNickName()); + wechatInfo.setAvatar(wxUserInfo.getAvatarUrl()); + wechatInfo.setGender(Integer.valueOf(wxUserInfo.getGender())); + wechatInfo.setCountry(wxUserInfo.getCountry()); + wechatInfo.setProvince(wxUserInfo.getProvince()); + wechatInfo.setCity(wxUserInfo.getCity()); + wechatInfo.setUpdatedAt(new Date()); + fateUserWechatMapper.updateFateUserWechat(wechatInfo); + + // 更新用户基本信息 + userInfo.setNickname(wxUserInfo.getNickName()); + userInfo.setAvatar(wxUserInfo.getAvatarUrl()); + userInfo.setGender(Integer.valueOf(wxUserInfo.getGender())); + if (StringUtils.isNotEmpty(wxUserInfo.getProvince())) + { + userInfo.setProvince(wxUserInfo.getProvince()); + } + if (StringUtils.isNotEmpty(wxUserInfo.getCity())) + { + userInfo.setCity(wxUserInfo.getCity()); + } + fateUserInfoMapper.updateFateUserInfo(userInfo); + } + } + + // 7. 构建响应 + response.setUserId(userInfo.getUserId()); + response.setUsername(userInfo.getUsername()); + response.setNickname(userInfo.getNickname()); + response.setAvatar(userInfo.getAvatar()); + response.setToken(generateToken(userInfo.getUserId())); + response.setUserLevel(userInfo.getUserLevel()); + response.setCoin(userInfo.getCoin()); + response.setDiamond(userInfo.getDiamond()); + response.setIsNewUser(isNewUser); + + return response; + } + catch (Exception e) + { + e.printStackTrace(); + throw new RuntimeException("微信登录失败:" + e.getMessage()); + } + } + + /** + * 生成令牌 + */ + private String generateToken(Long userId) + { + // TODO: 实现JWT或自定义token生成逻辑 + // 这里简单使用userId和时间戳生成token + return "token_" + userId + "_" + System.currentTimeMillis(); + } +}