更新一键登录

This commit is contained in:
2026-02-01 09:52:03 +08:00
parent e17b8a78d0
commit 5413de4a60
19 changed files with 1199 additions and 2 deletions

View File

@@ -0,0 +1,53 @@
package com.vetti.hotake.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 社交登录日志对象 hotake_social_login_log
*
* @author vetti
*/
@Data
public class HotakeSocialLoginLog implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("主键ID")
private Long id;
@ApiModelProperty("系统用户ID")
private Long userId;
@ApiModelProperty("第三方平台类型")
private String provider;
@ApiModelProperty("第三方平台用户ID")
private String providerUserId;
@ApiModelProperty("登录类型login/register/bind")
private String loginType;
@ApiModelProperty("登录IP")
private String loginIp;
@ApiModelProperty("登录地点")
private String loginLocation;
@ApiModelProperty("浏览器类型")
private String browser;
@ApiModelProperty("操作系统")
private String os;
@ApiModelProperty("登录状态0成功 1失败")
private String status;
@ApiModelProperty("提示消息")
private String msg;
@ApiModelProperty("登录时间")
private Date loginTime;
}

View File

@@ -0,0 +1,52 @@
package com.vetti.hotake.domain;
import com.vetti.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
* 用户社交账号绑定对象 hotake_social_user
*
* @author vetti
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class HotakeSocialUser extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("主键ID")
private Long id;
@ApiModelProperty("系统用户ID")
private Long userId;
@ApiModelProperty("第三方平台类型google/microsoft/linkedin")
private String provider;
@ApiModelProperty("第三方平台用户唯一标识")
private String providerUserId;
@ApiModelProperty("第三方平台邮箱")
private String email;
@ApiModelProperty("第三方平台用户名")
private String name;
@ApiModelProperty("第三方平台头像URL")
private String avatar;
@ApiModelProperty("访问令牌")
private String accessToken;
@ApiModelProperty("刷新令牌")
private String refreshToken;
@ApiModelProperty("令牌过期时间")
private Date tokenExpireTime;
@ApiModelProperty("第三方平台原始用户信息JSON")
private String rawUserInfo;
}

View File

@@ -0,0 +1,30 @@
package com.vetti.hotake.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 社交登录请求DTO
*
* @author vetti
*/
@Data
public class HotakeSocialLoginRequestDto {
@NotBlank(message = "平台类型不能为空")
@ApiModelProperty(value = "第三方平台类型google/microsoft/linkedin", required = true)
private String provider;
@NotBlank(message = "授权码不能为空")
@ApiModelProperty(value = "OAuth授权码", required = true)
private String code;
@NotBlank(message = "用户类型不能为空")
@ApiModelProperty(value = "用户类型(interviewer:面试官,candidate:候选者)", required = true)
private String sysUserType;
@ApiModelProperty("state参数用于防止CSRF攻击")
private String state;
}

View File

@@ -0,0 +1,32 @@
package com.vetti.hotake.domain.dto;
import com.vetti.common.core.domain.entity.SysUser;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 社交登录返回结果DTO
*
* @author vetti
*/
@Data
public class HotakeSocialLoginResultDto {
@ApiModelProperty("令牌")
private String token;
@ApiModelProperty("用户ID")
private Long userId;
@ApiModelProperty("用户类型(interviewer:面试官,candidate:候选者)")
private String sysUserType;
@ApiModelProperty("是否新注册用户(true:新注册,false:已存在用户登录)")
private Boolean isNewUser;
@ApiModelProperty("第三方平台类型")
private String provider;
@ApiModelProperty("用户信息对象")
private SysUser user;
}

View File

@@ -0,0 +1,40 @@
package com.vetti.hotake.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 第三方平台用户信息DTO统一格式
*
* @author vetti
*/
@Data
public class HotakeSocialUserInfoDto {
@ApiModelProperty("第三方平台类型")
private String provider;
@ApiModelProperty("第三方平台用户唯一标识")
private String providerUserId;
@ApiModelProperty("邮箱")
private String email;
@ApiModelProperty("用户名/昵称")
private String name;
@ApiModelProperty("头像URL")
private String avatar;
@ApiModelProperty("访问令牌")
private String accessToken;
@ApiModelProperty("刷新令牌")
private String refreshToken;
@ApiModelProperty("令牌有效期(秒)")
private Long expiresIn;
@ApiModelProperty("原始用户信息JSON")
private String rawUserInfo;
}

View File

@@ -0,0 +1,16 @@
package com.vetti.hotake.mapper;
import com.vetti.hotake.domain.HotakeSocialLoginLog;
/**
* 社交登录日志Mapper接口
*
* @author vetti
*/
public interface HotakeSocialLoginLogMapper {
/**
* 新增社交登录日志
*/
int insert(HotakeSocialLoginLog log);
}

View File

@@ -0,0 +1,45 @@
package com.vetti.hotake.mapper;
import com.vetti.hotake.domain.HotakeSocialUser;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 用户社交账号绑定Mapper接口
*
* @author vetti
*/
public interface HotakeSocialUserMapper {
/**
* 根据provider和providerUserId查询社交用户
*/
HotakeSocialUser selectByProviderAndProviderUserId(@Param("provider") String provider,
@Param("providerUserId") String providerUserId);
/**
* 根据用户ID和provider查询社交绑定
*/
HotakeSocialUser selectByUserIdAndProvider(@Param("userId") Long userId, @Param("provider") String provider);
/**
* 根据用户ID查询所有社交绑定
*/
List<HotakeSocialUser> selectByUserId(@Param("userId") Long userId);
/**
* 新增社交用户绑定
*/
int insert(HotakeSocialUser socialUser);
/**
* 更新社交用户绑定
*/
int update(HotakeSocialUser socialUser);
/**
* 删除社交用户绑定(逻辑删除)
*/
int deleteById(@Param("id") Long id);
}

View File

@@ -0,0 +1,49 @@
package com.vetti.hotake.service;
import com.vetti.common.core.domain.entity.SysUser;
import com.vetti.hotake.domain.HotakeSocialUser;
import com.vetti.hotake.domain.dto.HotakeSocialLoginRequestDto;
import com.vetti.hotake.domain.dto.HotakeSocialLoginResultDto;
import java.util.List;
/**
* 社交用户服务接口
*
* @author vetti
*/
public interface IHotakeSocialUserService {
/**
* 社交登录(登录或自动注册)
*
* @param requestDto 社交登录请求
* @return 登录结果
*/
HotakeSocialLoginResultDto socialLogin(HotakeSocialLoginRequestDto requestDto);
/**
* 获取OAuth授权URL
*
* @param provider 平台类型
* @param state state参数
* @return 授权URL
*/
String getAuthorizationUrl(String provider, String state);
/**
* 根据用户ID查询绑定的社交账号
*
* @param userId 用户ID
* @return 社交账号列表
*/
List<HotakeSocialUser> listByUserId(Long userId);
/**
* 解绑社交账号
*
* @param userId 用户ID
* @param provider 平台类型
*/
void unbind(Long userId, String provider);
}

View File

@@ -129,8 +129,8 @@ public class HotakeReferenceCheckServiceImpl extends BaseServiceImpl implements
if (CollectionUtil.isNotEmpty(cvInfoList)) {
HotakeCvInfo cvInfo = cvInfoList.get(0);
if (StrUtil.isNotEmpty(cvInfo.getAnalyzedCvJson())) {
HotakeCvInfoDto cvInfoDto = JSONUtil.toBean(cvInfo.getAnalyzedCvJson(), HotakeCvInfoDto.class);
if (StrUtil.isNotEmpty(cvInfo.getCvTemplateJson())) {
HotakeCvInfoDto cvInfoDto = JSONUtil.toBean(cvInfo.getCvTemplateJson(), HotakeCvInfoDto.class);
if (cvInfoDto != null && CollectionUtil.isNotEmpty(cvInfoDto.getExperience())) {
experienceList = cvInfoDto.getExperience();
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vetti.hotake.mapper.HotakeSocialLoginLogMapper">
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into hotake_social_login_log (
user_id, provider, provider_user_id, login_type, login_ip, login_location,
browser, os, status, msg, login_time
) values (
#{userId}, #{provider}, #{providerUserId}, #{loginType}, #{loginIp}, #{loginLocation},
#{browser}, #{os}, #{status}, #{msg}, #{loginTime}
)
</insert>
</mapper>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vetti.hotake.mapper.HotakeSocialUserMapper">
<resultMap type="com.vetti.hotake.domain.HotakeSocialUser" id="HotakeSocialUserResult">
<id property="id" column="id"/>
<result property="userId" column="user_id"/>
<result property="provider" column="provider"/>
<result property="providerUserId" column="provider_user_id"/>
<result property="email" column="email"/>
<result property="name" column="name"/>
<result property="avatar" column="avatar"/>
<result property="accessToken" column="access_token"/>
<result property="refreshToken" column="refresh_token"/>
<result property="tokenExpireTime" column="token_expire_time"/>
<result property="rawUserInfo" column="raw_user_info"/>
<result property="delFlag" column="del_flag"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="selectColumns">
id, user_id, provider, provider_user_id, email, name, avatar, access_token, refresh_token,
token_expire_time, raw_user_info, del_flag, create_by, create_time, update_by, update_time, remark
</sql>
<select id="selectByProviderAndProviderUserId" resultMap="HotakeSocialUserResult">
select <include refid="selectColumns"/>
from hotake_social_user
where provider = #{provider} and provider_user_id = #{providerUserId} and del_flag = '0'
</select>
<select id="selectByUserIdAndProvider" resultMap="HotakeSocialUserResult">
select <include refid="selectColumns"/>
from hotake_social_user
where user_id = #{userId} and provider = #{provider} and del_flag = '0'
</select>
<select id="selectByUserId" resultMap="HotakeSocialUserResult">
select <include refid="selectColumns"/>
from hotake_social_user
where user_id = #{userId} and del_flag = '0'
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into hotake_social_user (
user_id, provider, provider_user_id, email, name, avatar, access_token, refresh_token,
token_expire_time, raw_user_info, del_flag, create_by, create_time, remark
) values (
#{userId}, #{provider}, #{providerUserId}, #{email}, #{name}, #{avatar}, #{accessToken}, #{refreshToken},
#{tokenExpireTime}, #{rawUserInfo}, '0', #{createBy}, now(), #{remark}
)
</insert>
<update id="update">
update hotake_social_user
<set>
<if test="userId != null">user_id = #{userId},</if>
<if test="email != null">email = #{email},</if>
<if test="name != null">name = #{name},</if>
<if test="avatar != null">avatar = #{avatar},</if>
<if test="accessToken != null">access_token = #{accessToken},</if>
<if test="refreshToken != null">refresh_token = #{refreshToken},</if>
<if test="tokenExpireTime != null">token_expire_time = #{tokenExpireTime},</if>
<if test="rawUserInfo != null">raw_user_info = #{rawUserInfo},</if>
update_by = #{updateBy},
update_time = now()
</set>
where id = #{id}
</update>
<update id="deleteById">
update hotake_social_user set del_flag = '2', update_time = now() where id = #{id}
</update>
</mapper>