Compare commits

..

10 Commits

Author SHA1 Message Date
ID
94bee0be90 更新 2026-02-24 21:01:54 +08:00
4277ff1869 优化问题修改 2026-02-04 22:50:00 +08:00
ID
7e77f64cc1 背景调查 新增 批量添加接口 2026-02-04 20:59:14 +08:00
ID
daaf3d4bfa 更新第三方登录的配置文件 2026-02-04 17:29:17 +08:00
ID
1544286664 更新遗漏的文件 2026-02-04 15:59:10 +08:00
ID
42fea892c6 更新遗漏的文件 2026-02-04 15:55:37 +08:00
ID
102fe8a94b 修改 2026-02-04 15:42:15 +08:00
ID
2c64f7575f 修改端口 2026-02-04 14:06:10 +08:00
ID
251d7ca502 新增匿名访问地址 /oauth2/authorize/** 2026-02-04 14:05:32 +08:00
ID
4085b44d0b 修改bug:招聘者:候选者列表中,工作经验的“年限时长“数据好想不太正确 2026-02-04 09:34:23 +08:00
23 changed files with 1098 additions and 79 deletions

View File

@@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 参考检查Controller
* 背景调查Controller
*
* @author vetti
* @date 2026-01-29
@@ -30,9 +30,9 @@ public class HotakeReferenceCheckController extends BaseController {
private IHotakeReferenceCheckService hotakeReferenceCheckService;
/**
* 查询参考检查列表
* 查询背景调查列表
*/
@ApiOperation("查询参考检查列表(分页)")
@ApiOperation("查询背景调查列表(分页)")
@GetMapping("/getPagelist")
public TableWebDataInfo<HotakeReferenceCheck> list(HotakeReferenceCheck hotakeReferenceCheck) {
startPage();
@@ -42,9 +42,9 @@ public class HotakeReferenceCheckController extends BaseController {
}
/**
* 查询参考检查列表
* 查询背景调查列表
*/
@ApiOperation("查询参考检查列表(无分页)")
@ApiOperation("查询背景调查列表(无分页)")
@GetMapping("/getList")
public R<List<HotakeReferenceCheck>> getList(HotakeReferenceCheck hotakeReferenceCheck) {
hotakeReferenceCheck.setCreateBy(getUsername());
@@ -53,39 +53,50 @@ public class HotakeReferenceCheckController extends BaseController {
}
/**
* 获取参考检查详细信息
* 获取背景调查详细信息
*/
@ApiOperation("获取参考检查详细信息")
@ApiOperation("获取背景调查详细信息")
@GetMapping(value = "/{id}")
public R<HotakeReferenceCheck> getInfo(@ApiParam("参考检查ID") @PathVariable("id") Integer id) {
public R<HotakeReferenceCheck> getInfo(@ApiParam("背景调查ID") @PathVariable("id") Integer id) {
return R.ok(hotakeReferenceCheckService.selectHotakeReferenceCheckById(id), "");
}
/**
* 新增参考检
* 新增背景调
*/
@ApiOperation("新增参考检")
@Log(title = "参考检", businessType = BusinessType.INSERT)
@ApiOperation("新增背景调")
@Log(title = "背景调", businessType = BusinessType.INSERT)
@PostMapping
public R<HotakeReferenceCheck> add(@RequestBody HotakeReferenceCheck hotakeReferenceCheck) {
return R.ok(hotakeReferenceCheckService.insertHotakeReferenceCheck(hotakeReferenceCheck));
}
/**
* 修改参考检
* 批量新增背景调
*/
@ApiOperation("修改参考检")
@Log(title = "参考检", businessType = BusinessType.UPDATE)
@ApiOperation("批量新增背景调")
@Log(title = "背景调", businessType = BusinessType.INSERT)
@PostMapping("/batch")
public R<Integer> batchAdd(@RequestBody List<HotakeReferenceCheck> hotakeReferenceCheckList) {
int count = hotakeReferenceCheckService.batchInsertHotakeReferenceCheck(hotakeReferenceCheckList);
return R.ok(count);
}
/**
* 修改背景调查
*/
@ApiOperation("修改背景调查")
@Log(title = "背景调查", businessType = BusinessType.UPDATE)
@PutMapping
public R<HotakeReferenceCheck> edit(@RequestBody HotakeReferenceCheck hotakeReferenceCheck) {
return R.ok(hotakeReferenceCheckService.updateHotakeReferenceCheck(hotakeReferenceCheck));
}
/**
* 删除参考检
* 删除背景调
*/
@ApiOperation("删除参考检")
@Log(title = "参考检", businessType = BusinessType.DELETE)
@ApiOperation("删除背景调")
@Log(title = "背景调", businessType = BusinessType.DELETE)
@DeleteMapping("/{id}")
public R remove(@PathVariable Integer id) {
return R.ok(hotakeReferenceCheckService.deleteHotakeReferenceCheckById(id));

View File

@@ -2,7 +2,7 @@
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8686
port: 8080
servlet:
# 应用的访问路径
context-path: /
@@ -40,9 +40,9 @@ spring:
druid:
# 主库数据源
master:
url: jdbc:mysql://13.211.168.80:3306/vetti_service?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: vetti_service
password: Hotake@2025
url: jdbc:mysql://ddns.hotake.cn:13306/vetti_service?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: Hamkke@2021
# 从库数据源
slave:
# 从数据源开关/默认关闭
@@ -207,7 +207,7 @@ oauth2:
google:
client-id: 398978985110-ve0usu381mmdio12ff01iqvv1g087qvi.apps.googleusercontent.com # Google Cloud Console获取
client-secret: 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 # Google Cloud Console获取
redirect-uri: https://vetti.hotake.cn/oauth2/callback/google
redirect-uri: https://vetti.com.au/oauth2/callback/google
scope: openid email profile # Google标准scope空格分隔
auth-uri: https://accounts.google.com/o/oauth2/v2/auth # Google授权地址
token-uri: https://oauth2.googleapis.com/token # Google令牌地址
@@ -220,7 +220,7 @@ oauth2:
microsoft:
client-id: 608cbc4f-3ee2-4f51-a72c-b2e3133fc5b2 # Azure Portal获取
client-secret: gvw8Q~dwG8Sv7HN3R3W3R7TQtcZyvrh88ZiJubPa # Azure Portal获取
redirect-uri: https://vetti.hotake.cn/api/oauth2/callback/microsoft
redirect-uri: https://vetti.com.au/oauth2/callback/microsoft
scope: openid,email,profile,User.Read # 需要额外的User.Read权限才能读取用户信息
auth-uri: https://login.microsoftonline.com/common/oauth2/v2.0/authorize # 微软授权地址
token-uri: https://login.microsoftonline.com/common/oauth2/v2.0/token # 微软令牌地址
@@ -233,7 +233,7 @@ oauth2:
linkedin:
client-id: 86uq3opzshd3bq # LinkedIn Developer Portal获取
client-secret: WPL_AP1.mipgyxfgfBoN12Th.1TXeFg== # LinkedIn Developer Portal获取
redirect-uri: https://vetti.hotake.cn/api/oauth2/callback/linkedin
redirect-uri: https://vetti.com.au/oauth2/callback/linkedin
scope: openid profile email # LinkedIn使用OpenID Connectscope顺序和命名略有不同
auth-uri: https://www.linkedin.com/oauth/v2/authorization # LinkedIn授权地址
token-uri: https://www.linkedin.com/oauth/v2/accessToken # LinkedIn令牌地址

View File

@@ -86,3 +86,10 @@ HotakeSecurityController10004 = All other sessions terminated
# Logout related
HotakeSecurityServiceImpl10011 = User logged out
# Trusted device related messages
HotakeSecurityServiceImpl10012 = Updated trusted device
HotakeSecurityServiceImpl10013 = Added trusted device
HotakeSecurityServiceImpl10014 = Device does not exist or no permission to operate
HotakeSecurityServiceImpl10015 = Removed trusted device

View File

@@ -83,3 +83,10 @@ HotakeSecurityController10004 = 所有其他会话已终止
# 退出登录相关
HotakeSecurityServiceImpl10011 = 用户退出登录
# 可信设备相关消息
HotakeSecurityServiceImpl10012 = 更新可信设备
HotakeSecurityServiceImpl10013 = 添加可信设备
HotakeSecurityServiceImpl10014 = 设备不存在或无权操作
HotakeSecurityServiceImpl10015 = 移除可信设备

View File

@@ -154,6 +154,25 @@ public class ResumeTextExtractor {
* 3. 设置合理的经验范围限制1-30年
* 4. 如果无法估算返回随机的合理值2-10年
*
* TODO: 修复工作年限计算错误 - Bug修复说明
* 问题:原有算法直接用 endYear - earliestYear 计算工作年限,
* 这会导致将教育时间也计算在内,例如:
* - 2006年上大学2025年工作 = 19年错误
* - 实际应该是2010年毕业后开始工作约15年
*
* 修复方案:
* 1. 优先使用AI解析的experienceYear字段已在AI提示词中修复
* 2. 此方法作为备用估算,采用保守策略:
* - 假设教育时间约4年本科
* - 从最早年份+4年开始计算工作经验
* - 这样可以避免将教育时间计入工作年限
* 3. 如果年份跨度小于4年说明可能没有教育经历直接计算
*
* 注意这只是备用估算方法主要依赖AI解析的准确结果
*
* 修改日期2026-02-04
* 修改人sxc
*
* @param text 简历文本
* @return 估算的工作经验年数
*/
@@ -178,9 +197,25 @@ public class ResumeTextExtractor {
int latestYear = years.get(years.size() - 1);
int currentYear = Year.now().getValue();
// 计算工作经验:从最早年份到最新年份(不超过当前年份)
// 计算工作经验:从最新年份(不超过当前年份)
int endYear = Math.min(latestYear, currentYear);
int experience = endYear - earliestYear;
// TODO: 修复点 - 排除教育时间
// 假设教育时间约4年从最早年份+4年开始计算工作经验
// 这样可以避免将大学时间计入工作年限
int yearSpan = endYear - earliestYear;
int experience;
if (yearSpan <= 4) {
// 如果年份跨度小于等于4年说明可能没有教育经历或者是短期工作
// 直接使用年份差
experience = yearSpan;
} else {
// 年份跨度大于4年假设前4年是教育时间
// 从最早年份+4年开始计算工作经验
int workStartYear = earliestYear + 4;
experience = endYear - workStartYear;
}
// 限制经验年数在合理范围内1-30年
return Math.max(1, Math.min(experience, 30));

View File

@@ -114,7 +114,7 @@ public class SecurityConfig
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.antMatchers("/login", "/register", "/captchaImage","/aiCommon/**","/voice-websocket/multiple/**",
"/voice-websocket/**","/voice-websocket/multiplePcm/**","/verification/email/send","/verification/email/verify","/verification/phone/send",
"/forgotPassword","/verification/email/register/send","/voice-websocket/elevenLabsAgent/**").permitAll()
"/forgotPassword","/verification/email/register/send","/voice-websocket/elevenLabsAgent/**","/oauth2/authorize/**","/prod-api/oauth2/authorize/**").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()

View File

@@ -0,0 +1,35 @@
package com.vetti.hotake.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/**
* 修改密码DTO
*
* @author vetti
* @date 2026-02-02
*/
@Data
@ApiModel("修改密码请求")
public class SecurityChangePasswordDto
{
/** 当前密码 */
@ApiModelProperty(value = "当前密码", required = true)
@NotBlank(message = "当前密码不能为空")
private String currentPassword;
/** 新密码 */
@ApiModelProperty(value = "新密码", required = true)
@NotBlank(message = "新密码不能为空")
@Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间")
private String newPassword;
/** 确认新密码 */
@ApiModelProperty(value = "确认新密码", required = true)
@NotBlank(message = "确认密码不能为空")
private String confirmPassword;
}

View File

@@ -0,0 +1,65 @@
package com.vetti.hotake.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* 登录会话VO
*
* @author vetti
* @date 2026-02-02
*/
@Data
@ApiModel("登录会话信息")
public class SecuritySessionVo
{
/** 会话ID */
@ApiModelProperty("会话ID")
private Long id;
/** 设备名称(浏览器 + 操作系统) */
@ApiModelProperty("设备名称")
private String deviceName;
/** 设备类型 */
@ApiModelProperty("设备类型")
private String deviceType;
/** 浏览器 */
@ApiModelProperty("浏览器")
private String browser;
/** 操作系统 */
@ApiModelProperty("操作系统")
private String os;
/** IP地址 */
@ApiModelProperty("IP地址")
private String ipAddress;
/** 地理位置 */
@ApiModelProperty("地理位置")
private String location;
/** 登录时间 */
@ApiModelProperty("登录时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date loginTime;
/** 最后活动时间 */
@ApiModelProperty("最后活动时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date lastActivityTime;
/** 是否当前会话 */
@ApiModelProperty("是否当前会话")
private Boolean isCurrent;
/** 是否活跃 */
@ApiModelProperty("是否活跃")
private Boolean isActive;
}

View File

@@ -35,6 +35,14 @@ public interface HotakeReferenceCheckMapper
*/
public int insertHotakeReferenceCheck(HotakeReferenceCheck hotakeReferenceCheck);
/**
* 批量新增参考检查
*
* @param hotakeReferenceCheckList 参考检查列表
* @return 结果
*/
public int batchInsertHotakeReferenceCheck(List<HotakeReferenceCheck> hotakeReferenceCheckList);
/**
* 修改参考检查
*

View File

@@ -0,0 +1,97 @@
package com.vetti.hotake.mapper;
import com.vetti.hotake.domain.HotakeSecurityLoginSessions;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 用户登录会话Mapper接口
*
* @author vetti
* @date 2026-02-02
*/
public interface HotakeSecurityLoginSessionsMapper
{
/**
* 查询用户登录会话
*
* @param id 用户登录会话主键
* @return 用户登录会话
*/
public HotakeSecurityLoginSessions selectHotakeSecurityLoginSessionsById(Long id);
/**
* 根据会话令牌查询会话
*
* @param sessionToken 会话令牌
* @return 用户登录会话
*/
public HotakeSecurityLoginSessions selectHotakeSecurityLoginSessionsByToken(String sessionToken);
/**
* 查询用户登录会话列表
*
* @param hotakeSecurityLoginSessions 用户登录会话
* @return 用户登录会话集合
*/
public List<HotakeSecurityLoginSessions> selectHotakeSecurityLoginSessionsList(HotakeSecurityLoginSessions hotakeSecurityLoginSessions);
/**
* 查询用户的活跃会话列表
*
* @param userId 用户ID
* @return 用户登录会话集合
*/
public List<HotakeSecurityLoginSessions> selectActiveSessionsByUserId(Long userId);
/**
* 新增用户登录会话
*
* @param hotakeSecurityLoginSessions 用户登录会话
* @return 结果
*/
public int insertHotakeSecurityLoginSessions(HotakeSecurityLoginSessions hotakeSecurityLoginSessions);
/**
* 修改用户登录会话
*
* @param hotakeSecurityLoginSessions 用户登录会话
* @return 结果
*/
public int updateHotakeSecurityLoginSessions(HotakeSecurityLoginSessions hotakeSecurityLoginSessions);
/**
* 删除用户登录会话
*
* @param id 用户登录会话主键
* @return 结果
*/
public int deleteHotakeSecurityLoginSessionsById(Long id);
/**
* 批量删除用户登录会话
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteHotakeSecurityLoginSessionsByIds(Long[] ids);
/**
* 终止用户的其他会话(除了当前会话)
*
* @param userId 用户ID
* @param currentSessionToken 当前会话令牌
* @return 结果
*/
public int terminateOtherSessions(@Param("userId") Long userId, @Param("currentSessionToken") String currentSessionToken);
/**
* 终止指定会话
*
* @param id 会话ID
* @param userId 用户ID
* @return 结果
*/
public int terminateSession(@Param("id") Long id, @Param("userId") Long userId);
}

View File

@@ -0,0 +1,71 @@
package com.vetti.hotake.mapper;
import com.vetti.hotake.domain.HotakeSecurityPasswordHistory;
import java.util.List;
/**
* 密码历史记录Mapper接口
*
* @author vetti
* @date 2026-02-02
*/
public interface HotakeSecurityPasswordHistoryMapper
{
/**
* 查询密码历史记录
*
* @param id 密码历史记录主键
* @return 密码历史记录
*/
public HotakeSecurityPasswordHistory selectHotakeSecurityPasswordHistoryById(Long id);
/**
* 查询密码历史记录列表
*
* @param hotakeSecurityPasswordHistory 密码历史记录
* @return 密码历史记录集合
*/
public List<HotakeSecurityPasswordHistory> selectHotakeSecurityPasswordHistoryList(HotakeSecurityPasswordHistory hotakeSecurityPasswordHistory);
/**
* 查询用户最近的密码历史记录
*
* @param userId 用户ID
* @param limit 查询数量
* @return 密码历史记录集合
*/
public List<HotakeSecurityPasswordHistory> selectRecentPasswordHistory(Long userId, int limit);
/**
* 新增密码历史记录
*
* @param hotakeSecurityPasswordHistory 密码历史记录
* @return 结果
*/
public int insertHotakeSecurityPasswordHistory(HotakeSecurityPasswordHistory hotakeSecurityPasswordHistory);
/**
* 修改密码历史记录
*
* @param hotakeSecurityPasswordHistory 密码历史记录
* @return 结果
*/
public int updateHotakeSecurityPasswordHistory(HotakeSecurityPasswordHistory hotakeSecurityPasswordHistory);
/**
* 删除密码历史记录
*
* @param id 密码历史记录主键
* @return 结果
*/
public int deleteHotakeSecurityPasswordHistoryById(Long id);
/**
* 批量删除密码历史记录
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteHotakeSecurityPasswordHistoryByIds(Long[] ids);
}

View File

@@ -0,0 +1,62 @@
package com.vetti.hotake.mapper;
import com.vetti.hotake.domain.HotakeSecuritySecurityLogs;
import java.util.List;
/**
* 安全日志Mapper接口
*
* @author vetti
* @date 2026-02-02
*/
public interface HotakeSecuritySecurityLogsMapper
{
/**
* 查询安全日志
*
* @param id 安全日志主键
* @return 安全日志
*/
public HotakeSecuritySecurityLogs selectHotakeSecuritySecurityLogsById(Long id);
/**
* 查询安全日志列表
*
* @param hotakeSecuritySecurityLogs 安全日志
* @return 安全日志集合
*/
public List<HotakeSecuritySecurityLogs> selectHotakeSecuritySecurityLogsList(HotakeSecuritySecurityLogs hotakeSecuritySecurityLogs);
/**
* 新增安全日志
*
* @param hotakeSecuritySecurityLogs 安全日志
* @return 结果
*/
public int insertHotakeSecuritySecurityLogs(HotakeSecuritySecurityLogs hotakeSecuritySecurityLogs);
/**
* 修改安全日志
*
* @param hotakeSecuritySecurityLogs 安全日志
* @return 结果
*/
public int updateHotakeSecuritySecurityLogs(HotakeSecuritySecurityLogs hotakeSecuritySecurityLogs);
/**
* 删除安全日志
*
* @param id 安全日志主键
* @return 结果
*/
public int deleteHotakeSecuritySecurityLogsById(Long id);
/**
* 批量删除安全日志
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteHotakeSecuritySecurityLogsByIds(Long[] ids);
}

View File

@@ -0,0 +1,70 @@
package com.vetti.hotake.mapper;
import com.vetti.hotake.domain.HotakeSecuritySettings;
import java.util.List;
/**
* 用户安全设置Mapper接口
*
* @author vetti
* @date 2026-02-02
*/
public interface HotakeSecuritySettingsMapper
{
/**
* 查询用户安全设置
*
* @param id 用户安全设置主键
* @return 用户安全设置
*/
public HotakeSecuritySettings selectHotakeSecuritySettingsById(Long id);
/**
* 根据用户ID查询安全设置
*
* @param userId 用户ID
* @return 用户安全设置
*/
public HotakeSecuritySettings selectHotakeSecuritySettingsByUserId(Long userId);
/**
* 查询用户安全设置列表
*
* @param hotakeSecuritySettings 用户安全设置
* @return 用户安全设置集合
*/
public List<HotakeSecuritySettings> selectHotakeSecuritySettingsList(HotakeSecuritySettings hotakeSecuritySettings);
/**
* 新增用户安全设置
*
* @param hotakeSecuritySettings 用户安全设置
* @return 结果
*/
public int insertHotakeSecuritySettings(HotakeSecuritySettings hotakeSecuritySettings);
/**
* 修改用户安全设置
*
* @param hotakeSecuritySettings 用户安全设置
* @return 结果
*/
public int updateHotakeSecuritySettings(HotakeSecuritySettings hotakeSecuritySettings);
/**
* 删除用户安全设置
*
* @param id 用户安全设置主键
* @return 结果
*/
public int deleteHotakeSecuritySettingsById(Long id);
/**
* 批量删除用户安全设置
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteHotakeSecuritySettingsByIds(Long[] ids);
}

View File

@@ -0,0 +1,96 @@
package com.vetti.hotake.mapper;
import com.vetti.hotake.domain.HotakeSecurityTrustedDevices;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 可信设备Mapper接口
*
* @author vetti
* @date 2026-02-02
*/
public interface HotakeSecurityTrustedDevicesMapper
{
/**
* 查询可信设备
*
* @param id 可信设备主键
* @return 可信设备
*/
public HotakeSecurityTrustedDevices selectHotakeSecurityTrustedDevicesById(Long id);
/**
* 根据用户ID和设备ID查询可信设备
*
* @param userId 用户ID
* @param deviceId 设备ID
* @return 可信设备
*/
public HotakeSecurityTrustedDevices selectByUserIdAndDeviceId(@Param("userId") Long userId, @Param("deviceId") String deviceId);
/**
* 查询可信设备列表
*
* @param hotakeSecurityTrustedDevices 可信设备
* @return 可信设备集合
*/
public List<HotakeSecurityTrustedDevices> selectHotakeSecurityTrustedDevicesList(HotakeSecurityTrustedDevices hotakeSecurityTrustedDevices);
/**
* 查询用户的可信设备列表
*
* @param userId 用户ID
* @return 可信设备集合
*/
public List<HotakeSecurityTrustedDevices> selectTrustedDevicesByUserId(Long userId);
/**
* 新增可信设备
*
* @param hotakeSecurityTrustedDevices 可信设备
* @return 结果
*/
public int insertHotakeSecurityTrustedDevices(HotakeSecurityTrustedDevices hotakeSecurityTrustedDevices);
/**
* 修改可信设备
*
* @param hotakeSecurityTrustedDevices 可信设备
* @return 结果
*/
public int updateHotakeSecurityTrustedDevices(HotakeSecurityTrustedDevices hotakeSecurityTrustedDevices);
/**
* 删除可信设备
*
* @param id 可信设备主键
* @return 结果
*/
public int deleteHotakeSecurityTrustedDevicesById(Long id);
/**
* 批量删除可信设备
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteHotakeSecurityTrustedDevicesByIds(Long[] ids);
/**
* 移除用户的可信设备
*
* @param id 设备记录ID
* @param userId 用户ID
* @return 结果
*/
public int removeTrustedDevice(@Param("id") Long id, @Param("userId") Long userId);
/**
* 清理过期的可信设备
*
* @return 结果
*/
public int cleanExpiredTrustedDevices();
}

View File

@@ -35,6 +35,14 @@ public interface IHotakeReferenceCheckService
*/
public HotakeReferenceCheck insertHotakeReferenceCheck(HotakeReferenceCheck hotakeReferenceCheck);
/**
* 批量新增参考检查
*
* @param hotakeReferenceCheckList 参考检查列表
* @return 结果
*/
public int batchInsertHotakeReferenceCheck(List<HotakeReferenceCheck> hotakeReferenceCheckList);
/**
* 修改参考检查
*

View File

@@ -3,6 +3,7 @@ package com.vetti.hotake.service;
import com.vetti.hotake.domain.HotakeSecuritySettings;
import com.vetti.hotake.domain.dto.SecurityChangePasswordDto;
import com.vetti.hotake.domain.vo.SecuritySessionVo;
import com.vetti.hotake.domain.vo.SecurityTrustedDeviceVo;
import java.util.List;
@@ -97,4 +98,37 @@ public interface IHotakeSecurityService
* @return 结果
*/
int updateSessionLogout(String sessionToken);
/**
* 获取当前用户的可信设备列表
*
* @return 可信设备列表
*/
List<SecurityTrustedDeviceVo> getTrustedDevices();
/**
* 添加可信设备
*
* @param deviceId 设备唯一标识
* @param deviceName 设备名称
* @return 结果
*/
int addTrustedDevice(String deviceId, String deviceName);
/**
* 移除可信设备
*
* @param id 设备记录ID
* @return 结果
*/
int removeTrustedDevice(Long id);
/**
* 检查设备是否为可信设备
*
* @param userId 用户ID
* @param deviceId 设备唯一标识
* @return 是否可信
*/
boolean isTrustedDevice(Long userId, String deviceId);
}

View File

@@ -1427,6 +1427,7 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
String resultJsonOne = resultStrOne.replaceAll("```json", "").replaceAll("```", "");
log.info("候选人面试综合评估:{}", resultJsonOne);
Map dataMap = JSONUtil.toBean(resultJsonOne, Map.class);
if(dataMap != null){
Map scoreBreakdownMap = (Map) dataMap.get("score_breakdown");
if (scoreBreakdownMap != null) {
Map assessmentMap = (Map) scoreBreakdownMap.get("assessment");
@@ -1451,6 +1452,7 @@ public class HotakeAiCommonToolsServiceImpl extends BaseServiceImpl implements I
dto.setQuestionsDetails(details);
}
}
}
dto.setOverallScore(dataMap.get("overall_score").toString());
dto.setRecommendation(dataMap.get("recommendation").toString());

View File

@@ -547,7 +547,21 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC
List<Map<String, String>> list = new LinkedList();
Map<String, String> entity = new HashMap<>();
entity.put("role", "user");
entity.put("content", "从下面提供的文本中提取所有能识别到的简历信息只提取原文中存在的内容不要补充、不推测、不总结。需要提取的字段包括name姓名或人名、phone电话号码、email电子邮件地址、experienceYear根据工作经验计算出来的工作年限、position岗位或者简历中自己期望的职位等、location地点或者地址、家庭住址、links所有链接地址、currentWork(当前工作公司)、about关于我/自我介绍、skills_tools关键资格许可证、注册/会员资格、认证、languages语言能力,主要就是Languages下面的语言、experience工作经历,除了title、company、location、durationStart、durationEnd,其他的都放到description里面,并且description里面要根据换行符分成不同的content),日期要拆分成开始时间(durationStart)和结束时间(durationEnd)、education教育经历,日期要拆分成开始时间(durationStart)和结束时间(durationEnd))。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\\"name\\\": \\\"\\\", \\\"phone\\\": \\\"\\\", \\\"currentWork\\\": \\\"\\\", \\\"position\\\": \\\"\\\", \\\"location\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"experienceYear\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息" +
// TODO: 修复工作年限计算错误 - Bug修复说明
// 问题:之前的提示词中"experienceYear根据工作经验计算出来的工作年限"描述不够明确,
// 导致AI可能会将教育时间也计算在内或者简单地用最早年份到最新年份的差值
// 例如2006年上大学到2025年工作 = 19年错误实际应该是2010年毕业后开始工作约15年
//
// 修复方案:在提示词中明确工作年限的计算规则:
// 1. 只计算experience工作经历中的时间段不包括education教育经历
// 2. 累加所有工作经历的实际工作时长(年)
// 3. 如果工作结束时间是"Present"、"Current"、"至今"等,使用当前年份(2026)计算
// 4. 计算公式:总工作年限 = Σ(每段工作的结束年份 - 开始年份)
// 5. 结果保留整数,向下取整
//
// 修改日期2026-02-04
// 修改人sxc
entity.put("content", "从下面提供的文本中提取所有能识别到的简历信息只提取原文中存在的内容不要补充、不推测、不总结。需要提取的字段包括name姓名或人名、phone电话号码、email电子邮件地址、experienceYear工作年限重要只计算experience工作经历中的实际工作时长不包括education教育时间。计算方法累加所有工作经历的年数如果结束时间是Present/Current/至今则用2026年计算例如2018-01到2020-06工作2年+2021-03到Present工作5年=总共7年工作经验、position岗位或者简历中自己期望的职位等、location地点或者地址、家庭住址、links所有链接地址、currentWork(当前工作公司)、about关于我/自我介绍、skills_tools关键资格许可证、注册/会员资格、认证、languages语言能力,主要就是Languages下面的语言、experience工作经历,除了title、company、location、durationStart、durationEnd,其他的都放到description里面,并且description里面要根据换行符分成不同的content),日期要拆分成开始时间(durationStart)和结束时间(durationEnd)、education教育经历,日期要拆分成开始时间(durationStart)和结束时间(durationEnd))。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\\"name\\\": \\\"\\\", \\\"phone\\\": \\\"\\\", \\\"currentWork\\\": \\\"\\\", \\\"position\\\": \\\"\\\", \\\"location\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"experienceYear\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息" +
"不要解释说明,不要改写内容,其中日期字段要转换格式为:yyyy-MM例如 Apr 2016,要转换成 2016-04,并且只有年要补充固定月份01月例如2025要转换成2025-01。以下为待处理文本" + contents);
//根据AI做
list.add(entity);

View File

@@ -277,7 +277,7 @@ public class HotakeInitScreQuestionsReplyRecordInfoServiceImpl extends BaseServi
// HotakeCandidateCompatibilityDto compatibilityDto = aiCommonToolsService.handleCandidateCompatibility(compatibilityVo);
// applyInfo.setCandidateCompatibilityJson(JSONUtil.toJsonStr(compatibilityDto));
// applyInfo.setCandidateCompatibilityScore(compatibilityDto.getOverallScore());
try{
//候选人面试综合评估
HotakeCandidateInterviewEvaluationVo evaluationVo = new HotakeCandidateInterviewEvaluationVo();
@@ -305,6 +305,9 @@ public class HotakeInitScreQuestionsReplyRecordInfoServiceImpl extends BaseServi
HotakeCandidateInterviewEvaluationDto evaluationDto = aiCommonToolsService.getCandidateInterviewEvaluation(evaluationVo);
applyInfo.setEvaluationJson(JSONUtil.toJsonStr(evaluationDto));
applyInfo.setEvaluationScore(new BigDecimal(evaluationDto.getOverallScore()));
}catch (Exception e){
e.printStackTrace();
}
applyInfo.setRecruiterId(rolesInf.getRecruiterId());
applyInfo.setStage(StageEnum.APPLIED.getCode());
@@ -333,7 +336,21 @@ public class HotakeInitScreQuestionsReplyRecordInfoServiceImpl extends BaseServi
List<Map<String, String>> list = new LinkedList();
Map<String, String> entity = new HashMap<>();
entity.put("role", "user");
entity.put("content", "从下面提供的文本中提取所有能识别到的简历信息只提取原文中存在的内容不要补充、不推测、不总结。需要提取的字段包括name姓名或人名、phone电话号码、email电子邮件地址、experienceYear根据工作经验计算出来的工作年限、position岗位或者简历中自己期望的职位等、location地点或者地址、家庭住址、links所有链接地址、currentWork(当前工作公司)、about关于我/自我介绍、skills_tools关键资格许可证、注册/会员资格、认证、languages语言能力,主要就是Languages下面的语言、experience工作经历,除了title、company、location、durationStart、durationEnd,其他的都放到description里面,并且description里面要根据换行符分成不同的content),日期要拆分成开始时间(durationStart)和结束时间(durationEnd)、education教育经历,日期要拆分成开始时间(durationStart)和结束时间(durationEnd))。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\\"name\\\": \\\"\\\", \\\"phone\\\": \\\"\\\", \\\"currentWork\\\": \\\"\\\", \\\"position\\\": \\\"\\\", \\\"location\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"experienceYear\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息不要解释说明不要改写内容。以下为待处理文本" + contents);
// TODO: 修复工作年限计算错误 - Bug修复说明
// 问题:之前的提示词中"experienceYear根据工作经验计算出来的工作年限"描述不够明确,
// 导致AI可能会将教育时间也计算在内或者简单地用最早年份到最新年份的差值
// 例如2006年上大学到2025年工作 = 19年错误实际应该是2010年毕业后开始工作约15年
//
// 修复方案:在提示词中明确工作年限的计算规则:
// 1. 只计算experience工作经历中的时间段不包括education教育经历
// 2. 累加所有工作经历的实际工作时长(年)
// 3. 如果工作结束时间是"Present"、"Current"、"至今"等,使用当前年份(2026)计算
// 4. 计算公式:总工作年限 = Σ(每段工作的结束年份 - 开始年份)
// 5. 结果保留整数,向下取整
//
// 修改日期2026-02-04
// 修改人sxc
entity.put("content", "从下面提供的文本中提取所有能识别到的简历信息只提取原文中存在的内容不要补充、不推测、不总结。需要提取的字段包括name姓名或人名、phone电话号码、email电子邮件地址、experienceYear工作年限重要只计算experience工作经历中的实际工作时长不包括education教育时间。计算方法累加所有工作经历的年数如果结束时间是Present/Current/至今则用2026年计算例如2018-01到2020-06工作2年+2021-03到Present工作5年=总共7年工作经验、position岗位或者简历中自己期望的职位等、location地点或者地址、家庭住址、links所有链接地址、currentWork(当前工作公司)、about关于我/自我介绍、skills_tools关键资格许可证、注册/会员资格、认证、languages语言能力,主要就是Languages下面的语言、experience工作经历,除了title、company、location、durationStart、durationEnd,其他的都放到description里面,并且description里面要根据换行符分成不同的content),日期要拆分成开始时间(durationStart)和结束时间(durationEnd)、education教育经历,日期要拆分成开始时间(durationStart)和结束时间(durationEnd))。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\\"name\\\": \\\"\\\", \\\"phone\\\": \\\"\\\", \\\"currentWork\\\": \\\"\\\", \\\"position\\\": \\\"\\\", \\\"location\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"experienceYear\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息不要解释说明不要改写内容。以下为待处理文本" + contents);
//根据AI做
list.add(entity);
String resultCv = chatGPTClient.handleAiChat(JSONUtil.toJsonStr(list), "JX");

View File

@@ -73,6 +73,24 @@ public class HotakeReferenceCheckServiceImpl extends BaseServiceImpl implements
return hotakeReferenceCheck;
}
/**
* 批量新增参考检查
*
* @param hotakeReferenceCheckList 参考检查列表
* @return 结果
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int batchInsertHotakeReferenceCheck(List<HotakeReferenceCheck> hotakeReferenceCheckList) {
if (CollectionUtil.isEmpty(hotakeReferenceCheckList)) {
return 0;
}
for (HotakeReferenceCheck hotakeReferenceCheck : hotakeReferenceCheckList) {
fill(FillTypeEnum.INSERT.getCode(), hotakeReferenceCheck);
}
return hotakeReferenceCheckMapper.batchInsertHotakeReferenceCheck(hotakeReferenceCheckList);
}
/**
* 修改参考检查
*

View File

@@ -16,12 +16,15 @@ import com.vetti.hotake.domain.HotakeSecurityLoginSessions;
import com.vetti.hotake.domain.HotakeSecurityPasswordHistory;
import com.vetti.hotake.domain.HotakeSecuritySecurityLogs;
import com.vetti.hotake.domain.HotakeSecuritySettings;
import com.vetti.hotake.domain.HotakeSecurityTrustedDevices;
import com.vetti.hotake.domain.dto.SecurityChangePasswordDto;
import com.vetti.hotake.domain.vo.SecuritySessionVo;
import com.vetti.hotake.domain.vo.SecurityTrustedDeviceVo;
import com.vetti.hotake.mapper.HotakeSecurityLoginSessionsMapper;
import com.vetti.hotake.mapper.HotakeSecurityPasswordHistoryMapper;
import com.vetti.hotake.mapper.HotakeSecuritySecurityLogsMapper;
import com.vetti.hotake.mapper.HotakeSecuritySettingsMapper;
import com.vetti.hotake.mapper.HotakeSecurityTrustedDevicesMapper;
import com.vetti.hotake.service.IHotakeSecurityService;
import com.vetti.system.service.ISysUserService;
import eu.bitwalker.useragentutils.Browser;
@@ -60,6 +63,9 @@ public class HotakeSecurityServiceImpl extends BaseServiceImpl implements IHotak
@Autowired
private HotakeSecuritySecurityLogsMapper securityLogsMapper;
@Autowired
private HotakeSecurityTrustedDevicesMapper trustedDevicesMapper;
@Autowired
private ISysUserService userService;
@@ -451,4 +457,202 @@ public class HotakeSecurityServiceImpl extends BaseServiceImpl implements IHotak
}
return 0;
}
/**
* 获取当前用户的可信设备列表
*
* @return 可信设备列表
*/
@Transactional(readOnly = true)
@Override
public List<SecurityTrustedDeviceVo> getTrustedDevices()
{
Long userId = SecurityUtils.getUserId();
// 查询用户的可信设备
List<HotakeSecurityTrustedDevices> devicesList = trustedDevicesMapper.selectTrustedDevicesByUserId(userId);
// 获取当前设备标识可以从请求头或Cookie中获取
String currentDeviceId = getCurrentDeviceId();
// 转换为VO
List<SecurityTrustedDeviceVo> voList = new ArrayList<>();
for (HotakeSecurityTrustedDevices device : devicesList)
{
SecurityTrustedDeviceVo vo = new SecurityTrustedDeviceVo();
vo.setId(device.getId());
vo.setDeviceId(device.getDeviceId());
vo.setDeviceName(device.getDeviceName());
vo.setDeviceType(device.getDeviceType());
vo.setBrowser(device.getBrowser());
vo.setOs(device.getOs());
vo.setIpAddress(device.getIpAddress());
vo.setLocation(device.getLocation());
vo.setTrustExpiresAt(device.getTrustExpiresAt());
vo.setCreateTime(device.getCreateTime());
vo.setIsCurrent(device.getDeviceId().equals(currentDeviceId));
voList.add(vo);
}
return voList;
}
/**
* 添加可信设备
*
* @param deviceId 设备唯一标识
* @param deviceName 设备名称
* @return 结果
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int addTrustedDevice(String deviceId, String deviceName)
{
Long userId = SecurityUtils.getUserId();
// 检查是否已存在
HotakeSecurityTrustedDevices existDevice = trustedDevicesMapper.selectByUserIdAndDeviceId(userId, deviceId);
if (existDevice != null)
{
// 已存在,更新信任状态
existDevice.setIsTrusted(1);
existDevice.setDeviceName(deviceName);
// 设置信任过期时间30天
existDevice.setTrustExpiresAt(new Date(System.currentTimeMillis() + 30L * 24 * 60 * 60 * 1000));
fill(FillTypeEnum.UPDATE.getCode(), existDevice);
recordSecurityLog(userId, "TRUSTED_DEVICE_UPDATE",
MessageUtils.messageCustomize("HotakeSecurityServiceImpl10012") + "" + deviceName, "SUCCESS");
return trustedDevicesMapper.updateHotakeSecurityTrustedDevices(existDevice);
}
// 解析User-Agent
String userAgent = ServletUtils.getRequest().getHeader("User-Agent");
UserAgent ua = UserAgent.parseUserAgentString(userAgent);
Browser browser = ua.getBrowser();
OperatingSystem os = ua.getOperatingSystem();
// 获取IP和位置
String ipAddress = IpUtils.getIpAddr();
String location = AddressUtils.getRealAddressByIP(ipAddress);
// 判断设备类型
String deviceType = "Desktop";
if (os.getDeviceType() != null)
{
deviceType = os.getDeviceType().getName();
}
// 创建新的可信设备记录
HotakeSecurityTrustedDevices device = new HotakeSecurityTrustedDevices();
device.setUserId(userId);
device.setDeviceId(deviceId);
device.setDeviceName(StringUtils.isNotEmpty(deviceName) ? deviceName : browser.getName() + " / " + os.getName());
device.setDeviceType(deviceType);
device.setBrowser(browser.getName());
device.setOs(os.getName());
device.setIpAddress(ipAddress);
device.setLocation(location);
device.setIsTrusted(1);
// 设置信任过期时间30天
device.setTrustExpiresAt(new Date(System.currentTimeMillis() + 30L * 24 * 60 * 60 * 1000));
fill(FillTypeEnum.INSERT.getCode(), device);
// 记录安全日志
recordSecurityLog(userId, "TRUSTED_DEVICE_ADD",
MessageUtils.messageCustomize("HotakeSecurityServiceImpl10013") + "" + device.getDeviceName(), "SUCCESS");
return trustedDevicesMapper.insertHotakeSecurityTrustedDevices(device);
}
/**
* 移除可信设备
*
* @param id 设备记录ID
* @return 结果
*/
@Transactional(rollbackFor = Exception.class)
@Override
public int removeTrustedDevice(Long id)
{
Long userId = SecurityUtils.getUserId();
// 查询设备信息
HotakeSecurityTrustedDevices device = trustedDevicesMapper.selectHotakeSecurityTrustedDevicesById(id);
if (device == null || !device.getUserId().equals(userId))
{
throw new ServiceException(MessageUtils.messageCustomize("HotakeSecurityServiceImpl10014"));
}
// 删除设备
int result = trustedDevicesMapper.removeTrustedDevice(id, userId);
// 记录安全日志
recordSecurityLog(userId, "TRUSTED_DEVICE_REMOVE",
MessageUtils.messageCustomize("HotakeSecurityServiceImpl10015") + "" + device.getDeviceName(), "SUCCESS");
return result;
}
/**
* 检查设备是否为可信设备
*
* @param userId 用户ID
* @param deviceId 设备唯一标识
* @return 是否可信
*/
@Transactional(readOnly = true)
@Override
public boolean isTrustedDevice(Long userId, String deviceId)
{
if (StringUtils.isEmpty(deviceId))
{
return false;
}
HotakeSecurityTrustedDevices device = trustedDevicesMapper.selectByUserIdAndDeviceId(userId, deviceId);
if (device == null || device.getIsTrusted() != 1)
{
return false;
}
// 检查是否过期
if (device.getTrustExpiresAt() != null && device.getTrustExpiresAt().before(new Date()))
{
return false;
}
return true;
}
/**
* 获取当前设备标识
* 可以从请求头或Cookie中获取这里简单实现
*/
private String getCurrentDeviceId()
{
HttpServletRequest request = ServletUtils.getRequest();
// 优先从请求头获取
String deviceId = request.getHeader("X-Device-Id");
if (StringUtils.isEmpty(deviceId))
{
// 从Cookie获取
javax.servlet.http.Cookie[] cookies = request.getCookies();
if (cookies != null)
{
for (javax.servlet.http.Cookie cookie : cookies)
{
if ("device_id".equals(cookie.getName()))
{
deviceId = cookie.getValue();
break;
}
}
}
}
return deviceId;
}
}

View File

@@ -77,6 +77,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</trim>
</insert>
<insert id="batchInsertHotakeReferenceCheck" parameterType="java.util.List">
insert into hotake_reference_check
(company, full_name, position, relationship, email, save_job_experience, del_flag, create_by, create_time, update_by, update_time, remark)
values
<foreach collection="list" item="item" separator=",">
(
#{item.company},
#{item.fullName},
#{item.position},
#{item.relationship},
#{item.email},
#{item.saveJobExperience},
#{item.delFlag},
#{item.createBy},
#{item.createTime},
#{item.updateBy},
#{item.updateTime},
#{item.remark}
)
</foreach>
</insert>
<update id="updateHotakeReferenceCheck" parameterType="HotakeReferenceCheck">
update hotake_reference_check
<trim prefix="SET" suffixOverrides=",">

View File

@@ -0,0 +1,136 @@
<?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.HotakeSecurityTrustedDevicesMapper">
<resultMap type="HotakeSecurityTrustedDevices" id="HotakeSecurityTrustedDevicesResult">
<result property="id" column="id" />
<result property="userId" column="user_id" />
<result property="deviceId" column="device_id" />
<result property="deviceName" column="device_name" />
<result property="deviceType" column="device_type" />
<result property="browser" column="browser" />
<result property="os" column="os" />
<result property="ipAddress" column="ip_address" />
<result property="location" column="location" />
<result property="isTrusted" column="is_trusted" />
<result property="trustExpiresAt" column="trust_expires_at"/>
<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="selectHotakeSecurityTrustedDevicesVo">
select id, user_id, device_id, device_name, device_type, browser, os, ip_address, location, is_trusted, trust_expires_at, create_by, create_time, update_by, update_time, remark from hotake_security_trusted_devices
</sql>
<select id="selectHotakeSecurityTrustedDevicesList" parameterType="HotakeSecurityTrustedDevices" resultMap="HotakeSecurityTrustedDevicesResult">
<include refid="selectHotakeSecurityTrustedDevicesVo"/>
<where>
<if test="userId != null "> and user_id = #{userId}</if>
<if test="deviceId != null and deviceId != ''"> and device_id = #{deviceId}</if>
<if test="deviceName != null and deviceName != ''"> and device_name like concat('%', #{deviceName}, '%')</if>
<if test="deviceType != null and deviceType != ''"> and device_type = #{deviceType}</if>
<if test="browser != null and browser != ''"> and browser = #{browser}</if>
<if test="os != null and os != ''"> and os = #{os}</if>
<if test="isTrusted != null "> and is_trusted = #{isTrusted}</if>
</where>
order by create_time desc
</select>
<select id="selectHotakeSecurityTrustedDevicesById" parameterType="Long" resultMap="HotakeSecurityTrustedDevicesResult">
<include refid="selectHotakeSecurityTrustedDevicesVo"/>
where id = #{id}
</select>
<select id="selectByUserIdAndDeviceId" resultMap="HotakeSecurityTrustedDevicesResult">
<include refid="selectHotakeSecurityTrustedDevicesVo"/>
where user_id = #{userId} and device_id = #{deviceId}
</select>
<select id="selectTrustedDevicesByUserId" parameterType="Long" resultMap="HotakeSecurityTrustedDevicesResult">
<include refid="selectHotakeSecurityTrustedDevicesVo"/>
where user_id = #{userId} and is_trusted = 1 and (trust_expires_at is null or trust_expires_at > now())
order by create_time desc
</select>
<insert id="insertHotakeSecurityTrustedDevices" parameterType="HotakeSecurityTrustedDevices" useGeneratedKeys="true" keyProperty="id">
insert into hotake_security_trusted_devices
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">user_id,</if>
<if test="deviceId != null and deviceId != ''">device_id,</if>
<if test="deviceName != null">device_name,</if>
<if test="deviceType != null">device_type,</if>
<if test="browser != null">browser,</if>
<if test="os != null">os,</if>
<if test="ipAddress != null">ip_address,</if>
<if test="location != null">location,</if>
<if test="isTrusted != null">is_trusted,</if>
<if test="trustExpiresAt != null">trust_expires_at,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remark != null">remark,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
<if test="deviceId != null and deviceId != ''">#{deviceId},</if>
<if test="deviceName != null">#{deviceName},</if>
<if test="deviceType != null">#{deviceType},</if>
<if test="browser != null">#{browser},</if>
<if test="os != null">#{os},</if>
<if test="ipAddress != null">#{ipAddress},</if>
<if test="location != null">#{location},</if>
<if test="isTrusted != null">#{isTrusted},</if>
<if test="trustExpiresAt != null">#{trustExpiresAt},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remark != null">#{remark},</if>
</trim>
</insert>
<update id="updateHotakeSecurityTrustedDevices" parameterType="HotakeSecurityTrustedDevices">
update hotake_security_trusted_devices
<trim prefix="SET" suffixOverrides=",">
<if test="userId != null">user_id = #{userId},</if>
<if test="deviceId != null and deviceId != ''">device_id = #{deviceId},</if>
<if test="deviceName != null">device_name = #{deviceName},</if>
<if test="deviceType != null">device_type = #{deviceType},</if>
<if test="browser != null">browser = #{browser},</if>
<if test="os != null">os = #{os},</if>
<if test="ipAddress != null">ip_address = #{ipAddress},</if>
<if test="location != null">location = #{location},</if>
<if test="isTrusted != null">is_trusted = #{isTrusted},</if>
<if test="trustExpiresAt != null">trust_expires_at = #{trustExpiresAt},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="remark != null">remark = #{remark},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteHotakeSecurityTrustedDevicesById" parameterType="Long">
delete from hotake_security_trusted_devices where id = #{id}
</delete>
<delete id="deleteHotakeSecurityTrustedDevicesByIds" parameterType="String">
delete from hotake_security_trusted_devices where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<delete id="removeTrustedDevice">
delete from hotake_security_trusted_devices where id = #{id} and user_id = #{userId}
</delete>
<delete id="cleanExpiredTrustedDevices">
delete from hotake_security_trusted_devices where trust_expires_at is not null and trust_expires_at &lt; now()
</delete>
</mapper>