Commit 613f3bfd by zwb

招聘会演示平台搭建与简历导入

parent 71cbf619
Showing with 2913 additions and 0 deletions
package com.bkty.system.api;
import com.bkty.system.api.domain.bo.RemoteUserBo;
import com.bkty.system.api.domain.vo.RemoteUserVo;
import com.bkty.system.api.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.user.UserException;
import java.util.List;
/**
* 用户服务
*
* @author Lion Li
*/
public interface RemoteUserService {
/**
* 通过用户名查询用户信息
*
* @param username 用户名
* @return 结果
*/
LoginUser getUserInfo(String username) throws UserException;
/**
* 通过用户id查询用户信息
*
* @param userId 用户id
* @return 结果
*/
LoginUser getUserInfo(Long userId) throws UserException;
/**
* 通过手机号查询用户信息
*
* @param phonenumber 手机号
* @param tenantId 租户id
* @return 结果
*/
LoginUser getUserInfoByPhonenumber(String phonenumber, String tenantId) throws UserException;
/**
* 通过邮箱查询用户信息
*
* @param email 邮箱
* @param tenantId 租户id
* @return 结果
*/
LoginUser getUserInfoByEmail(String email, String tenantId) throws UserException;
/**
* 注册用户信息
*
* @param remoteUserBo 用户信息
* @return 结果
*/
Boolean registerUserInfo(RemoteUserBo remoteUserBo) throws UserException, ServiceException;
/**
* 通过userId查询用户账户
*
* @param userId 用户id
* @return 结果
*/
String selectUserNameById(Long userId);
/**
* 通过用户ID查询用户昵称
*
* @param userId 用户id
* @return 结果
*/
String selectNicknameById(Long userId);
/**
* 通过用户ID查询用户账户
*
* @param userIds 用户ID 多个用逗号隔开
* @return 用户名称
*/
String selectNicknameByIds(String userIds);
/**
* 通过用户ID查询用户手机号
*
* @param userId 用户id
* @return 用户手机号
*/
String selectPhonenumberById(Long userId);
/**
* 通过用户ID查询用户邮箱
*
* @param userId 用户id
* @return 用户邮箱
*/
String selectEmailById(Long userId);
/**
* 更新用户信息
*
* @param userId 用户ID
* @param ip IP地址
*/
void recordLoginInfo(Long userId, String ip);
/**
* 通过用户ID查询用户列表
*
* @param userIds 用户ids
* @return 用户列表
*/
List<RemoteUserVo> selectListByIds(List<Long> userIds);
/**
* 通过角色ID查询用户ID
*
* @param roleIds 角色ids
* @return 用户ids
*/
List<Long> selectUserIdsByRoleIds(List<Long> roleIds);
}
package com.bkty.system.api.domain.bo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 系统访问记录表 sys_logininfor
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class RemoteLogininforBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 访问ID
*/
private Long infoId;
/**
* 租户编号
*/
private String tenantId;
/**
* 用户账号
*/
private String userName;
/**
* 客户端
*/
private String clientKey;
/**
* 设备类型
*/
private String deviceType;
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 登录状态(0成功 1失败)
*/
private String status;
/**
* 提示消息
*/
private String msg;
/**
* 访问时间
*/
private Date loginTime;
/**
* 请求参数
*/
private Map<String, Object> params = new HashMap<>();
}
package com.bkty.system.api.domain.bo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 操作日志记录表 oper_log
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class RemoteOperLogBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 日志主键
*/
private Long operId;
/**
* 租户编号
*/
private String tenantId;
/**
* 模块标题
*/
private String title;
/**
* 业务类型(0其它 1新增 2修改 3删除)
*/
private Integer businessType;
/**
* 方法名称
*/
private String method;
/**
* 请求方式
*/
private String requestMethod;
/**
* 操作类别(0其它 1后台用户 2手机端用户)
*/
private Integer operatorType;
/**
* 操作人员
*/
private String operName;
/**
* 部门名称
*/
private String deptName;
/**
* 请求URL
*/
private String operUrl;
/**
* 主机地址
*/
private String operIp;
/**
* 操作地点
*/
private String operLocation;
/**
* 请求参数
*/
private String operParam;
/**
* 返回参数
*/
private String jsonResult;
/**
* 操作状态(0正常 1异常)
*/
private Integer status;
/**
* 错误消息
*/
private String errorMsg;
/**
* 操作时间
*/
private Date operTime;
/**
* 消耗时间
*/
private Long costTime;
/**
* 请求参数
*/
private Map<String, Object> params = new HashMap<>();
}
package com.bkty.system.api.domain.bo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
* 社会化关系业务对象 sys_social
*
* @author Michelle.Chung
*/
@Data
@NoArgsConstructor
public class RemoteSocialBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键
*/
private Long id;
/**
* 的唯一ID
*/
private String authId;
/**
* 用户来源
*/
private String source;
/**
* 用户的授权令牌
*/
private String accessToken;
/**
* 用户的授权令牌的有效期,部分平台可能没有
*/
private int expireIn;
/**
* 刷新令牌,部分平台可能没有
*/
private String refreshToken;
/**
* 平台唯一id
*/
private String openId;
/**
* 用户的 ID
*/
private Long userId;
/**
* 平台的授权信息,部分平台可能没有
*/
private String accessCode;
/**
* 用户的 unionid
*/
private String unionId;
/**
* 授予的权限,部分平台可能没有
*/
private String scope;
/**
* 授权的第三方账号
*/
private String userName;
/**
* 授权的第三方昵称
*/
private String nickName;
/**
* 授权的第三方邮箱
*/
private String email;
/**
* 授权的第三方头像地址
*/
private String avatar;
/**
* 个别平台的授权信息,部分平台可能没有
*/
private String tokenType;
/**
* id token,部分平台可能没有
*/
private String idToken;
/**
* 小米平台用户的附带属性,部分平台可能没有
*/
private String macAlgorithm;
/**
* 小米平台用户的附带属性,部分平台可能没有
*/
private String macKey;
/**
* 用户的授权code,部分平台可能没有
*/
private String code;
/**
* Twitter平台用户的附带属性,部分平台可能没有
*/
private String oauthToken;
/**
* Twitter平台用户的附带属性,部分平台可能没有
*/
private String oauthTokenSecret;
}
package com.bkty.system.api.domain.bo;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.common.core.constant.UserConstants;
import org.dromara.common.core.xss.Xss;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 用户信息业务对象 sys_user
*
* @author Michelle.Chung
*/
@Data
@NoArgsConstructor
public class RemoteUserBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
private Long userId;
/**
* 租户ID
*/
private String tenantId;
/**
* 部门ID
*/
private Long deptId;
/**
* 用户账号
*/
@Xss(message = "用户账号不能包含脚本字符")
@NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符")
private String userName;
/**
* 用户昵称
*/
@Xss(message = "用户昵称不能包含脚本字符")
@Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符")
private String nickName;
/**
* 用户类型(sys_user系统用户)
*/
private String userType;
/**
* 用户邮箱
*/
@Email(message = "邮箱格式不正确")
@Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
private String email;
/**
* 手机号码
*/
private String phonenumber;
/**
* 用户性别(0男 1女 2未知)
*/
private String sex;
/**
* 头像地址
*/
private Long avatar;
/**
* 密码
*/
private String password;
/**
* 帐号状态(0正常 1停用)
*/
private String status;
/**
* 最后登录IP
*/
private String loginIp;
/**
* 最后登录时间
*/
private Date loginDate;
/**
* 备注
*/
private String remark;
/**
* 数据权限 当前角色ID
*/
private Long roleId;
public RemoteUserBo(Long userId) {
this.userId = userId;
}
public boolean isSuperAdmin() {
return UserConstants.SUPER_ADMIN_ID.equals(this.userId);
}
}
package com.bkty.system.api.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 租户视图对象
*
* @author zhujie
*/
@Data
public class RemoteTenantVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 租户编号
*/
private String tenantId;
/**
* 联系人
*/
private String contactUserName;
/**
* 联系电话
*/
private String contactPhone;
/**
* 企业名称
*/
private String companyName;
/**
* 统一社会信用代码
*/
private String licenseNumber;
/**
* 地址
*/
private String address;
/**
* 域名
*/
private String domain;
/**
* 企业简介
*/
private String intro;
/**
* 备注
*/
private String remark;
/**
* 租户套餐编号
*/
private Long packageId;
/**
* 过期时间
*/
private Date expireTime;
/**
* 用户数量(-1不限制)
*/
private Long accountCount;
/**
* 租户状态(0正常 1停用)
*/
private String status;
}
package com.bkty.system.api.domain.vo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 用户
*
* @author Michelle.Chung
*/
@Data
@NoArgsConstructor
public class RemoteUserVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
private Long userId;
/**
* 部门ID
*/
private Long deptId;
/**
* 用户账号
*/
private String userName;
/**
* 用户昵称
*/
private String nickName;
/**
* 用户类型(sys_user系统用户)
*/
private String userType;
/**
* 用户邮箱
*/
private String email;
/**
* 手机号码
*/
private String phonenumber;
/**
* 用户性别(0男 1女 2未知)
*/
private String sex;
/**
* 帐号状态(0正常 1停用)
*/
private String status;
/**
* 创建时间
*/
private Date createTime;
}
package com.bkty.form;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.domain.model.LoginBody;
import org.hibernate.validator.constraints.Length;
import static org.dromara.common.core.constant.UserConstants.*;
/**
* 用户注册对象
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RegisterBody extends LoginBody {
/**
* 用户名
*/
@NotBlank(message = "{user.username.not.blank}")
@Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
private String username;
/**
* 用户密码
*/
@NotBlank(message = "{user.password.not.blank}")
@Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
private String password;
/**
* 用户类型
*/
private String userType;
}
package org.dromara.common.core.annotation;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* 自定义注解防止表单重复提交
*
* @author jiangxiaoge
*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {
/**
* 间隔时间(ms),小于此时间视为重复提交
*/
int interval() default 5000;
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
/**
* 提示消息 支持国际化 格式为 {code}
*/
String message() default "请误重复提交";
}
package org.dromara.common.core.aspectj;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.alibaba.fastjson2.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.dromara.common.core.annotation.RepeatSubmit;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.WarnException;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringContextHolder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.dromara.common.core.constant.CacheConstants;
import java.time.Duration;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
/**
* 防止重复提交(参考美团GTIS防重系统)
*
* @author Lion Li
*/
@Aspect
public class RepeatSubmitAspect {
private static final ThreadLocal<String> KEY_CACHE = new ThreadLocal<>();
private static final RedisTemplate<String, String> CLIENT = SpringContextHolder.getBean(RedisTemplate.class);
@Before("@annotation(repeatSubmit)")
public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable {
// 如果注解不为0 则使用注解数值
long interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval());
if (interval < 1000) {
throw new WarnException("重复提交间隔时间不能小于'1'秒");
}
HttpServletRequest request = ServletUtils.getRequest();
String nowParams = argsArrayToString(point.getArgs());
// 请求地址(作为存放cache的key值)
String url = request.getRequestURI();
// 唯一值(没有消息头则使用请求地址)
String submitKey = Objects.toString(request.getHeader("Authorization"), "");
submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
// 唯一标识(指定key + url + 消息头)
String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey;
if (setObjectIfAbsent(cacheRepeatKey, "", Duration.ofMillis(interval))) {
KEY_CACHE.set(cacheRepeatKey);
} else {
throw new WarnException(repeatSubmit.message());
}
}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) {
if (jsonResult instanceof R<?> r) {
try {
// 成功则不删除redis数据 保证在有效时间内无法重复提交
if (r.getCode() == R.ok().getCode()) {
return;
}
CLIENT.delete(KEY_CACHE.get());
} finally {
KEY_CACHE.remove();
}
}
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param e 异常
*/
@AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) {
CLIENT.delete(KEY_CACHE.get());
KEY_CACHE.remove();
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray) {
StringJoiner params = new StringJoiner(" ");
if (ArrayUtil.isEmpty(paramsArray)) {
return params.toString();
}
for (Object o : paramsArray) {
if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
params.add(JSON.toJSONString(o));
}
}
return params.toString();
}
/**
* 判断是否需要过滤的对象。
*
* @param o 对象信息。
* @return 如果是需要过滤的对象,则返回true;否则返回false。
*/
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o) {
Class<?> clazz = o.getClass();
if (clazz.isArray()) {
return MultipartFile.class.isAssignableFrom(clazz.getComponentType());
} else if (Collection.class.isAssignableFrom(clazz)) {
Collection collection = (Collection) o;
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) o;
for (Object value : map.values()) {
return value instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
/**
* 如果不存在则设置 并返回 true 如果存在则返回 false
*
* @param key 缓存的键值
* @param value 缓存的值
* @param duration 缓存的过期时间
* @return set成功或失败
*/
public boolean setObjectIfAbsent(final String key, final String value, final Duration duration) {
// 获取 ValueOperations
ValueOperations<String, String> valueOperations = CLIENT.opsForValue();
// 使用 setIfAbsent 来设置值,如果键不存在
Boolean isSet = CLIENT.opsForValue().setIfAbsent(key, value);
// 如果 setIfAbsent 返回 true,表示设置成功
if (Boolean.TRUE.equals(isSet)) {
// 设置过期时间
CLIENT.expire(key, duration);
return true;
}
return false; // 如果键已存在,则返回 false
}
}
package org.dromara.common.core.constant;
import cn.hutool.core.lang.RegexPool;
/**
* 常用正则表达式字符串
* <p>
* 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/
*
* @author Feng
*/
public interface RegexConstants extends RegexPool {
/**
* 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
*/
String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$";
/**
* 权限标识必须符合 tool:build:list 格式,或者空字符串
*/
String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$";
/**
* 身份证号码(后6位)
*/
String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
/**
* QQ号码
*/
String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$";
/**
* 邮政编码
*/
String POSTAL_CODE = "^[1-9]\\d{5}$";
/**
* 注册账号
*/
String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$";
/**
* 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符
*/
String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
/**
* 通用状态(0表示正常,1表示停用)
*/
String STATUS = "^[01]$";
}
package org.dromara.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
* @author jiangxiaoge
* @description 简历相关
* @data 2024/12/16
**/
public class ResumeEnum {
@AllArgsConstructor
@NoArgsConstructor
@Getter
public enum ResumeExtra {
ZYJN(0, "专业技能"),
YD(1, "个人优势"),
HJXX(2, "获奖信息"),
XQAH(3, "兴趣爱好");
private int code;
private String name;
}
@AllArgsConstructor
@NoArgsConstructor
@Getter
public enum ResumeOptimizationStatus {
NOT_OPTIMIZED(0, "未优化"),
OPTIMIZATION_FAILED(1, "优化失败"),
OPTIMIZATION_SUCCESSFUL(2, "优化成功"),
OPTIMIZING(3, "优化中");
private int code;
private String description;
}
@AllArgsConstructor
@NoArgsConstructor
@Getter
public enum ResumeModel{
CUSTOM(0, "自定义模块", "custom", 9),
CLUB(1, "校园经历", "clubs", 7),//社团经历-校园经历
EDU(2, "教育经历", "edus", 0),
HOBBLES(3, "兴趣爱好", "hobbles", 6),
INTERNSHIP(4, "实习经历", "internships", 8),
WORK_EXP(5, "工作经历", "workExps", 1),
PROJECT(6, "项目经历", "projects",2),
SKILL(7, "专业技能", "skill", 3),
ADVANTAGE(8, "个人优势", "advantage", 4),
AWARDS(9, "荣誉奖项", "awards", 5),//获奖信息-荣誉奖项
BASE(10, "基本信息", "base", 99),
;
private int code;
private String name;
private String webCode;
private int sort;
public static String getWebCodeByCode(Integer code){
for (ResumeModel thisEnum : EnumSet.allOf(ResumeModel.class)) {
if (thisEnum.code == code){
return thisEnum.webCode;
}
}
return null;
}
public static List<Integer> getResumeModel(){
List<Integer> codeList = new ArrayList<>();
for (ResumeModel model : EnumSet.allOf(ResumeModel.class)) {
if (model != ResumeModel.CUSTOM && model != ResumeModel.BASE){
codeList.add(model.getCode());
}
}
return codeList;
}
}
}
package org.dromara.common.core.factory;
import cn.hutool.core.lang.PatternPool;
import org.dromara.common.core.constant.RegexConstants;
import java.util.regex.Pattern;
/**
* 正则表达式模式池工厂
* <p>初始化的时候将正则表达式加入缓存池当中</p>
* <p>提高正则表达式的性能,避免重复编译相同的正则表达式</p>
*
* @author 21001
*/
public class RegexPatternPoolFactory extends PatternPool {
/**
* 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
*/
public static final Pattern DICTIONARY_TYPE = get(RegexConstants.DICTIONARY_TYPE);
/**
* 身份证号码(后6位)
*/
public static final Pattern ID_CARD_LAST_6 = get(RegexConstants.ID_CARD_LAST_6);
/**
* QQ号码
*/
public static final Pattern QQ_NUMBER = get(RegexConstants.QQ_NUMBER);
/**
* 邮政编码
*/
public static final Pattern POSTAL_CODE = get(RegexConstants.POSTAL_CODE);
/**
* 注册账号
*/
public static final Pattern ACCOUNT = get(RegexConstants.ACCOUNT);
/**
* 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符
*/
public static final Pattern PASSWORD = get(RegexConstants.PASSWORD);
/**
* 通用状态(0表示正常,1表示停用)
*/
public static final Pattern STATUS = get(RegexConstants.STATUS);
}
package org.dromara.common.core.utils.ip;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.ObjectUtil;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.file.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import java.io.File;
/**
* 根据ip地址定位工具类,离线方式
* 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">集成 ip2region 实现离线IP地址定位库</a>
*
* @author lishuyan
*/
@Slf4j
public class RegionUtils {
private static final Searcher SEARCHER;
static {
String fileName = "/ip2region.xdb";
File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
if (!FileUtils.exist(existFile)) {
ClassPathResource fileStream = new ClassPathResource(fileName);
if (ObjectUtil.isEmpty(fileStream.getStream())) {
throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
}
FileUtils.writeFromStream(fileStream.getStream(), existFile);
}
String dbPath = existFile.getPath();
// 1、从 dbPath 加载整个 xdb 到内存。
byte[] cBuff;
try {
cBuff = Searcher.loadContentFromFile(dbPath);
} catch (Exception e) {
throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage());
}
// 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
try {
SEARCHER = Searcher.newWithBuffer(cBuff);
} catch (Exception e) {
throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage());
}
}
/**
* 根据IP地址离线获取城市
*/
public static String getCityInfo(String ip) {
try {
ip = ip.trim();
// 3、执行查询
String region = SEARCHER.search(ip);
return region.replace("0|", "").replace("|0", "");
} catch (Exception e) {
log.error("IP地址离线获取城市异常 {}", ip);
return "未知";
}
}
}
package org.dromara.common.core.utils.reflect;
import cn.hutool.core.util.ReflectUtil;
import org.dromara.common.core.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.lang.reflect.Method;
/**
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
*
* @author Lion Li
*/
@SuppressWarnings("rawtypes")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ReflectUtils extends ReflectUtil {
private static final String SETTER_PREFIX = "set";
private static final String GETTER_PREFIX = "get";
/**
* 调用Getter方法.
* 支持多级,如:对象名.对象名.方法
*/
@SuppressWarnings("unchecked")
public static <E> E invokeGetter(Object obj, String propertyName) {
Object object = obj;
for (String name : StringUtils.split(propertyName, ".")) {
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
object = invoke(object, getterMethodName);
}
return (E) object;
}
/**
* 调用Setter方法, 仅匹配方法名。
* 支持多级,如:对象名.对象名.方法
*/
public static <E> void invokeSetter(Object obj, String propertyName, E value) {
Object object = obj;
String[] names = StringUtils.split(propertyName, ".");
for (int i = 0; i < names.length; i++) {
if (i < names.length - 1) {
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
object = invoke(object, getterMethodName);
} else {
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
Method method = getMethodByName(object.getClass(), setterMethodName);
invoke(object, method, value);
}
}
}
}
package org.dromara.common.core.utils.regex;
import cn.hutool.core.util.ReUtil;
import org.dromara.common.core.constant.RegexConstants;
/**
* 正则相关工具类
*
* @author Feng
*/
public final class RegexUtils extends ReUtil {
/**
* 从输入字符串中提取匹配的部分,如果没有匹配则返回默认值
*
* @param input 要提取的输入字符串
* @param regex 用于匹配的正则表达式,可以使用 {@link RegexConstants} 中定义的常量
* @param defaultInput 如果没有匹配时返回的默认值
* @return 如果找到匹配的部分,则返回匹配的部分,否则返回默认值
*/
public static String extractFromString(String input, String regex, String defaultInput) {
try {
String str = ReUtil.get(regex, input, 1);
return str == null ? defaultInput : str;
} catch (Exception e) {
return defaultInput;
}
}
}
package org.dromara.common.core.utils.regex;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.lang.Validator;
import org.dromara.common.core.factory.RegexPatternPoolFactory;
import java.util.regex.Pattern;
/**
* 正则字段校验器
* 主要验证字段非空、是否为满足指定格式等
*
* @author Feng
*/
public class RegexValidator extends Validator {
/**
* 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
*/
public static final Pattern DICTIONARY_TYPE = RegexPatternPoolFactory.DICTIONARY_TYPE;
/**
* 身份证号码(后6位)
*/
public static final Pattern ID_CARD_LAST_6 = RegexPatternPoolFactory.ID_CARD_LAST_6;
/**
* QQ号码
*/
public static final Pattern QQ_NUMBER = RegexPatternPoolFactory.QQ_NUMBER;
/**
* 邮政编码
*/
public static final Pattern POSTAL_CODE = RegexPatternPoolFactory.POSTAL_CODE;
/**
* 注册账号
*/
public static final Pattern ACCOUNT = RegexPatternPoolFactory.ACCOUNT;
/**
* 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符
*/
public static final Pattern PASSWORD = RegexPatternPoolFactory.PASSWORD;
/**
* 通用状态(0表示正常,1表示停用)
*/
public static final Pattern STATUS = RegexPatternPoolFactory.STATUS;
/**
* 检查输入的账号是否匹配预定义的规则
*
* @param value 要验证的账号
* @return 如果账号符合规则,返回 true;否则,返回 false。
*/
public static boolean isAccount(CharSequence value) {
return isMatchRegex(ACCOUNT, value);
}
/**
* 验证输入的账号是否符合规则,如果不符合,则抛出 ValidateException 异常
*
* @param value 要验证的账号
* @param errorMsg 验证失败时抛出的异常消息
* @param <T> CharSequence 的子类型
* @return 如果验证通过,返回输入的账号
* @throws ValidateException 如果验证失败
*/
public static <T extends CharSequence> T validateAccount(T value, String errorMsg) throws ValidateException {
if (!isAccount(value)) {
throw new ValidateException(errorMsg);
}
return value;
}
/**
* 检查输入的状态是否匹配预定义的规则
*
* @param value 要验证的状态
* @return 如果状态符合规则,返回 true;否则,返回 false。
*/
public static boolean isStatus(CharSequence value) {
return isMatchRegex(STATUS, value);
}
/**
* 验证输入的状态是否符合规则,如果不符合,则抛出 ValidateException 异常
*
* @param value 要验证的状态
* @param errorMsg 验证失败时抛出的异常消息
* @param <T> CharSequence 的子类型
* @return 如果验证通过,返回输入的状态
* @throws ValidateException 如果验证失败
*/
public static <T extends CharSequence> T validateStatus(T value, String errorMsg) throws ValidateException {
if (!isStatus(value)) {
throw new ValidateException(errorMsg);
}
return value;
}
}
package org.dromara.common.dubbo.enumd;
import lombok.AllArgsConstructor;
/**
* 请求日志泛型
*
* @author Lion Li
*/
@AllArgsConstructor
public enum RequestLogEnum {
/**
* info 基础信息
*/
INFO,
/**
* param 参数信息
*/
PARAM,
/**
* full 全部
*/
FULL;
}
package org.dromara.common.redis.config.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.redisson.config.ReadMode;
import org.redisson.config.SubscriptionMode;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Redisson 配置属性
*
* @author Lion Li
*/
@Data
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
/**
* redis缓存key前缀
*/
private String keyPrefix;
/**
* 线程池数量,默认值 = 当前处理核数量 * 2
*/
private int threads;
/**
* Netty线程池数量,默认值 = 当前处理核数量 * 2
*/
private int nettyThreads;
/**
* 单机服务配置
*/
private SingleServerConfig singleServerConfig;
/**
* 集群服务配置
*/
private ClusterServersConfig clusterServersConfig;
@Data
@NoArgsConstructor
public static class SingleServerConfig {
/**
* 客户端名称
*/
private String clientName;
/**
* 最小空闲连接数
*/
private int connectionMinimumIdleSize;
/**
* 连接池大小
*/
private int connectionPoolSize;
/**
* 连接空闲超时,单位:毫秒
*/
private int idleConnectionTimeout;
/**
* 命令等待超时,单位:毫秒
*/
private int timeout;
/**
* 发布和订阅连接池大小
*/
private int subscriptionConnectionPoolSize;
}
@Data
@NoArgsConstructor
public static class ClusterServersConfig {
/**
* 客户端名称
*/
private String clientName;
/**
* master最小空闲连接数
*/
private int masterConnectionMinimumIdleSize;
/**
* master连接池大小
*/
private int masterConnectionPoolSize;
/**
* slave最小空闲连接数
*/
private int slaveConnectionMinimumIdleSize;
/**
* slave连接池大小
*/
private int slaveConnectionPoolSize;
/**
* 连接空闲超时,单位:毫秒
*/
private int idleConnectionTimeout;
/**
* 命令等待超时,单位:毫秒
*/
private int timeout;
/**
* 发布和订阅连接池大小
*/
private int subscriptionConnectionPoolSize;
/**
* 读取模式
*/
private ReadMode readMode;
/**
* 订阅模式
*/
private SubscriptionMode subscriptionMode;
}
}
package org.dromara.common.redis.handler;
import cn.hutool.http.HttpStatus;
import com.baomidou.lock.exception.LockFailureException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* Redis异常处理器
*
* @author AprilWind
*/
@Slf4j
@RestControllerAdvice
public class RedisExceptionHandler {
/**
* 分布式锁Lock4j异常
*/
@ExceptionHandler(LockFailureException.class)
public R<Void> handleLockFailureException(LockFailureException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("获取锁失败了'{}',发生Lock4j异常.", requestURI, e);
return R.fail(HttpStatus.HTTP_UNAVAILABLE, "业务处理中,请稍后再试...");
}
}
package org.dromara.common.web.config;
import org.dromara.common.web.handler.GlobalExceptionHandler;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 通用配置
*
* @author Lion Li
*/
@AutoConfiguration
public class ResourcesConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
/**
* 全局异常处理器
*/
@Bean
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
}
}
package com.bkty.system.config;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.util.Timeout;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
/**
* RestTemplate
* @author jiangxiaoge
*/
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate(httpRequestFactory());
}
// 创建一个不带服务发现的 RestTemplate
@Bean
public RestTemplate nonLoadBalancedRestTemplate() {
return new RestTemplate(httpRequestFactory());
}
@Bean
public HttpComponentsClientHttpRequestFactory httpRequestFactory() {
// 使用自定义 HttpClient
CloseableHttpClient httpClient = httpClient();
// 创建工厂并绑定 HttpClient
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
@Bean
public CloseableHttpClient httpClient() {
// 配置连接池
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(2000); // 最大连接数
connectionManager.setDefaultMaxPerRoute(200); // 每个路由的最大连接数
// 配置超时
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.ofSeconds(12)) // 连接超时
.setResponseTimeout(Timeout.ofSeconds(48)) // 响应超时
.build();
// 创建 HttpClient,并绑定连接池和超时配置
return HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig)
.build();
}
// 创建一个跳过 SSL 验证的 SSLContext
private SSLContext getSSLContext() {
try {
// 创建一个信任管理器,接受所有证书
TrustManager[] trustAllCertificates = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// 使用自定义的信任管理器创建 SSLContext
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial((chain, authType) -> true) // 忽略 SSL 证书验证
.build();
// 使用该 SSLContext
return sslContext;
} catch (NoSuchAlgorithmException | java.security.KeyManagementException e) {
throw new RuntimeException("Error creating SSLContext", e);
} catch (KeyStoreException e) {
throw new RuntimeException(e);
}
}
}
package com.bkty.system.domain.dto;
import lombok.Data;
/**
* @author jiangxiaoge
* @description 简历列表item
* @data 2025/5/19
**/
@Data
public class ResumeListItemCache {
private Integer publicStatus;
private Long resumeId;
private String resumeName;
private Integer runType;
private String updateTime;
private Integer resumeScore;
// 使用状态 0=可用 1=作废
private Integer useStatus;
private String appraise;
//默认状态 0=默认 1=不默认
private Integer defaultStatus;
private String sex;
private String avatar;
}
package com.bkty.system.domain.dto;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @author jiangxiaoge
* @description 创建简历dto
* @data 2024/12/16
**/
@Data
public class ResumeMakeDto {
/**简历名称*/
private String resumeName;
/**模版id*/
private String template;
/**平台*/
private int platform;
/**未来简历id*/
private Long platformResumeId;
/**未来简历自定义id*/
private Long platformCustomId;
/**简历id*/
private String resumeId;
/**模块id*/
private Long modelId;
/**个人简历基本信息*/
private Map<String, Object> bodyData;
private List<String> platformIdList;
/**简历模块排序列表*/
private Map<Long, Integer> modelSort;
private Integer runType;
/**简历发布状态 0.未发布 1.已发布*/
private Integer publicStatus;
private Long userId;
/**权益id*/
private String vipId;
/**模版ID*/
private String templateId;
/**模版名称*/
private String templateName;
}
package com.bkty.system.domain.dto;
import lombok.Data;
/**
* @author jiangxiaoge
* @description 简历模块优化建议缓存
* @data 2025/5/19
**/
@Data
public class ResumeModelOptimizationCache {
private Integer runStatus;
private String optimizationDesc;
private String updateTime;
public ResumeModelOptimizationCache() {
}
public ResumeModelOptimizationCache(Integer runStatus, String optimizationDesc, String updateTime) {
this.runStatus = runStatus;
this.optimizationDesc = optimizationDesc;
this.updateTime = updateTime;
}
}
package com.bkty.system.domain.dto;
import lombok.Data;
/**
* @author jiangxiaoge
* @description 简历缓存-简历列表
* @data 2025/1/8
**/
@Data
public class ResumeMsgCache {
/**简历id*/
private String id;
/**简历名称*/
private String resumeName;
/**更新时间*/
private String updateTime;
/**运行状态*/
private Integer runType;
}
package com.bkty.system.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import lombok.Data;
/**
* @author Herbert
* @description 简历模版表
* @data 2025/05/07
**/
@Data
@TableName("resume_template")
public class ResumeTemplate extends BaseEntity {
/**
* 主键id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* 模版名称
*/
private String templateName;
/**
* 模版路径
*/
private String templatePath;
/**
* 模版别名
*/
private String templateAlias;
/**
* 模版预览地址
*/
private String templatePreview;
/**
* 平台ID
*/
private String platformId;
private static final long serialVersionUID = 1L;
}
package com.bkty.system.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import org.dromara.common.core.utils.Entity;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author Herbert
* @description 简历翻译记录表
* @data 2025/04/08
**/
@Data
@TableName("resume_translation_record")
public class ResumeTranslationRecord extends Entity {
/**主键ID*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**简历ID*/
private Long resumeId;
/**简历名称或文件名称*/
private String resumeName;
/**简历类型:选择已有简历-1;简历上传-2;*/
private String type;
/**智能体简历上传文件ID*/
private String agentFileId;
/**简历上传文件ID*/
private String fileId;
/**此参数仅在对话产生的消息中返回*/
private String chatId;
/**此消息所在的会话ID*/
private String conversationId;
/**编写此消息的智能体ID。此参数仅在对话产生的消息中返回*/
private String botId;
/**生成的html路径*/
private String pathHtml;
/**生成的pdf路径*/
private String pathPdf;
/**生成的png路径*/
private String pathPng;
/**智能体翻译后的内容*/
private String content;
/**对话的运行状态。取值为:created-对话已创建;in_progress-智能体正在处理中;completed-智能体已完成处理,本次对话结束;failed-对话失败;requires_action-对话中断,需要进一步处理;canceled-对话已取消*/
private String status;
/**删除标志(0代表存在 2代表删除)*/
@TableLogic
private String delFlag;
@Schema(description = "模版名称")
private String templateName;
@Schema(description = "源语言")
private String sourceLanguage;
@Schema(description = "目标语言")
private String targetLanguage;
}
package com.bkty.system.domain.vo;
import lombok.Data;
import org.dromara.common.core.annotation.JxgInitField;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/17
**/
@Data
public class ResumeBase {
/**生日*/
@JxgInitField("Age")
private String birthday;
/**头像url*/
@JxgInitField("Avatar")
private String avatar;
/**城市*/
@JxgInitField("City")
private String city;
/**名族*/
@JxgInitField("Ethnic")
private String ethnic;
/**岗位*/
@JxgInitField("JobInt")
private String jobInt;
/**邮箱*/
@JxgInitField("Mail")
private String email;
/**名字*/
@JxgInitField("Name")
private String username;
/**薪资*/
@JxgInitField("Pay")
private String pay;
/**手机号*/
@JxgInitField("Phone")
private String phone;
/**政治面貌*/
@JxgInitField("PolStatus")
private String polStatus;
/**性别*/
@JxgInitField("Sex")
private String sex;
/**工作状态*/
@JxgInitField("Status")
private String workStatus;
/**微信号*/
@JxgInitField("Wechat")
private String wechat;
/**参加工作时间*/
@JxgInitField(value = "WorkTime")
private String workTime;
/**简历名称*/
@JxgInitField(value = "ResumeName")
private String resumeName;
}
package com.bkty.system.domain.vo;
import lombok.Data;
/**
* @author jiangxiaoge
* @description 简历基本信息vo
* @data 2024/12/17
**/
@Data
public class ResumeBaseVo {
private Long resumeId;
private String platformId;
private String resumeName;
private String updateTime;
private Integer runType;
private Integer publicStatus;
}
package com.bkty.system.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "根据模版生成pdf,html,png文件请求类")
public class ResumeByPdfVo {
@Schema(description = "html名称")
private String htmlName;
@Schema(description = "pdf名称")
private String pdfName;
@Schema(description = "png名称")
private String pngName;
@Schema(description = "png名称小图")
private String pngNameMin;
@Schema(description = "简历名称")
private String resumeName;
@Schema(description = "简历翻译记录ID")
private Long resumeTranslationRecordId;
@Schema(description = "用户名称")
private String userName;
}
package com.bkty.system.domain.vo;
import lombok.Data;
import org.dromara.common.core.annotation.JxgInitField;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/17
**/
@Data
public class ResumeClub {
/**社团名称*/
@JxgInitField("Name")
private String clubName;
/**担任角色*/
@JxgInitField("RoleName")
private String roleName;
/**开始时间*/
@JxgInitField("StartTime")
private String startTime;
/**结束时间*/
@JxgInitField("EndTime")
private String endTime;
/**详情*/
@JxgInitField("Description")
private String description;
@JxgInitField("Performance")
private String performance;
}
package com.bkty.system.domain.vo;
import lombok.Data;
import org.dromara.common.core.annotation.JxgInitField;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/17
**/
@Data
public class ResumeEdu {
/**学历*/
@JxgInitField("Degree")
private String degree;
/**开始时间*/
@JxgInitField("StartYear")
private String startTime;
/**结束时间*/
@JxgInitField("EndYear")
private String endTime;
/**专业*/
@JxgInitField("Major")
private String major;
/**学校*/
@JxgInitField("School")
private String school;
/**详情*/
@JxgInitField("Description")
private String description;
}
package com.bkty.system.domain.vo;
import lombok.Data;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/17
**/
@Data
public class ResumeExtra {
/**数据类型 0.专业技能1.个人优势.获奖信息3.兴趣爱好*/
private Integer dataType;
/**详情*/
private String description;
private String infoName;
private String startTime;
private String endTime;
}
package com.bkty.system.domain.vo;
import org.dromara.common.core.annotation.JxgInitField;
import lombok.Data;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/17
**/
@Data
public class ResumeInternship {
@JxgInitField("Corp")
private String internshipCorp;
/**实习岗位*/
@JxgInitField("Post")
private String internshipPost;
/**成果*/
@JxgInitField("Performance")
private String performance;
/**开始时间*/
@JxgInitField("StartTime")
private String startTime;
/**结束时间*/
@JxgInitField("EndTime")
private String endTime;
/**详情*/
@JxgInitField("Content")
private String description;
}
package com.bkty.system.domain.vo;
import lombok.Data;
/**
* @author jiangxiaoge
* @description 模块列表vo
* @data 2024/12/20
**/
@Data
public class ResumeModelVo {
private Long modelId;
private Integer modelType;
private Integer dataSort;
private int isShow;
private String modelName;
private String webCode;
}
package com.bkty.system.domain.vo;
import org.dromara.common.core.annotation.JxgInitField;
import lombok.Data;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/17
**/
@Data
public class ResumeProject {
/**项目名称*/
@JxgInitField("Name")
private String projectName;
/**职位名称*/
@JxgInitField("RoleName")
private String roleName;
/**开始时间*/
@JxgInitField("StartTime")
private String startTime;
/**结束时间*/
@JxgInitField("EndTime")
private String endTime;
/**职位描述*/
@JxgInitField("Description")
private String description;
/**项目描述*/
@JxgInitField("Performance")
private String performance;
}
package com.bkty.system.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
/**
* @Desc:
* @author: Herbert
* @date: 2025/3/17 13:56
* @since: 17
*/
@Data
@Schema(description = "简历智作统计数据预处理返回类")
public class ResumeSmartCreationVo {
@Schema(description = "用户ID")
private String userId;
@Schema(description = "用户名称")
private String username;
@Schema(description = "手机号")
private String phone;
@Schema(description = "学号")
private String studentNumber;
@Schema(description = "学校ID")
private String schoolId;
@Schema(description = "简历id")
private String resumeId;
@Schema(description = "简历名称")
private String resumeName;
@Schema(description = "简历创建时间")
private Date resumeCreateTime;
@Schema(description = "简历更新时间")
private Date resumeUpdateTime;
@Schema(description = "简历评分")
private Integer resumeScore;
@Schema(description = "简历优化次数")
private Integer optimizeCounts;
@Schema(description = "下载次数")
private Integer downloadCounts;
@Schema(description = "简历是否删除(true:已删除,false:未删除)")
private Boolean resumeIsDeleted;
@Schema(description = "简历评价")
private String appraise;
@Schema(description = "模版名称")
private String templateName;
}
package com.bkty.system.domain.vo;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author jiangxiaoge
* @description 个人简历
* @data 2024/12/17
**/
@Data
public class ResumeVo {
//基本信息
private ResumeBase base;
//社团信息
private Map<String, Object> clubs;
//教育经历
private Map<String, Object> edus;
//兴趣爱好
private Map<String, Object> hobbles;
//实习经历
private Map<String, Object> internships;
//项目经历
private Map<String, Object> projects;
//工作经验
private Map<String, Object> workExps;
private Map<String, Object> skill;
private Map<String, Object> advantage;
private Map<String, Object> awards;
private List<Map<String, Object>> custom = new ArrayList<>();
}
package com.bkty.system.domain.vo;
import org.dromara.common.core.annotation.JxgInitField;
import lombok.Data;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/17
**/
@Data
public class ResumeWorkExp {
@JxgInitField("Content")
private String workContent;
/**公司名称*/
@JxgInitField("Corp")
private String corpName;
/**开始时间*/
@JxgInitField("StartTime")
private String startTime;
/**结束时间*/
@JxgInitField("EndTime")
private String endTime;
/**结束时间*/
@JxgInitField("Post")
private String postName;
/**工作成绩*/
@JxgInitField("Performance")
private String performance;
}
package com.bkty.system.dubbo;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bkty.system.api.RemoteUserService;
import com.bkty.system.api.domain.bo.RemoteUserBo;
import com.bkty.system.api.domain.vo.RemoteUserVo;
import com.bkty.system.api.model.LoginUser;
import com.bkty.system.domain.dto.SysUserDto;
import com.bkty.system.domain.entity.SysUser;
import com.bkty.system.domain.vo.SysUserVo;
import com.bkty.system.mapper.SysUserMapper;
import com.bkty.system.service.ISysPermissionService;
import com.bkty.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.apache.dubbo.config.annotation.DubboService;
import org.dromara.common.core.enums.UserStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 用户服务
*
* @author Lion Li
*/
@RequiredArgsConstructor
@Service
@DubboService
public class RemoteUserServiceImpl implements RemoteUserService {
private final ISysUserService userService;
private final SysUserMapper userMapper;
private final ISysPermissionService permissionService;
/**
* 通过用户名查询用户信息
*
* @param username 用户名
* @return 结果
*/
@Override
public LoginUser getUserInfo(String username) throws UserException {
System.out.println("进入查询用户方法");
SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
if (ObjectUtil.isNull(sysUser)) {
throw new UserException("user.not.exists", username);
}
if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) {
throw new UserException("user.blocked", username);
}
// 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
return buildLoginUser(sysUser);
}
/**
* 通过用户id查询用户信息
*
* @param userId 用户id
* @return 结果
*/
@Override
public LoginUser getUserInfo(Long userId) throws UserException {
SysUserVo sysUser = userMapper.selectVoById(userId);
if (ObjectUtil.isNull(sysUser)) {
throw new UserException("user.not.exists", "");
}
if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) {
throw new UserException("user.blocked", sysUser.getUserName());
}
// 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
return buildLoginUser(sysUser);
}
/**
* 通过手机号查询用户信息
*
* @param phonenumber 手机号
* @param tenantId 租户id
* @return 结果
*/
@Override
public LoginUser getUserInfoByPhonenumber(String phonenumber, String tenantId) throws UserException {
SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber));
if (ObjectUtil.isNull(sysUser)) {
throw new UserException("user.not.exists", phonenumber);
}
if (UserStatus.DISABLE.getCode().equals(sysUser.getStatus())) {
throw new UserException("user.blocked", phonenumber);
}
// 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
return buildLoginUser(sysUser);
}
/**
* 通过邮箱查询用户信息
*
* @param email 邮箱
* @param tenantId 租户id
* @return 结果
*/
@Override
public LoginUser getUserInfoByEmail(String email, String tenantId) throws UserException {
SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email));
if (ObjectUtil.isNull(user)) {
throw new UserException("user.not.exists", email);
}
if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
throw new UserException("user.blocked", email);
}
// 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
return buildLoginUser(user);
}
/**
* 注册用户信息
*
* @param remoteUserBo 用户信息
* @return 结果
*/
@Override
public Boolean registerUserInfo(RemoteUserBo remoteUserBo) throws UserException, ServiceException {
SysUserDto sysUserDto = MapstructUtils.convert(remoteUserBo, SysUserDto.class);
String username = sysUserDto.getUserName();
boolean exist = userMapper.exists(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getUserName, sysUserDto.getUserName()));
if (exist) {
throw new UserException("user.register.save.error", username);
}
return userService.registerUser(sysUserDto, remoteUserBo.getTenantId());
}
/**
* 通过用户ID查询用户账户
*
* @param userId 用户ID
* @return 用户账户
*/
@Override
public String selectUserNameById(Long userId) {
return userService.selectUserNameById(userId);
}
/**
* 通过用户ID查询用户昵称
*
* @param userId 用户ID
* @return 用户昵称
*/
@Override
public String selectNicknameById(Long userId) {
return userService.selectNicknameById(userId);
}
/**
* 通过用户ID查询用户账户
*
* @param userIds 用户ID 多个用逗号隔开
* @return 用户账户
*/
@Override
public String selectNicknameByIds(String userIds) {
return userService.selectNicknameByIds(userIds);
}
/**
* 通过用户ID查询用户手机号
*
* @param userId 用户id
* @return 用户手机号
*/
@Override
public String selectPhonenumberById(Long userId) {
return userService.selectPhonenumberById(userId);
}
/**
* 通过用户ID查询用户邮箱
*
* @param userId 用户id
* @return 用户邮箱
*/
@Override
public String selectEmailById(Long userId) {
return userService.selectEmailById(userId);
}
/**
* 构建登录用户
*/
private LoginUser buildLoginUser(SysUserVo userVo) {
LoginUser loginUser = new LoginUser();
loginUser.setTenantId(userVo.getTenantId());
loginUser.setTenantName(userVo.getTenantName());
loginUser.setUserId(userVo.getUserId());
loginUser.setDeptId(userVo.getDeptId());
loginUser.setUsername(userVo.getUserName());
loginUser.setNickname(userVo.getNickName());
loginUser.setPassword(userVo.getPassword());
loginUser.setUserType(userVo.getUserType());
loginUser.setMenuPermission(permissionService.getMenuPermission(userVo.getUserId()));
loginUser.setRolePermission(permissionService.getRolePermission(userVo.getUserId()));
return loginUser;
}
/**
* 更新用户信息
*
* @param userId 用户ID
* @param ip IP地址
*/
@Override
public void recordLoginInfo(Long userId, String ip) {
SysUser sysUser = new SysUser();
sysUser.setUserId(userId);
sysUser.setLoginIp(ip);
sysUser.setLoginDate(DateUtils.getNowDate());
sysUser.setUpdateBy(String.valueOf(userId));
DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser));
}
/**
* 通过用户ID查询用户列表
*
* @param userIds 用户ids
* @return 用户列表
*/
@Override
public List<RemoteUserVo> selectListByIds(List<Long> userIds) {
List<SysUserVo> sysUserVos = userService.selectUserByIds(userIds, null);
return MapstructUtils.convert(sysUserVos, RemoteUserVo.class);
}
/**
* 通过角色ID查询用户ID
*
* @param roleIds 角色ids
* @return 用户ids
*/
@Override
public List<Long> selectUserIdsByRoleIds(List<Long> roleIds) {
return userService.selectUserIdsByRoleIds(roleIds);
}
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.ResumeTemplate;
import org.apache.ibatis.annotations.Mapper;
/**
* @author Herbert
* @description 简历模版表
* @data 2025/05/07
**/
@Mapper
public interface ResumeTemplateMapper extends BaseMapper<ResumeTemplate> {
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.ResumeTranslationRecord;
import org.apache.ibatis.annotations.Mapper;
/**
* @author Herbert
* @description 简历翻译记录表
* @data 2025/04/08
**/
@Mapper
public interface ResumeTranslationRecordMapper extends BaseMapper<ResumeTranslationRecord> {
}
package com.bkty.system.service.resume;
import com.bkty.system.domain.dto.ResumeListItemCache;
import com.bkty.system.domain.dto.ResumeMakeDto;
import com.bkty.system.domain.dto.ResumeModelOptimizationCache;
import com.bkty.system.domain.entity.FunctionResumeBase;
import com.bkty.system.domain.entity.FunctionResumeModel;
import com.bkty.system.domain.entity.FunctionResumeModelOptimization;
import com.bkty.system.domain.vo.ResumeBaseVo;
import com.bkty.system.domain.vo.ResumeModelVo;
import java.util.List;
import java.util.Map;
/**
* @author jiangxiaoge
* @description 简历缓存Service
* @data 2025/1/8
**/
public interface ResumeCacheService {
/**
* 根据用户的简历列表缓存
*/
void saveUserResumeListCache(FunctionResumeBase base, Long userId);
/**
* 查询用户简历信息缓存
* @param userId
* @return
*/
List<ResumeBaseVo> queryUserResumeCache(Long userId);
/**
* 删除简历基础信息缓存
* @param resumeId
* @param userId
*/
void delResumeCache(Long resumeId, Long userId);
/**
* 根据简历id保存简历模块列表缓存
* @param model
* @param resumeId
*/
void saveResumeModelListCache(FunctionResumeModel model, Long resumeId);
/**
* 查询简历模块信息缓存
* @param resumeId
* @return
*/
List<ResumeModelVo> queryResumeModelCache(Long resumeId);
/**
* 删除简历模块基础信息缓存
* @param modelId
* @param resumeId
*/
void delResumeModelCache(Long modelId, Long resumeId);
/**
* 保存简历模块具体数据
* @param modelId
* @param resumeId
* @param jsonData
*/
void saveResumeModelDataCache(Long modelId, Long resumeId, String jsonData);
/**
* 查询简历模块具体数据
* @param modelId
* @param resumeId
* @return
*/
String queryResumeModelDataCache(Long modelId, Long resumeId);
/**
* 删除简历模块具体数据
* @param modelId
* @param resumeId
*/
void delResumeModelDataCache(Long modelId, Long resumeId);
/**
* 修改模块排序缓存
* @param dto
*/
void updateModelSort(ResumeMakeDto dto);
/**
* 刷新模块优化缓存
* @param optimization
*/
void refreshModelOptimization(FunctionResumeModelOptimization optimization);
/**
* 查询缓存模块优化信息
* @param id
* @param resumeId
* @return
*/
Map<String, ResumeModelOptimizationCache> queryResumeModelOptimization(Long id, Long resumeId);
/**
* 查询简历列表缓存
* @param aLong
* @return
*/
List<ResumeListItemCache> queryResumeList(Long aLong);
/**
* 刷新简历列表缓存
* @param caches
* @param aLong
*/
void refreshResumeListCache(List<ResumeListItemCache> caches, Long aLong);
/**
* 简历缓存模版信息
*/
void saveResumeTemplateCache(Map<String,Object> templateMap, String resumeId);
void delResumeListCache(Long userId);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment