Commit 34583954 by zwb

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

parent 12ed3dbd
Showing with 3570 additions and 0 deletions
package com.bkty.service;
import com.bkty.domain.vo.LoginVo;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
/**
* 授权策略
*
* @author Michelle.Chung
*/
public interface IAuthStrategy {
String BASE_NAME = "AuthStrategy";
/**
* 登录
*
* @param body 登录对象
* @param grantType 授权类型
* @return 登录验证信息
*/
static LoginVo login(String body, String grantType) {
// 授权类型和客户端id
String beanName = grantType + BASE_NAME;
if (!SpringUtils.containsBean(beanName)) {
throw new ServiceException("授权类型不正确!");
}
IAuthStrategy instance = SpringUtils.getBean(beanName);
return instance.login(body);
}
/**
* 登录
*
* @param body 登录对象
* @return 登录验证信息
*/
LoginVo login(String body);
}
package org.dromara.common.core.config;
import org.dromara.common.core.aspectj.RepeatSubmitAspect;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
@AutoConfiguration(after = RedisTemplate.class)
public class IdempotentAutoConfiguration {
@Bean
public RepeatSubmitAspect repeatSubmitAspect() {
return new RepeatSubmitAspect();
}
}
\ No newline at end of file
package org.dromara.common.core.constant;
/**
* 全局的key常量 (业务无关的key)
*
* @author Lion Li
*/
public interface GlobalConstants {
/**
* 全局 redis key (业务无关的key)
*/
String GLOBAL_REDIS_KEY = "global:";
/**
* 验证码 redis key
*/
String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
/**
* 防重提交 redis key
*/
String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:";
/**
* 限流 redis key
*/
String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:";
/**
* 三方认证 redis key
*/
String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:";
}
package org.dromara.common.core.constant;
/**
* 返回状态码
*
* @author Lion Li
*/
public interface HttpStatus {
/**
* 操作成功
*/
int SUCCESS = 200;
/**
* 对象创建成功
*/
int CREATED = 201;
/**
* 请求已经被接受
*/
int ACCEPTED = 202;
/**
* 操作已经执行成功,但是没有返回数据
*/
int NO_CONTENT = 204;
/**
* 资源已被移除
*/
int MOVED_PERM = 301;
/**
* 重定向
*/
int SEE_OTHER = 303;
/**
* 资源没有被修改
*/
int NOT_MODIFIED = 304;
/**
* 参数列表错误(缺少,格式不匹配)
*/
int BAD_REQUEST = 400;
/**
* 未授权
*/
int UNAUTHORIZED = 401;
/**
* 访问受限,授权过期
*/
int FORBIDDEN = 403;
/**
* 资源,服务未找到
*/
int NOT_FOUND = 404;
/**
* 不允许的http方法
*/
int BAD_METHOD = 405;
/**
* 资源冲突,或者资源被锁
*/
int CONFLICT = 409;
/**
* 不支持的数据,媒体类型
*/
int UNSUPPORTED_TYPE = 415;
/**
* 系统内部错误
*/
int ERROR = 500;
/**
* 接口未实现
*/
int NOT_IMPLEMENTED = 501;
/**
* 系统警告消息
*/
int WARN = 601;
}
package org.dromara.common.core.enums;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
* 文件类型枚举类
*图片、音频类不超过5MB,其他支持的文件格式不超过10MB
* @author Herbert
* @history 2024/6/12 新建
* @since 17
*/
public enum FileTypeEnum {
DOC("application/msword", "doc", 50*1024*1024L, "50MB"),
DOCX("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx", 50*1024*1024L, "50MB"),
XLS("application/vnd.ms-excel", "xls", 50*1024*1024L, "50MB"),
XLSX("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx", 50*1024*1024L, "50MB"),
PDF("application/pdf","pdf", 50*1024*1024L, "50MB"),
JPG("image/jpeg","jpg", 50*1024*1024L, "5MB"),
BMP("image/bmp","bmp", 50*1024*1024L, "5MB"),
JPEG("image/jpeg","jpeg", 50*1024*1024L, "5MB"),
GIF("image/gif","gif", 50*1024*1024L, "5MB"),
PNG("image/png","png", 50*1024*1024L, "5MB"),
SVG("image/svg+xml","svg", 50*1024*1024L, "5MB"),
ICO("image/x-icon","ico", 50*1024*1024L, "5MB"),
;
private String contentType;
private String suffix;
private long maxSize;
private String sizeDescription;
FileTypeEnum(String contentType, String suffix, long maxSize, String sizeDescription) {
this.contentType = contentType;
this.suffix = suffix;
this.maxSize = maxSize;
this.sizeDescription = sizeDescription;
}
/**
*
* 根据文件后缀查找对应的文件类型枚举类,未找到则返回空
* @param suffix
* @return
* @author Herbert
* @history 2019/5/22 新建
*/
public static FileTypeEnum getEnumBySuffix(String suffix){
for (FileTypeEnum typeEnum : EnumSet.allOf(FileTypeEnum.class)) {
if(typeEnum.getSuffix().equalsIgnoreCase(suffix)){
return typeEnum;
}
}
return null;
}
/**
* 将枚举值转换成列表,方便下拉框使用
* @return
*/
public static List<String> getCollectType(){
List<String> lstResult = new ArrayList<>(10);
for (FileTypeEnum typeEnum : EnumSet.allOf(FileTypeEnum.class)) {
if(!typeEnum.getSuffix().equals("key") && !typeEnum.getSuffix().equals("cry")){
lstResult.add(typeEnum.getSuffix());
}
}
return lstResult;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public long getMaxSize() {
return maxSize;
}
public void setMaxSize(long maxSize) {
this.maxSize = maxSize;
}
public String getSizeDescription() {
return sizeDescription;
}
public void setSizeDescription(String sizeDescription) {
this.sizeDescription = sizeDescription;
}
}
package org.dromara.common.core.exception.file;
import org.dromara.common.core.exception.base.BaseException;
import java.io.Serial;
/**
* 文件信息异常类
*
* @author ruoyi
*/
public class FileException extends BaseException {
@Serial
private static final long serialVersionUID = 1L;
public FileException(String code, Object[] args) {
super("file", code, args, null);
}
}
package org.dromara.common.core.exception.file;
import java.io.Serial;
/**
* 文件名称超长限制异常类
*
* @author ruoyi
*/
public class FileNameLengthLimitExceededException extends FileException {
@Serial
private static final long serialVersionUID = 1L;
public FileNameLengthLimitExceededException(int defaultFileNameLength) {
super("upload.filename.exceed.length", new Object[]{defaultFileNameLength});
}
}
package org.dromara.common.core.exception.file;
import java.io.Serial;
/**
* 文件名大小限制异常类
*
* @author ruoyi
*/
public class FileSizeLimitExceededException extends FileException {
@Serial
private static final long serialVersionUID = 1L;
public FileSizeLimitExceededException(long defaultMaxSize) {
super("upload.exceed.maxSize", new Object[]{defaultMaxSize});
}
}
package org.dromara.common.core.utils;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @Desc: 公共属性基类
* @author: jiangxiaoge
* @date: 2023/11/16 15:02
* @since: 1.8
*/
@Data
public class Entity implements Serializable {
public static final long serialVersionUID = 1L;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
/**
* 修改人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "修改人")
private String updateBy;
/**
* 创建时间
*/
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改时间
*/
@Schema(description = "修改时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
package org.dromara.common.core.utils.file;
import cn.hutool.core.io.FileUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* 文件处理工具类
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class FileUtils extends FileUtil {
/**
* 下载文件名重新编码
*
* @param response 响应对象
* @param realFileName 真实文件名
*/
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) {
String percentEncodedFileName = percentEncode(realFileName);
String contentDispositionValue = "attachment; filename=%s;filename*=utf-8''%s".formatted(percentEncodedFileName, percentEncodedFileName);
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
response.setHeader("Content-disposition", contentDispositionValue);
response.setHeader("download-filename", percentEncodedFileName);
}
/**
* 百分号编码工具方法
*
* @param s 需要百分号编码的字符串
* @return 百分号编码后的字符串
*/
public static String percentEncode(String s) {
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8);
return encode.replaceAll("\\+", "%20");
}
}
package org.dromara.common.encrypt.annotation;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import java.lang.annotation.*;
/**
* 字段加密注解
*
* @author 老马
*/
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
/**
* 加密算法
*/
AlgorithmType algorithm() default AlgorithmType.DEFAULT;
/**
* 秘钥。AES、SM4需要
*/
String password() default "";
/**
* 公钥。RSA、SM2需要
*/
String publicKey() default "";
/**
* 私钥。RSA、SM2需要
*/
String privateKey() default "";
/**
* 编码方式。对加密算法为BASE64的不起作用
*/
EncodeType encode() default EncodeType.DEFAULT;
}
package org.dromara.common.encrypt.config;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import org.dromara.common.encrypt.core.EncryptorManager;
import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor;
import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor;
import org.dromara.common.encrypt.properties.EncryptorProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* 加解密配置
*
* @author 老马
* @version 4.6.0
*/
@AutoConfiguration(after = MybatisPlusAutoConfiguration.class)
@EnableConfigurationProperties({EncryptorProperties.class, MybatisPlusProperties.class})
@ConditionalOnClass(MybatisPlusAutoConfiguration.class)
@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true")
public class EncryptorAutoConfiguration {
@Autowired
private EncryptorProperties properties;
@Bean
public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) {
return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage());
}
@Bean
public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) {
return new MybatisEncryptInterceptor(encryptorManager, properties);
}
@Bean
public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
return new MybatisDecryptInterceptor(encryptorManager, properties);
}
}
package org.dromara.common.encrypt.core;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import lombok.Data;
/**
* 加密上下文 用于encryptor传递必要的参数。
*
* @author 老马
* @version 4.6.0
*/
@Data
public class EncryptContext {
/**
* 默认算法
*/
private AlgorithmType algorithm;
/**
* 安全秘钥
*/
private String password;
/**
* 公钥
*/
private String publicKey;
/**
* 私钥
*/
private String privateKey;
/**
* 编码方式,base64/hex
*/
private EncodeType encode;
}
package org.dromara.common.encrypt.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.io.Resources;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 加密管理类
*
* @author 老马
* @version 4.6.0
*/
@Slf4j
@NoArgsConstructor
public class EncryptorManager {
/**
* 缓存加密器
*/
Map<Integer, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
/**
* 类加密字段缓存
*/
Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();
/**
* 构造方法传入类加密字段缓存
*
* @param typeAliasesPackage 实体类包
*/
public EncryptorManager(String typeAliasesPackage) {
scanEncryptClasses(typeAliasesPackage);
}
/**
* 获取类加密字段缓存
*/
public Set<Field> getFieldCache(Class<?> sourceClazz) {
if (ObjectUtil.isNotNull(fieldCache)) {
return fieldCache.get(sourceClazz);
}
return null;
}
/**
* 注册加密执行者到缓存
*
* @param encryptContext 加密执行者需要的相关配置参数
*/
public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {
int key = encryptContext.hashCode();
if (encryptorMap.containsKey(key)) {
return encryptorMap.get(key);
}
IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);
encryptorMap.put(key, encryptor);
return encryptor;
}
/**
* 移除缓存中的加密执行者
*
* @param encryptContext 加密执行者需要的相关配置参数
*/
public void removeEncryptor(EncryptContext encryptContext) {
this.encryptorMap.remove(encryptContext.hashCode());
}
/**
* 根据配置进行加密。会进行本地缓存对应的算法和对应的秘钥信息。
*
* @param value 待加密的值
* @param encryptContext 加密相关的配置信息
*/
public String encrypt(String value, EncryptContext encryptContext) {
IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
return encryptor.encrypt(value, encryptContext.getEncode());
}
/**
* 根据配置进行解密
*
* @param value 待解密的值
* @param encryptContext 加密相关的配置信息
*/
public String decrypt(String value, EncryptContext encryptContext) {
IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
return encryptor.decrypt(value);
}
/**
* 通过 typeAliasesPackage 设置的扫描包 扫描缓存实体
*/
private void scanEncryptClasses(String typeAliasesPackage) {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
try {
for (String packagePattern : packagePatternArray) {
String path = ClassUtils.convertClassNameToResourcePath(packagePattern);
Resource[] resources = resolver.getResources(classpath + path + "/*.class");
for (Resource resource : resources) {
ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata();
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
Set<Field> encryptFieldSet = getEncryptFieldSetFromClazz(clazz);
if (CollUtil.isNotEmpty(encryptFieldSet)) {
fieldCache.put(clazz, encryptFieldSet);
}
}
}
} catch (Exception e) {
log.error("初始化数据安全缓存时出错:{}", e.getMessage());
}
}
/**
* 获得一个类的加密字段集合
*/
private Set<Field> getEncryptFieldSetFromClazz(Class<?> clazz) {
Set<Field> fieldSet = new HashSet<>();
// 判断clazz如果是接口,内部类,匿名类就直接返回
if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) {
return fieldSet;
}
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
fieldSet.addAll(Arrays.asList(fields));
clazz = clazz.getSuperclass();
}
fieldSet = fieldSet.stream().filter(field ->
field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
.collect(Collectors.toSet());
for (Field field : fieldSet) {
field.setAccessible(true);
}
return fieldSet;
}
}
package org.dromara.common.encrypt.filter;
import cn.hutool.core.util.RandomUtil;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.WriteListener;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
import org.dromara.common.encrypt.utils.EncryptUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
/**
* 加密响应参数包装类
*
* @author Michelle.Chung
*/
public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
private final ByteArrayOutputStream byteArrayOutputStream;
private final ServletOutputStream servletOutputStream;
private final PrintWriter printWriter;
public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException {
super(response);
this.byteArrayOutputStream = new ByteArrayOutputStream();
this.servletOutputStream = this.getOutputStream();
this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream));
}
@Override
public PrintWriter getWriter() {
return printWriter;
}
@Override
public void flushBuffer() throws IOException {
if (servletOutputStream != null) {
servletOutputStream.flush();
}
if (printWriter != null) {
printWriter.flush();
}
}
@Override
public void reset() {
byteArrayOutputStream.reset();
}
public byte[] getResponseData() throws IOException {
flushBuffer();
return byteArrayOutputStream.toByteArray();
}
public String getContent() throws IOException {
flushBuffer();
return byteArrayOutputStream.toString();
}
/**
* 获取加密内容
*
* @param servletResponse response
* @param publicKey RSA公钥 (用于加密 AES 秘钥)
* @param headerFlag 请求头标志
* @return 加密内容
* @throws IOException
*/
public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException {
// 生成秘钥
String aesPassword = RandomUtil.randomString(32);
// 秘钥使用 Base64 编码
String encryptAes = EncryptUtils.encryptByBase64(aesPassword);
// Rsa 公钥加密 Base64 编码
String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey);
// 设置响应头
servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag);
servletResponse.setHeader(headerFlag, encryptPassword);
servletResponse.setHeader("Access-Control-Allow-Origin", "*");
servletResponse.setHeader("Access-Control-Allow-Methods", "*");
servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
// 获取原始内容
String originalBody = this.getContent();
// 对内容进行加密
return EncryptUtils.encryptByAes(originalBody, aesPassword);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new ServletOutputStream() {
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
@Override
public void write(int b) throws IOException {
byteArrayOutputStream.write(b);
}
@Override
public void write(byte[] b) throws IOException {
byteArrayOutputStream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
byteArrayOutputStream.write(b, off, len);
}
};
}
}
package org.dromara.common.encrypt.properties;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 加解密属性配置类
*
* @author 老马
* @version 4.6.0
*/
@Data
@ConfigurationProperties(prefix = "mybatis-encryptor")
public class EncryptorProperties {
/**
* 过滤开关
*/
private Boolean enable;
/**
* 默认算法
*/
private AlgorithmType algorithm;
/**
* 安全秘钥
*/
private String password;
/**
* 公钥
*/
private String publicKey;
/**
* 私钥
*/
private String privateKey;
/**
* 编码方式,base64/hex
*/
private EncodeType encode;
}
package org.dromara.common.excel.annotation;
import org.dromara.common.core.utils.StringUtils;
import java.lang.annotation.*;
/**
* 字典格式化
*
* @author Lion Li
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelDictFormat {
/**
* 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
*/
String dictType() default "";
/**
* 读取内容转表达式 (如: 0=男,1=女,2=未知)
*/
String readConverterExp() default "";
/**
* 分隔符,读取字符串组内容
*/
String separator() default StringUtils.SEPARATOR;
}
package org.dromara.common.excel.annotation;
import java.lang.annotation.*;
/**
* 枚举格式化
*
* @author Liang
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelEnumFormat {
/**
* 字典枚举类型
*/
Class<? extends Enum<?>> enumClass();
/**
* 字典枚举类中对应的code属性名称,默认为code
*/
String codeField() default "code";
/**
* 字典枚举类中对应的text属性名称,默认为text
*/
String textField() default "text";
}
package org.dromara.common.excel.convert;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
/**
* 大数值转换
* Excel 数值长度位15位 大于15位的数值转换位字符串
*
* @author Lion Li
*/
@Slf4j
public class ExcelBigNumberConvert implements Converter<Long> {
@Override
public Class<Long> supportJavaTypeKey() {
return Long.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
return Convert.toLong(cellData.getData());
}
@Override
public WriteCellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (ObjectUtil.isNotNull(object)) {
String str = Convert.toStr(object);
if (str.length() > 15) {
return new WriteCellData<>(str);
}
}
WriteCellData<Object> cellData = new WriteCellData<>(new BigDecimal(object));
cellData.setType(CellDataTypeEnum.NUMBER);
return cellData;
}
}
package org.dromara.common.excel.convert;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.excel.utils.ExcelUtil;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
/**
* 字典格式化转换处理
*
* @author Lion Li
*/
@Slf4j
public class ExcelDictConvert implements Converter<Object> {
@Override
public Class<Object> supportJavaTypeKey() {
return Object.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return null;
}
@Override
public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
ExcelDictFormat anno = getAnnotation(contentProperty.getField());
String type = anno.dictType();
String label = cellData.getStringValue();
String value;
if (StringUtils.isBlank(type)) {
value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator());
} else {
value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator());
}
return Convert.convert(contentProperty.getField().getType(), value);
}
@Override
public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (ObjectUtil.isNull(object)) {
return new WriteCellData<>("");
}
ExcelDictFormat anno = getAnnotation(contentProperty.getField());
String type = anno.dictType();
String value = Convert.toStr(object);
String label;
if (StringUtils.isBlank(type)) {
label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator());
} else {
label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator());
}
return new WriteCellData<>(label);
}
private ExcelDictFormat getAnnotation(Field field) {
return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class);
}
}
package org.dromara.common.excel.convert;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.ExcelEnumFormat;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* 枚举格式化转换处理
*
* @author Liang
*/
@Slf4j
public class ExcelEnumConvert implements Converter<Object> {
@Override
public Class<Object> supportJavaTypeKey() {
return Object.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return null;
}
@Override
public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
cellData.checkEmpty();
// Excel中填入的是枚举中指定的描述
Object textValue = switch (cellData.getType()) {
case STRING, DIRECT_STRING, RICH_TEXT_STRING -> cellData.getStringValue();
case NUMBER -> cellData.getNumberValue();
case BOOLEAN -> cellData.getBooleanValue();
default -> throw new IllegalArgumentException("单元格类型异常!");
};
// 如果是空值
if (ObjectUtil.isNull(textValue)) {
return null;
}
Map<Object, String> enumCodeToTextMap = beforeConvert(contentProperty);
// 从Java输出至Excel是code转text
// 因此从Excel转Java应该将text与code对调
Map<Object, Object> enumTextToCodeMap = new HashMap<>();
enumCodeToTextMap.forEach((key, value) -> enumTextToCodeMap.put(value, key));
// 应该从text -> code中查找
Object codeValue = enumTextToCodeMap.get(textValue);
return Convert.convert(contentProperty.getField().getType(), codeValue);
}
@Override
public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (ObjectUtil.isNull(object)) {
return new WriteCellData<>("");
}
Map<Object, String> enumValueMap = beforeConvert(contentProperty);
String value = Convert.toStr(enumValueMap.get(object), "");
return new WriteCellData<>(value);
}
private Map<Object, String> beforeConvert(ExcelContentProperty contentProperty) {
ExcelEnumFormat anno = getAnnotation(contentProperty.getField());
Map<Object, String> enumValueMap = new HashMap<>();
Enum<?>[] enumConstants = anno.enumClass().getEnumConstants();
for (Enum<?> enumConstant : enumConstants) {
Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField());
String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField());
enumValueMap.put(codeValue, textValue);
}
return enumValueMap;
}
private ExcelEnumFormat getAnnotation(Field field) {
return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class);
}
}
package org.dromara.common.excel.core;
import com.alibaba.excel.read.listener.ReadListener;
/**
* Excel 导入监听
*
* @author Lion Li
*/
public interface ExcelListener<T> extends ReadListener<T> {
ExcelResult<T> getExcelResult();
}
package org.dromara.common.excel.core;
import java.util.List;
/**
* excel返回对象
*
* @author Lion Li
*/
public interface ExcelResult<T> {
/**
* 对象列表
*/
List<T> getList();
/**
* 错误列表
*/
List<String> getErrorList();
/**
* 导入回执
*/
String getAnalysis();
}
package org.dromara.common.web.config;
import jakarta.servlet.DispatcherType;
import org.dromara.common.web.config.properties.XssProperties;
import org.dromara.common.web.filter.XssFilter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
/**
* Filter配置
*
* @author Lion Li
*/
@AutoConfiguration
@EnableConfigurationProperties(XssProperties.class)
public class FilterConfig {
@Bean
@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
public FilterRegistrationBean<XssFilter> xssFilterRegistration() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter());
registration.addUrlPatterns("/*");
registration.setName("xssFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE + 1);
return registration;
}
}
package org.dromara.common.web.config;
import org.dromara.common.web.core.I18nLocaleResolver;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.LocaleResolver;
/**
* 国际化配置
*
* @author Lion Li
*/
@AutoConfiguration(before = WebMvcAutoConfiguration.class)
public class I18nConfig {
@Bean
public LocaleResolver localeResolver() {
return new I18nLocaleResolver();
}
}
package org.dromara.common.web.core;
import org.springframework.web.servlet.LocaleResolver;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Locale;
/**
* 获取请求头国际化信息
*
* @author Lion Li
*/
public class I18nLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String language = httpServletRequest.getHeader("content-language");
Locale locale = Locale.getDefault();
if (language != null && language.length() > 0) {
String[] split = language.split("_");
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
package org.dromara.common.web.handler;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.SseException;
import org.dromara.common.core.exception.base.BaseException;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import java.io.IOException;
/**
* 全局异常处理器
*
* @author Lion Li
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 请求方式不支持
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public R<Void> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
return R.fail(HttpStatus.HTTP_BAD_METHOD, e.getMessage());
}
/**
* 业务异常
*/
@ExceptionHandler(ServiceException.class)
public R<Void> handleServiceException(ServiceException e, HttpServletRequest request) {
log.error(e.getMessage());
Integer code = e.getCode();
return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage());
}
/**
* 认证失败
*/
@ResponseStatus(org.springframework.http.HttpStatus.UNAUTHORIZED)
@ExceptionHandler(SseException.class)
public String handleNotLoginException(SseException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
return JsonUtils.toJsonString(R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"));
}
/**
* servlet异常
*/
@ExceptionHandler(ServletException.class)
public R<Void> handleServletException(ServletException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return R.fail(e.getMessage());
}
/**
* 业务异常
*/
@ExceptionHandler(BaseException.class)
public R<Void> handleBaseException(BaseException e, HttpServletRequest request) {
log.error(e.getMessage());
return R.fail(e.getMessage());
}
/**
* 请求路径中缺少必需的路径变量
*/
@ExceptionHandler(MissingPathVariableException.class)
public R<Void> handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI);
return R.fail(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
}
/**
* 请求参数类型不匹配
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public R<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI);
return R.fail(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue()));
}
/**
* 找不到路由
*/
@ExceptionHandler(NoHandlerFoundException.class)
public R<Void> handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}'不存在.", requestURI);
return R.fail(HttpStatus.HTTP_NOT_FOUND, e.getMessage());
}
/**
* 拦截未知的运行时异常
*/
@ResponseStatus(org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(IOException.class)
public void handleRuntimeException(IOException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
if (requestURI.contains("sse")) {
// sse 经常性连接中断 例如关闭浏览器 直接屏蔽
return;
}
log.error("请求地址'{}',连接中断", requestURI, e);
}
/**
* 拦截未知的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return R.fail(e.getMessage());
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public R<Void> handleException(Exception e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
return R.fail(e.getMessage());
}
/**
* 自定义验证异常
*/
@ExceptionHandler(BindException.class)
public R<Void> handleBindException(BindException e) {
log.error(e.getMessage());
String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", ");
return R.fail(message);
}
/**
* 自定义验证异常
*/
@ExceptionHandler(ConstraintViolationException.class)
public R<Void> constraintViolationException(ConstraintViolationException e) {
log.error(e.getMessage());
String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
return R.fail(message);
}
/**
* 自定义验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error(e.getMessage());
String message = StreamUtils.join(e.getBindingResult().getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", ");
return R.fail(message);
}
}
package com.bkty.gateway.filter;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.same.SaSameUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 转发认证过滤器(内部服务外网隔离)
*
* @author Lion Li
*/
@Component
public class ForwardAuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 未开启配置则直接跳过
if (!SaManager.getConfig().getCheckSameToken()) {
return chain.filter(exchange);
}
ServerHttpRequest newRequest = exchange
.getRequest()
.mutate()
// 为请求追加 Same-Token 参数
.header(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken())
.build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
return chain.filter(newExchange);
}
@Override
public int getOrder() {
return -100;
}
}
package com.bkty.gateway.filter;
import com.bkty.gateway.utils.WebFluxUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 全局缓存获取body请求数据(解决流不能重复读取问题)
*
* @author Lion Li
*/
@Component
public class GlobalCacheRequestFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 只缓存json类型请求
if (!WebFluxUtils.isJsonRequest(exchange)) {
return chain.filter(exchange);
}
return ServerWebExchangeUtils.cacheRequestBody(exchange, (serverHttpRequest) -> {
if (serverHttpRequest == exchange.getRequest()) {
return chain.filter(exchange);
}
return chain.filter(exchange.mutate().request(serverHttpRequest).build());
});
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
}
}
package com.bkty.gateway.filter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/**
* 跨域配置
*
* @author Lion Li
*/
@Component
public class GlobalCorsFilter implements WebFilter, Ordered {
/**
* 这里为支持的请求头,如果有自定义的header字段请自己添加
*/
private static final String ALLOWED_HEADERS =
"X-Requested-With, Content-Language, Content-Type, " +
"Authorization, clientid, credential, X-XSRF-TOKEN, " +
"isToken, token, Admin-Token, App-Token, Encrypt-Key, isEncrypt";
/**
* 允许的请求方法
*/
private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD";
/**
* 允许的请求来源,使用 * 表示允许任何来源
*/
private static final String ALLOWED_ORIGIN = "*";
/**
* 允许前端访问的响应头,使用 * 表示允许任何响应头
*/
private static final String ALLOWED_EXPOSE = "*";
/**
* 预检请求的缓存时间,单位为秒(此处设置为 5 小时)
*/
private static final String MAX_AGE = "18000L";
/**
* 实现跨域配置的 Web 过滤器
*
* @param exchange ServerWebExchange 对象,表示一次 Web 交换
* @param chain WebFilterChain 对象,表示一组 Web 过滤器链
* @return Mono<Void> 表示异步的过滤器链处理结果
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 判断请求是否为跨域请求
if (CorsUtils.isCorsRequest(request)) {
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = response.getHeaders();
headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
headers.add("Access-Control-Max-Age", MAX_AGE);
headers.add("Access-Control-Allow-Credentials", "true");
// 处理预检请求的 OPTIONS 方法,直接返回成功状态码
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
package com.bkty.gateway.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.i18n.SimpleLocaleContext;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Locale;
/**
* 全局国际化处理
*
* @author Lion Li
*/
@Slf4j
@Component
public class GlobalI18nFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String language = exchange.getRequest().getHeaders().getFirst("content-language");
Locale locale = Locale.getDefault();
if (language != null && language.length() > 0) {
String[] split = language.split("_");
locale = new Locale(split[0], split[1]);
}
LocaleContextHolder.setLocaleContext(new SimpleLocaleContext(locale), true);
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
package com.bkty.gateway.filter;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.bkty.gateway.config.properties.ApiDecryptProperties;
import com.bkty.gateway.config.properties.CustomGatewayProperties;
import com.bkty.gateway.utils.WebFluxUtils;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.json.utils.JsonUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 全局日志过滤器
* <p>
* 用于打印请求执行参数与响应时间等等
*
* @author Lion Li
*/
@Slf4j
@Component
public class GlobalLogFilter implements GlobalFilter, Ordered {
@Autowired
private CustomGatewayProperties customGatewayProperties;
@Autowired
private ApiDecryptProperties apiDecryptProperties;
private static final String START_TIME = "startTime";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (!customGatewayProperties.getRequestLog()) {
return chain.filter(exchange);
}
ServerHttpRequest request = exchange.getRequest();
String path = WebFluxUtils.getOriginalRequestUrl(exchange);
String url = request.getMethod().name() + " " + path;
// 打印请求参数
if (WebFluxUtils.isJsonRequest(exchange)) {
if (apiDecryptProperties.getEnabled()
&& ObjectUtil.isNotNull(request.getHeaders().getFirst(apiDecryptProperties.getHeaderFlag()))) {
log.info("[PLUS]开始请求 => URL[{}],参数类型[encrypt]", url);
} else {
String jsonParam = WebFluxUtils.resolveBodyFromCacheRequest(exchange);
log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam);
}
} else {
MultiValueMap<String, String> parameterMap = request.getQueryParams();
if (MapUtil.isNotEmpty(parameterMap)) {
String parameters = JsonUtils.toJsonString(parameterMap);
log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters);
} else {
log.info("[PLUS]开始请求 => URL[{}],无参数", url);
}
}
exchange.getAttributes().put(START_TIME, System.currentTimeMillis());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute(START_TIME);
if (startTime != null) {
long executeTime = (System.currentTimeMillis() - startTime);
log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", url, executeTime);
}
}));
}
@Override
public int getOrder() {
// 日志处理器在负载均衡器之后执行 负载均衡器会导致线程切换 无法获取上下文内容
// 如需在日志内操作线程上下文 例如获取登录用户数据等 可以打开下方注释代码
// return ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER - 1;
return Ordered.LOWEST_PRECEDENCE;
}
}
package com.bkty.gateway.handler;
import com.bkty.gateway.utils.WebFluxUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 网关统一异常处理
*
* @author ruoyi
*/
@Slf4j
@Order(-1)
@Configuration
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (exchange.getResponse().isCommitted()) {
return Mono.error(ex);
}
String msg;
if (ex instanceof NotFoundException) {
msg = "服务未找到";
} else if (ex instanceof ResponseStatusException responseStatusException) {
msg = responseStatusException.getMessage();
} else {
msg = "内部服务器错误";
}
log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage());
return WebFluxUtils.webFluxResponseWriter(response, msg);
}
}
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 lombok.Data;
import org.dromara.common.core.annotation.JxgInitField;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* @author jiangxiaoge
* @description 个人简历数据主表
* @data 2024/12/16
**/
@Data
@TableName("function_resume_base")
public class FunctionResumeBase extends BaseEntity {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**简历使用人*/
private Long userId;
/**创建类型 0.未来简历*/
private Integer createType;
/**对应平台的简历id*/
private String platformId;
/**简历名称*/
private String resumeName;
/**生日*/
@JxgInitField("birthday")
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("email")
private String email;
/**名字*/
@JxgInitField("username")
private String username;
/**薪资*/
@JxgInitField("pay")
private String pay;
/**手机号*/
@JxgInitField("phone")
private String phone;
/**政治面貌*/
@JxgInitField("polStatus")
private String polStatus;
/**性别*/
@JxgInitField("sex")
private String sex;
/**工作状态*/
@JxgInitField("workStatus")
private String workStatus;
/**微信号*/
@JxgInitField("wechat")
private String wechat;
/**参加工作时间*/
@JxgInitField(value = "workTime")
private String workTime;
/**简历导入状态 0.已完成 1.导入中 2.导入失败*/
private Integer runType;
/**简历发布状态 0.未发布 1.已发布*/
private Integer publicStatus;
/**默认状态 0=默认 1=不默认*/
private Integer defaultStatus;
/**模版ID*/
private String templateId;
/**模版名称*/
private String templateName;
}
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 lombok.Data;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* @author jiangxiaoge
* @description 个人简历完整优化记录表
* @data 2025/5/19
**/
@Data
@TableName("function_resume_base_optimization")
public class FunctionResumeBaseOptimization extends BaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
private Long userId;
private Long resumeId;
// 简历评分
private Integer resumeScore;
// 使用状态 0=可用 1=作废
private Integer useStatus;
private String appraise;
// 优化建议
private String optimizationDesc;
// 优化详情
private String optimizationDetail;
}
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 lombok.Data;
import org.dromara.common.core.annotation.JxgInitField;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* @author jiangxiaoge
* @description 个人简历学校社团经历
* @data 2024/12/16
**/
@Data
@TableName("function_resume_club")
public class FunctionResumeClub extends BaseEntity {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**简历id*/
private Long resumeId;
private Long modelId;
/**社团名称*/
@JxgInitField("clubName")
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;
/**排序*/
private Integer dataSort;
}
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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.dromara.common.core.utils.Entity;
/**
* @author Herbert
* @description 简历下载次数表
* @data 2025/04/08
**/
@Data
@TableName("function_resume_download_number")
public class FunctionResumeDownloadNumber extends Entity {
/**主键ID*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**用户id*/
private String userId;
/**简历id*/
private String resumeId;
/**下载次数*/
private Integer downloadCounts;
/**简历优化次数*/
private Integer optimizeCounts;
/**
* 是否删除(true:已删除,false:未删除)
*/
@TableLogic
@Schema(description = "是否删除(true:已删除,false:未删除)")
private Boolean isDeleted = false;
}
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 lombok.Data;
import org.dromara.common.core.annotation.JxgInitField;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* @author jiangxiaoge
* @description 个人简历教育经历
* @data 2024/12/16
**/
@Data
@TableName("function_resume_edu")
public class FunctionResumeEdu extends BaseEntity {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**简历id*/
private Long resumeId;
/**模块id*/
private Long modelId;
/**学历*/
@JxgInitField("degree")
private String degree;
/**开始时间*/
@JxgInitField("startTime")
private String startTime;
/**结束时间*/
@JxgInitField("endTime")
private String endTime;
/**专业*/
@JxgInitField("major")
private String major;
/**学校*/
@JxgInitField("school")
private String school;
/**详情*/
@JxgInitField("description")
private String description;
/**排序*/
private Integer dataSort;
}
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 lombok.Data;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* @author jiangxiaoge
* @description 个人简历额外信息
* @data 2024/12/16
**/
@Data
@TableName("function_resume_extra")
public class FunctionResumeExtra extends BaseEntity {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**简历id*/
private Long resumeId;
private Long modelId;
/**数据类型 0.专业技能1.个人优势.获奖信息3.兴趣爱好*/
private Integer dataType;
/**详情*/
private String description;
}
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 lombok.Data;
import org.dromara.common.core.annotation.JxgInitField;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* @author jiangxiaoge
* @description 个人简历实习经历
* @data 2024/12/16
**/
@Data
@TableName("function_resume_internship")
public class FunctionResumeInternship extends BaseEntity {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**简历id*/
private Long resumeId;
private Long modelId;
/**实习公司*/
@JxgInitField("internshipCorp")
private String internshipCorp;
/**实习岗位*/
@JxgInitField("internshipPost")
private String internshipPost;
/**成果*/
@JxgInitField("performance")
private String performance;
/**开始时间*/
@JxgInitField("startTime")
private String startTime;
/**结束时间*/
@JxgInitField("endTime")
private String endTime;
/**详情*/
@JxgInitField("description")
private String description;
private Integer dataSort;
}
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 lombok.Data;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* @author jiangxiaoge
* @description 个人简历自定义模块
* @data 2024/12/16
**/
@Data
@TableName("function_resume_model")
public class FunctionResumeModel extends BaseEntity {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**简历id*/
private Long resumeId;
/**平台自定义id*/
private String platformCustomId;
/**模块类型 ResumeEnum.ResumeModel枚举类型*/
private Integer modelType;
/**模块名称*/
private String modelName;
/**是否展示0.显示 1.不显示*/
private Integer isShow;
/**排序*/
private Integer dataSort;
}
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 lombok.Data;
import org.dromara.common.core.annotation.JxgInitField;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* @author jiangxiaoge
* @description 个人简历自定义模块详情
* @data 2024/12/16
**/
@Data
@TableName("function_resume_model_custom")
public class FunctionResumeModelCustom extends BaseEntity {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**简历id*/
private Long resumeId;
/**模块id*/
private Long modelId;
/**自定义模块名称*/
@JxgInitField("infoName")
private String infoName;
/**详情*/
@JxgInitField("description")
private String description;
/**开始时间*/
@JxgInitField("startTime")
private String startTime;
/**结束时间*/
@JxgInitField("endTime")
private String endTime;
private Integer dataSort;
}
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 jiangxiaoge
* @description 简历模块优化建议表
* @data 2025/5/19
**/
@Data
@TableName("function_resume_model_optimization")
public class FunctionResumeModelOptimization extends BaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
private Long userId;
private Long resumeId;
// ResumeEnum.ResumeModel枚举
private String modelCode;
// 运行状态 0=未优化 1=优化失败 2=优化成功 3=优化中
private Integer runStatus;
// 优化建议
private String optimizationDesc;
}
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.core.annotation.JxgInitField;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import lombok.Data;
/**
* @author jiangxiaoge
* @description 个人简历项目经历
* @data 2024/12/16
**/
@Data
@TableName("function_resume_project")
public class FunctionResumeProject extends BaseEntity {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**简历id*/
private Long resumeId;
/**模块id*/
private Long modelId;
/**项目名称*/
@JxgInitField("projectName")
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;
/**排序*/
private Integer dataSort;
}
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 jiangxiaoge
* @description 简历描述向量信息
* @data 2025/3/16
**/
@Data
@TableName("function_resume_sketch")
public class FunctionResumeSketch extends BaseEntity {
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
private Long resumeId;
private String sketch;
private String vectorQuantity;
}
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 jiangxiaoge
* @description 简历模版
* @data 2025/1/7
**/
@Data
@TableName("function_resume_template")
public class FunctionResumeTemplate extends BaseEntity {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**模版路径*/
private String templatePath;
/**模版名称*/
private String templateName;
}
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.core.annotation.JxgInitField;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import lombok.Data;
/**
* @author jiangxiaoge
* @description 个人简历工作经历
* @data 2024/12/16
**/
@Data
@TableName("function_resume_work_exp")
public class FunctionResumeWorkExp extends BaseEntity {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**简历id*/
private Long resumeId;
private Long modelId;
/** 工作内容 */
@JxgInitField("workContent")
private String workContent;
/**公司名称*/
@JxgInitField("corpName")
private String corpName;
/**开始时间*/
@JxgInitField("startTime")
private String startTime;
/**结束时间*/
@JxgInitField("endTime")
private String endTime;
/**岗位名称*/
@JxgInitField("postName")
private String postName;
/**工作成绩*/
@JxgInitField("performance")
private String performance;
/**排序*/
private Integer dataSort;
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.dto.ResumeListItemCache;
import com.bkty.system.domain.entity.FunctionResumeBase;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* @author jiangxiaoge
* @description 个人简历主表
* @data 2024/12/16
**/
@Mapper
public interface FunctionResumeBaseMapper extends BaseMapper<FunctionResumeBase> {
@Select("select t1.public_status as publicStatus," +
" t1.id as resumeId," +
" t1.resume_name as resumeName," +
" t1.run_type as runType," +
" t1.sex as sex," +
" t1.avatar as avatar," +
" DATE_FORMAT(t1.update_time, '%Y-%m-%d %H:%i:%s') as updateTime," +
" t2.resume_score as resumeScore," +
" t2.use_status as useStatus," +
" t2.appraise," +
" t1.default_status as defaultStatus " +
"from function_resume_base t1 " +
" left join function_resume_base_optimization t2 on t1.id = t2.resume_id " +
"where t1.user_id = #{userId} " +
" and t1.is_deleted = false order by t1.default_status ASC,t1.update_time ASC")
List<ResumeListItemCache> queryResumeListV2(@Param("userId") Long userId);
@Update("""
UPDATE function_resume_base
SET default_status = CASE
WHEN id = #{resumeId} THEN 0
ELSE 1
END
WHERE user_id = #{userId};
""")
void defultResume(@Param("resumeId") Long resumeId, @Param("userId") Long userId);
@Select("select * from function_resume_base where id = #{resumeId}")
FunctionResumeBase selectResumeById(@Param("resumeId") Long resumeId);
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeBaseOptimization;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @author jiangxiaoge
* @description
* @data 2025/5/19
**/
@Mapper
public interface FunctionResumeBaseOptimizationMapper extends BaseMapper<FunctionResumeBaseOptimization> {
@Delete("""
delete from function_resume_base_optimization where resume_id = #{resumeId} and user_id = #{userId}
""")
int deleteData(@Param("resumeId") Long resumeId, @Param("userId") Long userId);
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeClub;
import org.apache.ibatis.annotations.Mapper;
/**
* @author jiangxiaoge
* @description 学校经历
* @data 2024/12/16
**/
@Mapper
public interface FunctionResumeClubMapper extends BaseMapper<FunctionResumeClub> {
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeDownloadNumber;
import org.apache.ibatis.annotations.Mapper;
/**
* @author Herbert
* @description 简历下载次数表
* @data 2025/05/07
**/
@Mapper
public interface FunctionResumeDownloadNumberMapper extends BaseMapper<FunctionResumeDownloadNumber> {
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeEdu;
import org.apache.ibatis.annotations.Mapper;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/16
**/
@Mapper
public interface FunctionResumeEduMapper extends BaseMapper<FunctionResumeEdu> {
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeExtra;
import org.apache.ibatis.annotations.Mapper;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/16
**/
@Mapper
public interface FunctionResumeExtraMapper extends BaseMapper<FunctionResumeExtra> {
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeInternship;
import org.apache.ibatis.annotations.Mapper;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/16
**/
@Mapper
public interface FunctionResumeInternshipMapper extends BaseMapper<FunctionResumeInternship> {
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeModelCustom;
import org.apache.ibatis.annotations.Mapper;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/16
**/
@Mapper
public interface FunctionResumeModelCustomMapper extends BaseMapper<FunctionResumeModelCustom> {
}
package com.bkty.system.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.bkty.system.domain.entity.FunctionResumeModel;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
/**
* @author jiangxiaoge
* @description 个人简历模块Mapper
* @data 2024/12/16
**/
@Mapper
public interface FunctionResumeModelMapper extends MPJBaseMapper<FunctionResumeModel> {
@Select("""
select t1.*
from function_resume_model t1
inner join function_resume_base t2 on t1.resume_id = t2.id
where t2.is_deleted = false
and t1.is_deleted = false
and t1.model_type = #{modelType}
and t2.id = #{resumeId}
""")
List<FunctionResumeModel> selectResumeModelByResumeId(@Param("resumeId") Long resumeId,
@Param("modelType") Integer modelType);
/**
* 查询最大排序
* @param resumeId
* @return
*/
@Select("select max(data_sort) from function_resume_model where resume_id = #{resumeId} and is_deleted = 0")
int selectResumeModelMaxSort(@Param("resumeId") Long resumeId);
@Update({
"<script>",
"UPDATE function_resume_model",
"SET is_show = 0, data_sort = CASE id",
"<foreach collection='updates' index='id' item='dataSort'>",
"WHEN #{id} THEN #{dataSort}",
"</foreach>",
"END",
"WHERE id IN",
"<foreach collection='updates.keySet()' item='id' open='(' separator=',' close=')'>",
"#{id}",
"</foreach>",
"</script>"
})
void updateDataSortByIds(@Param("updates") Map<Long, Integer> updates);
@Insert({
"<script>",
"INSERT INTO function_resume_model (id, resume_id, platform_custom_id, model_type, model_name, is_show, data_sort, create_by, create_time, update_by, update_time, is_deleted) ",
"VALUES ",
"<foreach collection='models' item='item' separator=','>",
"(#{item.id}, #{item.resumeId}, #{item.platformCustomId}, #{item.modelType}, #{item.modelName}, #{item.isShow}, #{item.dataSort}, #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime}, #{item.isDeleted})",
"</foreach>",
"</script>"
})
void insertBatch(@Param("models") List<FunctionResumeModel> models);
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeModelOptimization;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @author jiangxiaoge
* @description 简历模块优化建议
* @data 2025/5/19
**/
@Mapper
public interface FunctionResumeModelOptimizationMapper extends BaseMapper<FunctionResumeModelOptimization> {
@Delete("delete from function_resume_model_optimization where resume_id = #{resumeId} and model_code = #{modelCode}")
int deleteData(@Param("resumeId") Long resumeId, @Param("modelCode") String modelCode);
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeProject;
import org.apache.ibatis.annotations.Mapper;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/16
**/
@Mapper
public interface FunctionResumeProjectMapper extends BaseMapper<FunctionResumeProject> {
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeSketch;
import org.apache.ibatis.annotations.Mapper;
/**
* @author jiangxiaoge
* @description
* @data 2025/3/16
**/
@Mapper
public interface FunctionResumeSketchMapper extends BaseMapper<FunctionResumeSketch> {
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeTemplate;
import org.apache.ibatis.annotations.Mapper;
/**
* @author jiangxiaoge
* @description 简历模版Mapper
* @data 2025/1/7
**/
@Mapper
public interface FunctionResumeTemplateMapper extends BaseMapper<FunctionResumeTemplate> {
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.FunctionResumeWorkExp;
import org.apache.ibatis.annotations.Mapper;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/16
**/
@Mapper
public interface FunctionResumeWorkExpMapper extends BaseMapper<FunctionResumeWorkExp> {
}
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.console.controller;
import com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor;
import com.alibaba.nacos.core.cluster.health.ModuleHealthCheckerHolder;
import com.alibaba.nacos.core.cluster.health.ReadinessResult;
import com.alibaba.nacos.core.paramcheck.ExtractorManager;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* Health Controller.
*
* @author <a href="mailto:huangxiaoyu1018@gmail.com">hxy1991</a>
*/
@RestController("consoleHealth")
@RequestMapping("/v1/console/health")
@ExtractorManager.Extractor(httpExtractor = ConsoleDefaultHttpParamExtractor.class)
public class HealthController {
/**
* Whether the Nacos is in broken states or not, and cannot recover except by being restarted.
*
* @return HTTP code equal to 200 indicates that Nacos is in right states. HTTP code equal to 500 indicates that
* Nacos is in broken states.
*/
@GetMapping("/liveness")
public ResponseEntity<String> liveness() {
return ResponseEntity.ok().body("OK");
}
/**
* Ready to receive the request or not.
*
* @return HTTP code equal to 200 indicates that Nacos is ready. HTTP code equal to 500 indicates that Nacos is not
* ready.
*/
@GetMapping("/readiness")
public ResponseEntity<String> readiness(HttpServletRequest request) {
ReadinessResult result = ModuleHealthCheckerHolder.getInstance().checkReadiness();
if (result.isSuccess()) {
return ResponseEntity.ok().body("OK");
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result.getResultMessage());
}
}
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.console.controller.v2;
import com.alibaba.nacos.api.model.v2.Result;
import com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor;
import com.alibaba.nacos.core.cluster.health.ModuleHealthCheckerHolder;
import com.alibaba.nacos.core.cluster.health.ReadinessResult;
import com.alibaba.nacos.core.paramcheck.ExtractorManager;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* Health ControllerV2.
*
* @author DiligenceLai
*/
@RestController("consoleHealthV2")
@RequestMapping("/v2/console/health")
@ExtractorManager.Extractor(httpExtractor = ConsoleDefaultHttpParamExtractor.class)
public class HealthControllerV2 {
/**
* Whether the Nacos is in broken states or not, and cannot recover except by being restarted.
*
* @return HTTP code equal to 200 indicates that Nacos is in right states. HTTP code equal to 500 indicates that
* Nacos is in broken states.
*/
@GetMapping("/liveness")
public Result<String> liveness() {
return Result.success("ok");
}
/**
* Ready to receive the request or not.
*
* @return HTTP code equal to 200 indicates that Nacos is ready. HTTP code equal to 500 indicates that Nacos is not
* ready.
*/
@GetMapping("/readiness")
public Result<String> readiness(HttpServletRequest request) {
ReadinessResult result = ModuleHealthCheckerHolder.getInstance().checkReadiness();
if (result.isSuccess()) {
return Result.success("ok");
}
return Result.failure(result.getResultMessage());
}
}
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@font-face {
/*无边框*/
font-family: "iconfont-1";
src: url('icon1/iconfont.eot?t=1458627591'); /* IE9*/
src: url('icon1/iconfont.eot?t=1458627591#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('icon1/iconfont.woff?t=1458627591') format('woff'), /* chrome, firefox */ url('icon1/iconfont.ttf?t=1458627591') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('icon1/iconfont.svg?t=1458627591#iconfont') format('svg'); /* iOS 4.1- */
}
@font-face {
/*有边框*/
font-family: "iconfont-2";
src: url('icon/iconfont.eot'); /* IE9*/
src: url('icon/iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('icon/iconfont.woff') format('woff'), /* chrome, firefox */ url('icon/iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('icon/iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
/* 有边框 */
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale;
}
.iconfont-1 {
/*无边框*/
font-family: "iconfont-1" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale;
}
.iconfont-2 {
/*有边框*/
font-family: "iconfont-2" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale;
}
.logo {
}
.panel-logo {
padding-right: 2px;
font-size: 18px;
display: inline-block;
color: #333;
}
.icon-lg {
font-size: 80px !important;
}
.icon-size-md {
font-size: 40px !important;
vertical-align: middle;
}
.icon-size-lg {
font-size: 80px !important;
vertical-align: middle;
}
.icon-hsf:before {
content: "\e62f" !important;
}
.icon-rocketmq:before {
content: "\e632" !important;
}
.icon-notify:before {
content: "\e61e" !important;
}
.icon-tddl:before {
content: "\e61e" !important;
}
.icon-pandora:before {
content: "\e622" !important;
}
.icon-ailtomcat:before {
content: "\e628" !important;
}
.icon-configserver:before {
content: "\e61e" !important;
}
.icon-diamondserver:before {
content: "\e62a" !important;
}
.icon-vipserver:before {
content: "\e625" !important;
}
.icon-eagleeye:before {
content: "\e62c" !important;
}
.icon-tengine:before {
content: "\e635" !important;
}
.icon-tair:before {
content: "\e634" !important;
}
.icon-hbase:before {
content: "\e62d" !important;
}
.icon-jstorm:before {
content: "\e627" !important;
}
.icon-histore:before {
content: "\e62e" !important;
}
.icon-jingwei:before {
content: "\e61e" !important;
}
.icon-txc:before {
content: "\e636" !important;
}
.icon-edas:before {
content: "\e620" !important;
}
.icon-csb:before {
content: "\e61e" !important;
}
.icon-ons:before {
content: "\e630" !important;
}
.icon-drds:before {
content: "\e61f" !important;
}
.icon-duct:before {
content: "\e62b" !important;
}
.icon-amazon:before {
content: "\e61e" !important;
}
.icon-autoload:before {
content: "\e61e" !important;
}
.icon-switch:before {
content: "\e633" !important;
}
.icon-sentinel:before {
content: "\e623" !important;
}
.icon-preplan:before {
content: "\e631" !important;
}
.icon-moses:before {
content: "\e61e" !important;
}
.icon-zeus:before {
content: "\e61e" !important;
}
.icon-athena:before {
content: "\e61e" !important;
}
.icon-bcp:before {
content: "\e61e" !important;
}
.icon-lark:before {
content: "\e61e" !important;
}
.icon-nest:before {
content: "\e61e" !important;
}
.icon-monkeyking:before {
content: "\e61e" !important;
}
.icon-tab:before {
content: "\e61e" !important;
}
.icon-oceanus:before {
content: "\e61e" !important;
}
.icon-eos :before {
content: "\e61e" !important;
}
.icon-sonar:before {
content: "\e61e" !important;
}
.icon-ai:before {
content: "\e605" !important;
}
.icon-hotcode:before {
content: "\e621" !important;
}
.icon-taokeeper:before {
content: "\e624" !important;
}
.icon-mdl:before {
content: "\e61e" !important;
}
.icon-mw:before {
content: "\e61e" !important;
}
.icon-default:before {
content: "\e607" !important;
}
.icon-alitomcat:before {
content: "\e607" !important;
}
/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424)
* Released under the MIT license
* https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md
*-----------------------------------------------------------------------------*/
define("vs/basic-languages/src/fsharp",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={comments:{lineComment:"//",blockComment:["(*","*)"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},t.language={defaultToken:"",tokenPostfix:".fs",keywords:["abstract","and","atomic","as","assert","asr","base","begin","break","checked","component","const","constraint","constructor","continue","class","default","delegate","do","done","downcast","downto","elif","else","end","exception","eager","event","external","extern","false","finally","for","fun","function","fixed","functor","global","if","in","include","inherit","inline","interface","internal","land","lor","lsl","lsr","lxor","lazy","let","match","member","mod","module","mutable","namespace","method","mixin","new","not","null","of","open","or","object","override","private","parallel","process","protected","pure","public","rec","return","static","sealed","struct","sig","then","to","true","tailcall","trait","try","type","upcast","use","val","void","virtual","volatile","when","while","with","yield"],symbols:/[=><!~?:&|+\-*\^%;\.,\/]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,integersuffix:/[uU]?[yslnLI]?/,floatsuffix:/[fFmM]?/,tokenizer:{root:[[/[a-zA-Z_]\w*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/\[<.*>\]/,"annotation"],[/^#(if|else|endif)/,"keyword"],[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,"delimiter"],[/\d*\d+[eE]([\-+]?\d+)?(@floatsuffix)/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?(@floatsuffix)/,"number.float"],[/0x[0-9a-fA-F]+LF/,"number.float"],[/0x[0-9a-fA-F]+(@integersuffix)/,"number.hex"],[/0b[0-1]+(@integersuffix)/,"number.bin"],[/\d+(@integersuffix)/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"""/,"string",'@string."""'],[/"/,"string",'@string."'],[/\@"/,{token:"string.quote",next:"@litstring"}],[/'[^\\']'B?/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\(\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\*]+/,"comment"],[/\*\)/,"comment","@pop"],[/\*/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/("""|"B?)/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]],litstring:[[/[^"]+/,"string"],[/""/,"string.escape"],[/"/,{token:"string.quote",next:"@pop"}]]}}});
\ No newline at end of file
/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424)
* Released under the MIT license
* https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md
*-----------------------------------------------------------------------------*/
define("vs/basic-languages/src/go",["require","exports"],function(e,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.conf={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"`",close:"`",notIn:["string"]},{open:'"',close:'"',notIn:["string"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"`",close:"`"},{open:'"',close:'"'},{open:"'",close:"'"}]},n.language={defaultToken:"",tokenPostfix:".go",keywords:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var","bool","true","false","uint8","uint16","uint32","uint64","int8","int16","int32","int64","float32","float64","complex64","complex128","byte","rune","uint","int","uintptr","string","nil"],operators:["+","-","*","/","%","&","|","^","<<",">>","&^","+=","-=","*=","/=","%=","&=","|=","^=","<<=",">>=","&^=","&&","||","<-","++","--","==","<",">","=","!","!=","<=",">=",":=","...","(",")","","]","{","}",",",";",".",":"],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/[a-zA-Z_]\w*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/\[\[.*\]\]/,"annotation"],[/^\s*#\w+/,"keyword"],[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\d+[eE]([\-+]?\d+)?/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F']*[0-9a-fA-F]/,"number.hex"],[/0[0-7']*[0-7]/,"number.octal"],[/0[bB][0-1']*[0-1]/,"number.binary"],[/\d[\d']*/,"number"],[/\d/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"],[/`/,"string","@rawstring"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@doccomment"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],doccomment:[[/[^\/*]+/,"comment.doc"],[/\/\*/,"comment.doc.invalid"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],rawstring:[[/[^\`]/,"string"],[/`/,"string","@pop"]]}}});
\ No newline at end of file
/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424)
* Released under the MIT license
* https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md
*-----------------------------------------------------------------------------*/
define("vs/basic-languages/src/handlebars",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n="undefined"==typeof monaco?self.monaco:monaco,a=["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"];t.conf={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,comments:{blockComment:["{{!--","--}}"]},brackets:[["\x3c!--","--\x3e"],["<",">"],["{{","}}"],["{","}"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"<",close:">"},{open:'"',close:'"'},{open:"'",close:"'"}],onEnterRules:[{beforeText:new RegExp("<(?!(?:"+a.join("|")+"))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),afterText:/^<\/(\w[\w\d]*)\s*>$/i,action:{indentAction:n.languages.IndentAction.IndentOutdent}},{beforeText:new RegExp("<(?!(?:"+a.join("|")+"))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),action:{indentAction:n.languages.IndentAction.Indent}}]},t.language={defaultToken:"",tokenPostfix:"",tokenizer:{root:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.root"}],[/<!DOCTYPE/,"metatag.html","@doctype"],[/<!--/,"comment.html","@comment"],[/(<)(\w+)(\/>)/,["delimiter.html","tag.html","delimiter.html"]],[/(<)(script)/,["delimiter.html",{token:"tag.html",next:"@script"}]],[/(<)(style)/,["delimiter.html",{token:"tag.html",next:"@style"}]],[/(<)([:\w]+)/,["delimiter.html",{token:"tag.html",next:"@otherTag"}]],[/(<\/)(\w+)/,["delimiter.html",{token:"tag.html",next:"@otherTag"}]],[/</,"delimiter.html"],[/\{/,"delimiter.html"],[/[^<{]+/]],doctype:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.comment"}],[/[^>]+/,"metatag.content.html"],[/>/,"metatag.html","@pop"]],comment:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.comment"}],[/-->/,"comment.html","@pop"],[/[^-]+/,"comment.content.html"],[/./,"comment.content.html"]],otherTag:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.otherTag"}],[/\/?>/,"delimiter.html","@pop"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/]],script:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.script"}],[/type/,"attribute.name","@scriptAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/(<\/)(script\s*)(>)/,["delimiter.html","tag.html",{token:"delimiter.html",next:"@pop"}]]],scriptAfterType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.scriptAfterType"}],[/=/,"delimiter","@scriptAfterTypeEquals"],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptAfterTypeEquals:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.scriptAfterTypeEquals"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptWithCustomType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.scriptWithCustomType.$S2"}],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptEmbedded:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInEmbeddedState.scriptEmbedded.$S2",nextEmbedded:"@pop"}],[/<\/script/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}]],style:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.style"}],[/type/,"attribute.name","@styleAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/(<\/)(style\s*)(>)/,["delimiter.html","tag.html",{token:"delimiter.html",next:"@pop"}]]],styleAfterType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.styleAfterType"}],[/=/,"delimiter","@styleAfterTypeEquals"],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleAfterTypeEquals:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.styleAfterTypeEquals"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleWithCustomType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.styleWithCustomType.$S2"}],[/>/,{token:"delimiter.html",next:"@styleEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleEmbedded:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInEmbeddedState.styleEmbedded.$S2",nextEmbedded:"@pop"}],[/<\/style/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}]],handlebarsInSimpleState:[[/\{\{\{?/,"delimiter.handlebars"],[/\}\}\}?/,{token:"delimiter.handlebars",switchTo:"@$S2.$S3"}],{include:"handlebarsRoot"}],handlebarsInEmbeddedState:[[/\{\{\{?/,"delimiter.handlebars"],[/\}\}\}?/,{token:"delimiter.handlebars",switchTo:"@$S2.$S3",nextEmbedded:"$S3"}],{include:"handlebarsRoot"}],handlebarsRoot:[[/[#/][^\s}]+/,"keyword.helper.handlebars"],[/else\b/,"keyword.helper.handlebars"],[/[\s]+/],[/[^}]/,"variable.parameter.handlebars"]]}}});
\ No newline at end of file
/*!-----------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424)
* Released under the MIT license
* https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md
*-----------------------------------------------------------------------------*/
define("vs/basic-languages/src/html",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n="undefined"==typeof monaco?self.monaco:monaco,i=["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"];t.conf={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,comments:{blockComment:["\x3c!--","--\x3e"]},brackets:[["\x3c!--","--\x3e"],["<",">"],["{","}"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:'"',close:'"'},{open:"'",close:"'"},{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"}],onEnterRules:[{beforeText:new RegExp("<(?!(?:"+i.join("|")+"))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$","i"),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:n.languages.IndentAction.IndentOutdent}},{beforeText:new RegExp("<(?!(?:"+i.join("|")+"))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),action:{indentAction:n.languages.IndentAction.Indent}}]},t.language={defaultToken:"",tokenPostfix:".html",ignoreCase:!0,tokenizer:{root:[[/<!DOCTYPE/,"metatag","@doctype"],[/<!--/,"comment","@comment"],[/(<)((?:[\w\-]+:)?[\w\-]+)(\s*)(\/>)/,["delimiter","tag","","delimiter"]],[/(<)(script)/,["delimiter",{token:"tag",next:"@script"}]],[/(<)(style)/,["delimiter",{token:"tag",next:"@style"}]],[/(<)((?:[\w\-]+:)?[\w\-]+)/,["delimiter",{token:"tag",next:"@otherTag"}]],[/(<\/)((?:[\w\-]+:)?[\w\-]+)/,["delimiter",{token:"tag",next:"@otherTag"}]],[/</,"delimiter"],[/[^<]+/]],doctype:[[/[^>]+/,"metatag.content"],[/>/,"metatag","@pop"]],comment:[[/-->/,"comment","@pop"],[/[^-]+/,"comment.content"],[/./,"comment.content"]],otherTag:[[/\/?>/,"delimiter","@pop"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/]],script:[[/type/,"attribute.name","@scriptAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/(<\/)(script\s*)(>)/,["delimiter","tag",{token:"delimiter",next:"@pop"}]]],scriptAfterType:[[/=/,"delimiter","@scriptAfterTypeEquals"],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptAfterTypeEquals:[[/"([^"]*)"/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptWithCustomType:[[/>/,{token:"delimiter",next:"@scriptEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptEmbedded:[[/<\/script/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}],[/[^<]+/,""]],style:[[/type/,"attribute.name","@styleAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/(<\/)(style\s*)(>)/,["delimiter","tag",{token:"delimiter",next:"@pop"}]]],styleAfterType:[[/=/,"delimiter","@styleAfterTypeEquals"],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleAfterTypeEquals:[[/"([^"]*)"/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleWithCustomType:[[/>/,{token:"delimiter",next:"@styleEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleEmbedded:[[/<\/style/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}],[/[^<]+/,""]]}}});
\ No newline at end of file
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