Commit 12ed3dbd by zwb

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

parent 39ca2334
Showing with 4693 additions and 0 deletions
package com.bkty.config;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.ShearCaptcha;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import java.awt.*;
/**
* 验证码配置
*
* @author Lion Li
*/
@Configuration
public class CaptchaConfig {
private static final int WIDTH = 160;
private static final int HEIGHT = 60;
private static final Color BACKGROUND = Color.LIGHT_GRAY;
private static final Font FONT = new Font("Arial", Font.BOLD, 48);
/**
* 圆圈干扰验证码
*/
@Lazy
@Bean
public CircleCaptcha circleCaptcha() {
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(WIDTH, HEIGHT);
captcha.setBackground(BACKGROUND);
captcha.setFont(FONT);
return captcha;
}
/**
* 线段干扰的验证码
*/
@Lazy
@Bean
public LineCaptcha lineCaptcha() {
LineCaptcha captcha = CaptchaUtil.createLineCaptcha(WIDTH, HEIGHT);
captcha.setBackground(BACKGROUND);
captcha.setFont(FONT);
return captcha;
}
/**
* 扭曲干扰验证码
*/
@Lazy
@Bean
public ShearCaptcha shearCaptcha() {
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(WIDTH, HEIGHT);
captcha.setBackground(BACKGROUND);
captcha.setFont(FONT);
return captcha;
}
}
package com.bkty.controller;
import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.IdUtil;
import com.bkty.domain.vo.CaptchaVo;
import com.bkty.enums.CaptchaType;
import com.bkty.properties.CaptchaProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
/**
* 验证码操作处理
*
* @author Lion Li
*/
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
public class CaptchaController {
private final CaptchaProperties captchaProperties;
/**
* 生成验证码
*/
@GetMapping("/code")
public R<CaptchaVo> getCode() {
CaptchaVo captchaVo = new CaptchaVo();
boolean captchaEnabled = captchaProperties.getEnabled();
if (!captchaEnabled) {
captchaVo.setCaptchaEnabled(false);
return R.ok(captchaVo);
}
// 保存验证码信息
String uuid = IdUtil.simpleUUID();
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
// 生成验证码
CaptchaType captchaType = captchaProperties.getType();
boolean isMath = CaptchaType.MATH == captchaType;
Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
captcha.setGenerator(codeGenerator);
captcha.createCode();
// 如果是数学验证码,使用SpEL表达式处理验证码结果
String code = captcha.getCode();
if (isMath) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
code = exp.getValue(String.class);
}
System.out.println("验证码是:" + code);
RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
captchaVo.setUuid(uuid);
captchaVo.setImg(captcha.getImageBase64());
return R.ok(captchaVo);
}
}
package com.bkty.domain.vo;
import lombok.Data;
/**
* 验证码信息
*
* @author Michelle.Chung
*/
@Data
public class CaptchaVo {
/**
* 是否开启验证码
*/
private Boolean captchaEnabled = true;
private String uuid;
/**
* 验证码图片
*/
private String img;
}
package com.bkty;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
@EnableDubbo
@SpringBootApplication
public class employmentBusinessPCAuthApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(employmentBusinessPCAuthApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
System.out.println("(♥◠‿◠)ノ゙ 认证授权中心启动成功 ლ(´ڡ`ლ)゙ ");
}
}
\ No newline at end of file
package com.bkty.enums;
import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.ShearCaptcha;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 验证码类别
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum CaptchaCategory {
/**
* 线段干扰
*/
LINE(LineCaptcha.class),
/**
* 圆圈干扰
*/
CIRCLE(CircleCaptcha.class),
/**
* 扭曲干扰
*/
SHEAR(ShearCaptcha.class);
private final Class<? extends AbstractCaptcha> clazz;
}
package com.bkty.enums;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.captcha.generator.RandomGenerator;
import com.bkty.captcha.UnsignedMathGenerator;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 验证码类型
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum CaptchaType {
/**
* 数字
*/
MATH(UnsignedMathGenerator.class),
/**
* 字符
*/
CHAR(RandomGenerator.class);
private final Class<? extends CodeGenerator> clazz;
}
package com.bkty.form;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.domain.model.LoginBody;
/**
* 邮件登录对象
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class EmailLoginBody extends LoginBody {
/**
* 邮箱
*/
@NotBlank(message = "{user.email.not.blank}")
@Email(message = "{user.email.not.valid}")
private String email;
/**
* 邮箱code
*/
@NotBlank(message = "{email.code.not.blank}")
private String emailCode;
}
package com.bkty.properties;
import com.bkty.enums.CaptchaCategory;
import com.bkty.enums.CaptchaType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
* 验证码配置
*
* @author ruoyi
*/
@Data
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.captcha")
public class CaptchaProperties {
/**
* 验证码类型
*/
private CaptchaType type;
/**
* 验证码类别
*/
private CaptchaCategory category;
/**
* 数字验证码位数
*/
private Integer numberLength;
/**
* 字符验证码长度
*/
private Integer charLength;
/**
* 验证码开关
*/
private Boolean enabled;
}
package org.dromara.common.core.constant;
/**
* 缓存的key 常量
*
* @author Lion Li
*/
public interface CacheConstants {
/**
* 在线用户 redis key
*/
String ONLINE_TOKEN_KEY = "online_tokens:";
/**
* 登录账户密码错误次数 redis key
*/
String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
/**
* 运营平台token
*/
String YYPT_TOKEN_KEY = "yypt_token_key";
/**
* token请求头
*/
String TOKEN_BEARER = "Bearer ";
/**
* 扣子token国外
*/
String REDIS_COZE_TOKEN_KEY = "coze:token";
/**
* 全局 redis key (业务无关的key)
*/
String GLOBAL_REDIS_KEY = "global:";
/**
* 防重提交 redis key
*/
String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:";
/**
* 扣子token国内
*/
String REDIS_CN_COZE_TOKEN_KEY = "coze:cn:token";
/**cas配置项redis key*/
String REDIS_KEY_CAS_CONFIG = "casConfig";
/**个人简历redis缓存 user:resume:{userId}:{resumeId}*/
String REDIS_USER_RESUME_KEY = "user:resume:%s:%s";
/**个人简历redis缓存 推荐岗位用*/
String REDIS_USER_RESUME_RECOMMEND_KEY = "recommend_resume_data:%s";
}
package org.dromara.common.core.constant;
/**
* 缓存组名称常量
* <p>
* key 格式为 cacheNames#ttl#maxIdleTime#maxSize
* <p>
* ttl 过期时间 如果设置为0则不过期 默认为0
* maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0
* maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0
* <p>
* 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
*
* @author Lion Li
*/
public interface CacheNames {
/**
* 演示案例
*/
String DEMO_CACHE = "demo:cache#60s#10m#20";
/**
* 系统配置
*/
String SYS_CONFIG = "sys_config";
/**
* 数据字典
*/
String SYS_DICT = "sys_dict";
/**
* 用户账户
*/
String SYS_USER_NAME = "sys_user_name#30d";
/**
* 用户名称
*/
String SYS_NICKNAME = "sys_nickname#30d";
/**
* 部门
*/
String SYS_DEPT = "sys_dept#30d";
/**
* OSS内容
*/
String SYS_OSS = "sys_oss#30d";
/**
* OSS配置
*/
String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config";
/**
* 在线用户
*/
String ONLINE_TOKEN = "online_tokens";
}
package org.dromara.common.core.constant;
/**
* 通用常量信息
*
* @author ruoyi
*/
public interface Constants {
/**
* UTF-8 字符集
*/
String UTF8 = "UTF-8";
/**
* GBK 字符集
*/
String GBK = "GBK";
/**
* www主域
*/
String WWW = "www.";
/**
* http请求
*/
String HTTP = "http://";
/**
* https请求
*/
String HTTPS = "https://";
/**
* 通用成功标识
*/
String SUCCESS = "0";
/**
* 通用失败标识
*/
String FAIL = "1";
/**
* 登录成功
*/
String LOGIN_SUCCESS = "Success";
/**
* 注销
*/
String LOGOUT = "Logout";
/**
* 注册
*/
String REGISTER = "Register";
/**
* 登录失败
*/
String LOGIN_FAIL = "Error";
/**
* 验证码有效期(分钟)
*/
Integer CAPTCHA_EXPIRATION = 2;
/**
* 令牌
*/
String TOKEN = "token";
/**
* 顶级部门id
*/
Long TOP_PARENT_ID = 0L;
String ES_INDEX_POSITION = "function_position_portrait_";
/** 全字段时间格式化字符串模式:yyyy-MM-dd HH:mm:ss */
public static final String DATE_FULL_FORMAT = "yyyy-MM-dd HH:mm:ss";
}
package org.dromara.common.core.constant;
/**
* @author jiangxiaoge
* @description
* @data 2025/3/13
**/
public interface CozeConstants {
String HTTPS = "https://";
String COZE_API_DOMAIN_NAME = "api.coze.com";
String COZE_TOKEN_BEARER = "Bearer ";
String COZR_REQUEST_FIELD = "CODE_TOKEN";
String COZE_RESPONSE_CODE = "code";
String COZE_RESPONSE_DATA = "data";
/*String COZE_OAUTH_ID = "1149310550186";
String COZE_OAUTH_PUBLIC_KEY_STR = "uvxkTCWh2L-a3gwYsoahx71rks8m_UiXAo7sReh0Eoo";
String COZE_OAUTH_PRIVATE_KEY_STR = """
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC8cAZSuxMtFO0i
/FiwKSEW48hCAIU6hdZvRWyP6sFn6Qudkdd2n9EtiUm7/FVv+3DsSRk2lr76DAaE
ND+gdZScA75puSoQwIPHK3tCW6Mvnaff3bOjT/cCo+qSAl9DwwDQq8673IH9myB0
/t9xuyFR+lWeEh4CAxpQP1Fme/SB+kotDSSmbzXdzzUEKSpNZRPikuhtZAxUFMuG
a9nhVSo1lDab3Xeanxk6/+ZqpYrN2PIJ4E7SryHWU61GBZTHuA3Of1VPPBdtKgB+
4H6paorcf0VOtvrz9QtpgELkFtqxgIKLKors5dlBX2N1qOKXokfx/CNAjBrw9aDz
bUpCSPDfAgMBAAECggEADpCUFInsIgRnjdg2XKtZKOVDQcK7iXn1yJTznq4U89yh
7rRqlHsm1l+xqMuIqQakxEakIcGGElqimE5XiqX77hEmAxLySuguZ1MXjFn7437K
jnWa4GkaXQRhaZawWVL1uatF5+RT0c/PMuvORoKhwS3Zt0WMNziZLJGwT30OOYuG
ghWKUyMRvBl9r/HrVUNRSei2W/7jbgBG/zFZWmlS00rQVMcFzHen6WkxBEnYwS7/
DgiM0maFB5eTMD0e/C0GU9dcwizRS0l0EdwHDcQUEof6ArpYzy1iinpGzQR8qfUo
0MEn7DXrqQAB8RUnySmvsi25j+6kqPGrlw+FMlnOmQKBgQDfTVav3iC0ex0yqITO
N+dg6ipMXsePi0Vl7MyeIAsARstijhD5Jil3lNYCbi7TIaeCDntLfwHHEV0WevEl
14kTUPAhtYKsozat3NZfshx966cp47qgQq/CsAgcGqGBFf1D8AC91qJvnt8A7T0M
xbMV1GCJlM4TzO4Ee8ydOzuqNwKBgQDYB8L4C7rKGeJPGmRjzpqarOemAKZ48vk+
Dj8Bv+QXJuzUlHbt6rh5V3iIAhSSEiQIZylVwbtIA6AA20lvtOBxPHT3oqPwqJuy
fNaz9lz+IK0FeV76KeuRdJ7f8XHUFsiejoe+AhTSu4JiU4DKNJxybplQW42vBf5V
9IbQDo56mQKBgHhCb4Axkc2BTF0+8p8Y7mx8UCFnHfSJnethl2VFfoc8mUZPmoaD
7esv5f3TFzavtkXNxMsI8I0kJcz4kyy9Il6bxWdyGJkfIVCLUYyiIMQUCKf/tD+h
CoVARm3UTcx2ajqPY+bZVAvnNNSL78gUJPmJsiAGWuHBCSKzBaRcBSsLAoGAEnZv
WkWeXCFkLwCBbeo9WlTGo3BQyetyE7Sh++NLehMQkIZzt5ek1nH8UhtjaKx2OeWf
Kb4Z5Giy5v/Wwd5ChEr3cCaNPkcar2Ft9AltHybpwpsB2/1zLYY7AcafxNGBqdNi
lIGXB/+QgBjE3AMtD7aWEcERVdD8UJpKX0OvN5kCgYBhK3dmk/+JFalwVAKrg5u9
3BBJwdpyiuJfbgy789GcVPNLorlU3adssDkCO7tbBM9PVtxd1zBZFFCGFp0TCqVJ
oQWq5ax5MsdjoFYXcTiRiLounkwZM3GpKGEBhpYMZo5ipP97nfpoqvks0uIWgzbq
vn9riRXPNFO9+uGbYMy9nw==
""";*/
//海外
String COZE_OAUTH_ID = "1168633192414";
String COZE_OAUTH_PUBLIC_KEY_STR = "nE8ymC2sb8q84w6hiSijoKAuxH93wygob9BAOP18cyI";
String COZE_OAUTH_PRIVATE_KEY_STR = """
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrun+MXy7xp47k
QQ3lL8e/lkjz7H+iMKLFIgCGhN9drxcozbyzohIFiaiVz9YTggu81hj/3KB3wHrR
J8BrTrF9Cql8uPlLTyAMdas73vFNMaX9fiJAS4ve/H3a9CA12dF6c67E+tTMJ0GX
JMesxfSnyNTIiTkR5WkFbfYAbwQPSPWQYyrpmC+i1UlwIIgO0XIyk+RPR/irBYjg
iCAHdNRLG1bp2qVFS5O8xGV9arC7v4ox8AjgjUoUV6glEdRmaa2VWQ+/Hmb0Uu64
sL8VvAh/iKbqv6NJ/Pwm2BVSUizNLLS/oennHvqL6xhlkxFcGiTkMSc0PIbokE70
nBm8rhRJAgMBAAECggEAGlDjGOTBMeEGga4CbhurVY23MkIMsRMY7nKud+7Gt/mL
6AHXKAPvFRAqWILYAHdOSyrX++WYZaGGcKTR1N/jTREkNeu2sDa454/kpLEec0Dn
efgpUYcCMTKqKrJ8eSQe+M/feWoi0AI4B+xn+O8A9fFNwkldCIQKWv2l1B0vFxRL
yVDRW8/8Jw9LWXfvsvCmejxwfMZQ6eqSN0PzojZ89HoRkmbtRayYWeGc3tAL8rqo
cwJd20ipCLsPbahviKByzsLR00hQOdLhSL5y1JbzrqHLHvCTaQSiJIOz9U7YY2S8
NCd8S0zI/8S8Nx/sHdgzM80jDFVynlQuDCXmTYtBnQKBgQDdhxRXxQFyBE45WbfX
4MHloL8cTY5DeIv3RB/v/DrjL1/TfiOIHsIXJXGFIwa8FvHfizFqRM4RUl+a0ncv
A+/hqUfyFkoeykua6TgBz4j8cieHROl2wIYIcutArGyToqFb9BndOUHe/gVX/99w
G/zZVSsgcUkIA5rqe/FMHK6lhwKBgQDGc5ZidZaqRVnOlVF82G36+K/g1DRgITsj
s1gUj57Nk1u4H3X3/ucjQrjYImPioLWf5yNV5KlrWJ1g8DbyvxUzTkp0THlYPr/m
iu9OVsyYIfAhine5IHf3rPHXGEb6tjqAaPFJtWgmbLC02ovOyEAjqotlXq9PYs9M
2+CNC+jrrwKBgGJd8eWLYDntuYJz2VOtxMufVl1N/ubs51HT2U6qNMfeqHF0KHjw
F4VJph/VTs9EbQCzgSzMAqjkZ/mKjUu+Ns2AaqcPyLflYi8Wcj0zl4NpeS3ypqyJ
jrq/tMDSD6B67aW0PavjGeHX8C2EPAY/F0a2L+FHDIPXffmzMZF3hwGPAoGBAK+E
TY55Z67itWIzyCV+Gg9tYcichlI7Md4KYa2VtKMnYGiBUsnq5azW9zsZd3RaJSju
ud7e5USu1EqHSmH5xJti9BDWDkCRBxK08KA9vqgS5BoWKHtQjyo8CIzvyN82ypkF
t9o0GpBZcxQRcrOJgsjIro8SZkT1JFla4sh8rx27AoGADI7cEovOc2lC2FoAuWgY
fEsufAANC3hM5EbifYlCqvel7lOvFGRY7Aqr8YQLRB0fgWmDh0tClblXEb9pNXqs
mKGnHYNNVXCcefH5uxZLymziZcFDUWFc8u3hZ+Pk0U1mDM2HkWQ/y7dMj6GamprR
M3UzJ3TjhGUVxxaO1UX3unA=
""";
}
package org.dromara.common.core.constant;
/**
* @author jiangxiaoge
* @description coze常量
* @data 2024/12/12
**/
public interface CozeConstsnts {
String COZE_API_DOMAIN_NAME = "api.coze.com";
String COZE_CN_API_DOMAIN_NAME = "api.coze.cn";
String COZE_TOKEN_BEARER = "Bearer ";
String COZR_REQUEST_FIELD = "CODE_TOKEN";
String COZE_RESPONSE_CODE = "code";
String COZE_RESPONSE_DATA = "data";
String COZE_SOURCE_NAME = "EN";
String COZE_HEADER_TOKEN_NAME = "X-Coze-Source";
//冯小虎国内版
String COZE_CN_OAUTH_ID = "1147350075148";
String COZE_CN_OAUTH_PUBLIC_KEY_STR = "WifGCMYz8XNE5JTG5K-Ph-_lAlxWwXef-YO0PaFeNBQ";
String COZE_CN_OAUTH_PRIVATE_KEY_STR = """
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDaMgKlzO3pTsp1
onaVRhgO/vvLPdp+/LL5EQZ1jgbFjbO6X5Brl9rGU+/q09E2l7k3bV1CWV/wHxjI
R+V+G+fHgBSI1oo6F6fD8fIxUrC84kvtjoOMjFgmiis4JSL/X8hvg1GCril0a57N
j0IEdXOzjeuRPFYcBCchCDJl5qa25HgcRWdvOGuBLzlHF/Np/MHqB1jcRTs7VP1s
kge0h3BXgC0uIWnqMRijtEV0OFnGKO44uqa9T5x4ibXl6BXqIp3WpvH1I2g7NNhm
YWI4zG7OZ9gDPccXu8Z5HxAhtOnkoPYNZv6rTUw4rXvM7JOaSiisKvjBtJ/++foT
jaHAtoLRAgMBAAECggEALBE1n67HzfrvwNpTAMrrSPwOdnnDiyoWK19Y4p0QmQ2J
YvayAEjnEm8ZYV/uPQxfh+Y6k4/hZGmjY6/BlbqlERG5E/ngaOMmKtjOhU+iCkPZ
+dxyjQw7MW1uVSSK8PCzVnI7IB10meKya+mm96eQ+o6LKQX6jiX4kR9KrnHq8bzL
HI4syGjE4EEPseZjZ+DeNoXSCJ2rN1DP4CAIaBHYBKdL3GNzohth4V/oHWu6e8fU
QqdDX6qo/O6uVbCrrndeCooSk2fDf0yQIdUMHsAi45/fhfHPDQtvqwCbrd/j44z5
3lRsWe240maaxn+7ZrYAdzgHWA6ljx4vVYFXehUmkQKBgQD+Rr6iAl6mQnCe1L+5
vSAy8xXnlXfxwQAcfvxqwXFjmcr6fPgDADbionhNsBcZR4aYIncuLg+bhnc0kJ0r
tksQ/Cz4bqU38TxpC9OZiGZBAv35MSrDB4FktZ2TEHm/AMTTmwAN1V2pfPNouBxD
1QhLa8fJuMD9V9ZNGaMztIegqwKBgQDbrKcpQMzTYhARw2HysBsHWyE+Pv5qucUm
ickgsyOqDXvwKhidCgaDH3MF0iLNE3BvaiLcdUshrcP+gI8L80LEmLbW1l2P2kEr
OfXhEBgp2oYMMtTzOFUPF0lY49OKwX5Dfrx0T2+fBHX4hhrG8UAtnCHqaSerq6Gn
zH1TmZgCcwKBgB04LfHaCdxbKCvoULvcUtXn/y1kjKr4PbYIlaMC5a/HXX7ZW3qk
zKnQd7idspnSEVJsK3vgCSUbF5mvwI/kW83w3I4T0OKT0xhTJpavCOkRmfPHxZ0x
wJm9EQV0UDk1smiKmD/+s8ulMydmeMEGuvRHx2aT14GFVXf4qTto1GkPAoGAJtGK
tIqD53Kzvj/VhipCjHk5FaPSF4/+W8ILbaDaU4mQdi8qLE4COpeLHuM0TSL7QmTy
Bh0gCrKHKOkU3hq9mLoXzT0SSYJa88WJQp0yd+rqtysgYF57AXCvAGs6DZIFuu2h
ZbhH1ETYh/NCthcZrTWi9KZiYT6yEVHcb7J7nysCgYBmBOPYaWvckNYZj4M4Ydrs
fjMwlkCQc0B0n/bAJA2n73U/B9Bc/iUZH/TZvOcUkYV+zpC2rUDjXJHpeRvgxv7u
z0SZ6lsu/yJvfKiXqPppBd8DbeMa95MOwxs10VJYwfoGKDnogK9/S2iSIsApGzdM
VI8iRXIjfvcqm9H4+vqG/Q==
""";
/*String COZE_CN_OAUTH_ID = "1149310550186";
String COZE_CN_OAUTH_PUBLIC_KEY_STR = "uvxkTCWh2L-a3gwYsoahx71rks8m_UiXAo7sReh0Eoo";
String COZE_CN_OAUTH_PRIVATE_KEY_STR = """
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC8cAZSuxMtFO0i
/FiwKSEW48hCAIU6hdZvRWyP6sFn6Qudkdd2n9EtiUm7/FVv+3DsSRk2lr76DAaE
ND+gdZScA75puSoQwIPHK3tCW6Mvnaff3bOjT/cCo+qSAl9DwwDQq8673IH9myB0
/t9xuyFR+lWeEh4CAxpQP1Fme/SB+kotDSSmbzXdzzUEKSpNZRPikuhtZAxUFMuG
a9nhVSo1lDab3Xeanxk6/+ZqpYrN2PIJ4E7SryHWU61GBZTHuA3Of1VPPBdtKgB+
4H6paorcf0VOtvrz9QtpgELkFtqxgIKLKors5dlBX2N1qOKXokfx/CNAjBrw9aDz
bUpCSPDfAgMBAAECggEADpCUFInsIgRnjdg2XKtZKOVDQcK7iXn1yJTznq4U89yh
7rRqlHsm1l+xqMuIqQakxEakIcGGElqimE5XiqX77hEmAxLySuguZ1MXjFn7437K
jnWa4GkaXQRhaZawWVL1uatF5+RT0c/PMuvORoKhwS3Zt0WMNziZLJGwT30OOYuG
ghWKUyMRvBl9r/HrVUNRSei2W/7jbgBG/zFZWmlS00rQVMcFzHen6WkxBEnYwS7/
DgiM0maFB5eTMD0e/C0GU9dcwizRS0l0EdwHDcQUEof6ArpYzy1iinpGzQR8qfUo
0MEn7DXrqQAB8RUnySmvsi25j+6kqPGrlw+FMlnOmQKBgQDfTVav3iC0ex0yqITO
N+dg6ipMXsePi0Vl7MyeIAsARstijhD5Jil3lNYCbi7TIaeCDntLfwHHEV0WevEl
14kTUPAhtYKsozat3NZfshx966cp47qgQq/CsAgcGqGBFf1D8AC91qJvnt8A7T0M
xbMV1GCJlM4TzO4Ee8ydOzuqNwKBgQDYB8L4C7rKGeJPGmRjzpqarOemAKZ48vk+
Dj8Bv+QXJuzUlHbt6rh5V3iIAhSSEiQIZylVwbtIA6AA20lvtOBxPHT3oqPwqJuy
fNaz9lz+IK0FeV76KeuRdJ7f8XHUFsiejoe+AhTSu4JiU4DKNJxybplQW42vBf5V
9IbQDo56mQKBgHhCb4Axkc2BTF0+8p8Y7mx8UCFnHfSJnethl2VFfoc8mUZPmoaD
7esv5f3TFzavtkXNxMsI8I0kJcz4kyy9Il6bxWdyGJkfIVCLUYyiIMQUCKf/tD+h
CoVARm3UTcx2ajqPY+bZVAvnNNSL78gUJPmJsiAGWuHBCSKzBaRcBSsLAoGAEnZv
WkWeXCFkLwCBbeo9WlTGo3BQyetyE7Sh++NLehMQkIZzt5ek1nH8UhtjaKx2OeWf
Kb4Z5Giy5v/Wwd5ChEr3cCaNPkcar2Ft9AltHybpwpsB2/1zLYY7AcafxNGBqdNi
lIGXB/+QgBjE3AMtD7aWEcERVdD8UJpKX0OvN5kCgYBhK3dmk/+JFalwVAKrg5u9
3BBJwdpyiuJfbgy789GcVPNLorlU3adssDkCO7tbBM9PVtxd1zBZFFCGFp0TCqVJ
oQWq5ax5MsdjoFYXcTiRiLounkwZM3GpKGEBhpYMZo5ipP97nfpoqvks0uIWgzbq
vn9riRXPNFO9+uGbYMy9nw==
""";*/
//冯小虎海外版
String COZE_OAUTH_ID = "1168633192414";
String COZE_OAUTH_PUBLIC_KEY_STR = "nE8ymC2sb8q84w6hiSijoKAuxH93wygob9BAOP18cyI";
String COZE_OAUTH_PRIVATE_KEY_STR = """
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrun+MXy7xp47k
QQ3lL8e/lkjz7H+iMKLFIgCGhN9drxcozbyzohIFiaiVz9YTggu81hj/3KB3wHrR
J8BrTrF9Cql8uPlLTyAMdas73vFNMaX9fiJAS4ve/H3a9CA12dF6c67E+tTMJ0GX
JMesxfSnyNTIiTkR5WkFbfYAbwQPSPWQYyrpmC+i1UlwIIgO0XIyk+RPR/irBYjg
iCAHdNRLG1bp2qVFS5O8xGV9arC7v4ox8AjgjUoUV6glEdRmaa2VWQ+/Hmb0Uu64
sL8VvAh/iKbqv6NJ/Pwm2BVSUizNLLS/oennHvqL6xhlkxFcGiTkMSc0PIbokE70
nBm8rhRJAgMBAAECggEAGlDjGOTBMeEGga4CbhurVY23MkIMsRMY7nKud+7Gt/mL
6AHXKAPvFRAqWILYAHdOSyrX++WYZaGGcKTR1N/jTREkNeu2sDa454/kpLEec0Dn
efgpUYcCMTKqKrJ8eSQe+M/feWoi0AI4B+xn+O8A9fFNwkldCIQKWv2l1B0vFxRL
yVDRW8/8Jw9LWXfvsvCmejxwfMZQ6eqSN0PzojZ89HoRkmbtRayYWeGc3tAL8rqo
cwJd20ipCLsPbahviKByzsLR00hQOdLhSL5y1JbzrqHLHvCTaQSiJIOz9U7YY2S8
NCd8S0zI/8S8Nx/sHdgzM80jDFVynlQuDCXmTYtBnQKBgQDdhxRXxQFyBE45WbfX
4MHloL8cTY5DeIv3RB/v/DrjL1/TfiOIHsIXJXGFIwa8FvHfizFqRM4RUl+a0ncv
A+/hqUfyFkoeykua6TgBz4j8cieHROl2wIYIcutArGyToqFb9BndOUHe/gVX/99w
G/zZVSsgcUkIA5rqe/FMHK6lhwKBgQDGc5ZidZaqRVnOlVF82G36+K/g1DRgITsj
s1gUj57Nk1u4H3X3/ucjQrjYImPioLWf5yNV5KlrWJ1g8DbyvxUzTkp0THlYPr/m
iu9OVsyYIfAhine5IHf3rPHXGEb6tjqAaPFJtWgmbLC02ovOyEAjqotlXq9PYs9M
2+CNC+jrrwKBgGJd8eWLYDntuYJz2VOtxMufVl1N/ubs51HT2U6qNMfeqHF0KHjw
F4VJph/VTs9EbQCzgSzMAqjkZ/mKjUu+Ns2AaqcPyLflYi8Wcj0zl4NpeS3ypqyJ
jrq/tMDSD6B67aW0PavjGeHX8C2EPAY/F0a2L+FHDIPXffmzMZF3hwGPAoGBAK+E
TY55Z67itWIzyCV+Gg9tYcichlI7Md4KYa2VtKMnYGiBUsnq5azW9zsZd3RaJSju
ud7e5USu1EqHSmH5xJti9BDWDkCRBxK08KA9vqgS5BoWKHtQjyo8CIzvyN82ypkF
t9o0GpBZcxQRcrOJgsjIro8SZkT1JFla4sh8rx27AoGADI7cEovOc2lC2FoAuWgY
fEsufAANC3hM5EbifYlCqvel7lOvFGRY7Aqr8YQLRB0fgWmDh0tClblXEb9pNXqs
mKGnHYNNVXCcefH5uxZLymziZcFDUWFc8u3hZ+Pk0U1mDM2HkWQ/y7dMj6GamprR
M3UzJ3TjhGUVxxaO1UX3unA=
""";
/* wwj个人
String COZE_OAUTH_ID = "1198672851059";
String COZE_OAUTH_PUBLIC_KEY_STR = "NILbA9z8J2CbLBnAgFMN972DYp6tCXY3FNW91RQ5bOI";
String COZE_OAUTH_PRIVATE_KEY_STR = """
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQClNKJ1PjaDGyfi
FXssYUIqDPZedusSiwY569sQ+tMewM05rGP3eHe3lqdkTy1yFHI+LGe14WbFMEwA
pp7cU4bjbcvZq5qGG63IBXBSO+F293y+Ggh1t8gatxhKrU8+r+MKckqdIJFnjCzH
/ZCgzSXnXpYy4RjkW6C3eP+gN7dLgnCv9dTYyn3L7b+6cbYNJ9Xy4NSPC73n963l
8wyF7R51PYnRc1S+wdRMCruNeU7Lsayoba1T54P8aE+IFSdegbrOcKF2LQxUit7k
QV/Q/jjvS6QpFyMIRyk9CX1T+iAdlKM+sbIcRwp9dbNulNlsldZJVkPAPAKhzOC3
cJ4YtjcfAgMBAAECggEAHwZU8gambxWk9kjJcmqrZcE8bT4Q5FEWcL8xyy0Hg9F6
WPfeaCWH+gLBWc2iv7dGQxXMvjPfvG1XAJJJ6B+Jh+FhNHYNFV1SU+ttjgvvygst
MngqGY1Td2zmE3Qbmy6lyRrwlzR8CgGhkUc1p3UPRrWVKGXsakoo0vvWqax88CF4
MK5E1jTLDQpQArAqL66K7AlkB9aXz5q+Sb0K6BJnoCfwJ13ZqlwPuT+2c8whtqrm
G2/t3D/NkKXOxmJuVVJGSHz8yoYEP1sD8HmCQ0W7T8hGHnVtznQKeT5Fm2oSt/0n
S0YQU5ar2Nvf07YL2JLlaXBCxnWZjfKBc/7IQsL9dQKBgQDPKbQXa4Yqcon1qpL6
kP9qWb1qlKqUdcNhNkqWk2/Vjxugq7uZpL0Y0Wha2l2ldFVGk4x18ZfO6eCw7w3D
8tdPpJZQaq4XFwO2tw+sHr7ozImTspoeOxGgJd9rLTJNad/u0A/osHUNWexvoT/A
LAEUJKZg/LUb023LHsTMa++T/QKBgQDMJs4RqIOQSYWhYGUVkws8O/TvKwy681+J
Bz3IgwDqXuCEuji2iBbOW/nWwxdjBP/s2au+L+34b205u6WPVdAN+6Olw6/SnEdD
OkIBI/PRBlJ+UJ7QUyfvAZQygKUFdsDQq4x24d9gCzH48J9wFKqj/yY24tgttqW8
F7Ggnc4MSwKBgQCkHca1UxJ76DeRLqu340zlJOjhWZt1d4HsLoWSmoTba78YX1SE
mLTRx3vjleDgZbP08IkzGs0IN9IweYGvPWADkUEfvhPi6PGhO48n7UeSyA8pujFM
R8Fj3P0NPEYAfCLSX3RrA/gHNrkPinAlvvavzYUdirWzsKDnbJ290LiAxQKBgQCr
exjy+jJwm2E02WxvP9FbKh2mW+nYix8/UabS6z90nndG5clHgW5urhFhFQRq90Ml
pv08xrPET4xWx0jc6j7Vz/54+bORXBX74U6Z+KIPfJkEYyHTaVYMy6248hgrS8HQ
RZi6LyZbXWv/Z9b05VckY+ish8UAOQBjyx84A/WUmwKBgQCb2YKK577akcE1xRmT
l+53LjrwXClOpVd14ViRpXwHmDTQ8O46lMD9FYLkhaudBvVRkEBJs2BMdmL23m/+
cR3fwB4P+eqC5gOJDDQ0mIewJBpxO0P8ZAm+pRhF/c4OUVZ8NF99f1s5BlTbUgQH
LR7zbl/RpqAStCs3+mJFacJBkQ==
""";*/
String COZE_STREAM_STATUS_CHAT_CREATED = "conversation.chat.created";
String COZE_STREAM_STATUS_CHAT_IN_PROGRESS = "conversation.chat.in_progress";
String COZE_STREAM_STATUS_MESSAGE_DELTA = "conversation.message.delta";
String COZE_STREAM_STATUS_MESSAGE_COMPLETED = "conversation.message.completed";
String COZE_STREAM_STATUS_CHAT_COMPLETED = "conversation.chat.completed";
String COZE_STREAM_STATUS_CHAT_FAILED = "conversation.chat.failed";
String COZE_STREAM_STATUS_DONE = "done";
String COZE_STREAM_STATUS_ERROR = "error";
}
package org.dromara.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 设备类型
* 针对一套 用户体系
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum DeviceType {
/**
* pc端
*/
PC("pc"),
/**
* app端
*/
APP("app"),
/**
* 小程序端
*/
XCX("xcx");
private final String device;
}
package org.dromara.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author Zhang Wenbiao
* @description EmploymentStatusEnum
* @datetime 2025/9/23 11:17
*/
@Getter
@AllArgsConstructor
public enum EmploymentStatusEnum {
/**
* 已就业
*/
ALREADY_EMPLOYMENT("已就业"),
/**
* 灵活就业
*/
FLEXIBLE_EMPLOYMENT("灵活就业"),
/**
* 升学深造
*/
EDUCATION_EMPLOYMENT("升学深造"),
/**
* 未就业
*/
UN_EMPLOYMENT("未就业"),
/**
* 其他
*/
OTHER_EMPLOYMENT("其他"),
/**
* 自主创业
*/
SELF_EMPLOYMENT("自主创业");
private final String employmentStatus;
}
package org.dromara.common.core.exception.user;
import java.io.Serial;
/**
* 验证码错误异常类
*
* @author Lion Li
*/
public class CaptchaException extends UserException {
@Serial
private static final long serialVersionUID = 1L;
public CaptchaException() {
super("user.jcaptcha.error");
}
public CaptchaException(String msg) {
super(msg);
}
}
package org.dromara.common.core.exception.user;
import java.io.Serial;
/**
* 验证码失效异常类
*
* @author ruoyi
*/
public class CaptchaExpireException extends UserException {
@Serial
private static final long serialVersionUID = 1L;
public CaptchaExpireException() {
super("user.jcaptcha.expire");
}
}
package org.dromara.common.core.service;
import org.dromara.common.core.utils.StringUtils;
import java.util.Map;
/**
* 字典服务服务
*
* @author Lion Li
*/
public interface DictService {
/**
* 根据字典类型和字典值获取字典标签
*
* @param dictType 字典类型
* @param dictValue 字典值
* @return 字典标签
*/
default String getDictLabel(String dictType, String dictValue) {
return getDictLabel(dictType, dictValue, StringUtils.SEPARATOR);
}
/**
* 根据字典类型和字典标签获取字典值
*
* @param dictType 字典类型
* @param dictLabel 字典标签
* @return 字典值
*/
default String getDictValue(String dictType, String dictLabel) {
return getDictValue(dictType, dictLabel, StringUtils.SEPARATOR);
}
/**
* 根据字典类型和字典值获取字典标签
*
* @param dictType 字典类型
* @param dictValue 字典值
* @param separator 分隔符
* @return 字典标签
*/
String getDictLabel(String dictType, String dictValue, String separator);
/**
* 根据字典类型和字典标签获取字典值
*
* @param dictType 字典类型
* @param dictLabel 字典标签
* @param separator 分隔符
* @return 字典值
*/
String getDictValue(String dictType, String dictLabel, String separator);
/**
* 获取字典下所有的字典值与标签
*
* @param dictType 字典类型
* @return dictValue为key,dictLabel为值组成的Map
*/
Map<String, String> getAllDictByDictType(String dictType);
}
package org.dromara.common.core.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
/**
* 时间工具类
*
* @author ruoyi
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
public static final String YYYY = "yyyy";
public static final String YYYY_MM = "yyyy-MM";
public static final String YYYY_MM_DD = "yyyy-MM-dd";
public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
private static final String[] PARSE_PATTERNS = {
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
/**
* 获取当前Date型日期
*
* @return Date() 当前日期
*/
public static Date getNowDate() {
return new Date();
}
/**
* 获取当前日期, 默认格式为yyyy-MM-dd
*
* @return String
*/
public static String getDate() {
return dateTimeNow(YYYY_MM_DD);
}
public static String getTime() {
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
}
public static String dateTimeNow() {
return dateTimeNow(YYYYMMDDHHMMSS);
}
public static String dateTimeNow(final String format) {
return parseDateToStr(format, new Date());
}
public static String dateTime(final Date date) {
return parseDateToStr(YYYY_MM_DD, date);
}
public static String parseDateToStr(final String format, final Date date) {
return new SimpleDateFormat(format).format(date);
}
public static Date dateTime(final String format, final String ts) {
try {
return new SimpleDateFormat(format).parse(ts);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
/**
* 日期路径 即年/月/日 如2018/08/08
*/
public static String datePath() {
Date now = new Date();
return DateFormatUtils.format(now, "yyyy/MM/dd");
}
/**
* 日期路径 即年/月/日 如20180808
*/
public static String dateTime() {
Date now = new Date();
return DateFormatUtils.format(now, "yyyyMMdd");
}
/**
* 日期型字符串转化为日期 格式
*/
public static Date parseDate(Object str) {
if (str == null) {
return null;
}
try {
return parseDate(str.toString(), PARSE_PATTERNS);
} catch (ParseException e) {
return null;
}
}
/**
* 获取服务器启动时间
*/
public static Date getServerStartDate() {
long time = ManagementFactory.getRuntimeMXBean().getStartTime();
return new Date(time);
}
/**
* 计算相差天数
*/
public static int differentDaysByMillisecond(Date date1, Date date2) {
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
}
/**
* 计算两个时间差
*/
public static String getDatePoor(Date endDate, Date nowDate) {
long nd = 1000 * 24 * 60 * 60;
long nh = 1000 * 60 * 60;
long nm = 1000 * 60;
// long ns = 1000;
// 获得两个时间的毫秒时间差异
long diff = endDate.getTime() - nowDate.getTime();
// 计算差多少天
long day = diff / nd;
// 计算差多少小时
long hour = diff % nd / nh;
// 计算差多少分钟
long min = diff % nd % nh / nm;
// 计算差多少秒//输出结果
// long sec = diff % nd % nh % nm / ns;
return day + "天" + hour + "小时" + min + "分钟";
}
/**
* 增加 LocalDateTime ==> Date
*/
public static Date toDate(LocalDateTime temporalAccessor) {
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
/**
* 增加 LocalDate ==> Date
*/
public static Date toDate(LocalDate temporalAccessor) {
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
}
package org.dromara.common.core.utils;
import org.dromara.common.core.enums.AnalysisCareerEnum;
import java.util.regex.Pattern;
public class EconomicTypeUtil {
public static Integer getEconomicType(String type) {
Integer ret = null;
if (matches(type, "机关|国有|事业") && !matches(type, "有限")) {
ret = AnalysisCareerEnum.CompanyNature.STATE_OWNED_BUSINESS.getCode();
} else if (matches(type, "集体|农民专业合作|农业专业合作") && !matches(type, "有限|事业|机关|国有")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "股份合作") && !matches(type, "有限|事业|机关|国有|集体|农民专业合作|农业专业合作")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "联营") && matches(type, "国有") && !matches(type, "集体")) {
ret = AnalysisCareerEnum.CompanyNature.STATE_OWNED_BUSINESS.getCode();
} else if (matches(type, "联营") && matches(type, "集体") && !matches(type, "国有")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "联营") && matches(type, "国有") && matches(type, "集体")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "联营") && !matches(type, "国有|集体")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "有限责任") && matches(type, "国有独资")) {
ret = AnalysisCareerEnum.CompanyNature.STATE_OWNED_BUSINESS.getCode();
} else if (matches(type, "有限责任") && !matches(type, "国有独资|港|澳|台|外商|中外|外国|自然人")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "股份有限|股份制") && !matches(type, "港|澳|台|外商|中外|外国|自然人")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "个人独资|自然人独资") && !matches(type, "港|澳|台|外商|中外|外国|责任|股份")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "私有合伙") && !matches(type, "港|澳|台|外商|中外|外国")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "自然人") && matches(type, "责任") && !matches(type, "港|澳|台|外商|中外|外国|境外")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "自然人") && matches(type, "股份") && !matches(type, "港|澳|台|外商|中外|外国|境外")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "个体|个人") && !matches(type, "港|澳|台|外商|中外|外国|独资|企业")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (matches(type, "自然人|私有") && !matches(type, "港|澳|台|外商|中外|外国|境外|独资|企业|责任|股份")) {
ret = AnalysisCareerEnum.CompanyNature.PRIVATE_ENTERPRISE.getCode();
} else if (!matches(type, "港|澳|台|外商|中外|外国|境外|独资|责任|股份|国有|机关|集体|事业|个体|个人|自然人|集体|联营|农民专业合作|农业专业合作|其他")) {
ret = AnalysisCareerEnum.CompanyNature.PUBLIC_INSTITUTION.getCode();
} else if (matches(type, "港|澳|台") && matches(type, "合资")) {
ret = AnalysisCareerEnum.CompanyNature.JOINT_VENTURE_ENTERPRISE.getCode();
} else if (matches(type, "港|澳|台") && matches(type, "合作")) {
ret = AnalysisCareerEnum.CompanyNature.GOVERNMENT_AGENCY.getCode();
} else if (matches(type, "港|澳|台") && matches(type, "独资")) {
ret = AnalysisCareerEnum.CompanyNature.GOVERNMENT_AGENCY.getCode();
} else if (matches(type, "港|澳|台") && matches(type, "股份")) {
ret = AnalysisCareerEnum.CompanyNature.GOVERNMENT_AGENCY.getCode();
} else if (matches(type, "港|澳|台") && !matches(type, "合资|合作|独资|股份")) {
ret = AnalysisCareerEnum.CompanyNature.JOINT_VENTURE_ENTERPRISE.getCode();
} else if (matches(type, "外商|外国|境外|中外") && matches(type, "合资|非独资")) {
ret = AnalysisCareerEnum.CompanyNature.JOINT_VENTURE_ENTERPRISE.getCode();
} else if (matches(type, "外商|外国|境外|中外") && matches(type, "合作")) {
ret = AnalysisCareerEnum.CompanyNature.JOINT_VENTURE_ENTERPRISE.getCode();
} else if (matches(type, "外商|外国|境外|中外") && matches(type, "独资") && !matches(type, "非独资")) {
ret = AnalysisCareerEnum.CompanyNature.OVERSEAS_FUNDED_ENTERPRISE.getCode();
} else if (matches(type, "外商|外国|境外|中外") && matches(type, "股份")) {
ret = AnalysisCareerEnum.CompanyNature.OVERSEAS_FUNDED_ENTERPRISE.getCode();
} else if (matches(type, "外商|外国|境外|中外") && !matches(type, "合资|合作|独资|股份")) {
ret = AnalysisCareerEnum.CompanyNature.OVERSEAS_FUNDED_ENTERPRISE.getCode();
} else if (!matches(type, "港|澳|台|外商|中外|外国|境外|独资|企业|责任|股份|国有|机关|集体|事业|个体|个人|自然人|集体|联营|农民专业合作|农业专业合作|港|澳|台|外商|外国|境外|其他")) {
ret = AnalysisCareerEnum.CompanyNature.SOCIAL_ORGANIZATION.getCode();
}
return ret;
}
// 用于正则匹配的辅助方法
private static boolean matches(String input, String pattern) {
if (input == null){
return false;
}
return Pattern.compile(pattern).matcher(input).find();
}
}
package org.dromara.common.core.validate;
/**
* 校验分组 edit
*
* @author Lion Li
*/
public interface EditGroup {
}
package org.dromara.common.dict.service.impl;
import com.github.benmanes.caffeine.cache.Cache;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 字典服务服务
*
* @author Lion Li
*/
@Service
public class DictServiceImpl implements DictService {
@Autowired
private Cache<Object, Object> ceffeine;
/*@DubboReference
private RemoteDictService remoteDictService;
*/
/**
* 根据字典类型和字典值获取字典标签
*
* @param dictType 字典类型
* @param dictValue 字典值
* @param separator 分隔符
* @return 字典标签
*/
@SuppressWarnings("unchecked")
@Override
public String getDictLabel(String dictType, String dictValue, String separator) {
// 优先从本地缓存获取
/*List<RemoteDictDataVo> datas = (List<RemoteDictDataVo>) ceffeine.get(CacheConstants.SYS_DICT_KEY + "remote:" + dictType, k -> {
return remoteDictService.selectDictDataByType(dictType);
});
Map<String, String> map = StreamUtils.toMap(datas, RemoteDictDataVo::getDictValue, RemoteDictDataVo::getDictLabel);
if (StringUtils.containsAny(dictValue, separator)) {
return Arrays.stream(dictValue.split(separator))
.map(v -> map.getOrDefault(v, StringUtils.EMPTY))
.collect(Collectors.joining(separator));
} else {
return map.getOrDefault(dictValue, StringUtils.EMPTY);
}*/
return null;
}
/**
* 根据字典类型和字典标签获取字典值
*
* @param dictType 字典类型
* @param dictLabel 字典标签
* @param separator 分隔符
* @return 字典值
*/
@SuppressWarnings("unchecked")
@Override
public String getDictValue(String dictType, String dictLabel, String separator) {
// 优先从本地缓存获取
/*List<RemoteDictDataVo> datas = (List<RemoteDictDataVo>) ceffeine.get(CacheConstants.SYS_DICT_KEY + "remote:" + dictType, k -> {
return remoteDictService.selectDictDataByType(dictType);
});*/
/*Map<String, String> map = StreamUtils.toMap(datas, RemoteDictDataVo::getDictLabel, RemoteDictDataVo::getDictValue);
if (StringUtils.containsAny(dictLabel, separator)) {
return Arrays.stream(dictLabel.split(separator))
.map(l -> map.getOrDefault(l, StringUtils.EMPTY))
.collect(Collectors.joining(separator));
} else {
return map.getOrDefault(dictLabel, StringUtils.EMPTY);
}*/
return null;
}
@Override
public Map<String, String> getAllDictByDictType(String dictType) {
/*List<RemoteDictDataVo> list = remoteDictService.selectDictDataByType(dictType);
return StreamUtils.toMap(list, RemoteDictDataVo::getDictValue, RemoteDictDataVo::getDictLabel);*/
return null;
}
}
package org.dromara.common.dict.utils;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.redis.utils.CacheUtils;
import java.util.List;
/**
* 字典工具类
*
* @author ruoyi
*/
public class DictUtils {
/**
* 设置字典缓存
*
* @param key 参数键
* @param dictDatas 字典数据列表
*/
/*public static void setDictCache(String key, List<RemoteDictDataVo> dictDatas) {
CacheUtils.put(CacheNames.SYS_DICT, key, dictDatas);
}*/
/**
* 获取字典缓存
*
* @param key 参数键
* @return dictDatas 字典数据列表
*/
/*public static List<RemoteDictDataVo> getDictCache(String key) {
return CacheUtils.get(CacheNames.SYS_DICT, key);
}*/
/**
* 删除指定字典缓存
*
* @param key 字典键
*/
public static void removeDictCache(String key) {
CacheUtils.evict(CacheNames.SYS_DICT, key);
}
/**
* 清空字典缓存
*/
public static void clearDictCache() {
CacheUtils.clear(CacheNames.SYS_DICT);
}
}
package org.dromara.common.dubbo.config;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.core.Ordered;
import java.net.Inet6Address;
import java.net.InetAddress;
/**
* dubbo自定义IP注入(避免IP不正确问题)
*
* @author Lion Li
*/
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
/**
* 获取该 BeanFactoryPostProcessor 的顺序,确保它在容器初始化过程中具有最高优先级
*
* @return 优先级顺序值,越小优先级越高
*/
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
/**
* 在 Spring 容器初始化过程中对 Bean 工厂进行后置处理
*
* @param beanFactory 可配置的 Bean 工厂
* @throws BeansException 如果在处理过程中发生错误
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String property = System.getProperty("DUBBO_IP_TO_REGISTRY");
if (StringUtils.isNotBlank(property)) {
return;
}
// 获取 InetUtils bean,用于获取 IP 地址
InetUtils inetUtils = beanFactory.getBean(InetUtils.class);
String ip = "127.0.0.1";
// 获取第一个非回环地址
InetAddress address = inetUtils.findFirstNonLoopbackAddress();
if (address != null) {
if (address instanceof Inet6Address) {
// 处理 IPv6 地址
String ipv6AddressString = address.getHostAddress();
if (ipv6AddressString.contains("%")) {
// 去掉可能存在的范围 ID
ipv6AddressString = ipv6AddressString.substring(0, ipv6AddressString.indexOf("%"));
}
ip = ipv6AddressString;
} else {
// 处理 IPv4 地址
ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
}
}
// 设置系统属性 DUBBO_IP_TO_REGISTRY 为获取到的 IP 地址
System.setProperty("DUBBO_IP_TO_REGISTRY", ip);
}
}
package org.dromara.common.dubbo.config;
import org.dromara.common.core.factory.YmlPropertySourceFactory;
import org.dromara.common.dubbo.properties.DubboCustomProperties;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
/**
* dubbo 配置类
*/
@AutoConfiguration
@EnableConfigurationProperties(DubboCustomProperties.class)
@PropertySource(value = "classpath:common-dubbo.yml", factory = YmlPropertySourceFactory.class)
public class DubboConfiguration {
/**
* dubbo自定义IP注入(避免IP不正确问题)
*/
@Bean
public BeanFactoryPostProcessor customBeanFactoryPostProcessor() {
return new CustomBeanFactoryPostProcessor();
}
}
package org.dromara.common.dubbo.filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.service.GenericService;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.dubbo.enumd.RequestLogEnum;
import org.dromara.common.dubbo.properties.DubboCustomProperties;
import org.dromara.common.json.utils.JsonUtils;
/**
* Dubbo 日志过滤器
* <p>
* 该过滤器通过实现 Dubbo 的 Filter 接口,在服务调用前后记录日志信息
* 可根据配置开关和日志级别输出不同详细程度的日志信息
* <p>
* 激活条件:
* - 在 Provider 和 Consumer 端都生效
* - 执行顺序设置为最大值,确保在所有其他过滤器之后执行
* <p>
* 使用 SpringUtils 获取配置信息,根据配置决定是否记录日志及日志详细程度
* <p>
* 使用 Lombok 的 @Slf4j 注解简化日志记录
*
* @author Lion Li
*/
@Slf4j
@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER}, order = Integer.MAX_VALUE)
public class DubboRequestFilter implements Filter {
/**
* Dubbo Filter 接口实现方法,处理服务调用逻辑并记录日志
*
* @param invoker Dubbo 服务调用者实例
* @param invocation 调用的具体方法信息
* @return 调用结果
* @throws RpcException 如果调用过程中发生异常
*/
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
DubboCustomProperties properties = SpringUtils.getBean(DubboCustomProperties.class);
// 如果未开启请求日志记录,则直接执行服务调用并返回结果
if (!properties.getRequestLog()) {
return invoker.invoke(invocation);
}
// 判断是 Provider 还是 Consumer
String client = CommonConstants.PROVIDER;
if (RpcContext.getServiceContext().isConsumerSide()) {
client = CommonConstants.CONSUMER;
}
// 构建基础日志信息
String baselog = "Client[" + client + "],InterfaceName=[" + invocation.getInvoker().getInterface().getSimpleName() + "],MethodName=[" + invocation.getMethodName() + "]";
// 根据日志级别输出不同详细程度的日志信息
if (properties.getLogLevel() == RequestLogEnum.INFO) {
log.info("DUBBO - 服务调用: {}", baselog);
} else {
log.info("DUBBO - 服务调用: {},Parameter={}", baselog, invocation.getArguments());
}
// 记录调用开始时间
long startTime = System.currentTimeMillis();
// 执行接口调用逻辑
Result result = invoker.invoke(invocation);
// 计算调用耗时
long elapsed = System.currentTimeMillis() - startTime;
// 如果发生异常且调用的不是泛化服务,则记录异常日志
if (result.hasException() && !invoker.getInterface().equals(GenericService.class)) {
log.error("DUBBO - 服务异常: {},Exception={}", baselog, result.getException());
} else {
// 根据日志级别输出服务响应信息
if (properties.getLogLevel() == RequestLogEnum.INFO) {
log.info("DUBBO - 服务响应: {},SpendTime=[{}ms]", baselog, elapsed);
} else if (properties.getLogLevel() == RequestLogEnum.FULL) {
log.info("DUBBO - 服务响应: {},SpendTime=[{}ms],Response={}", baselog, elapsed, JsonUtils.toJsonString(new Object[]{result.getValue()}));
}
}
return result;
}
}
package org.dromara.common.dubbo.properties;
import lombok.Data;
import org.dromara.common.dubbo.enumd.RequestLogEnum;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
/**
* 自定义配置
*
* @author Lion Li
*/
@Data
@RefreshScope
@ConfigurationProperties(prefix = "dubbo.custom")
public class DubboCustomProperties {
/**
* 是否开启请求日志记录
*/
private Boolean requestLog;
/**
* 日志级别
*/
private RequestLogEnum logLevel;
}
# 内置配置 不允许修改 如需修改请在 nacos 上写相同配置覆盖
dubbo:
application:
logger: slf4j
# 元数据中心 local 本地 remote 远程 这里使用远程便于其他服务获取
metadataType: remote
# 可选值 interface、instance、all,默认是 all,即接口级地址、应用级地址都注册
register-mode: instance
service-discovery:
# FORCE_INTERFACE,只消费接口级地址,如无地址则报错,单订阅 2.x 地址
# APPLICATION_FIRST,智能决策接口级/应用级地址,双订阅
# FORCE_APPLICATION,只消费应用级地址,如无地址则报错,单订阅 3.x 地址
migration: FORCE_APPLICATION
# 注册中心配置
registry:
address: nacos://${spring.cloud.nacos.server-addr}
group: DUBBO_GROUP
username: ${spring.cloud.nacos.username}
password: ${spring.cloud.nacos.password}
parameters:
namespace: ${spring.profiles.active}
metadata-report:
address: redis://${spring.data.redis.host}:${spring.data.redis.port}
group: DUBBO_GROUP
username: dubbo
password: ${spring.data.redis.password}
# 集群开关
cluster: false
parameters:
namespace: ${spring.profiles.active}
database: ${spring.data.redis.database}
timeout: ${spring.data.redis.timeout}
# 集群地址 cluster 为 true 生效
backup: 127.0.0.1:6379,127.0.0.1:6381
# 消费者相关配置
consumer:
# 结果缓存(LRU算法)
# 会有数据不一致问题 建议在注解局部开启
cache: false
# 支持校验注解
validation: jvalidationNew
# 调用重试 不包括第一次 0为不需要重试
retries: 0
# 初始化检查
check: true
package org.dromara.common.encrypt.enumd;
/**
* 编码类型
*
* @author 老马
* @version 4.6.0
*/
public enum EncodeType {
/**
* 默认使用yml配置
*/
DEFAULT,
/**
* base64编码
*/
BASE64,
/**
* 16进制编码
*/
HEX;
}
package org.dromara.common.encrypt.filter;
import cn.hutool.core.util.ObjectUtil;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.annotation.ApiEncrypt;
import org.dromara.common.encrypt.properties.ApiDecryptProperties;
import org.springframework.http.HttpMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.io.IOException;
/**
* Crypto 过滤器
*
* @author wdhcr
*/
public class CryptoFilter implements Filter {
private final ApiDecryptProperties properties;
public CryptoFilter(ApiDecryptProperties properties) {
this.properties = properties;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletResponse servletResponse = (HttpServletResponse) response;
// 获取加密注解
ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest);
boolean responseFlag = apiEncrypt != null && apiEncrypt.response();
ServletRequest requestWrapper = null;
ServletResponse responseWrapper = null;
EncryptResponseBodyWrapper responseBodyWrapper = null;
// 是否为 put 或者 post 请求
if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) {
// 是否存在加密标头
String headerValue = servletRequest.getHeader(properties.getHeaderFlag());
if (StringUtils.isNotBlank(headerValue)) {
// 请求解密
requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());
} else {
// 是否有注解,有就报错,没有放行
if (ObjectUtil.isNotNull(apiEncrypt)) {
HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
exceptionResolver.resolveException(
servletRequest, servletResponse, null,
new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN));
return;
}
}
}
// 判断是否响应加密
if (responseFlag) {
responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse);
responseWrapper = responseBodyWrapper;
}
chain.doFilter(
ObjectUtil.defaultIfNull(requestWrapper, request),
ObjectUtil.defaultIfNull(responseWrapper, response));
if (responseFlag) {
servletResponse.reset();
// 对原始内容加密
String encryptContent = responseBodyWrapper.getEncryptContent(
servletResponse, properties.getPublicKey(), properties.getHeaderFlag());
// 对加密后的内容写出
servletResponse.getWriter().write(encryptContent);
}
}
/**
* 获取 ApiEncrypt 注解
*/
private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) {
RequestMappingHandlerMapping handlerMapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
// 获取注解
try {
HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest);
if (ObjectUtil.isNotNull(mappingHandler)) {
Object handler = mappingHandler.getHandler();
if (ObjectUtil.isNotNull(handler)) {
// 从handler获取注解
if (handler instanceof HandlerMethod handlerMethod) {
return handlerMethod.getMethodAnnotation(ApiEncrypt.class);
}
}
}
} catch (Exception e) {
return null;
}
return null;
}
@Override
public void destroy() {
}
}
package org.dromara.common.encrypt.filter;
import cn.hutool.core.io.IoUtil;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.encrypt.utils.EncryptUtils;
import org.springframework.http.MediaType;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* 解密请求参数工具类
*
* @author wdhcr
*/
public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public DecryptRequestBodyWrapper(HttpServletRequest request, String privateKey, String headerFlag) throws IOException {
super(request);
// 获取 AES 密码 采用 RSA 加密
String headerRsa = request.getHeader(headerFlag);
String decryptAes = EncryptUtils.decryptByRsa(headerRsa, privateKey);
// 解密 AES 密码
String aesPassword = EncryptUtils.decryptByBase64(decryptAes);
request.setCharacterEncoding(Constants.UTF8);
byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false);
String requestBody = new String(readBytes, StandardCharsets.UTF_8);
// 解密 body 采用 AES 加密
String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword);
body = decryptBody.getBytes(StandardCharsets.UTF_8);
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public int getContentLength() {
return body.length;
}
@Override
public long getContentLengthLong() {
return body.length;
}
@Override
public String getContentType() {
return MediaType.APPLICATION_JSON_VALUE;
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() {
return bais.read();
}
@Override
public int available() {
return body.length;
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
package org.dromara.common.excel.annotation;
import org.dromara.common.excel.core.CellMergeStrategy;
import java.lang.annotation.*;
/**
* excel 列单元格合并(合并列相同项)
*
* 需搭配 {@link CellMergeStrategy} 策略使用
*
* @author Lion Li
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface CellMerge {
/**
* col index
*/
int index() default -1;
/**
* 合并需要依赖的其他字段名称
*/
String[] mergeBy() default {};
}
package org.dromara.common.excel.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.WorkbookWriteHandler;
import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.excel.annotation.CellMerge;
import java.lang.reflect.Field;
import java.util.*;
/**
* 列值重复合并策略
*
* @author Lion Li
*/
@Slf4j
public class CellMergeStrategy extends AbstractMergeStrategy implements WorkbookWriteHandler {
private final List<CellRangeAddress> cellList;
private final boolean hasTitle;
private int rowIndex;
public CellMergeStrategy(List<?> list, boolean hasTitle) {
this.hasTitle = hasTitle;
// 行合并开始下标
this.rowIndex = hasTitle ? 1 : 0;
this.cellList = handle(list, hasTitle);
}
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
//单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空
final int rowIndex = cell.getRowIndex();
if (CollUtil.isNotEmpty(cellList)){
for (CellRangeAddress cellAddresses : cellList) {
final int firstRow = cellAddresses.getFirstRow();
if (cellAddresses.isInRange(cell) && rowIndex != firstRow){
cell.setBlank();
}
}
}
}
@Override
public void afterWorkbookDispose(final WorkbookWriteHandlerContext context) {
//当前表格写完后,统一写入
if (CollUtil.isNotEmpty(cellList)){
for (CellRangeAddress item : cellList) {
context.getWriteContext().writeSheetHolder().getSheet().addMergedRegion(item);
}
}
}
@SneakyThrows
private List<CellRangeAddress> handle(List<?> list, boolean hasTitle) {
List<CellRangeAddress> cellList = new ArrayList<>();
if (CollUtil.isEmpty(list)) {
return cellList;
}
Field[] fields = ReflectUtils.getFields(list.get(0).getClass(), field -> !"serialVersionUID".equals(field.getName()));
// 有注解的字段
List<Field> mergeFields = new ArrayList<>();
List<Integer> mergeFieldsIndex = new ArrayList<>();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.isAnnotationPresent(CellMerge.class)) {
CellMerge cm = field.getAnnotation(CellMerge.class);
mergeFields.add(field);
mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index());
if (hasTitle) {
ExcelProperty property = field.getAnnotation(ExcelProperty.class);
rowIndex = Math.max(rowIndex, property.value().length);
}
}
}
Map<Field, RepeatCell> map = new HashMap<>();
// 生成两两合并单元格
for (int i = 0; i < list.size(); i++) {
for (int j = 0; j < mergeFields.size(); j++) {
Field field = mergeFields.get(j);
Object val = ReflectUtils.invokeGetter(list.get(i), field.getName());
int colNum = mergeFieldsIndex.get(j);
if (!map.containsKey(field)) {
map.put(field, new RepeatCell(val, i));
} else {
RepeatCell repeatCell = map.get(field);
Object cellValue = repeatCell.getValue();
if (cellValue == null || "".equals(cellValue)) {
// 空值跳过不合并
continue;
}
if (!cellValue.equals(val)) {
if ((i - repeatCell.getCurrent() > 1)) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
map.put(field, new RepeatCell(val, i));
} else if (i == list.size() - 1) {
if (i > repeatCell.getCurrent() && isMerge(list, i, field)) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
}
} else if (!isMerge(list, i, field)) {
if ((i - repeatCell.getCurrent() > 1)) {
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
}
map.put(field, new RepeatCell(val, i));
}
}
}
}
return cellList;
}
private boolean isMerge(List<?> list, int i, Field field) {
boolean isMerge = true;
CellMerge cm = field.getAnnotation(CellMerge.class);
final String[] mergeBy = cm.mergeBy();
if (StrUtil.isAllNotBlank(mergeBy)) {
//比对当前list(i)和list(i - 1)的各个属性值一一比对 如果全为真 则为真
for (String fieldName : mergeBy) {
final Object valCurrent = ReflectUtil.getFieldValue(list.get(i), fieldName);
final Object valPre = ReflectUtil.getFieldValue(list.get(i - 1), fieldName);
if (!Objects.equals(valPre, valCurrent)) {
//依赖字段如有任一不等值,则标记为不可合并
isMerge = false;
}
}
}
return isMerge;
}
@Data
@AllArgsConstructor
static class RepeatCell {
private Object value;
private int current;
}
}
package org.dromara.common.excel.core;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.Set;
/**
* Excel 导入监听
*
* @author Yjoioooo
* @author Lion Li
*/
@Slf4j
@NoArgsConstructor
public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements ExcelListener<T> {
/**
* 是否Validator检验,默认为是
*/
private Boolean isValidate = Boolean.TRUE;
/**
* excel 表头数据
*/
private Map<Integer, String> headMap;
/**
* 导入回执
*/
private ExcelResult<T> excelResult;
public DefaultExcelListener(boolean isValidate) {
this.excelResult = new DefaultExcelResult<>();
this.isValidate = isValidate;
}
/**
* 处理异常
*
* @param exception ExcelDataConvertException
* @param context Excel 上下文
*/
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
String errMsg = null;
if (exception instanceof ExcelDataConvertException excelDataConvertException) {
// 如果是某一个单元格的转换异常 能获取到具体行号
Integer rowIndex = excelDataConvertException.getRowIndex();
Integer columnIndex = excelDataConvertException.getColumnIndex();
errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常<br/>",
rowIndex + 1, columnIndex + 1, headMap.get(columnIndex));
if (log.isDebugEnabled()) {
log.error(errMsg);
}
}
if (exception instanceof ConstraintViolationException constraintViolationException) {
Set<ConstraintViolation<?>> constraintViolations = constraintViolationException.getConstraintViolations();
String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, ", ");
errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg);
if (log.isDebugEnabled()) {
log.error(errMsg);
}
}
excelResult.getErrorList().add(errMsg);
throw new ExcelAnalysisException(errMsg);
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headMap = headMap;
log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap));
}
@Override
public void invoke(T data, AnalysisContext context) {
if (isValidate) {
ValidatorUtils.validate(data);
}
excelResult.getList().add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.debug("所有数据解析完成!");
}
@Override
public ExcelResult<T> getExcelResult() {
return excelResult;
}
}
package org.dromara.common.excel.core;
import cn.hutool.core.util.StrUtil;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
/**
* 默认excel返回对象
*
* @author Yjoioooo
* @author Lion Li
*/
public class DefaultExcelResult<T> implements ExcelResult<T> {
/**
* 数据对象list
*/
@Setter
private List<T> list;
/**
* 错误信息列表
*/
@Setter
private List<String> errorList;
public DefaultExcelResult() {
this.list = new ArrayList<>();
this.errorList = new ArrayList<>();
}
public DefaultExcelResult(List<T> list, List<String> errorList) {
this.list = list;
this.errorList = errorList;
}
public DefaultExcelResult(ExcelResult<T> excelResult) {
this.list = excelResult.getList();
this.errorList = excelResult.getErrorList();
}
@Override
public List<T> getList() {
return list;
}
@Override
public List<String> getErrorList() {
return errorList;
}
/**
* 获取导入回执
*
* @return 导入回执
*/
@Override
public String getAnalysis() {
int successCount = list.size();
int errorCount = errorList.size();
if (successCount == 0) {
return "读取失败,未解析到数据";
} else {
if (errorCount == 0) {
return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount);
} else {
return "";
}
}
}
}
package org.dromara.common.excel.core;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* <h1>Excel下拉可选项</h1>
* 注意:为确保下拉框解析正确,传值务必使用createOptionValue()做为值的拼接
*
* @author Emil.Zhang
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuppressWarnings("unused")
public class DropDownOptions {
/**
* 一级下拉所在列index,从0开始算
*/
private int index = 0;
/**
* 二级下拉所在的index,从0开始算,不能与一级相同
*/
private int nextIndex = 0;
/**
* 一级下拉所包含的数据
*/
private List<String> options = new ArrayList<>();
/**
* 二级下拉所包含的数据Map
* <p>以每一个一级选项值为Key,每个一级选项对应的二级数据为Value</p>
*/
private Map<String, List<String>> nextOptions = new HashMap<>();
/**
* 分隔符
*/
private static final String DELIMITER = "_";
/**
* 创建只有一级的下拉选
*/
public DropDownOptions(int index, List<String> options) {
this.index = index;
this.options = options;
}
/**
* <h2>创建每个选项可选值</h2>
* <p>注意:不能以数字,特殊符号开头,选项中不可以包含任何运算符号</p>
*
* @param vars 可选值内包含的参数
* @return 合规的可选值
*/
public static String createOptionValue(Object... vars) {
StringBuilder stringBuffer = new StringBuilder();
String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$";
for (int i = 0; i < vars.length; i++) {
String var = StrUtil.trimToEmpty(String.valueOf(vars[i]));
if (!var.matches(regex)) {
throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字");
}
stringBuffer.append(var);
if (i < vars.length - 1) {
// 直至最后一个前,都以_作为切割线
stringBuffer.append(DELIMITER);
}
}
if (stringBuffer.toString().matches("^\\d_*$")) {
throw new ServiceException("禁止以数字开头");
}
return stringBuffer.toString();
}
/**
* 将处理后合理的可选值解析为原始的参数
*
* @param option 经过处理后的合理的可选项
* @return 原始的参数
*/
public static List<String> analyzeOptionValue(String option) {
return StrUtil.split(option, DELIMITER, true, true);
}
/**
* 创建级联下拉选项
*
* @param parentList 父实体可选项原始数据
* @param parentIndex 父下拉选位置
* @param sonList 子实体可选项原始数据
* @param sonIndex 子下拉选位置
* @param parentHowToGetIdFunction 父类如何获取唯一标识
* @param sonHowToGetParentIdFunction 子类如何获取父类的唯一标识
* @param howToBuildEveryOption 如何生成下拉选内容
* @return 级联下拉选项
*/
public static <T> DropDownOptions buildLinkedOptions(List<T> parentList,
int parentIndex,
List<T> sonList,
int sonIndex,
Function<T, Number> parentHowToGetIdFunction,
Function<T, Number> sonHowToGetParentIdFunction,
Function<T, String> howToBuildEveryOption) {
DropDownOptions parentLinkSonOptions = new DropDownOptions();
// 先创建父类的下拉
parentLinkSonOptions.setIndex(parentIndex);
parentLinkSonOptions.setOptions(
parentList.stream()
.map(howToBuildEveryOption)
.collect(Collectors.toList())
);
// 提取父-子级联下拉
Map<String, List<String>> sonOptions = new HashMap<>();
// 父级依据自己的ID分组
Map<Number, List<T>> parentGroupByIdMap =
parentList.stream().collect(Collectors.groupingBy(parentHowToGetIdFunction));
// 遍历每个子集,提取到Map中
sonList.forEach(everySon -> {
if (parentGroupByIdMap.containsKey(sonHowToGetParentIdFunction.apply(everySon))) {
// 找到对应的上级
T parentObj = parentGroupByIdMap.get(sonHowToGetParentIdFunction.apply(everySon)).get(0);
// 提取名称和ID作为Key
String key = howToBuildEveryOption.apply(parentObj);
// Key对应的Value
List<String> thisParentSonOptionList;
if (sonOptions.containsKey(key)) {
thisParentSonOptionList = sonOptions.get(key);
} else {
thisParentSonOptionList = new ArrayList<>();
sonOptions.put(key, thisParentSonOptionList);
}
// 往Value中添加当前子集选项
thisParentSonOptionList.add(howToBuildEveryOption.apply(everySon));
}
});
parentLinkSonOptions.setNextIndex(sonIndex);
parentLinkSonOptions.setNextOptions(sonOptions);
return parentLinkSonOptions;
}
}
package org.dromara.common.log.enums;
/**
* 业务操作类型
*
* @author ruoyi
*/
public enum BusinessType {
/**
* 其它
*/
OTHER,
/**
* 新增
*/
INSERT,
/**
* 修改
*/
UPDATE,
/**
* 删除
*/
DELETE,
/**
* 授权
*/
GRANT,
/**
* 导出
*/
EXPORT,
/**
* 导入
*/
IMPORT,
/**
* 强退
*/
FORCE,
/**
* 生成代码
*/
GENCODE,
/**
* 清空数据
*/
CLEAN,
}
package org.dromara.common.mybatis.annotation;
import java.lang.annotation.*;
/**
* 数据权限注解,用于标记数据权限的占位符关键字和替换值
* <p>
* 一个注解只能对应一个模板
* </p>
*
* @author Lion Li
* @version 3.5.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataColumn {
/**
* 数据权限模板的占位符关键字,默认为 "deptName"
*
* @return 占位符关键字数组
*/
String[] key() default "deptName";
/**
* 数据权限模板的占位符替换值,默认为 "dept_id"
*
* @return 占位符替换值数组
*/
String[] value() default "dept_id";
/**
* 权限标识符 用于通过菜单权限标识符来获取数据权限
* 拥有此标识符的角色 将不会拼接此角色的数据过滤sql
*
* @return 权限标识符
*/
String permission() default "";
}
package org.dromara.common.mybatis.annotation;
import java.lang.annotation.*;
/**
* 数据权限组注解,用于标记数据权限配置数组
*
* @author Lion Li
* @version 3.5.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {
/**
* 数据权限配置数组,用于指定数据权限的占位符关键字和替换值
*
* @return 数据权限配置数组
*/
DataColumn[] value();
/**
* 权限拼接标识符(用于指定连接语句的sql符号)
* 如不填 默认 select 用 OR 其他语句用 AND
* 内容 OR 或者 AND
*/
String joinStr() default "";
}
package org.dromara.common.mybatis.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.core.utils.StringUtils;
/**
* 数据库类型
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum DataBaseType {
/**
* MySQL
*/
MY_SQL("MySQL"),
/**
* Oracle
*/
ORACLE("Oracle"),
/**
* PostgreSQL
*/
POSTGRE_SQL("PostgreSQL"),
/**
* SQL Server
*/
SQL_SERVER("Microsoft SQL Server");
/**
* 数据库类型
*/
private final String type;
/**
* 根据数据库产品名称查找对应的数据库类型
*
* @param databaseProductName 数据库产品名称
* @return 对应的数据库类型枚举值,如果未找到则返回 null
*/
public static DataBaseType find(String databaseProductName) {
if (StringUtils.isBlank(databaseProductName)) {
return null;
}
for (DataBaseType type : values()) {
if (type.getType().equals(databaseProductName)) {
return type;
}
}
return null;
}
}
package org.dromara.common.mybatis.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
/**
* 数据权限类型枚举
* <p>
* 支持使用 SpEL 模板表达式定义 SQL 查询条件
* 内置数据:
* - {@code user}: 当前登录用户信息,参考 {@link }
* 内置服务:
* - {@code sdss}: 系统数据权限服务,参考 SysDataScopeService
* 如需扩展数据,可以通过 {@link DataPermissionHelper} 进行操作
* 如需扩展服务,可以通过 SysDataScopeService 自行编写
* </p>
*
* @author Lion Li
* @version 3.5.0
*/
@Getter
@AllArgsConstructor
public enum DataScopeType {
/**
* 全部数据权限
*/
ALL("1", "", ""),
/**
* 自定数据权限
*/
CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", " 1 = 0 "),
/**
* 部门数据权限
*/
DEPT("3", " #{#deptName} = #{#user.deptId} ", " 1 = 0 "),
/**
* 部门及以下数据权限
*/
DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", " 1 = 0 "),
/**
* 仅本人数据权限
*/
SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 ");
private final String code;
/**
* SpEL 模板表达式,用于构建 SQL 条件
*/
private final String sqlTemplate;
/**
* 如果不满足 {@code sqlTemplate} 的条件,则使用此默认 SQL 表达式
*/
private final String elseSql;
/**
* 根据枚举代码查找对应的枚举值
*
* @param code 枚举代码
* @return 对应的枚举值,如果未找到则返回 null
*/
public static DataScopeType findCode(String code) {
if (StringUtils.isBlank(code)) {
return null;
}
for (DataScopeType type : values()) {
if (type.getCode().equals(code)) {
return type;
}
}
return null;
}
}
package org.dromara.common.mybatis.filter;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import java.util.Map;
/**
* dubbo 数据权限参数传递
*
* @author Lion Li
*/
@Slf4j
@Activate(group = {CommonConstants.CONSUMER})
public class DubboDataPermissionFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
RpcServiceContext context = RpcContext.getServiceContext();
Map<String, Object> dataPermissionContext = DataPermissionHelper.getContext();
context.setObjectAttachment(DataPermissionHelper.DATA_PERMISSION_KEY, dataPermissionContext);
return invoker.invoke(invocation);
}
}
package org.dromara.common.mybatis.helper;
import cn.hutool.core.convert.Convert;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.mybatis.enums.DataBaseType;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 数据库助手
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DataBaseHelper {
private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class);
/**
* 获取当前数据库类型
*/
public static DataBaseType getDataBaseType() {
DataSource dataSource = DS.determineDataSource();
try (Connection conn = dataSource.getConnection()) {
DatabaseMetaData metaData = conn.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
return DataBaseType.find(databaseProductName);
} catch (SQLException e) {
throw new ServiceException(e.getMessage());
}
}
public static boolean isMySql() {
return DataBaseType.MY_SQL == getDataBaseType();
}
public static boolean isOracle() {
return DataBaseType.ORACLE == getDataBaseType();
}
public static boolean isPostgerSql() {
return DataBaseType.POSTGRE_SQL == getDataBaseType();
}
public static boolean isSqlServer() {
return DataBaseType.SQL_SERVER == getDataBaseType();
}
public static String findInSet(Object var1, String var2) {
DataBaseType dataBasyType = getDataBaseType();
String var = Convert.toStr(var1);
if (dataBasyType == DataBaseType.SQL_SERVER) {
// charindex(',100,' , ',0,100,101,') <> 0
return "charindex(',%s,' , ','+%s+',') <> 0".formatted(var, var2);
} else if (dataBasyType == DataBaseType.POSTGRE_SQL) {
// (select strpos(',0,100,101,' , ',100,')) <> 0
return "(select strpos(','||%s||',' , ',%s,')) <> 0".formatted(var2, var);
} else if (dataBasyType == DataBaseType.ORACLE) {
// instr(',0,100,101,' , ',100,') <> 0
return "instr(','||%s||',' , ',%s,') <> 0".formatted(var2, var);
}
// find_in_set('100' , '0,100,101')
return "find_in_set('%s' , %s) <> 0".formatted(var, var2);
}
/**
* 获取当前加载的数据库名
*/
public static List<String> getDataSourceNameList() {
return new ArrayList<>(DS.getDataSources().keySet());
}
}
package org.dromara.common.mybatis.helper;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaStorage;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.function.Supplier;
/**
* 数据权限助手
*
* @author Lion Li
* @version 3.5.0
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings("unchecked cast")
public class DataPermissionHelper {
public static final String DATA_PERMISSION_KEY = "data:permission";
private static final ThreadLocal<Stack<Integer>> REENTRANT_IGNORE = ThreadLocal.withInitial(Stack::new);
/**
* 从上下文中获取指定键的变量值,并将其转换为指定的类型
*
* @param key 变量的键
* @param <T> 变量值的类型
* @return 指定键的变量值,如果不存在则返回 null
*/
public static <T> T getVariable(String key) {
Map<String, Object> context = getContext();
return (T) context.get(key);
}
/**
* 向上下文中设置指定键的变量值
*
* @param key 要设置的变量的键
* @param value 要设置的变量值
*/
public static void setVariable(String key, Object value) {
Map<String, Object> context = getContext();
context.put(key, value);
}
/**
* 获取数据权限上下文
*
* @return 存储在SaStorage中的Map对象,用于存储数据权限相关的上下文信息
* @throws NullPointerException 如果数据权限上下文类型异常,则抛出NullPointerException
*/
public static Map<String, Object> getContext() {
SaStorage saStorage = SaHolder.getStorage();
Object attribute = saStorage.get(DATA_PERMISSION_KEY);
if (ObjectUtil.isNull(attribute)) {
saStorage.set(DATA_PERMISSION_KEY, new HashMap<>());
attribute = saStorage.get(DATA_PERMISSION_KEY);
}
if (attribute instanceof Map map) {
return map;
}
throw new NullPointerException("data permission context type exception");
}
private static IgnoreStrategy getIgnoreStrategy() {
Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL"));
if (ignoreStrategyLocal instanceof ThreadLocal<?> IGNORE_STRATEGY_LOCAL) {
if (IGNORE_STRATEGY_LOCAL.get() instanceof IgnoreStrategy ignoreStrategy) {
return ignoreStrategy;
}
}
return null;
}
/**
* 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭)
*/
public static void enableIgnore() {
IgnoreStrategy ignoreStrategy = getIgnoreStrategy();
if (ObjectUtil.isNull(ignoreStrategy)) {
InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build());
} else {
ignoreStrategy.setDataPermission(true);
}
Stack<Integer> reentrantStack = REENTRANT_IGNORE.get();
reentrantStack.push(reentrantStack.size() + 1);
}
/**
* 关闭忽略数据权限
*/
public static void disableIgnore() {
IgnoreStrategy ignoreStrategy = getIgnoreStrategy();
if (ObjectUtil.isNotNull(ignoreStrategy)) {
boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName())
&& !Boolean.TRUE.equals(ignoreStrategy.getBlockAttack())
&& !Boolean.TRUE.equals(ignoreStrategy.getIllegalSql())
&& !Boolean.TRUE.equals(ignoreStrategy.getTenantLine())
&& CollectionUtil.isEmpty(ignoreStrategy.getOthers());
Stack<Integer> reentrantStack = REENTRANT_IGNORE.get();
boolean empty = reentrantStack.isEmpty() || reentrantStack.pop() == 1;
if (noOtherIgnoreStrategy && empty) {
InterceptorIgnoreHelper.clearIgnoreStrategy();
} else if (empty) {
ignoreStrategy.setDataPermission(false);
}
}
}
/**
* 在忽略数据权限中执行
*
* @param handle 处理执行方法
*/
public static void ignore(Runnable handle) {
enableIgnore();
try {
handle.run();
} finally {
disableIgnore();
}
}
/**
* 在忽略数据权限中执行
*
* @param handle 处理执行方法
*/
public static <T> T ignore(Supplier<T> handle) {
enableIgnore();
try {
return handle.get();
} finally {
disableIgnore();
}
}
}
# 内置配置 不允许修改 如需修改请在 nacos 上写相同配置覆盖
# MyBatisPlus配置
# https://baomidou.com/config/
mybatis-plus:
# 启动时是否检查 MyBatis XML 文件的存在,默认不检查
checkConfigLocation: false
configuration:
# 自动驼峰命名规则(camel case)映射
mapUnderscoreToCamelCase: true
# MyBatis 自动映射策略
# NONE:不启用 PARTIAL:只对非嵌套 resultMap 自动映射 FULL:对所有 resultMap 自动映射
autoMappingBehavior: FULL
# MyBatis 自动映射时未知列或未知属性处理策
# NONE:不做处理 WARNING:打印相关警告 FAILING:抛出异常和详细信息
autoMappingUnknownColumnBehavior: NONE
# 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl
# 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl
# 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl
logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl
global-config:
# 是否打印 Logo banner
banner: true
dbConfig:
# 主键类型
# AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
idType: ASSIGN_ID
# 逻辑已删除值(框架表均使用此值 禁止随意修改)
logicDeleteValue: 2
# 逻辑未删除值
logicNotDeleteValue: 0
insertStrategy: NOT_NULL
updateStrategy: NOT_NULL
whereStrategy: NOT_NULL
package org.dromara.common.redis.config;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.dromara.common.redis.manager.PlusSpringCacheManager;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import java.util.concurrent.TimeUnit;
/**
* 缓存配置
*
* @author Lion Li
*/
@AutoConfiguration
@EnableCaching
public class CacheConfiguration {
/**
* caffeine 本地缓存处理器
*/
@Bean
public Cache<Object, Object> caffeine() {
return Caffeine.newBuilder()
// 设置最后一次写入或访问后经过固定时间过期
.expireAfterWrite(30, TimeUnit.SECONDS)
// 初始的缓存空间大小
.initialCapacity(100)
// 缓存的最大条数
.maximumSize(1000)
.build();
}
/**
* 自定义缓存管理器 整合spring-cache
*/
@Bean
public CacheManager cacheManager() {
return new PlusSpringCacheManager();
}
}
package org.dromara.common.redis.manager;
import org.dromara.common.core.utils.SpringUtils;
import org.springframework.cache.Cache;
import java.util.concurrent.Callable;
/**
* Cache 装饰器模式(用于扩展 Caffeine 一级缓存)
*
* @author LionLi
*/
public class CaffeineCacheDecorator implements Cache {
private static final com.github.benmanes.caffeine.cache.Cache<Object, Object>
CAFFEINE = SpringUtils.getBean("caffeine");
private final String name;
private final Cache cache;
public CaffeineCacheDecorator(String name, Cache cache) {
this.name = name;
this.cache = cache;
}
@Override
public String getName() {
return name;
}
@Override
public Object getNativeCache() {
return cache.getNativeCache();
}
public String getUniqueKey(Object key) {
return name + ":" + key;
}
@Override
public ValueWrapper get(Object key) {
Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key));
return (ValueWrapper) o;
}
@SuppressWarnings("unchecked")
@Override
public <T> T get(Object key, Class<T> type) {
Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, type));
return (T) o;
}
@Override
public void put(Object key, Object value) {
CAFFEINE.invalidate(getUniqueKey(key));
cache.put(key, value);
}
@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
CAFFEINE.invalidate(getUniqueKey(key));
return cache.putIfAbsent(key, value);
}
@Override
public void evict(Object key) {
evictIfPresent(key);
}
@Override
public boolean evictIfPresent(Object key) {
boolean b = cache.evictIfPresent(key);
if (b) {
CAFFEINE.invalidate(getUniqueKey(key));
}
return b;
}
@Override
public void clear() {
cache.clear();
}
@Override
public boolean invalidate() {
return cache.invalidate();
}
@SuppressWarnings("unchecked")
@Override
public <T> T get(Object key, Callable<T> valueLoader) {
Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, valueLoader));
return (T) o;
}
}
package org.dromara.common.redis.utils;
import org.dromara.common.core.utils.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.RMap;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import java.util.Set;
/**
* 缓存操作工具类
*
* @author Michelle.Chung
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked"})
public class CacheUtils {
private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class);
/**
* 获取缓存值
*
* @param cacheNames 缓存组名称
* @param key 缓存key
*/
public static <T> T get(String cacheNames, Object key) {
Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key);
return wrapper != null ? (T) wrapper.get() : null;
}
/**
* 保存缓存值
*
* @param cacheNames 缓存组名称
* @param key 缓存key
* @param value 缓存值
*/
public static void put(String cacheNames, Object key, Object value) {
CACHE_MANAGER.getCache(cacheNames).put(key, value);
}
/**
* 删除缓存值
*
* @param cacheNames 缓存组名称
* @param key 缓存key
*/
public static void evict(String cacheNames, Object key) {
CACHE_MANAGER.getCache(cacheNames).evict(key);
}
/**
* 清空缓存值
*
* @param cacheNames 缓存组名称
*/
public static void clear(String cacheNames) {
CACHE_MANAGER.getCache(cacheNames).clear();
}
}
# 内置配置 不允许修改 如需修改请在 nacos 上写相同配置覆盖
# Sa-Token配置
sa-token:
# 允许动态设置 token 有效期
dynamic-active-timeout: true
# 允许从 请求参数 读取 token
is-read-body: true
# 允许从 header 读取 token
is-read-header: true
# 关闭 cookie 鉴权 从根源杜绝 csrf 漏洞风险
is-read-cookie: false
# token前缀
token-prefix: "Bearer"
package org.dromara.common.translation.core.impl;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.common.translation.core.TranslationInterface;
import lombok.AllArgsConstructor;
/**
* 字典翻译实现
*
* @author Lion Li
*/
@AllArgsConstructor
@TranslationType(type = TransConstant.DICT_TYPE_TO_LABEL)
public class DictTypeTranslationImpl implements TranslationInterface<String> {
private final DictService dictService;
@Override
public String translation(Object key, String other) {
if (key instanceof String && StringUtils.isNotBlank(other)) {
return dictService.getDictLabel(other, key.toString());
}
return null;
}
}
package com.bkty.gateway.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
* 自定义gateway参数配置
*
* @author Lion Li
*/
@Data
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "spring.cloud.gateway")
public class CustomGatewayProperties {
/**
* 请求日志
*/
private Boolean requestLog;
}
package com.bkty.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
/**
* @author jiangxiaoge
* @description
* @data 2025/2/3
**/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class employmentBusinessPCGatewayApplication {
public static void main(String[] args) {
// 标记 sentinel 类型为 网关
System.setProperty("csp.sentinel.app.type", "1");
SpringApplication application = new SpringApplication(employmentBusinessPCGatewayApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
System.out.println("(♥◠‿◠)ノ゙ 网关启动成功 ლ(´ڡ`ლ)゙ ");
}
}
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds
#FROM bellsoft/liberica-openjdk-debian:21.0.3-cds
#FROM findepi/graalvm:java17-native
LABEL maintainer="Lion Li"
RUN mkdir -p /ruoyi/system/logs \
/ruoyi/system/temp \
/ruoyi/skywalking/agent
WORKDIR /ruoyi/system
ENV SERVER_PORT=9201 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
EXPOSE ${SERVER_PORT}
ADD ./target/ruoyi-system.jar ./app.jar
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
#-Dskywalking.agent.service_name=ruoyi-system \
#-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
-XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
-jar app.jar
package com.bkty.system.config;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
@Component
public class CommonAsyncThreadPool {
private ThreadPoolTaskExecutor taskThreadPool;
@PostConstruct
public void init() {
log.info("🧵 初始化通用异步线程池...");
this.taskThreadPool = new ThreadPoolTaskExecutor();
this.taskThreadPool.setCorePoolSize(8);
this.taskThreadPool.setMaxPoolSize(16);
this.taskThreadPool.setQueueCapacity(64);
this.taskThreadPool.setThreadNamePrefix("CommonAsync-");
this.taskThreadPool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 调用方执行
this.taskThreadPool.initialize();
log.info("✅ 通用异步线程池初始化成功");
}
public ThreadPoolTaskExecutor getExecutor() {
return taskThreadPool;
}
@PreDestroy
public void destroy() {
if (this.taskThreadPool != null) {
log.info("🛑 销毁通用异步线程池...");
this.taskThreadPool.shutdown();
}
}
}
\ No newline at end of file
package com.bkty.system.domain.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @author Herbert
* @description
* @data 2025/3/26
**/
@Data
public class EditPasswordDto {
@NotNull(message = "用户ID不能为空")
private Long userId;
@NotBlank(message = "旧密码不能为空")
private String password;
@NotBlank(message = "新密码不能为空")
private String newPassword;
}
package com.bkty.system.domain.vo;
import com.bkty.system.domain.entity.SysDept;
import io.github.linpeilie.annotations.AutoMapper;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 菜单权限视图对象 sys_menu
*
* @author Michelle.Chung
*/
@Data
@Schema(description = "部门管理返回类")
@AutoMapper(target = SysDept.class)
public class DeptVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 部门ID
*/
private String deptId;
/**
* 父部门ID
*/
private String parentId;
/**
* 部门名称
*/
private String deptName;
/**
* 部门类别编码
*/
private String deptCategory;
/**
* 显示顺序
*/
private Integer orderNum;
/**
* 部门状态:0正常,1停用
*/
private String status;
/**
* 祖级列表
*/
private String ancestors;
/**
* 部门别名
*/
private String deptAlias;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改时间
*/
private Date updateTime;
/**
* 子部门
*/
private List<DeptVo> children = new ArrayList<>();
}
package com.bkty.system;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 系统模块
*
* @author ruoyi
*/
@EnableDubbo
@EnableScheduling
@SpringBootApplication
public class employmentBusinessPCSystemApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(employmentBusinessPCSystemApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
System.out.println("(♥◠‿◠)ノ゙ 系统模块启动成功 ლ(´ڡ`ლ)゙ ");
}
}
package com.bkty.system.service.comm;
import org.springframework.web.multipart.MultipartFile;
/**
* @author Herbert
* @description 通用接口
* @data 2025/04/08
**/
public interface CommService {
/**
* 文件上传返回minio链接
*/
String fileUpload(MultipartFile file);
/**
* 上传文件到扣子
*/
String agentFileUpload(byte[] fileBytes,String fileName) throws Exception;
}
package com.bkty.system.service.comm.impl;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.bkty.system.config.MinioProperties;
import com.bkty.system.service.comm.CommService;
import com.bkty.system.service.coze.CozeApiService;
import org.dromara.common.core.constant.Constants;
import com.bkty.system.utils.MinioUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.CozeConstsnts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.util.UUID;
/**
* @author Herbert
* @description 通用接口
* @data 2025/04/08
**/
@Slf4j
@RequiredArgsConstructor
@Service
public class CommServiceImpl implements CommService {
private final MinioUtils minioUtils;
private final MinioProperties minioTemplate;
private final CozeApiService cozeApiService;
@Override
public String fileUpload(MultipartFile file) {
try {
// 获取文件后缀
String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.') + 1);
String fileName = UUID.randomUUID() + "." + suffix;
String contentType = minioUtils.getContentTypeBySuffix(suffix);
minioUtils.putObjectAudio(minioTemplate.getBucket(), "file/" + fileName, file.getInputStream(), contentType);
String avatarPath = minioTemplate.getUrl() + "/" + minioTemplate.getBucket() + "/file/" + fileName;
return avatarPath;
} catch (Exception e) {
log.error("上传头像或文件失败", e);
throw new RuntimeException("文件上传失败:" + e.getMessage());
}
}
@Override
public String agentFileUpload(byte[] fileBytes,String fileName) throws Exception {
String url = Constants.HTTPS + CozeConstsnts.COZE_CN_API_DOMAIN_NAME + "/v1/files/upload";
String cozeToken = cozeApiService.getCozeTokenCn("CN");
// 发送 multipart/form-data 请求
String response = HttpRequest.post(url)
.header(HttpHeaders.AUTHORIZATION, CozeConstsnts.COZE_TOKEN_BEARER + cozeToken)
.form("file", fileBytes, fileName) // Hutool 自动识别文件内容和文件名
.execute().body();
JSONObject jsonObject = JSON.parseObject(response);
if (jsonObject.getInteger("code") == 0) {
JSONObject data = jsonObject.getJSONObject("data");
String fileId = data.getString("id");
return fileId;
}
return "";
}
}
package com.bkty.system.service.coze;
/**
* @author jiangxiaoge
* @description CozeApiService
* @data 2024/12/12
**/
public interface CozeApiService {
/**
* 获取cozeToken国内
* @return
*/
String getCozeTokenCn(String cozeSource) throws Exception;
}
package com.bkty.system.service.coze.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONReader;
import com.bkty.system.service.coze.CozeApiService;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.CozeConstsnts;
import org.dromara.common.core.exception.WarnException;
import org.dromara.common.core.utils.AESUtil;
import org.dromara.common.core.utils.JWTGenerator;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.TimeTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.netty.http.client.HttpClient;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @author jiangxiaoge
* @description
* @data 2024/12/12
**/
@Slf4j
@Service
public class CozeApiServiceImpl implements CozeApiService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
@Qualifier("nonLoadBalancedRestTemplate")
private RestTemplate restTemplate;
@Override
public String getCozeTokenCn(String cozeSource) throws Exception {
String redisKey = CacheConstants.REDIS_CN_COZE_TOKEN_KEY;
String publicKey = CozeConstsnts.COZE_CN_OAUTH_PUBLIC_KEY_STR;
String oauthId = CozeConstsnts.COZE_CN_OAUTH_ID;
String apiDomainName = CozeConstsnts.COZE_CN_API_DOMAIN_NAME;
String privateKey = CozeConstsnts.COZE_CN_OAUTH_PRIVATE_KEY_STR;
if (Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))){
return redisTemplate.opsForValue().get(redisKey);
}
Map<String, Object> headerMap = Map.of("alg", "RS256",
"typ", "JWT",
"kid", publicKey);
//String headerString = Base64.getEncoder().encodeToString(JSON.toJSONString(headerMap).getBytes());
long iat = TimeTool.nowMilli();
long expiredTime = TimeTool.nowMilli() + TimeUnit.MINUTES.toMillis(10);
Map<String, Object> payloadMap = Map.of("iss", oauthId,
"aud", apiDomainName,
"iat", iat,
"exp", expiredTime,
"jti", UUID.randomUUID());
String payloadString = JSON.toJSONString(payloadMap);
String jwtString = JWTGenerator.generatorJwtString(headerMap, payloadString, privateKey);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set(HttpHeaders.AUTHORIZATION, CozeConstsnts.COZE_TOKEN_BEARER + jwtString);
// 创建请求体(示例 JSON 数据)
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("duration_seconds", 86399);
requestBody.put("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
// 发送 POST 请求并获取响应
ResponseEntity<String> response = restTemplate.exchange(
Constants.HTTPS + apiDomainName + "/api/permission/oauth2/token", HttpMethod.POST, requestEntity, String.class);
log.info("测试1请求token:{}", response.getBody());
com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(response.getBody());
String accessToken;
if (jsonObject != null) {
accessToken = jsonObject.getString("access_token");
redisTemplate.opsForValue().set(redisKey, accessToken, 86399 - 60, TimeUnit.SECONDS);
return accessToken;
}
throw new WarnException("获取coze令牌失败");
}
}
/*
* 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.config;
import com.alibaba.nacos.console.filter.XssFilter;
import com.alibaba.nacos.core.code.ControllerMethodsCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import javax.annotation.PostConstruct;
import java.time.ZoneId;
/**
* Console config.
*
* @author yshen
* @author nkorange
* @since 1.2.0
*/
@Component
@EnableScheduling
@PropertySource("/application.properties")
public class ConsoleConfig {
@Autowired
private ControllerMethodsCache methodsCache;
@Value("${nacos.console.ui.enabled:true}")
private boolean consoleUiEnabled;
/**
* Init.
*/
@PostConstruct
public void init() {
methodsCache.initClassMethod("com.alibaba.nacos.core.controller");
methodsCache.initClassMethod("com.alibaba.nacos.naming.controllers");
methodsCache.initClassMethod("com.alibaba.nacos.config.server.controller");
methodsCache.initClassMethod("com.alibaba.nacos.console.controller");
}
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedHeader("*");
config.setMaxAge(18000L);
config.addAllowedMethod("*");
config.addAllowedOriginPattern("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
@Bean
public XssFilter xssFilter() {
return new XssFilter();
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(ZoneId.systemDefault().toString());
}
public boolean isConsoleUiEnabled() {
return consoleUiEnabled;
}
}
/*
* Copyright 1999-2023 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.config;
import com.alibaba.nacos.sys.module.ModuleState;
import com.alibaba.nacos.sys.module.ModuleStateBuilder;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
/**
* Console module state builder.
*
* @author xiweng.yy
*/
public class ConsoleModuleStateBuilder implements ModuleStateBuilder {
public static final String CONSOLE_MODULE = "console";
private static final String CONSOLE_UI_ENABLED = "console_ui_enabled";
@Override
public ModuleState build() {
ModuleState result = new ModuleState(CONSOLE_MODULE);
try {
ConsoleConfig consoleConfig = ApplicationUtils.getBean(ConsoleConfig.class);
result.newState(CONSOLE_UI_ENABLED, consoleConfig.isConsoleUiEnabled());
} catch (Exception ignored) {
}
return result;
}
}
/*
* 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.exception;
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
import com.alibaba.nacos.common.model.RestResultUtils;
import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.core.utils.Commons;
import com.alibaba.nacos.plugin.auth.exception.AccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.util.HtmlUtils;
import javax.servlet.http.HttpServletRequest;
/**
* Exception handler for console module.
*
* @author nkorange
* @since 1.2.0
*/
@ControllerAdvice
public class ConsoleExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleExceptionHandler.class);
@ExceptionHandler(AccessException.class)
private ResponseEntity<String> handleAccessException(AccessException e) {
LOGGER.error("got exception. {}", e.getErrMsg());
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg());
}
@ExceptionHandler(IllegalArgumentException.class)
private ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ExceptionUtil.getAllExceptionMsg(e));
}
@ExceptionHandler(NacosRuntimeException.class)
private ResponseEntity<String> handleNacosRuntimeException(NacosRuntimeException e) {
LOGGER.error("got exception. {}", e.getMessage());
return ResponseEntity.status(e.getErrCode()).body(ExceptionUtil.getAllExceptionMsg(e));
}
@ExceptionHandler(Exception.class)
private ResponseEntity<Object> handleException(HttpServletRequest request, Exception e) {
String uri = request.getRequestURI();
LOGGER.error("CONSOLE {}", uri, e);
if (uri.contains(Commons.NACOS_SERVER_VERSION_V2)) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(RestResultUtils.failed(HtmlUtils.htmlEscape(ExceptionUtil.getAllExceptionMsg(e), "utf-8")));
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(HtmlUtils.htmlEscape(ExceptionUtil.getAllExceptionMsg(e), "utf-8"));
}
}
/*
* Copyright 1999-2023 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.paramcheck;
import com.alibaba.nacos.common.paramcheck.ParamInfo;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
/**
* Console default http param extractor.
*
* @author zhuoguang
*/
public class ConsoleDefaultHttpParamExtractor extends AbstractHttpParamExtractor {
@Override
public List<ParamInfo> extractParam(HttpServletRequest request) {
ParamInfo paramInfo = new ParamInfo();
paramInfo.setNamespaceId(getAliasNamespaceId(request));
paramInfo.setNamespaceShowName(getAliasNamespaceShowName(request));
ArrayList<ParamInfo> paramInfos = new ArrayList<>();
paramInfos.add(paramInfo);
return paramInfos;
}
private String getAliasNamespaceId(HttpServletRequest request) {
String namespaceId = request.getParameter("namespaceId");
if (StringUtils.isBlank(namespaceId)) {
namespaceId = request.getParameter("customNamespaceId");
}
return namespaceId;
}
private String getAliasNamespaceShowName(HttpServletRequest request) {
String namespaceShowName = request.getParameter("namespaceName");
return namespaceShowName;
}
}
#
# Copyright 1999-2023 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.
#
com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor
\ No newline at end of file
#
# Copyright 1999-2023 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.
#
com.alibaba.nacos.console.config.ConsoleModuleStateBuilder
/*
* 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.
*/
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
color: black;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
.CodeMirror-guttermarker { color: black; }
.CodeMirror-guttermarker-subtle { color: #999; }
/* CURSOR */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
background: #7e7;
}
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-moz-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@-webkit-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror-overwrite .CodeMirror-cursor {}
.cm-tab { display: inline-block; text-decoration: inherit; }
.CodeMirror-rulers {
position: absolute;
left: 0; right: 0; top: -50px; bottom: -20px;
overflow: hidden;
}
.CodeMirror-ruler {
border-left: 1px solid #ccc;
top: 0; bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
.CodeMirror-composing { border-bottom: 2px solid; }
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
.CodeMirror-scroll {
overflow: scroll !important; /* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
min-height: 100%;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
}
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
.CodeMirror-gutter-background {
position: absolute;
top: 0; bottom: 0;
z-index: 4;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
.CodeMirror-lines {
cursor: text;
min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
.CodeMirror-widget {}
.CodeMirror-rtl pre { direction: rtl; }
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
.CodeMirror-measure pre { position: static; }
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
div.CodeMirror-dragcursors {
visibility: visible;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }
/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }
/*
* 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.
*/
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineOption("fullScreen", false, function(cm, val, old) {
if (old == CodeMirror.Init) old = false;
if (!old == !val) return;
if (val) setFullscreen(cm);
else setNormal(cm);
});
function setFullscreen(cm) {
var wrap = cm.getWrapperElement();
cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
width: wrap.style.width, height: wrap.style.height};
wrap.style.width = "";
wrap.style.height = "auto";
wrap.className += " CodeMirror-fullscreen";
document.documentElement.style.overflow = "hidden";
cm.refresh();
}
function setNormal(cm) {
var wrap = cm.getWrapperElement();
wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
document.documentElement.style.overflow = "";
var info = cm.state.fullScreenRestore;
wrap.style.width = info.width; wrap.style.height = info.height;
window.scrollTo(info.scrollLeft, info.scrollTop);
cm.refresh();
}
});
/*
* 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.
*/
// Depends on jsonlint.js from https://github.com/zaach/jsonlint
// declare global: jsonlint
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("lint", "json", function(text) {
var found = [];
jsonlint.parseError = function(str, hash) {
var loc = hash.loc;
found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: str});
};
try { jsonlint.parse(text); }
catch(e) {}
return found;
});
});
/*
* 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.
*/
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var GUTTER_ID = "CodeMirror-lint-markers";
function showTooltip(e, content) {
var tt = document.createElement("div");
tt.className = "CodeMirror-lint-tooltip";
tt.appendChild(content.cloneNode(true));
document.body.appendChild(tt);
function position(e) {
if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
tt.style.left = (e.clientX + 5) + "px";
}
CodeMirror.on(document, "mousemove", position);
position(e);
if (tt.style.opacity != null) tt.style.opacity = 1;
return tt;
}
function rm(elt) {
if (elt.parentNode) elt.parentNode.removeChild(elt);
}
function hideTooltip(tt) {
if (!tt.parentNode) return;
if (tt.style.opacity == null) rm(tt);
tt.style.opacity = 0;
setTimeout(function() { rm(tt); }, 600);
}
function showTooltipFor(e, content, node) {
var tooltip = showTooltip(e, content);
function hide() {
CodeMirror.off(node, "mouseout", hide);
if (tooltip) { hideTooltip(tooltip); tooltip = null; }
}
var poll = setInterval(function() {
if (tooltip) for (var n = node;; n = n.parentNode) {
if (n && n.nodeType == 11) n = n.host;
if (n == document.body) return;
if (!n) { hide(); break; }
}
if (!tooltip) return clearInterval(poll);
}, 400);
CodeMirror.on(node, "mouseout", hide);
}
function LintState(cm, options, hasGutter) {
this.marked = [];
this.options = options;
this.timeout = null;
this.hasGutter = hasGutter;
this.onMouseOver = function(e) { onMouseOver(cm, e); };
this.waitingFor = 0
}
function parseOptions(_cm, options) {
if (options instanceof Function) return {getAnnotations: options};
if (!options || options === true) options = {};
return options;
}
function clearMarks(cm) {
var state = cm.state.lint;
if (state.hasGutter) cm.clearGutter(GUTTER_ID);
for (var i = 0; i < state.marked.length; ++i)
state.marked[i].clear();
state.marked.length = 0;
}
function makeMarker(labels, severity, multiple, tooltips) {
var marker = document.createElement("div"), inner = marker;
marker.className = "CodeMirror-lint-marker-" + severity;
if (multiple) {
inner = marker.appendChild(document.createElement("div"));
inner.className = "CodeMirror-lint-marker-multiple";
}
if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
showTooltipFor(e, labels, inner);
});
return marker;
}
function getMaxSeverity(a, b) {
if (a == "error") return a;
else return b;
}
function groupByLine(annotations) {
var lines = [];
for (var i = 0; i < annotations.length; ++i) {
var ann = annotations[i], line = ann.from.line;
(lines[line] || (lines[line] = [])).push(ann);
}
return lines;
}
function annotationTooltip(ann) {
var severity = ann.severity;
if (!severity) severity = "error";
var tip = document.createElement("div");
tip.className = "CodeMirror-lint-message-" + severity;
tip.appendChild(document.createTextNode(ann.message));
return tip;
}
function lintAsync(cm, getAnnotations, passOptions) {
var state = cm.state.lint
var id = ++state.waitingFor
function abort() {
id = -1
cm.off("change", abort)
}
cm.on("change", abort)
getAnnotations(cm.getValue(), function(annotations, arg2) {
cm.off("change", abort)
if (state.waitingFor != id) return
if (arg2 && annotations instanceof CodeMirror) annotations = arg2
updateLinting(cm, annotations)
}, passOptions, cm);
}
function startLinting(cm) {
var state = cm.state.lint, options = state.options;
var passOptions = options.options || options; // Support deprecated passing of `options` property in options
var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint");
if (!getAnnotations) return;
if (options.async || getAnnotations.async) {
lintAsync(cm, getAnnotations, passOptions)
} else {
updateLinting(cm, getAnnotations(cm.getValue(), passOptions, cm));
}
}
function updateLinting(cm, annotationsNotSorted) {
clearMarks(cm);
var state = cm.state.lint, options = state.options;
var annotations = groupByLine(annotationsNotSorted);
for (var line = 0; line < annotations.length; ++line) {
var anns = annotations[line];
if (!anns) continue;
var maxSeverity = null;
var tipLabel = state.hasGutter && document.createDocumentFragment();
for (var i = 0; i < anns.length; ++i) {
var ann = anns[i];
var severity = ann.severity;
if (!severity) severity = "error";
maxSeverity = getMaxSeverity(maxSeverity, severity);
if (options.formatAnnotation) ann = options.formatAnnotation(ann);
if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
className: "CodeMirror-lint-mark-" + severity,
__annotation: ann
}));
}
if (state.hasGutter)
cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1,
state.options.tooltips));
}
if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
}
function onChange(cm) {
var state = cm.state.lint;
if (!state) return;
clearTimeout(state.timeout);
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
}
function popupTooltips(annotations, e) {
var target = e.target || e.srcElement;
var tooltip = document.createDocumentFragment();
for (var i = 0; i < annotations.length; i++) {
var ann = annotations[i];
tooltip.appendChild(annotationTooltip(ann));
}
showTooltipFor(e, tooltip, target);
}
function onMouseOver(cm, e) {
var target = e.target || e.srcElement;
if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
var annotations = [];
for (var i = 0; i < spans.length; ++i) {
annotations.push(spans[i].__annotation);
}
if (annotations.length) popupTooltips(annotations, e);
}
CodeMirror.defineOption("lint", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
clearMarks(cm);
if (cm.state.lint.options.lintOnChange !== false)
cm.off("change", onChange);
CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
clearTimeout(cm.state.lint.timeout);
delete cm.state.lint;
}
if (val) {
var gutters = cm.getOption("gutters"), hasLintGutter = false;
for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
if (state.options.lintOnChange !== false)
cm.on("change", onChange);
if (state.options.tooltips != false)
CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
startLinting(cm);
}
});
CodeMirror.defineExtension("performLint", function() {
if (this.state.lint) startLinting(this);
});
});
/*!-----------------------------------------------------------------------------
* 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/coffee",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\#%\^\&\*\(\)\=\$\-\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{blockComment:["###","###"],lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},t.language={defaultToken:"",ignoreCase:!0,tokenPostfix:".coffee",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"}],regEx:/\/(?!\/\/)(?:[^\/\\]|\\.)*\/[igm]*/,keywords:["and","or","is","isnt","not","on","yes","@","no","off","true","false","null","this","new","delete","typeof","in","instanceof","return","throw","break","continue","debugger","if","else","switch","for","while","do","try","catch","finally","class","extends","super","undefined","then","unless","until","loop","of","by","when"],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*/,"variable.predefined"],[/[a-zA-Z_]\w*/,{cases:{this:"variable.predefined","@keywords":{token:"keyword.$0"},"@default":""}}],[/[ \t\r\n]+/,""],[/###/,"comment","@comment"],[/#.*$/,"comment"],["///",{token:"regexp",next:"@hereregexp"}],[/^(\s*)(@regEx)/,["","regexp"]],[/(\()(\s*)(@regEx)/,["@brackets","","regexp"]],[/(\,)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\=)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\:)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\[)(\s*)(@regEx)/,["@brackets","","regexp"]],[/(\!)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\&)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\|)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\?)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\{)(\s*)(@regEx)/,["@brackets","","regexp"]],[/(\;)(\s*)(@regEx)/,["","","regexp"]],[/}/,{cases:{"$S2==interpolatedstring":{token:"string",next:"@pop"},"@default":"@brackets"}}],[/[{}()\[\]]/,"@brackets"],[/@symbols/,"delimiter"],[/\d+[eE]([\-+]?\d+)?/,"number.float"],[/\d+\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F]+/,"number.hex"],[/0[0-7]+(?!\d)/,"number.octal"],[/\d+/,"number"],[/[,.]/,"delimiter"],[/"""/,"string",'@herestring."""'],[/'''/,"string","@herestring.'''"],[/"/,{cases:{"@eos":"string","@default":{token:"string",next:'@string."'}}}],[/'/,{cases:{"@eos":"string","@default":{token:"string",next:"@string.'"}}}]],string:[[/[^"'\#\\]+/,"string"],[/@escapes/,"string.escape"],[/\./,"string.escape.invalid"],[/\./,"string.escape.invalid"],[/#{/,{cases:{'$S2=="':{token:"string",next:"root.interpolatedstring"},"@default":"string"}}],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}],[/#/,"string"]],herestring:[[/("""|''')/,{cases:{"$1==$S2":{token:"string",next:"@pop"},"@default":"string"}}],[/[^#\\'"]+/,"string"],[/['"]+/,"string"],[/@escapes/,"string.escape"],[/\./,"string.escape.invalid"],[/#{/,{token:"string.quote",next:"root.interpolatedstring"}],[/#/,"string"]],comment:[[/[^#]+/,"comment"],[/###/,"comment","@pop"],[/#/,"comment"]],hereregexp:[[/[^\\\/#]+/,"regexp"],[/\\./,"regexp"],[/#.*$/,"comment"],["///[igm]*",{token:"regexp",next:"@pop"}],[/\//,"regexp"]]}}});
\ 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/cpp",["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:"'",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},t.language={defaultToken:"",tokenPostfix:".cpp",brackets:[{token:"delimiter.curly",open:"{",close:"}"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"delimiter.square",open:"[",close:"]"},{token:"delimiter.angle",open:"<",close:">"}],keywords:["abstract","amp","array","auto","bool","break","case","catch","char","class","const","constexpr","const_cast","continue","cpu","decltype","default","delegate","delete","do","double","dynamic_cast","each","else","enum","event","explicit","export","extern","false","final","finally","float","for","friend","gcnew","generic","goto","if","in","initonly","inline","int","interface","interior_ptr","internal","literal","long","mutable","namespace","new","noexcept","nullptr","__nullptr","operator","override","partial","pascal","pin_ptr","private","property","protected","public","ref","register","reinterpret_cast","restrict","return","safe_cast","sealed","short","signed","sizeof","static","static_assert","static_cast","struct","switch","template","this","thread_local","throw","tile_static","true","try","typedef","typeid","typename","union","unsigned","using","virtual","void","volatile","wchar_t","where","while","_asm","_based","_cdecl","_declspec","_fastcall","_if_exists","_if_not_exists","_inline","_multiple_inheritance","_pascal","_single_inheritance","_stdcall","_virtual_inheritance","_w64","__abstract","__alignof","__asm","__assume","__based","__box","__builtin_alignof","__cdecl","__clrcall","__declspec","__delegate","__event","__except","__fastcall","__finally","__forceinline","__gc","__hook","__identifier","__if_exists","__if_not_exists","__inline","__int128","__int16","__int32","__int64","__int8","__interface","__leave","__m128","__m128d","__m128i","__m256","__m256d","__m256i","__m64","__multiple_inheritance","__newslot","__nogc","__noop","__nounwind","__novtordisp","__pascal","__pin","__pragma","__property","__ptr32","__ptr64","__raise","__restrict","__resume","__sealed","__single_inheritance","__stdcall","__super","__thiscall","__try","__try_cast","__typeof","__unaligned","__unhook","__uuidof","__value","__virtual_inheritance","__w64","__wchar_t"],operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,integersuffix:/(ll|LL|u|U|l|L)?(ll|LL|u|U|l|L)?/,floatsuffix:/[fFlL]?/,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+)?(@floatsuffix)/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?(@floatsuffix)/,"number.float"],[/0[xX][0-9a-fA-F']*[0-9a-fA-F](@integersuffix)/,"number.hex"],[/0[0-7']*[0-7](@integersuffix)/,"number.octal"],[/0[bB][0-1']*[0-1](@integersuffix)/,"number.binary"],[/\d[\d']*\d(@integersuffix)/,"number"],[/\d(@integersuffix)/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"],[/'[^\\']'/,"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","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"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/csharp",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"'",close:"'",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}]},t.language={defaultToken:"",tokenPostfix:".cs",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],keywords:["extern","alias","using","bool","decimal","sbyte","byte","short","ushort","int","uint","long","ulong","char","float","double","object","dynamic","string","assembly","is","as","ref","out","this","base","new","typeof","void","checked","unchecked","default","delegate","var","const","if","else","switch","case","while","do","for","foreach","in","break","continue","goto","return","throw","try","catch","finally","lock","yield","from","let","where","join","on","equals","into","orderby","ascending","descending","select","group","by","namespace","partial","class","field","event","method","param","property","public","protected","internal","private","abstract","sealed","static","struct","readonly","volatile","virtual","override","params","get","set","add","remove","operator","true","false","implicit","explicit","interface","enum","null","async","await","fixed","sizeof","stackalloc","unsafe","nameof","when"],namespaceFollows:["namespace","using"],parenFollows:["if","for","while","switch","foreach","using","catch","when"],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:{"@namespaceFollows":{token:"keyword.$0",next:"@namespace"},"@keywords":{token:"keyword.$0",next:"@qualified"},"@default":{token:"identifier",next:"@qualified"}}}],{include:"@whitespace"},[/}/,{cases:{"$S2==interpolatedstring":{token:"string.quote",next:"@pop"},"$S2==litinterpstring":{token:"string.quote",next:"@pop"},"@default":"@brackets"}}],[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/[0-9_]*\.[0-9_]+([eE][\-+]?\d+)?[fFdD]?/,"number.float"],[/0[xX][0-9a-fA-F_]+/,"number.hex"],[/0[bB][01_]+/,"number.hex"],[/[0-9_]+/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,{token:"string.quote",next:"@string"}],[/\$\@"/,{token:"string.quote",next:"@litinterpstring"}],[/\@"/,{token:"string.quote",next:"@litstring"}],[/\$"/,{token:"string.quote",next:"@interpolatedstring"}],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],qualified:[[/[a-zA-Z_][\w]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],[/\./,"delimiter"],["","","@pop"]],namespace:[{include:"@whitespace"},[/[A-Z]\w*/,"namespace"],[/[\.=]/,"delimiter"],["","","@pop"]],comment:[[/[^\/*]+/,"comment"],["\\*/","comment","@pop"],[/[\/*]/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,{token:"string.quote",next:"@pop"}]],litstring:[[/[^"]+/,"string"],[/""/,"string.escape"],[/"/,{token:"string.quote",next:"@pop"}]],litinterpstring:[[/[^"{]+/,"string"],[/""/,"string.escape"],[/{{/,"string.escape"],[/}}/,"string.escape"],[/{/,{token:"string.quote",next:"root.litinterpstring"}],[/"/,{token:"string.quote",next:"@pop"}]],interpolatedstring:[[/[^\\"{]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/{{/,"string.escape"],[/}}/,"string.escape"],[/{/,{token:"string.quote",next:"root.interpolatedstring"}],[/"/,{token:"string.quote",next:"@pop"}]],whitespace:[[/^[ \t\v\f]*#((r)|(load))(?=\s)/,"directive.csx"],[/^[ \t\v\f]*#\w.*$/,"namespace.cpp"],[/[ \t\v\f\r\n]+/,""],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]]}}});
\ 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/css",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={wordPattern:/(#?-?\d*\.\d\w*%?)|((::|[@#.!:])?[\w-?]+%?)|::|[@#.!:]/g,comments:{blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},t.language={defaultToken:"",tokenPostfix:".css",ws:"[ \t\n\r\f]*",identifier:"-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*",brackets:[{open:"{",close:"}",token:"delimiter.bracket"},{open:"[",close:"]",token:"delimiter.bracket"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],tokenizer:{root:[{include:"@selector"}],selector:[{include:"@comments"},{include:"@import"},{include:"@strings"},["[@](keyframes|-webkit-keyframes|-moz-keyframes|-o-keyframes)",{token:"keyword",next:"@keyframedeclaration"}],["[@](page|content|font-face|-moz-document)",{token:"keyword"}],["[@](charset|namespace)",{token:"keyword",next:"@declarationbody"}],["(url-prefix)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],["(url)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],{include:"@selectorname"},["[\\*]","tag"],["[>\\+,]","delimiter"],["\\[",{token:"delimiter.bracket",next:"@selectorattribute"}],["{",{token:"delimiter.bracket",next:"@selectorbody"}]],selectorbody:[{include:"@comments"},["[*_]?@identifier@ws:(?=(\\s|\\d|[^{;}]*[;}]))","attribute.name","@rulevalue"],["}",{token:"delimiter.bracket",next:"@pop"}]],selectorname:[["(\\.|#(?=[^{])|%|(@identifier)|:)+","tag"]],selectorattribute:[{include:"@term"},["]",{token:"delimiter.bracket",next:"@pop"}]],term:[{include:"@comments"},["(url-prefix)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],["(url)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],{include:"@functioninvocation"},{include:"@numbers"},{include:"@name"},["([<>=\\+\\-\\*\\/\\^\\|\\~,])","delimiter"],[",","delimiter"]],rulevalue:[{include:"@comments"},{include:"@strings"},{include:"@term"},["!important","keyword"],[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}]],warndebug:[["[@](warn|debug)",{token:"keyword",next:"@declarationbody"}]],import:[["[@](import)",{token:"keyword",next:"@declarationbody"}]],urldeclaration:[{include:"@strings"},["[^)\r\n]+","string"],["\\)",{token:"delimiter.parenthesis",next:"@pop"}]],parenthizedterm:[{include:"@term"},["\\)",{token:"delimiter.parenthesis",next:"@pop"}]],declarationbody:[{include:"@term"},[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[/[^*/]+/,"comment"],[/./,"comment"]],name:[["@identifier","attribute.value"]],numbers:[["-?(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?",{token:"attribute.value.number",next:"@units"}],["#[0-9a-fA-F_]+(?!\\w)","attribute.value.hex"]],units:[["(em|ex|ch|rem|vmin|vmax|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?","attribute.value.unit","@pop"]],keyframedeclaration:[["@identifier","attribute.value"],["{",{token:"delimiter.bracket",switchTo:"@keyframebody"}]],keyframebody:[{include:"@term"},["{",{token:"delimiter.bracket",next:"@selectorbody"}],["}",{token:"delimiter.bracket",next:"@pop"}]],functioninvocation:[["@identifier\\(",{token:"attribute.value",next:"@functionarguments"}]],functionarguments:[["\\$@identifier@ws:","attribute.name"],["[,]","delimiter"],{include:"@term"},["\\)",{token:"attribute.value",next:"@pop"}]],strings:[['~?"',{token:"string",next:"@stringenddoublequote"}],["~?'",{token:"string",next:"@stringendquote"}]],stringenddoublequote:[["\\\\.","string"],['"',{token:"string",next:"@pop"}],[/[^\\"]+/,"string"],[".","string"]],stringendquote:[["\\\\.","string"],["'",{token:"string",next:"@pop"}],[/[^\\']+/,"string"],[".","string"]]}}});
\ 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/dockerfile",["require","exports"],function(e,s){"use strict";Object.defineProperty(s,"__esModule",{value:!0}),s.conf={brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},s.language={defaultToken:"",tokenPostfix:".dockerfile",instructions:/FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|VOLUME|LABEL|USER|WORKDIR|COPY|CMD|STOPSIGNAL|SHELL|ENTRYPOINT/,instructionAfter:/ONBUILD/,variableAfter:/ENV/,variable:/\${?[\w]+}?/,tokenizer:{root:[{include:"@whitespace"},{include:"@comment"},[/(@instructionAfter)(\s+)/,["keyword",{token:"",next:"@instructions"}]],["","keyword","@instructions"]],instructions:[[/(@variableAfter)(\s+)([\w]+)/,["keyword","",{token:"variable",next:"@arguments"}]],[/(@instructions)/,"keyword","@arguments"]],arguments:[{include:"@whitespace"},{include:"@strings"},[/(@variable)/,{cases:{"@eos":{token:"variable",next:"@popall"},"@default":"variable"}}],[/\\/,{cases:{"@eos":"","@default":""}}],[/./,{cases:{"@eos":{token:"",next:"@popall"},"@default":""}}]],whitespace:[[/\s+/,{cases:{"@eos":{token:"",next:"@popall"},"@default":""}}]],comment:[[/(^#.*$)/,"comment","@popall"]],strings:[[/'$/,"string","@popall"],[/'/,"string","@stringBody"],[/"$/,"string","@popall"],[/"/,"string","@dblStringBody"]],stringBody:[[/[^\\\$']/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}],[/\\./,"string.escape"],[/'$/,"string","@popall"],[/'/,"string","@pop"],[/(@variable)/,"variable"],[/\\$/,"string"],[/$/,"string","@popall"]],dblStringBody:[[/[^\\\$"]/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}],[/\\./,"string.escape"],[/"$/,"string","@popall"],[/"/,"string","@pop"],[/(@variable)/,"variable"],[/\\$/,"string"],[/$/,"string","@popall"]]}}});
\ 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