初始化

This commit is contained in:
2026-01-18 21:49:20 +08:00
parent e60298df01
commit 2f119ebf7f
11 changed files with 1144 additions and 0 deletions

34
ruoyi-client/pom.xml Normal file
View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.9.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-client</artifactId>
<description>
用户端服务模块,支持第三方登录(微信、支付宝、抖音)
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
</dependency>
<!-- 系统模块包含第三方SDK -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-system</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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;
}
}

View File

@ -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;
}
}

View File

@ -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<FateSkills> 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);
}

View File

@ -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<FateUserInfo> 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);
}

View File

@ -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<FateSkills> selectFateSkillsList(FateSkills fateSkills)
{
List<FateSkills> 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);
}
}

View File

@ -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<FateUserInfo> selectFateUserInfoList(FateUserInfo fateUserInfo)
{
List<FateUserInfo> 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);
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}