正在显示
100 个修改的文件
包含
2550 行增加
和
121 行删除
lh-common/lh-common-datasource/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>lh-common</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + </parent> | ||
| 11 | + | ||
| 12 | + <artifactId>lh-common-datasource</artifactId> | ||
| 13 | + | ||
| 14 | + <properties> | ||
| 15 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 16 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 17 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 18 | + </properties> | ||
| 19 | + | ||
| 20 | + <dependencies> | ||
| 21 | + <dependency> | ||
| 22 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 23 | + <artifactId>ruoyi-common</artifactId> | ||
| 24 | + </dependency> | ||
| 25 | + | ||
| 26 | + <dependency> | ||
| 27 | + <groupId>org.aspectj</groupId> | ||
| 28 | + <artifactId>aspectjrt</artifactId> | ||
| 29 | + </dependency> | ||
| 30 | + | ||
| 31 | + <!-- 阿里数据库连接池 --> | ||
| 32 | + <dependency> | ||
| 33 | + <groupId>com.alibaba</groupId> | ||
| 34 | + <artifactId>druid-spring-boot-starter</artifactId> | ||
| 35 | + </dependency> | ||
| 36 | + | ||
| 37 | + <dependency> | ||
| 38 | + <groupId>tk.mybatis</groupId> | ||
| 39 | + <artifactId>mapper</artifactId> | ||
| 40 | + <!-- 建议使用最新版本,最新版本请从项目首页查找 --> | ||
| 41 | + </dependency> | ||
| 42 | + <dependency> | ||
| 43 | + <groupId>tk.mybatis</groupId> | ||
| 44 | + <artifactId>mapper-spring-boot-starter</artifactId> | ||
| 45 | + </dependency> | ||
| 46 | + | ||
| 47 | + <!-- Mysql驱动包 --> | ||
| 48 | + <dependency> | ||
| 49 | + <groupId>mysql</groupId> | ||
| 50 | + <artifactId>mysql-connector-java</artifactId> | ||
| 51 | + </dependency> | ||
| 52 | + </dependencies> | ||
| 53 | +</project> |
| 1 | -package com.ruoyi.framework.aspectj; | 1 | +package com.zhonglai.luhui.datasource.aspectj; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.common.annotation.DataSource; | ||
| 4 | import com.ruoyi.common.utils.StringUtils; | 3 | import com.ruoyi.common.utils.StringUtils; |
| 5 | -import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; | 4 | +import com.zhonglai.luhui.datasource.config.DynamicDataSourceContextHolder; |
| 5 | +import com.zhonglai.luhui.datasource.enums.DataSource; | ||
| 6 | import org.aspectj.lang.ProceedingJoinPoint; | 6 | import org.aspectj.lang.ProceedingJoinPoint; |
| 7 | import org.aspectj.lang.annotation.Around; | 7 | import org.aspectj.lang.annotation.Around; |
| 8 | import org.aspectj.lang.annotation.Aspect; | 8 | import org.aspectj.lang.annotation.Aspect; |
| @@ -28,8 +28,8 @@ public class DataSourceAspect | @@ -28,8 +28,8 @@ public class DataSourceAspect | ||
| 28 | { | 28 | { |
| 29 | protected Logger logger = LoggerFactory.getLogger(getClass()); | 29 | protected Logger logger = LoggerFactory.getLogger(getClass()); |
| 30 | 30 | ||
| 31 | - @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" | ||
| 32 | - + "|| @within(com.ruoyi.common.annotation.DataSource)") | 31 | + @Pointcut("@annotation(com.zhonglai.luhui.datasource.enums.DataSource)" |
| 32 | + + "|| @within(com.zhonglai.luhui.datasource.enums.DataSource)") | ||
| 33 | public void dsPointCut() | 33 | public void dsPointCut() |
| 34 | { | 34 | { |
| 35 | 35 |
| 1 | -package com.ruoyi.framework.config; | 1 | +package com.zhonglai.luhui.datasource.config; |
| 2 | 2 | ||
| 3 | import com.alibaba.druid.pool.DruidDataSource; | 3 | import com.alibaba.druid.pool.DruidDataSource; |
| 4 | import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; | 4 | import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; |
| 5 | import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; | 5 | import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; |
| 6 | import com.alibaba.druid.util.Utils; | 6 | import com.alibaba.druid.util.Utils; |
| 7 | -import com.ruoyi.common.enums.DataSourceType; | 7 | +import com.zhonglai.luhui.datasource.enums.DataSourceType; |
| 8 | import com.ruoyi.common.utils.spring.SpringUtils; | 8 | import com.ruoyi.common.utils.spring.SpringUtils; |
| 9 | -import com.ruoyi.framework.config.properties.DruidProperties; | ||
| 10 | -import com.ruoyi.framework.datasource.DynamicDataSource; | ||
| 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 12 | import org.springframework.boot.context.properties.ConfigurationProperties; | 10 | import org.springframework.boot.context.properties.ConfigurationProperties; |
| 13 | import org.springframework.boot.web.servlet.FilterRegistrationBean; | 11 | import org.springframework.boot.web.servlet.FilterRegistrationBean; |
| 1 | -package com.ruoyi.framework.config.properties; | 1 | +package com.zhonglai.luhui.datasource.config; |
| 2 | 2 | ||
| 3 | import com.alibaba.druid.pool.DruidDataSource; | 3 | import com.alibaba.druid.pool.DruidDataSource; |
| 4 | import org.springframework.beans.factory.annotation.Value; | 4 | import org.springframework.beans.factory.annotation.Value; |
lh-common/lh-common-datasource/src/main/java/com/zhonglai/luhui/datasource/enums/DataSource.java
0 → 100644
| 1 | +package com.zhonglai.luhui.datasource.enums; | ||
| 2 | + | ||
| 3 | +import java.lang.annotation.*; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 自定义多数据源切换注解 | ||
| 7 | + * | ||
| 8 | + * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 | ||
| 9 | + * | ||
| 10 | + * @author ruoyi | ||
| 11 | + */ | ||
| 12 | +@Target({ ElementType.METHOD, ElementType.TYPE }) | ||
| 13 | +@Retention(RetentionPolicy.RUNTIME) | ||
| 14 | +@Documented | ||
| 15 | +@Inherited | ||
| 16 | +public @interface DataSource | ||
| 17 | +{ | ||
| 18 | + /** | ||
| 19 | + * 切换数据源名称 | ||
| 20 | + */ | ||
| 21 | + public DataSourceType value() default DataSourceType.MASTER; | ||
| 22 | +} |
lh-common/lh-common-firewall/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>Luhui</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + <relativePath>../../pom.xml</relativePath> | ||
| 11 | + </parent> | ||
| 12 | + | ||
| 13 | + <artifactId>lh-common-firewall</artifactId> | ||
| 14 | + | ||
| 15 | + <properties> | ||
| 16 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 17 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 18 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 19 | + </properties> | ||
| 20 | + | ||
| 21 | + <dependencies> | ||
| 22 | + <!-- SpringBoot Web容器 --> | ||
| 23 | + <dependency> | ||
| 24 | + <groupId>org.springframework.boot</groupId> | ||
| 25 | + <artifactId>spring-boot-starter-web</artifactId> | ||
| 26 | + </dependency> | ||
| 27 | + | ||
| 28 | + <!-- 验证码 --> | ||
| 29 | + <dependency> | ||
| 30 | + <groupId>com.github.penggle</groupId> | ||
| 31 | + <artifactId>kaptcha</artifactId> | ||
| 32 | + <exclusions> | ||
| 33 | + <exclusion> | ||
| 34 | + <artifactId>javax.servlet-api</artifactId> | ||
| 35 | + <groupId>javax.servlet</groupId> | ||
| 36 | + </exclusion> | ||
| 37 | + </exclusions> | ||
| 38 | + </dependency> | ||
| 39 | + | ||
| 40 | + <dependency> | ||
| 41 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 42 | + <artifactId>ruoyi-common-redis</artifactId> | ||
| 43 | + </dependency> | ||
| 44 | + | ||
| 45 | + <!-- SpringBoot 拦截器 --> | ||
| 46 | + <dependency> | ||
| 47 | + <groupId>org.springframework.boot</groupId> | ||
| 48 | + <artifactId>spring-boot-starter-aop</artifactId> | ||
| 49 | + </dependency> | ||
| 50 | + | ||
| 51 | + </dependencies> | ||
| 52 | +</project> |
| 1 | +package com.zhonglai.luhui.firewall.config; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.firewall.interceptor.RepeatSubmitInterceptor; | ||
| 4 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 5 | +import org.springframework.context.annotation.Configuration; | ||
| 6 | +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
| 7 | +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
| 8 | + | ||
| 9 | +@Configuration | ||
| 10 | +public class RepeatSubmitConfig implements WebMvcConfigurer { | ||
| 11 | + @Autowired | ||
| 12 | + private RepeatSubmitInterceptor repeatSubmitInterceptor; | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 自定义拦截规则 | ||
| 16 | + */ | ||
| 17 | + @Override | ||
| 18 | + public void addInterceptors(InterceptorRegistry registry) | ||
| 19 | + { | ||
| 20 | + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); | ||
| 21 | + } | ||
| 22 | +} |
| 1 | -package com.ruoyi.framework.interceptor.impl; | 1 | +package com.zhonglai.luhui.firewall.interceptor.impl; |
| 2 | 2 | ||
| 3 | import com.alibaba.fastjson.JSONObject; | 3 | import com.alibaba.fastjson.JSONObject; |
| 4 | import com.ruoyi.common.annotation.RepeatSubmit; | 4 | import com.ruoyi.common.annotation.RepeatSubmit; |
| 5 | import com.ruoyi.common.constant.Constants; | 5 | import com.ruoyi.common.constant.Constants; |
| 6 | -import com.ruoyi.common.core.redis.RedisCache; | ||
| 7 | import com.ruoyi.common.filter.RepeatedlyRequestWrapper; | 6 | import com.ruoyi.common.filter.RepeatedlyRequestWrapper; |
| 8 | import com.ruoyi.common.utils.StringUtils; | 7 | import com.ruoyi.common.utils.StringUtils; |
| 9 | import com.ruoyi.common.utils.http.HttpHelper; | 8 | import com.ruoyi.common.utils.http.HttpHelper; |
| 10 | -import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; | 9 | +import com.zhonglai.luhui.firewall.interceptor.RepeatSubmitInterceptor; |
| 10 | +import com.zhonglai.luhui.redis.service.RedisCache; | ||
| 11 | import org.springframework.beans.factory.annotation.Autowired; | 11 | import org.springframework.beans.factory.annotation.Autowired; |
| 12 | import org.springframework.beans.factory.annotation.Value; | 12 | import org.springframework.beans.factory.annotation.Value; |
| 13 | import org.springframework.stereotype.Component; | 13 | import org.springframework.stereotype.Component; |
lh-common/lh-common-log/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>lh-common</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + </parent> | ||
| 11 | + | ||
| 12 | + <artifactId>lh-common-log</artifactId> | ||
| 13 | + | ||
| 14 | + <properties> | ||
| 15 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 16 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 17 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 18 | + </properties> | ||
| 19 | + | ||
| 20 | +</project> |
| 1 | +//package com.zhonglai.luhui.log.aspectj; | ||
| 2 | +// | ||
| 3 | +//import com.alibaba.fastjson.JSON; | ||
| 4 | +//import com.ruoyi.common.annotation.Log; | ||
| 5 | +//import com.ruoyi.common.core.domain.BaseLoginUser; | ||
| 6 | +//import com.ruoyi.common.enums.BusinessStatus; | ||
| 7 | +//import com.ruoyi.common.enums.HttpMethod; | ||
| 8 | +//import com.ruoyi.common.utils.SecurityUtils; | ||
| 9 | +//import com.ruoyi.common.utils.ServletUtils; | ||
| 10 | +//import com.ruoyi.common.utils.StringUtils; | ||
| 11 | +//import com.ruoyi.common.utils.ip.IpUtils; | ||
| 12 | +//import com.ruoyi.system.domain.sys.SysOperLog; | ||
| 13 | +//import org.aspectj.lang.JoinPoint; | ||
| 14 | +//import org.aspectj.lang.annotation.AfterReturning; | ||
| 15 | +//import org.aspectj.lang.annotation.AfterThrowing; | ||
| 16 | +//import org.aspectj.lang.annotation.Aspect; | ||
| 17 | +//import org.slf4j.Logger; | ||
| 18 | +//import org.slf4j.LoggerFactory; | ||
| 19 | +//import org.springframework.stereotype.Component; | ||
| 20 | +//import org.springframework.validation.BindingResult; | ||
| 21 | +//import org.springframework.web.multipart.MultipartFile; | ||
| 22 | +//import org.springframework.web.servlet.HandlerMapping; | ||
| 23 | +// | ||
| 24 | +//import javax.servlet.http.HttpServletRequest; | ||
| 25 | +//import javax.servlet.http.HttpServletResponse; | ||
| 26 | +//import java.util.Collection; | ||
| 27 | +//import java.util.Map; | ||
| 28 | +// | ||
| 29 | +///** | ||
| 30 | +// * 操作日志记录处理 | ||
| 31 | +// * | ||
| 32 | +// * @author ruoyi | ||
| 33 | +// */ | ||
| 34 | +//@Aspect | ||
| 35 | +//@Component | ||
| 36 | +//public class LogAspect | ||
| 37 | +//{ | ||
| 38 | +// private static final Logger log = LoggerFactory.getLogger(LogAspect.class); | ||
| 39 | +// | ||
| 40 | +// /** | ||
| 41 | +// * 处理完请求后执行 | ||
| 42 | +// * | ||
| 43 | +// * @param joinPoint 切点 | ||
| 44 | +// */ | ||
| 45 | +// @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") | ||
| 46 | +// public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) | ||
| 47 | +// { | ||
| 48 | +// handleLog(joinPoint, controllerLog, null, jsonResult); | ||
| 49 | +// } | ||
| 50 | +// | ||
| 51 | +// /** | ||
| 52 | +// * 拦截异常操作 | ||
| 53 | +// * | ||
| 54 | +// * @param joinPoint 切点 | ||
| 55 | +// * @param e 异常 | ||
| 56 | +// */ | ||
| 57 | +// @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") | ||
| 58 | +// public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) | ||
| 59 | +// { | ||
| 60 | +// handleLog(joinPoint, controllerLog, e, null); | ||
| 61 | +// } | ||
| 62 | +// | ||
| 63 | +// protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) | ||
| 64 | +// { | ||
| 65 | +// try | ||
| 66 | +// { | ||
| 67 | +// // 获取当前的用户 | ||
| 68 | +// BaseLoginUser loginUser = SecurityUtils.getLoginUser(); | ||
| 69 | +// | ||
| 70 | +// // *========数据库日志=========*// | ||
| 71 | +// SysOperLog operLog = new SysOperLog(); | ||
| 72 | +// operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); | ||
| 73 | +// // 请求的地址 | ||
| 74 | +// String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); | ||
| 75 | +// operLog.setOperIp(ip); | ||
| 76 | +// operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); | ||
| 77 | +// if (loginUser != null) | ||
| 78 | +// { | ||
| 79 | +// operLog.setOperName(loginUser.getUsername()); | ||
| 80 | +// } | ||
| 81 | +// | ||
| 82 | +// if (e != null) | ||
| 83 | +// { | ||
| 84 | +// operLog.setStatus(BusinessStatus.FAIL.ordinal()); | ||
| 85 | +// operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); | ||
| 86 | +// } | ||
| 87 | +// // 设置方法名称 | ||
| 88 | +// String className = joinPoint.getTarget().getClass().getName(); | ||
| 89 | +// String methodName = joinPoint.getSignature().getName(); | ||
| 90 | +// operLog.setMethod(className + "." + methodName + "()"); | ||
| 91 | +// // 设置请求方式 | ||
| 92 | +// operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); | ||
| 93 | +// // 处理设置注解上的参数 | ||
| 94 | +// getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); | ||
| 95 | +// // 保存数据库 | ||
| 96 | +// AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); | ||
| 97 | +// } | ||
| 98 | +// catch (Exception exp) | ||
| 99 | +// { | ||
| 100 | +// // 记录本地异常日志 | ||
| 101 | +// log.error("==前置通知异常=="); | ||
| 102 | +// log.error("异常信息:{}", exp.getMessage()); | ||
| 103 | +// exp.printStackTrace(); | ||
| 104 | +// } | ||
| 105 | +// } | ||
| 106 | +// | ||
| 107 | +// /** | ||
| 108 | +// * 获取注解中对方法的描述信息 用于Controller层注解 | ||
| 109 | +// * | ||
| 110 | +// * @param log 日志 | ||
| 111 | +// * @param operLog 操作日志 | ||
| 112 | +// * @throws Exception | ||
| 113 | +// */ | ||
| 114 | +// public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception | ||
| 115 | +// { | ||
| 116 | +// // 设置action动作 | ||
| 117 | +// operLog.setBusinessType(log.businessType().ordinal()); | ||
| 118 | +// // 设置标题 | ||
| 119 | +// operLog.setTitle(log.title()); | ||
| 120 | +// // 设置操作人类别 | ||
| 121 | +// operLog.setOperatorType(log.operatorType().ordinal()); | ||
| 122 | +// // 是否需要保存request,参数和值 | ||
| 123 | +// if (log.isSaveRequestData()) | ||
| 124 | +// { | ||
| 125 | +// // 获取参数的信息,传入到数据库中。 | ||
| 126 | +// setRequestValue(joinPoint, operLog); | ||
| 127 | +// } | ||
| 128 | +// // 是否需要保存response,参数和值 | ||
| 129 | +// if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) | ||
| 130 | +// { | ||
| 131 | +// operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); | ||
| 132 | +// } | ||
| 133 | +// } | ||
| 134 | +// | ||
| 135 | +// /** | ||
| 136 | +// * 获取请求的参数,放到log中 | ||
| 137 | +// * | ||
| 138 | +// * @param operLog 操作日志 | ||
| 139 | +// * @throws Exception 异常 | ||
| 140 | +// */ | ||
| 141 | +// private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception | ||
| 142 | +// { | ||
| 143 | +// String requestMethod = operLog.getRequestMethod(); | ||
| 144 | +// if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) | ||
| 145 | +// { | ||
| 146 | +// String params = argsArrayToString(joinPoint.getArgs()); | ||
| 147 | +// operLog.setOperParam(StringUtils.substring(params, 0, 2000)); | ||
| 148 | +// } | ||
| 149 | +// else | ||
| 150 | +// { | ||
| 151 | +// Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); | ||
| 152 | +// operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000)); | ||
| 153 | +// } | ||
| 154 | +// } | ||
| 155 | +// | ||
| 156 | +// /** | ||
| 157 | +// * 参数拼装 | ||
| 158 | +// */ | ||
| 159 | +// private String argsArrayToString(Object[] paramsArray) | ||
| 160 | +// { | ||
| 161 | +// String params = ""; | ||
| 162 | +// if (paramsArray != null && paramsArray.length > 0) | ||
| 163 | +// { | ||
| 164 | +// for (Object o : paramsArray) | ||
| 165 | +// { | ||
| 166 | +// if (StringUtils.isNotNull(o) && !isFilterObject(o)) | ||
| 167 | +// { | ||
| 168 | +// try | ||
| 169 | +// { | ||
| 170 | +// Object jsonObj = JSON.toJSON(o); | ||
| 171 | +// params += jsonObj.toString() + " "; | ||
| 172 | +// } | ||
| 173 | +// catch (Exception e) | ||
| 174 | +// { | ||
| 175 | +// } | ||
| 176 | +// } | ||
| 177 | +// } | ||
| 178 | +// } | ||
| 179 | +// return params.trim(); | ||
| 180 | +// } | ||
| 181 | +// | ||
| 182 | +// /** | ||
| 183 | +// * 判断是否需要过滤的对象。 | ||
| 184 | +// * | ||
| 185 | +// * @param o 对象信息。 | ||
| 186 | +// * @return 如果是需要过滤的对象,则返回true;否则返回false。 | ||
| 187 | +// */ | ||
| 188 | +// @SuppressWarnings("rawtypes") | ||
| 189 | +// public boolean isFilterObject(final Object o) | ||
| 190 | +// { | ||
| 191 | +// Class<?> clazz = o.getClass(); | ||
| 192 | +// if (clazz.isArray()) | ||
| 193 | +// { | ||
| 194 | +// return clazz.getComponentType().isAssignableFrom(MultipartFile.class); | ||
| 195 | +// } | ||
| 196 | +// else if (Collection.class.isAssignableFrom(clazz)) | ||
| 197 | +// { | ||
| 198 | +// Collection collection = (Collection) o; | ||
| 199 | +// for (Object value : collection) | ||
| 200 | +// { | ||
| 201 | +// return value instanceof MultipartFile; | ||
| 202 | +// } | ||
| 203 | +// } | ||
| 204 | +// else if (Map.class.isAssignableFrom(clazz)) | ||
| 205 | +// { | ||
| 206 | +// Map map = (Map) o; | ||
| 207 | +// for (Object value : map.entrySet()) | ||
| 208 | +// { | ||
| 209 | +// Map.Entry entry = (Map.Entry) value; | ||
| 210 | +// return entry.getValue() instanceof MultipartFile; | ||
| 211 | +// } | ||
| 212 | +// } | ||
| 213 | +// return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse | ||
| 214 | +// || o instanceof BindingResult; | ||
| 215 | +// } | ||
| 216 | +//} |
lh-common/lh-common-swagger/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>Luhui</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + <relativePath>../../pom.xml</relativePath> | ||
| 11 | + </parent> | ||
| 12 | + | ||
| 13 | + <artifactId>lh-common-swagger</artifactId> | ||
| 14 | + | ||
| 15 | + <properties> | ||
| 16 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 17 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 18 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 19 | + </properties> | ||
| 20 | + | ||
| 21 | + <dependencies> | ||
| 22 | + <!-- 文档 --> | ||
| 23 | + <dependency > | ||
| 24 | + <groupId>io.springfox</groupId> | ||
| 25 | + <artifactId>springfox-swagger2</artifactId> | ||
| 26 | + <version>${swagger.version}</version> | ||
| 27 | + <exclusions> | ||
| 28 | + <exclusion> | ||
| 29 | + <groupId>io.swagger</groupId> | ||
| 30 | + <artifactId>swagger-models</artifactId> | ||
| 31 | + </exclusion> | ||
| 32 | + <exclusion> | ||
| 33 | + <groupId>com.google.guava</groupId> | ||
| 34 | + <artifactId>guava</artifactId> | ||
| 35 | + </exclusion> | ||
| 36 | + </exclusions> | ||
| 37 | + </dependency> | ||
| 38 | + <!--https://mvnrepository.com/artifact/io.swagger/swagger-models--> | ||
| 39 | + <dependency> | ||
| 40 | + <groupId>io.swagger</groupId> | ||
| 41 | + <artifactId>swagger-models</artifactId> | ||
| 42 | + <version>${swagger-models.version}</version> | ||
| 43 | + </dependency> | ||
| 44 | + <dependency> | ||
| 45 | + <groupId>io.springfox</groupId> | ||
| 46 | + <artifactId>springfox-swagger-ui</artifactId> | ||
| 47 | + <version>${swagger.version}</version> | ||
| 48 | + </dependency> | ||
| 49 | + <!--<!– https://mvnrepository.com/artifact/com.github.xiaoymin/swagger-bootstrap-ui –>--> | ||
| 50 | + <dependency> | ||
| 51 | + <groupId>com.github.xiaoymin</groupId> | ||
| 52 | + <artifactId>swagger-bootstrap-ui</artifactId> | ||
| 53 | + <version>${swagger-ui.version}</version> | ||
| 54 | + </dependency> | ||
| 55 | + | ||
| 56 | + </dependencies> | ||
| 57 | + | ||
| 58 | +</project> |
lh-common/lh-domain/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <parent> | ||
| 6 | + <artifactId>lh-common</artifactId> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <version>1.0-SNAPSHOT</version> | ||
| 9 | + </parent> | ||
| 10 | + <dependencies> | ||
| 11 | + <!-- 模型--> | ||
| 12 | + <dependency> | ||
| 13 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 14 | + <artifactId>ruoyi-common</artifactId> | ||
| 15 | + </dependency> | ||
| 16 | + <dependency> | ||
| 17 | + <groupId>io.swagger</groupId> | ||
| 18 | + <artifactId>swagger-annotations</artifactId> | ||
| 19 | + <version>1.6.2</version> | ||
| 20 | + <scope>compile</scope> | ||
| 21 | + </dependency> | ||
| 22 | + </dependencies> | ||
| 23 | + <modelVersion>4.0.0</modelVersion> | ||
| 24 | + | ||
| 25 | + <artifactId>lh-domain</artifactId> | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +</project> |
| 1 | package com.ruoyi.system.domain.entity; | 1 | package com.ruoyi.system.domain.entity; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.common.tool.BaseEntity; |
| 4 | +import com.ruoyi.common.annotation.PublicSQLConfig; | ||
| 5 | import org.apache.commons.lang3.builder.ToStringBuilder; | 5 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 6 | import org.apache.commons.lang3.builder.ToStringStyle; | 6 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 7 | 7 |
| 1 | package com.ruoyi.system.domain.entity; | 1 | package com.ruoyi.system.domain.entity; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.Excel; | ||
| 4 | -import com.ruoyi.system.domain.tool.Excel.ColumnType; | ||
| 5 | -import com.ruoyi.system.domain.tool.BaseEntity; | 3 | +import com.ruoyi.common.annotation.Excel; |
| 4 | +import com.ruoyi.common.tool.BaseEntity; | ||
| 6 | import org.apache.commons.lang3.builder.ToStringBuilder; | 5 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 7 | import org.apache.commons.lang3.builder.ToStringStyle; | 6 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 8 | 7 | ||
| @@ -19,11 +18,11 @@ public class SysDictData extends BaseEntity | @@ -19,11 +18,11 @@ public class SysDictData extends BaseEntity | ||
| 19 | private static final long serialVersionUID = 1L; | 18 | private static final long serialVersionUID = 1L; |
| 20 | 19 | ||
| 21 | /** 字典编码 */ | 20 | /** 字典编码 */ |
| 22 | - @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) | 21 | + @Excel(name = "字典编码", cellType = Excel.ColumnType.NUMERIC) |
| 23 | private Long dictCode; | 22 | private Long dictCode; |
| 24 | 23 | ||
| 25 | /** 字典排序 */ | 24 | /** 字典排序 */ |
| 26 | - @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) | 25 | + @Excel(name = "字典排序", cellType = Excel.ColumnType.NUMERIC) |
| 27 | private Long dictSort; | 26 | private Long dictSort; |
| 28 | 27 | ||
| 29 | /** 字典标签 */ | 28 | /** 字典标签 */ |
| 1 | +package com.ruoyi.system.domain.entity; import javax.validation.constraints.NotBlank;import javax.validation.constraints.Size; import com.ruoyi.common.annotation.Excel;import org.apache.commons.lang3.builder.ToStringBuilder;import org.apache.commons.lang3.builder.ToStringStyle;import com.ruoyi.common.tool.BaseEntity; /** * 字典类型表 sys_dict_type * * @author ruoyi */public class SysDictType extends BaseEntity{ private static final long serialVersionUID = 1L; /** 字典主键 */ @Excel(name = "字典主键", cellType = Excel.ColumnType.NUMERIC) private Long dictId; /** 字典名称 */ @Excel(name = "字典名称") private String dictName; /** 字典类型 */ @Excel(name = "字典类型") private String dictType; /** 状态(0正常 1停用) */ @Excel(name = "状态", readConverterExp = "0=正常,1=停用") private String status; public Long getDictId() { return dictId; } public void setDictId(Long dictId) { this.dictId = dictId; } @NotBlank(message = "字典名称不能为空") @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") public String getDictName() { return dictName; } public void setDictName(String dictName) { this.dictName = dictName; } @NotBlank(message = "字典类型不能为空") @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") public String getDictType() { return dictType; } public void setDictType(String dictType) { this.dictType = dictType; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) .append("dictId", getDictId()) .append("dictName", getDictName()) .append("dictType", getDictType()) .append("status", getStatus()) .append("createBy", getCreateBy()) .append("createTime", getCreateTime()) .append("updateBy", getUpdateBy()) .append("updateTime", getUpdateTime()) .append("remark", getRemark()) .toString(); }} |
| 1 | package com.ruoyi.system.domain.entity; | 1 | package com.ruoyi.system.domain.entity; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.common.tool.BaseEntity; |
| 4 | +import com.ruoyi.common.annotation.PublicSQLConfig; | ||
| 5 | import org.apache.commons.lang3.builder.ToStringBuilder; | 5 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 6 | import org.apache.commons.lang3.builder.ToStringStyle; | 6 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 7 | 7 |
| 1 | package com.ruoyi.system.domain.entity; | 1 | package com.ruoyi.system.domain.entity; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.Excel; | ||
| 4 | -import com.ruoyi.system.domain.tool.Excel.ColumnType; | ||
| 5 | -import com.ruoyi.system.domain.tool.BaseEntity; | 3 | +import com.ruoyi.common.annotation.Excel; |
| 4 | +import com.ruoyi.common.tool.BaseEntity; | ||
| 6 | import org.apache.commons.lang3.builder.ToStringBuilder; | 5 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 7 | import org.apache.commons.lang3.builder.ToStringStyle; | 6 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 8 | 7 | ||
| @@ -19,7 +18,7 @@ public class SysRole extends BaseEntity | @@ -19,7 +18,7 @@ public class SysRole extends BaseEntity | ||
| 19 | private static final long serialVersionUID = 1L; | 18 | private static final long serialVersionUID = 1L; |
| 20 | 19 | ||
| 21 | /** 角色ID */ | 20 | /** 角色ID */ |
| 22 | - @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) | 21 | + @Excel(name = "角色序号", cellType = Excel.ColumnType.NUMERIC) |
| 23 | private Long roleId; | 22 | private Long roleId; |
| 24 | 23 | ||
| 25 | /** 角色名称 */ | 24 | /** 角色名称 */ |
| @@ -2,12 +2,12 @@ package com.ruoyi.system.domain.entity; | @@ -2,12 +2,12 @@ package com.ruoyi.system.domain.entity; | ||
| 2 | 2 | ||
| 3 | import com.fasterxml.jackson.annotation.JsonIgnore; | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| 4 | import com.fasterxml.jackson.annotation.JsonProperty; | 4 | import com.fasterxml.jackson.annotation.JsonProperty; |
| 5 | -import com.ruoyi.system.domain.tool.Excel; | ||
| 6 | -import com.ruoyi.system.domain.tool.Excel.ColumnType; | ||
| 7 | -import com.ruoyi.system.domain.tool.Excel.Type; | ||
| 8 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 9 | -import com.ruoyi.system.domain.tool.Excels; | ||
| 10 | -import com.ruoyi.system.domain.tool.Xss; | 5 | +import com.ruoyi.common.annotation.Excel; |
| 6 | +import com.ruoyi.common.annotation.Excel.ColumnType; | ||
| 7 | +import com.ruoyi.common.annotation.Excel.Type; | ||
| 8 | +import com.ruoyi.common.annotation.Excels; | ||
| 9 | +import com.ruoyi.common.utils.xss.Xss; | ||
| 10 | +import com.ruoyi.common.tool.BaseEntity; | ||
| 11 | import org.apache.commons.lang3.builder.ToStringBuilder; | 11 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 12 | import org.apache.commons.lang3.builder.ToStringStyle; | 12 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 13 | 13 |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.iot; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | 3 | +import com.ruoyi.common.tool.BaseEntity; |
| 4 | import org.apache.commons.lang3.builder.ToStringBuilder; | 4 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 5 | import org.apache.commons.lang3.builder.ToStringStyle; | 5 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 6 | 6 | ||
| 7 | -import java.io.Serializable; | ||
| 8 | - | ||
| 9 | /** | 7 | /** |
| 10 | * 设备告警对象 iot_alert | 8 | * 设备告警对象 iot_alert |
| 11 | * | 9 | * |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.iot; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | 3 | +import com.ruoyi.common.tool.BaseEntity; |
| 4 | import org.apache.commons.lang3.builder.ToStringBuilder; | 4 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 5 | import org.apache.commons.lang3.builder.ToStringStyle; | 5 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 6 | 6 |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.iot; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | ||
| 4 | -import org.apache.commons.lang3.builder.ToStringBuilder; | ||
| 5 | -import org.apache.commons.lang3.builder.ToStringStyle; | 3 | +import com.ruoyi.common.annotation.PublicSQLConfig; |
| 6 | import io.swagger.annotations.ApiModel; | 4 | import io.swagger.annotations.ApiModel; |
| 7 | import io.swagger.annotations.ApiModelProperty; | 5 | import io.swagger.annotations.ApiModelProperty; |
| 8 | 6 |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.iot; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | ||
| 5 | -import org.apache.commons.lang3.builder.ToStringBuilder; | ||
| 6 | -import org.apache.commons.lang3.builder.ToStringStyle; | 3 | +import com.ruoyi.common.annotation.PublicSQLConfig; |
| 7 | import io.swagger.annotations.ApiModel; | 4 | import io.swagger.annotations.ApiModel; |
| 8 | import io.swagger.annotations.ApiModelProperty; | 5 | import io.swagger.annotations.ApiModelProperty; |
| 6 | +import org.apache.commons.lang3.builder.ToStringBuilder; | ||
| 7 | +import org.apache.commons.lang3.builder.ToStringStyle; | ||
| 9 | 8 | ||
| 10 | import java.io.Serializable; | 9 | import java.io.Serializable; |
| 11 | 10 |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.iot; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.common.annotation.PublicSQLConfig; |
| 5 | import org.apache.commons.lang3.builder.ToStringBuilder; | 4 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 6 | import org.apache.commons.lang3.builder.ToStringStyle; | 5 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 7 | import io.swagger.annotations.ApiModel; | 6 | import io.swagger.annotations.ApiModel; |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.iot; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.common.tool.BaseEntity; |
| 4 | +import com.ruoyi.common.annotation.PublicSQLConfig; | ||
| 5 | import io.swagger.annotations.ApiModel; | 5 | import io.swagger.annotations.ApiModel; |
| 6 | import io.swagger.annotations.ApiModelProperty; | 6 | import io.swagger.annotations.ApiModelProperty; |
| 7 | import org.apache.commons.lang3.builder.ToStringBuilder; | 7 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.iot; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.common.annotation.PublicSQLConfig; |
| 5 | import org.apache.commons.lang3.builder.ToStringBuilder; | 4 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 6 | import org.apache.commons.lang3.builder.ToStringStyle; | 5 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 7 | import io.swagger.annotations.ApiModel; | 6 | import io.swagger.annotations.ApiModel; |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.iot; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.system.domain.user.UserTerminalGroupRelation; |
| 4 | +import com.ruoyi.common.annotation.PublicSQLConfig; | ||
| 4 | import org.apache.commons.lang3.builder.ToStringBuilder; | 5 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 5 | import org.apache.commons.lang3.builder.ToStringStyle; | 6 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 6 | import io.swagger.annotations.ApiModel; | 7 | import io.swagger.annotations.ApiModel; |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.iot; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.common.annotation.PublicSQLConfig; |
| 5 | import org.apache.commons.lang3.builder.ToStringBuilder; | 4 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 6 | import org.apache.commons.lang3.builder.ToStringStyle; | 5 | import org.apache.commons.lang3.builder.ToStringStyle; |
| 7 | import io.swagger.annotations.ApiModel; | 6 | import io.swagger.annotations.ApiModel; |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.iot; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.common.tool.BaseEntity; |
| 4 | +import com.ruoyi.common.annotation.PublicSQLConfig; | ||
| 5 | import io.swagger.annotations.ApiModel; | 5 | import io.swagger.annotations.ApiModel; |
| 6 | import io.swagger.annotations.ApiModelProperty; | 6 | import io.swagger.annotations.ApiModelProperty; |
| 7 | import org.apache.commons.lang3.builder.ToStringBuilder; | 7 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.sys; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.Excel; | ||
| 5 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.common.tool.BaseEntity; |
| 4 | +import com.ruoyi.common.annotation.PublicSQLConfig; | ||
| 5 | +import com.ruoyi.common.annotation.Excel; | ||
| 6 | import io.swagger.annotations.ApiModel; | 6 | import io.swagger.annotations.ApiModel; |
| 7 | import io.swagger.annotations.ApiModelProperty; | 7 | import io.swagger.annotations.ApiModelProperty; |
| 8 | import org.apache.commons.lang3.builder.ToStringBuilder; | 8 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.sys; |
| 2 | 2 | ||
| 3 | import com.fasterxml.jackson.annotation.JsonFormat; | 3 | import com.fasterxml.jackson.annotation.JsonFormat; |
| 4 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 5 | -import com.ruoyi.system.domain.tool.Excel; | ||
| 6 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 4 | +import com.ruoyi.common.annotation.Excel; |
| 5 | +import com.ruoyi.common.tool.BaseEntity; | ||
| 6 | +import com.ruoyi.common.annotation.PublicSQLConfig; | ||
| 7 | 7 | ||
| 8 | import java.util.Date; | 8 | import java.util.Date; |
| 9 | 9 |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.sys; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | ||
| 5 | -import com.ruoyi.system.domain.tool.Xss; | 3 | +import com.ruoyi.common.tool.BaseEntity; |
| 4 | +import com.ruoyi.common.annotation.PublicSQLConfig; | ||
| 5 | +import com.ruoyi.common.utils.xss.Xss; | ||
| 6 | import io.swagger.annotations.ApiModel; | 6 | import io.swagger.annotations.ApiModel; |
| 7 | import io.swagger.annotations.ApiModelProperty; | 7 | import io.swagger.annotations.ApiModelProperty; |
| 8 | import org.apache.commons.lang3.builder.ToStringBuilder; | 8 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.sys; |
| 2 | 2 | ||
| 3 | import com.fasterxml.jackson.annotation.JsonFormat; | 3 | import com.fasterxml.jackson.annotation.JsonFormat; |
| 4 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 5 | -import com.ruoyi.system.domain.tool.Excel; | ||
| 6 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 4 | +import com.ruoyi.common.tool.BaseEntity; |
| 5 | +import com.ruoyi.common.annotation.Excel; | ||
| 6 | +import com.ruoyi.common.annotation.PublicSQLConfig; | ||
| 7 | import io.swagger.annotations.ApiModel; | 7 | import io.swagger.annotations.ApiModel; |
| 8 | import io.swagger.annotations.ApiModelProperty; | 8 | import io.swagger.annotations.ApiModelProperty; |
| 9 | 9 |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.sys; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.Excel; | ||
| 5 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.common.tool.BaseEntity; |
| 4 | +import com.ruoyi.common.annotation.Excel; | ||
| 5 | +import com.ruoyi.common.annotation.PublicSQLConfig; | ||
| 6 | import io.swagger.annotations.ApiModel; | 6 | import io.swagger.annotations.ApiModel; |
| 7 | import io.swagger.annotations.ApiModelProperty; | 7 | import io.swagger.annotations.ApiModelProperty; |
| 8 | import org.apache.commons.lang3.builder.ToStringBuilder; | 8 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.user; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | ||
| 5 | import io.swagger.annotations.ApiModel; | 3 | import io.swagger.annotations.ApiModel; |
| 6 | import io.swagger.annotations.ApiModelProperty; | 4 | import io.swagger.annotations.ApiModelProperty; |
| 7 | import org.apache.commons.lang3.builder.ToStringBuilder; | 5 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| @@ -14,10 +12,8 @@ import org.apache.commons.lang3.builder.ToStringStyle; | @@ -14,10 +12,8 @@ import org.apache.commons.lang3.builder.ToStringStyle; | ||
| 14 | * @date 2022-11-22 | 12 | * @date 2022-11-22 |
| 15 | */ | 13 | */ |
| 16 | @ApiModel("终端分组") | 14 | @ApiModel("终端分组") |
| 17 | -public class UserTerminalGroup extends BaseEntity | 15 | +public class UserTerminalGroup |
| 18 | { | 16 | { |
| 19 | - @PublicSQLConfig(isSelect=false) | ||
| 20 | - private static final long serialVersionUID = 1L; | ||
| 21 | 17 | ||
| 22 | /** 创建时间 */ | 18 | /** 创建时间 */ |
| 23 | @ApiModelProperty("创建时间") | 19 | @ApiModelProperty("创建时间") |
| 1 | -package com.ruoyi.system.domain; | 1 | +package com.ruoyi.system.domain.user; |
| 2 | 2 | ||
| 3 | -import com.ruoyi.system.domain.tool.BaseEntity; | ||
| 4 | -import com.ruoyi.system.domain.tool.PublicSQLConfig; | 3 | +import com.ruoyi.common.tool.BaseEntity; |
| 5 | import io.swagger.annotations.ApiModel; | 4 | import io.swagger.annotations.ApiModel; |
| 6 | import io.swagger.annotations.ApiModelProperty; | 5 | import io.swagger.annotations.ApiModelProperty; |
| 7 | import org.apache.commons.lang3.builder.ToStringBuilder; | 6 | import org.apache.commons.lang3.builder.ToStringBuilder; |
| @@ -16,8 +15,6 @@ import org.apache.commons.lang3.builder.ToStringStyle; | @@ -16,8 +15,6 @@ import org.apache.commons.lang3.builder.ToStringStyle; | ||
| 16 | @ApiModel("终端分组关系") | 15 | @ApiModel("终端分组关系") |
| 17 | public class UserTerminalGroupRelation extends BaseEntity | 16 | public class UserTerminalGroupRelation extends BaseEntity |
| 18 | { | 17 | { |
| 19 | - @PublicSQLConfig(isSelect=false) | ||
| 20 | - private static final long serialVersionUID = 1L; | ||
| 21 | 18 | ||
| 22 | /** 创建时间 */ | 19 | /** 创建时间 */ |
| 23 | @ApiModelProperty("创建时间") | 20 | @ApiModelProperty("创建时间") |
lh-common/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>Luhui</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + </parent> | ||
| 11 | + | ||
| 12 | + <artifactId>lh-common</artifactId> | ||
| 13 | + | ||
| 14 | + <properties> | ||
| 15 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 16 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 17 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 18 | + </properties> | ||
| 19 | + | ||
| 20 | + <modules> | ||
| 21 | + <module>lh-domain</module> | ||
| 22 | + <module>ruoyi-common</module> | ||
| 23 | + <module>ruoyi-system</module> | ||
| 24 | + <module>lh-public-dao</module> | ||
| 25 | + <module>ruoyi-common-core</module> | ||
| 26 | + <module>ruoyi-common-redis</module> | ||
| 27 | + <module>ruoyi-common-security</module> | ||
| 28 | + <module>ruoyi-auth</module> | ||
| 29 | + <module>lh-common-datasource</module> | ||
| 30 | + <module>lh-common-log</module> | ||
| 31 | + <module>lh-quartz</module> | ||
| 32 | + <module>ruoyi-generator</module> | ||
| 33 | + </modules> | ||
| 34 | + | ||
| 35 | + <packaging>pom</packaging> | ||
| 36 | + <description> | ||
| 37 | + lh-common通用模块 | ||
| 38 | + </description> | ||
| 39 | +</project> |
lh-jar/lh-jar-action/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>Luhui</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + <relativePath>../../pom.xml</relativePath> | ||
| 11 | + </parent> | ||
| 12 | + | ||
| 13 | + <artifactId>lh-jar-action</artifactId> | ||
| 14 | + | ||
| 15 | + <properties> | ||
| 16 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 17 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 18 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 19 | + </properties> | ||
| 20 | + | ||
| 21 | + <dependencies> | ||
| 22 | + <dependency> | ||
| 23 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 24 | + <artifactId>lh-jar-sys-service</artifactId> | ||
| 25 | + </dependency> | ||
| 26 | + </dependencies> | ||
| 27 | +</project> |
| 1 | +package com.zhonglai.luhui.action; | ||
| 2 | + | ||
| 3 | +import com.github.pagehelper.PageHelper; | ||
| 4 | +import com.github.pagehelper.PageInfo; | ||
| 5 | +import com.ruoyi.common.constant.HttpStatus; | ||
| 6 | +import com.ruoyi.common.core.domain.AjaxResult; | ||
| 7 | +import com.ruoyi.common.core.page.PageDomain; | ||
| 8 | +import com.ruoyi.common.core.page.TableDataInfo; | ||
| 9 | +import com.ruoyi.common.core.page.TableSupport; | ||
| 10 | +import com.ruoyi.common.utils.DateUtils; | ||
| 11 | +import com.ruoyi.common.utils.PageUtils; | ||
| 12 | +import com.ruoyi.common.utils.StringUtils; | ||
| 13 | +import com.ruoyi.common.utils.sql.SqlUtil; | ||
| 14 | +import org.slf4j.Logger; | ||
| 15 | +import org.slf4j.LoggerFactory; | ||
| 16 | +import org.springframework.web.bind.WebDataBinder; | ||
| 17 | +import org.springframework.web.bind.annotation.InitBinder; | ||
| 18 | + | ||
| 19 | +import java.beans.PropertyEditorSupport; | ||
| 20 | +import java.util.Date; | ||
| 21 | +import java.util.List; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * web层通用数据处理 | ||
| 25 | + * | ||
| 26 | + * @author ruoyi | ||
| 27 | + */ | ||
| 28 | +public class BaseController | ||
| 29 | +{ | ||
| 30 | + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 | ||
| 34 | + */ | ||
| 35 | + @InitBinder | ||
| 36 | + public void initBinder(WebDataBinder binder) | ||
| 37 | + { | ||
| 38 | + // Date 类型转换 | ||
| 39 | + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() | ||
| 40 | + { | ||
| 41 | + @Override | ||
| 42 | + public void setAsText(String text) | ||
| 43 | + { | ||
| 44 | + setValue(DateUtils.parseDate(text)); | ||
| 45 | + } | ||
| 46 | + }); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 设置请求分页数据 | ||
| 51 | + */ | ||
| 52 | + protected void startPage() | ||
| 53 | + { | ||
| 54 | + PageUtils.startPage(); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 设置请求排序数据 | ||
| 59 | + */ | ||
| 60 | + protected void startOrderBy() | ||
| 61 | + { | ||
| 62 | + PageDomain pageDomain = TableSupport.buildPageRequest(); | ||
| 63 | + if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) | ||
| 64 | + { | ||
| 65 | + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); | ||
| 66 | + PageHelper.orderBy(orderBy); | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + /** | ||
| 71 | + * 清理分页的线程变量 | ||
| 72 | + */ | ||
| 73 | + protected void clearPage() | ||
| 74 | + { | ||
| 75 | + PageUtils.clearPage(); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * 响应请求分页数据 | ||
| 80 | + */ | ||
| 81 | + @SuppressWarnings({ "rawtypes", "unchecked" }) | ||
| 82 | + protected TableDataInfo getDataTable(List<?> list) | ||
| 83 | + { | ||
| 84 | + TableDataInfo rspData = new TableDataInfo(); | ||
| 85 | + rspData.setCode(HttpStatus.SUCCESS); | ||
| 86 | + rspData.setMsg("查询成功"); | ||
| 87 | + rspData.setRows(list); | ||
| 88 | + rspData.setTotal(new PageInfo(list).getTotal()); | ||
| 89 | + return rspData; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + /** | ||
| 93 | + * 返回成功 | ||
| 94 | + */ | ||
| 95 | + public AjaxResult success() | ||
| 96 | + { | ||
| 97 | + return AjaxResult.success(); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + /** | ||
| 101 | + * 返回失败消息 | ||
| 102 | + */ | ||
| 103 | + public AjaxResult error() | ||
| 104 | + { | ||
| 105 | + return AjaxResult.error(); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * 返回成功消息 | ||
| 110 | + */ | ||
| 111 | + public AjaxResult success(String message) | ||
| 112 | + { | ||
| 113 | + return AjaxResult.success(message); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + /** | ||
| 117 | + * 返回失败消息 | ||
| 118 | + */ | ||
| 119 | + public AjaxResult error(String message) | ||
| 120 | + { | ||
| 121 | + return AjaxResult.error(message); | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + /** | ||
| 125 | + * 响应返回结果 | ||
| 126 | + * | ||
| 127 | + * @param rows 影响行数 | ||
| 128 | + * @return 操作结果 | ||
| 129 | + */ | ||
| 130 | + protected AjaxResult toAjax(int rows) | ||
| 131 | + { | ||
| 132 | + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + /** | ||
| 136 | + * 响应返回结果 | ||
| 137 | + * | ||
| 138 | + * @param result 结果 | ||
| 139 | + * @return 操作结果 | ||
| 140 | + */ | ||
| 141 | + protected AjaxResult toAjax(boolean result) | ||
| 142 | + { | ||
| 143 | + return result ? success() : error(); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + /** | ||
| 147 | + * 页面跳转 | ||
| 148 | + */ | ||
| 149 | + public String redirect(String url) | ||
| 150 | + { | ||
| 151 | + return StringUtils.format("redirect:{}", url); | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | +} |
lh-jar/lh-jar-chatgpt/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>Luhui</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + <relativePath>../../pom.xml</relativePath> | ||
| 11 | + </parent> | ||
| 12 | + | ||
| 13 | + <artifactId>lh-jar-chatgpt</artifactId> | ||
| 14 | + | ||
| 15 | + <properties> | ||
| 16 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 17 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 18 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 19 | + </properties> | ||
| 20 | + | ||
| 21 | + <dependencies> | ||
| 22 | + <dependency> | ||
| 23 | + <groupId>org.springframework.boot</groupId> | ||
| 24 | + <artifactId>spring-boot-starter-web</artifactId> | ||
| 25 | + </dependency> | ||
| 26 | + | ||
| 27 | + <dependency> | ||
| 28 | + <groupId>org.projectlombok</groupId> | ||
| 29 | + <artifactId>lombok</artifactId> | ||
| 30 | + <optional>true</optional> | ||
| 31 | + </dependency> | ||
| 32 | + <dependency> | ||
| 33 | + <groupId>org.springframework.boot</groupId> | ||
| 34 | + <artifactId>spring-boot-starter-test</artifactId> | ||
| 35 | + <scope>test</scope> | ||
| 36 | + </dependency> | ||
| 37 | + <dependency> | ||
| 38 | + <groupId>com.unfbx</groupId> | ||
| 39 | + <artifactId>chatgpt-java</artifactId> | ||
| 40 | + </dependency> | ||
| 41 | + <dependency> | ||
| 42 | + <groupId>org.springframework.boot</groupId> | ||
| 43 | + <artifactId>spring-boot-starter-websocket</artifactId> | ||
| 44 | + </dependency> | ||
| 45 | + <dependency> | ||
| 46 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 47 | + <artifactId>lh-common-swagger</artifactId> | ||
| 48 | + </dependency> | ||
| 49 | + </dependencies> | ||
| 50 | +</project> |
| 1 | +package com.zhonglai.luhui.chatgpt.config; | ||
| 2 | + | ||
| 3 | +import cn.hutool.cache.CacheUtil; | ||
| 4 | +import cn.hutool.cache.impl.TimedCache; | ||
| 5 | +import cn.hutool.core.date.DateUnit; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 描述: | ||
| 9 | + * | ||
| 10 | + * @author https:www.unfbx.com | ||
| 11 | + * @date 2023-03-10 | ||
| 12 | + */ | ||
| 13 | +public class LocalCache { | ||
| 14 | + /** | ||
| 15 | + * 缓存时长 | ||
| 16 | + */ | ||
| 17 | + public static final long TIMEOUT = 5 * DateUnit.MINUTE.getMillis(); | ||
| 18 | + /** | ||
| 19 | + * 清理间隔 | ||
| 20 | + */ | ||
| 21 | + private static final long CLEAN_TIMEOUT = 5 * DateUnit.MINUTE.getMillis(); | ||
| 22 | + /** | ||
| 23 | + * 缓存对象 | ||
| 24 | + */ | ||
| 25 | + public static final TimedCache<String, Object> CACHE = CacheUtil.newTimedCache(TIMEOUT); | ||
| 26 | + | ||
| 27 | + static { | ||
| 28 | + //启动定时任务 | ||
| 29 | + CACHE.schedulePrune(CLEAN_TIMEOUT); | ||
| 30 | + } | ||
| 31 | +} |
lh-jar/lh-jar-chatgpt/src/main/java/com/zhonglai/luhui/chatgpt/config/OpenAiStreamClientConfig.java
0 → 100644
| 1 | +package com.zhonglai.luhui.chatgpt.config; | ||
| 2 | + | ||
| 3 | +import com.unfbx.chatgpt.OpenAiStreamClient; | ||
| 4 | +import com.unfbx.chatgpt.function.KeyRandomStrategy; | ||
| 5 | +import com.unfbx.chatgpt.interceptor.OpenAILogger; | ||
| 6 | +import okhttp3.OkHttpClient; | ||
| 7 | +import okhttp3.logging.HttpLoggingInterceptor; | ||
| 8 | +import org.springframework.beans.factory.annotation.Value; | ||
| 9 | +import org.springframework.context.annotation.Bean; | ||
| 10 | +import org.springframework.context.annotation.Configuration; | ||
| 11 | + | ||
| 12 | +import java.net.InetSocketAddress; | ||
| 13 | +import java.net.Proxy; | ||
| 14 | +import java.util.List; | ||
| 15 | +import java.util.concurrent.TimeUnit; | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +@Configuration | ||
| 19 | +public class OpenAiStreamClientConfig { | ||
| 20 | + @Value("${chatgpt.token}") | ||
| 21 | + private List<String> apiKey; | ||
| 22 | + @Value("${chatgpt.apiHost}") | ||
| 23 | + private String apiHost; | ||
| 24 | + | ||
| 25 | + @Value("${chatgpt.proxy.isProxy:false}") | ||
| 26 | + private Boolean isProxy; | ||
| 27 | + @Value("${chatgpt.proxy.host:127.0.0.1}") | ||
| 28 | + private String host; | ||
| 29 | + @Value("${chatgpt.proxy.port:7890}") | ||
| 30 | + private Integer port; | ||
| 31 | + @Bean | ||
| 32 | + public OpenAiStreamClient openAiStreamClient() { | ||
| 33 | + HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger()); | ||
| 34 | + //!!!!!!测试或者发布到服务器千万不要配置Level == BODY!!!! | ||
| 35 | + //!!!!!!测试或者发布到服务器千万不要配置Level == BODY!!!! | ||
| 36 | + httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS); | ||
| 37 | + OkHttpClient.Builder builder = new OkHttpClient | ||
| 38 | + .Builder(); | ||
| 39 | + if(isProxy) | ||
| 40 | + { | ||
| 41 | + //本地开发需要配置代理地址 | ||
| 42 | + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port)); | ||
| 43 | + builder.proxy(proxy); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + OkHttpClient okHttpClient = builder.addInterceptor(httpLoggingInterceptor) | ||
| 47 | + .connectTimeout(30, TimeUnit.SECONDS) | ||
| 48 | + .writeTimeout(600, TimeUnit.SECONDS) | ||
| 49 | + .readTimeout(600, TimeUnit.SECONDS) | ||
| 50 | + .build(); | ||
| 51 | + return OpenAiStreamClient | ||
| 52 | + .builder() | ||
| 53 | + .apiHost(apiHost) | ||
| 54 | + .apiKey(apiKey) | ||
| 55 | + //自定义key使用策略 默认随机策略 | ||
| 56 | + .keyStrategy(new KeyRandomStrategy()) | ||
| 57 | + .okHttpClient(okHttpClient) | ||
| 58 | + .build(); | ||
| 59 | + } | ||
| 60 | +} |
lh-jar/lh-jar-chatgpt/src/main/java/com/zhonglai/luhui/chatgpt/config/WebSocketConfig.java
0 → 100644
| 1 | +package com.zhonglai.luhui.chatgpt.config; | ||
| 2 | + | ||
| 3 | +import org.springframework.context.annotation.Bean; | ||
| 4 | +import org.springframework.context.annotation.Configuration; | ||
| 5 | +import org.springframework.web.socket.server.standard.ServerEndpointExporter; | ||
| 6 | + | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 描述: | ||
| 10 | + * | ||
| 11 | + * @author https:www.unfbx.com | ||
| 12 | + * @since 2023-03-23 | ||
| 13 | + */ | ||
| 14 | +@Configuration | ||
| 15 | +public class WebSocketConfig { | ||
| 16 | + /** | ||
| 17 | + * 这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket,如果你使用外置的tomcat就不需要该配置文件 | ||
| 18 | + */ | ||
| 19 | + @Bean | ||
| 20 | + public ServerEndpointExporter serverEndpointExporter() { | ||
| 21 | + return new ServerEndpointExporter(); | ||
| 22 | + } | ||
| 23 | +} |
lh-jar/lh-jar-chatgpt/src/main/java/com/zhonglai/luhui/chatgpt/controller/ChatController.java
0 → 100644
| 1 | +package com.zhonglai.luhui.chatgpt.controller; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 4 | +import com.unfbx.chatgpt.entity.chat.ChatCompletion; | ||
| 5 | +import com.unfbx.chatgpt.exception.BaseException; | ||
| 6 | +import com.unfbx.chatgpt.exception.CommonError; | ||
| 7 | +import com.zhonglai.luhui.chatgpt.controller.request.ChatRequest; | ||
| 8 | +import com.zhonglai.luhui.chatgpt.controller.response.ChatResponse; | ||
| 9 | +import com.zhonglai.luhui.chatgpt.service.SseService; | ||
| 10 | +import lombok.extern.slf4j.Slf4j; | ||
| 11 | +import org.springframework.stereotype.Controller; | ||
| 12 | +import org.springframework.web.bind.annotation.*; | ||
| 13 | +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; | ||
| 14 | + | ||
| 15 | +import javax.servlet.http.HttpServletResponse; | ||
| 16 | +import java.util.Map; | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * 描述: | ||
| 20 | + * | ||
| 21 | + * @author https:www.unfbx.com | ||
| 22 | + * @date 2023-03-01 | ||
| 23 | + */ | ||
| 24 | +@Controller | ||
| 25 | +@Slf4j | ||
| 26 | +public class ChatController { | ||
| 27 | + | ||
| 28 | + private final SseService sseService; | ||
| 29 | + | ||
| 30 | + public ChatController(SseService sseService) { | ||
| 31 | + this.sseService = sseService; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 创建sse连接 | ||
| 36 | + * | ||
| 37 | + * @param headers | ||
| 38 | + * @return | ||
| 39 | + */ | ||
| 40 | + @CrossOrigin | ||
| 41 | + @GetMapping("/createSse") | ||
| 42 | + public SseEmitter createConnect(@RequestHeader Map<String, String> headers) { | ||
| 43 | + String uid = getUid(headers); | ||
| 44 | + return sseService.createSse(uid); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * 聊天接口 | ||
| 49 | + * | ||
| 50 | + * @param chatRequest | ||
| 51 | + * @param headers | ||
| 52 | + */ | ||
| 53 | + @CrossOrigin | ||
| 54 | + @PostMapping("/chat") | ||
| 55 | + @ResponseBody | ||
| 56 | + public ChatResponse sseChat(@RequestBody ChatRequest chatRequest, @RequestHeader Map<String, String> headers, HttpServletResponse response) { | ||
| 57 | + String uid = getUid(headers); | ||
| 58 | + return sseService.sseChat(uid, chatRequest, ChatCompletion.Model.GPT_3_5_TURBO_0301,null); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 关闭连接 | ||
| 63 | + * | ||
| 64 | + * @param headers | ||
| 65 | + */ | ||
| 66 | + @CrossOrigin | ||
| 67 | + @GetMapping("/closeSse") | ||
| 68 | + public void closeConnect(@RequestHeader Map<String, String> headers) { | ||
| 69 | + String uid = getUid(headers); | ||
| 70 | + sseService.closeSse(uid); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + @GetMapping("") | ||
| 74 | + public String index() { | ||
| 75 | + return "1.html"; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + @GetMapping("/websocket") | ||
| 79 | + public String websocket() { | ||
| 80 | + return "websocket.html"; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + /** | ||
| 84 | + * 获取uid | ||
| 85 | + * | ||
| 86 | + * @param headers | ||
| 87 | + * @return | ||
| 88 | + */ | ||
| 89 | + private String getUid(Map<String, String> headers) { | ||
| 90 | + String uid = headers.get("uid"); | ||
| 91 | + if (StrUtil.isBlank(uid)) { | ||
| 92 | + throw new BaseException(CommonError.SYS_ERROR); | ||
| 93 | + } | ||
| 94 | + return uid; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + | ||
| 98 | +} |
lh-jar/lh-jar-chatgpt/src/main/java/com/zhonglai/luhui/chatgpt/controller/request/ChatRequest.java
0 → 100644
| 1 | +package com.zhonglai.luhui.chatgpt.controller.request; | ||
| 2 | + | ||
| 3 | +import com.unfbx.chatgpt.entity.chat.ChatCompletion; | ||
| 4 | +import io.swagger.annotations.ApiModel; | ||
| 5 | +import io.swagger.annotations.ApiModelProperty; | ||
| 6 | +import lombok.Data; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 描述: | ||
| 12 | + * | ||
| 13 | + * @author https:www.unfbx.com | ||
| 14 | + * @sine 2023-04-08 | ||
| 15 | + */ | ||
| 16 | +@ApiModel("chatGPT流请求参数") | ||
| 17 | +@Data | ||
| 18 | +public class ChatRequest { | ||
| 19 | + /** | ||
| 20 | + * 客户端发送的问题参数 | ||
| 21 | + */ | ||
| 22 | + @ApiModelProperty("问题(支持传历史问题,来设置上下文)") | ||
| 23 | + private List<String> msg; | ||
| 24 | + @ApiModelProperty("模型") | ||
| 25 | + private ChatCompletion.Model model; | ||
| 26 | +} |
lh-jar/lh-jar-chatgpt/src/main/java/com/zhonglai/luhui/chatgpt/controller/response/ChatResponse.java
0 → 100644
| 1 | +package com.zhonglai.luhui.chatgpt.controller.response; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 描述: | ||
| 8 | + * | ||
| 9 | + * @author https:www.unfbx.com | ||
| 10 | + * @sine 2023-04-08 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class ChatResponse { | ||
| 14 | + /** | ||
| 15 | + * 问题消耗tokens | ||
| 16 | + */ | ||
| 17 | + @JsonProperty("question_tokens") | ||
| 18 | + private long questionTokens = 0; | ||
| 19 | +} |
| 1 | +package com.zhonglai.luhui.chatgpt.entity; | ||
| 2 | + | ||
| 3 | +import com.unfbx.chatgpt.entity.chat.Message; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import java.util.List; | ||
| 7 | +/** | ||
| 8 | + * 描述: | ||
| 9 | + * | ||
| 10 | + * @author https:www.unfbx.com | ||
| 11 | + * @date 2023-04-10 | ||
| 12 | + */ | ||
| 13 | +@Data | ||
| 14 | +public class Chat { | ||
| 15 | + | ||
| 16 | + private String uid; | ||
| 17 | + | ||
| 18 | + private List<Message> message; | ||
| 19 | +} |
| 1 | +package com.zhonglai.luhui.chatgpt.event; | ||
| 2 | + | ||
| 3 | +import org.springframework.http.MediaType; | ||
| 4 | +import org.springframework.lang.Nullable; | ||
| 5 | +import org.springframework.util.StringUtils; | ||
| 6 | +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; | ||
| 7 | +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; | ||
| 8 | + | ||
| 9 | +import java.nio.charset.StandardCharsets; | ||
| 10 | +import java.util.Collections; | ||
| 11 | +import java.util.LinkedHashSet; | ||
| 12 | +import java.util.Set; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * 描述: | ||
| 16 | + * | ||
| 17 | + * @author https:www.unfbx.com | ||
| 18 | + * @date 2023-02-28 | ||
| 19 | + */ | ||
| 20 | +public class MyEvent implements SseEmitter.SseEventBuilder { | ||
| 21 | + private static final MediaType TEXT_PLAIN = new MediaType("text", "plain", StandardCharsets.UTF_8); | ||
| 22 | + | ||
| 23 | + private final Set<ResponseBodyEmitter.DataWithMediaType> dataToSend = new LinkedHashSet<>(4); | ||
| 24 | + | ||
| 25 | + @Nullable | ||
| 26 | + private StringBuilder sb; | ||
| 27 | + | ||
| 28 | + @Override | ||
| 29 | + public SseEmitter.SseEventBuilder id(String id) { | ||
| 30 | + append("id:").append(id).append('\n'); | ||
| 31 | + return this; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + @Override | ||
| 35 | + public SseEmitter.SseEventBuilder name(String name) { | ||
| 36 | + append("event:").append(name).append('\n'); | ||
| 37 | + return this; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + public SseEmitter.SseEventBuilder reconnectTime(long reconnectTimeMillis) { | ||
| 42 | + append("retry:").append(String.valueOf(reconnectTimeMillis)).append('\n'); | ||
| 43 | + return this; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + @Override | ||
| 47 | + public SseEmitter.SseEventBuilder comment(String comment) { | ||
| 48 | + append(':').append(comment).append('\n'); | ||
| 49 | + return this; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + @Override | ||
| 53 | + public SseEmitter.SseEventBuilder data(Object object) { | ||
| 54 | + return data(object, null); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + @Override | ||
| 58 | + public SseEmitter.SseEventBuilder data(Object object, @Nullable MediaType mediaType) { | ||
| 59 | + saveAppendedText(); | ||
| 60 | + this.dataToSend.add(new ResponseBodyEmitter.DataWithMediaType(object, mediaType)); | ||
| 61 | + return this; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + MyEvent append(String text) { | ||
| 65 | + if (this.sb == null) { | ||
| 66 | + this.sb = new StringBuilder(); | ||
| 67 | + } | ||
| 68 | + this.sb.append(text); | ||
| 69 | + return this; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + MyEvent append(char ch) { | ||
| 73 | + if (this.sb == null) { | ||
| 74 | + this.sb = new StringBuilder(); | ||
| 75 | + } | ||
| 76 | + this.sb.append(ch); | ||
| 77 | + return this; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + @Override | ||
| 81 | + public Set<ResponseBodyEmitter.DataWithMediaType> build() { | ||
| 82 | + if (!StringUtils.hasLength(this.sb) && this.dataToSend.isEmpty()) { | ||
| 83 | + return Collections.emptySet(); | ||
| 84 | + } | ||
| 85 | + append('\n'); | ||
| 86 | + saveAppendedText(); | ||
| 87 | + return this.dataToSend; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + private void saveAppendedText() { | ||
| 91 | + if (this.sb != null) { | ||
| 92 | + this.dataToSend.add(new ResponseBodyEmitter.DataWithMediaType(this.sb.toString(), TEXT_PLAIN)); | ||
| 93 | + this.sb = null; | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | +} |
| 1 | +package com.zhonglai.luhui.chatgpt.listener; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 4 | +import com.unfbx.chatgpt.entity.chat.ChatCompletionResponse; | ||
| 5 | +import com.unfbx.chatgpt.entity.chat.Message; | ||
| 6 | +import com.zhonglai.luhui.chatgpt.event.MyEvent; | ||
| 7 | +import lombok.SneakyThrows; | ||
| 8 | +import lombok.extern.slf4j.Slf4j; | ||
| 9 | +import okhttp3.Response; | ||
| 10 | +import okhttp3.ResponseBody; | ||
| 11 | +import okhttp3.sse.EventSource; | ||
| 12 | +import okhttp3.sse.EventSourceListener; | ||
| 13 | +import org.springframework.http.MediaType; | ||
| 14 | +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; | ||
| 15 | + | ||
| 16 | +import java.util.Objects; | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * 描述:OpenAIEventSourceListener | ||
| 20 | + * | ||
| 21 | + * @author https:www.unfbx.com | ||
| 22 | + * @date 2023-02-22 | ||
| 23 | + */ | ||
| 24 | +@Slf4j | ||
| 25 | +public class OpenAISSEEventSourceListener extends EventSourceListener { | ||
| 26 | + | ||
| 27 | + private long tokens; | ||
| 28 | + | ||
| 29 | + private SseEmitter sseEmitter; | ||
| 30 | + | ||
| 31 | + public OpenAISSEEventSourceListener(SseEmitter sseEmitter) { | ||
| 32 | + this.sseEmitter = sseEmitter; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * {@inheritDoc} | ||
| 37 | + */ | ||
| 38 | + @Override | ||
| 39 | + public void onOpen(EventSource eventSource, Response response) { | ||
| 40 | + log.info("OpenAI建立sse连接..."); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * {@inheritDoc} | ||
| 45 | + */ | ||
| 46 | + @SneakyThrows | ||
| 47 | + @Override | ||
| 48 | + public void onEvent(EventSource eventSource, String id, String type, String data) { | ||
| 49 | + log.info("OpenAI返回数据{}:{}",type, data); | ||
| 50 | + tokens += 1; | ||
| 51 | + if (data.equals("[DONE]")) { | ||
| 52 | + log.info("OpenAI返回数据结束了"); | ||
| 53 | +// sseEmitter.send(new MyEvent().data("<br/><br/>tokens:" + tokens(), MediaType.TEXT_EVENT_STREAM)); | ||
| 54 | +// sseEmitter.send(SseEmitter.event() | ||
| 55 | +// .id("[TOKENS]") | ||
| 56 | +// .data("<br/><br/>tokens:" + tokens()) | ||
| 57 | +// .reconnectTime(3000) | ||
| 58 | +// ); | ||
| 59 | +// sseEmitter.send("[DONE]", MediaType.TEXT_EVENT_STREAM); | ||
| 60 | +// sseEmitter.send(SseEmitter.event() | ||
| 61 | +// .id("[DONE]") | ||
| 62 | +// .data("[DONE]") | ||
| 63 | +// .reconnectTime(3000) | ||
| 64 | +// ); | ||
| 65 | + // 传输完成后自动关闭sse | ||
| 66 | + sseEmitter.complete(); | ||
| 67 | + return; | ||
| 68 | + } | ||
| 69 | + ObjectMapper mapper = new ObjectMapper(); | ||
| 70 | + ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class); // 读取Json | ||
| 71 | + | ||
| 72 | + try { | ||
| 73 | + Message delta = completionResponse.getChoices().get(0).getDelta(); | ||
| 74 | + if(null != delta.getContent()) | ||
| 75 | + { | ||
| 76 | + sseEmitter.send(new MyEvent().data(delta.getContent(), MediaType.TEXT_EVENT_STREAM)); | ||
| 77 | + } | ||
| 78 | +// sseEmitter.send(SseEmitter.event() | ||
| 79 | +// .id(completionResponse.getId()) | ||
| 80 | +// .data(delta.getContent()) | ||
| 81 | +// .reconnectTime(3000) | ||
| 82 | +// ); | ||
| 83 | + } catch (Exception e) { | ||
| 84 | + log.error("sse信息推送失败!"); | ||
| 85 | + eventSource.cancel(); | ||
| 86 | + e.printStackTrace(); | ||
| 87 | + } | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + | ||
| 91 | + @Override | ||
| 92 | + public void onClosed(EventSource eventSource) { | ||
| 93 | + log.info("流式输出返回值总共{}tokens", tokens() - 2); | ||
| 94 | + log.info("OpenAI关闭sse连接..."); | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + | ||
| 98 | + @SneakyThrows | ||
| 99 | + @Override | ||
| 100 | + public void onFailure(EventSource eventSource, Throwable t, Response response) { | ||
| 101 | + if (Objects.isNull(response)) { | ||
| 102 | + return; | ||
| 103 | + } | ||
| 104 | + ResponseBody body = response.body(); | ||
| 105 | + if (Objects.nonNull(body)) { | ||
| 106 | + log.error("OpenAI sse连接异常data:{},异常:{}", body.string(), t); | ||
| 107 | + } else { | ||
| 108 | + log.error("OpenAI sse连接异常data:{},异常:{}", response, t); | ||
| 109 | + } | ||
| 110 | + eventSource.cancel(); | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + /** | ||
| 114 | + * tokens | ||
| 115 | + * @return | ||
| 116 | + */ | ||
| 117 | + public long tokens() { | ||
| 118 | + return tokens; | ||
| 119 | + } | ||
| 120 | +} |
| 1 | +package com.zhonglai.luhui.chatgpt.listener; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 4 | +import com.unfbx.chatgpt.entity.chat.ChatCompletionResponse; | ||
| 5 | +import lombok.SneakyThrows; | ||
| 6 | +import lombok.extern.slf4j.Slf4j; | ||
| 7 | +import okhttp3.Response; | ||
| 8 | +import okhttp3.ResponseBody; | ||
| 9 | +import okhttp3.sse.EventSource; | ||
| 10 | +import okhttp3.sse.EventSourceListener; | ||
| 11 | + | ||
| 12 | +import javax.websocket.Session; | ||
| 13 | +import java.util.Objects; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 描述:OpenAI流式输出Socket接收 | ||
| 17 | + * | ||
| 18 | + * @author https:www.unfbx.com | ||
| 19 | + * @date 2023-03-23 | ||
| 20 | + */ | ||
| 21 | +@Slf4j | ||
| 22 | +public class OpenAIWebSocketEventSourceListener extends EventSourceListener { | ||
| 23 | + | ||
| 24 | + private Session session; | ||
| 25 | + | ||
| 26 | + public OpenAIWebSocketEventSourceListener(Session session) { | ||
| 27 | + this.session = session; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * {@inheritDoc} | ||
| 32 | + */ | ||
| 33 | + @Override | ||
| 34 | + public void onOpen(EventSource eventSource, Response response) { | ||
| 35 | + log.info("OpenAI建立sse连接..."); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * {@inheritDoc} | ||
| 40 | + */ | ||
| 41 | + @SneakyThrows | ||
| 42 | + @Override | ||
| 43 | + public void onEvent(EventSource eventSource, String id, String type, String data) { | ||
| 44 | + log.info("OpenAI返回数据:{}", data); | ||
| 45 | + if (data.equals("[DONE]")) { | ||
| 46 | + log.info("OpenAI返回数据结束了"); | ||
| 47 | + session.getBasicRemote().sendText("[DONE]"); | ||
| 48 | + return; | ||
| 49 | + } | ||
| 50 | + ObjectMapper mapper = new ObjectMapper(); | ||
| 51 | + ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class); // 读取Json | ||
| 52 | + String delta = mapper.writeValueAsString(completionResponse.getChoices().get(0).getDelta()); | ||
| 53 | + session.getBasicRemote().sendText(delta); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + | ||
| 57 | + @Override | ||
| 58 | + public void onClosed(EventSource eventSource) { | ||
| 59 | + log.info("OpenAI关闭sse连接..."); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + | ||
| 63 | + @SneakyThrows | ||
| 64 | + @Override | ||
| 65 | + public void onFailure(EventSource eventSource, Throwable t, Response response) { | ||
| 66 | + if (Objects.isNull(response)) { | ||
| 67 | + return; | ||
| 68 | + } | ||
| 69 | + ResponseBody body = response.body(); | ||
| 70 | + if (Objects.nonNull(body)) { | ||
| 71 | + log.error("OpenAI sse连接异常data:{},异常:{}", body.string(), t); | ||
| 72 | + } else { | ||
| 73 | + log.error("OpenAI sse连接异常data:{},异常:{}", response, t); | ||
| 74 | + } | ||
| 75 | + eventSource.cancel(); | ||
| 76 | + } | ||
| 77 | +} |
lh-jar/lh-jar-chatgpt/src/main/java/com/zhonglai/luhui/chatgpt/service/CompleteCallback.java
0 → 100644
| 1 | +package com.zhonglai.luhui.chatgpt.service; | ||
| 2 | + | ||
| 3 | +import com.unfbx.chatgpt.entity.chat.ChatCompletion; | ||
| 4 | +import com.zhonglai.luhui.chatgpt.listener.OpenAISSEEventSourceListener; | ||
| 5 | + | ||
| 6 | +public interface CompleteCallback { | ||
| 7 | + void sseChatEnd(ChatCompletion chatCompletion, OpenAISSEEventSourceListener openAISSEEventSourceListener); | ||
| 8 | +} |
| 1 | +package com.zhonglai.luhui.chatgpt.service; | ||
| 2 | + | ||
| 3 | +import com.unfbx.chatgpt.entity.chat.ChatCompletion; | ||
| 4 | +import com.zhonglai.luhui.chatgpt.controller.request.ChatRequest; | ||
| 5 | +import com.zhonglai.luhui.chatgpt.controller.response.ChatResponse; | ||
| 6 | +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 描述: | ||
| 10 | + * | ||
| 11 | + * @author https:www.unfbx.com | ||
| 12 | + * @date 2023-04-08 | ||
| 13 | + */ | ||
| 14 | +public interface SseService { | ||
| 15 | + /** | ||
| 16 | + * 创建SSE | ||
| 17 | + * @param uid | ||
| 18 | + * @return | ||
| 19 | + */ | ||
| 20 | + SseEmitter createSse(String uid); | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 关闭SSE | ||
| 24 | + * @param uid | ||
| 25 | + */ | ||
| 26 | + void closeSse(String uid); | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 客户端发送消息到服务端 | ||
| 30 | + * @param uid | ||
| 31 | + * @param chatRequest | ||
| 32 | + */ | ||
| 33 | + ChatResponse sseChat(String uid, ChatRequest chatRequest, ChatCompletion.Model model, CompleteCallback completeCallback); | ||
| 34 | +} |
lh-jar/lh-jar-chatgpt/src/main/java/com/zhonglai/luhui/chatgpt/service/impl/SseServiceImpl.java
0 → 100644
| 1 | +package com.zhonglai.luhui.chatgpt.service.impl; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.ArrayUtil; | ||
| 4 | +import cn.hutool.core.util.StrUtil; | ||
| 5 | +import cn.hutool.json.JSONUtil; | ||
| 6 | +import com.unfbx.chatgpt.OpenAiStreamClient; | ||
| 7 | +import com.unfbx.chatgpt.entity.chat.ChatCompletion; | ||
| 8 | +import com.unfbx.chatgpt.entity.chat.Message; | ||
| 9 | +import com.unfbx.chatgpt.exception.BaseException; | ||
| 10 | +import com.zhonglai.luhui.chatgpt.config.LocalCache; | ||
| 11 | +import com.zhonglai.luhui.chatgpt.controller.request.ChatRequest; | ||
| 12 | +import com.zhonglai.luhui.chatgpt.controller.response.ChatResponse; | ||
| 13 | +import com.zhonglai.luhui.chatgpt.listener.OpenAISSEEventSourceListener; | ||
| 14 | +import com.zhonglai.luhui.chatgpt.service.CompleteCallback; | ||
| 15 | +import com.zhonglai.luhui.chatgpt.service.SseService; | ||
| 16 | +import lombok.extern.slf4j.Slf4j; | ||
| 17 | +import org.springframework.stereotype.Service; | ||
| 18 | +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; | ||
| 19 | + | ||
| 20 | +import java.io.IOException; | ||
| 21 | +import java.util.ArrayList; | ||
| 22 | +import java.util.List; | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * 描述: | ||
| 26 | + * | ||
| 27 | + * @author https:www.unfbx.com | ||
| 28 | + * @date 2023-04-08 | ||
| 29 | + */ | ||
| 30 | +@Service | ||
| 31 | +@Slf4j | ||
| 32 | +public class SseServiceImpl implements SseService { | ||
| 33 | + | ||
| 34 | + private final OpenAiStreamClient openAiStreamClient; | ||
| 35 | + | ||
| 36 | + public SseServiceImpl(OpenAiStreamClient openAiStreamClient) { | ||
| 37 | + this.openAiStreamClient = openAiStreamClient; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + public SseEmitter createSse(String uid) { | ||
| 42 | + //默认30秒超时,设置为0L则永不超时 | ||
| 43 | + SseEmitter sseEmitter = new SseEmitter(0l); | ||
| 44 | + //完成后回调 | ||
| 45 | + sseEmitter.onCompletion(() -> { | ||
| 46 | + log.info("[{}]结束连接...................", uid); | ||
| 47 | + LocalCache.CACHE.remove(uid); | ||
| 48 | + }); | ||
| 49 | + //超时回调 | ||
| 50 | + sseEmitter.onTimeout(() -> { | ||
| 51 | + log.info("[{}]连接超时...................", uid); | ||
| 52 | + }); | ||
| 53 | + //异常回调 | ||
| 54 | + sseEmitter.onError( | ||
| 55 | + throwable -> { | ||
| 56 | + try { | ||
| 57 | + log.info("[{}]连接异常,{}", uid, throwable.toString()); | ||
| 58 | + sseEmitter.send(SseEmitter.event() | ||
| 59 | + .id(uid) | ||
| 60 | + .name("发生异常!") | ||
| 61 | + .data(Message.builder().content("发生异常请重试!").build()) | ||
| 62 | + .reconnectTime(3000)); | ||
| 63 | + LocalCache.CACHE.put(uid, sseEmitter); | ||
| 64 | + } catch (IOException e) { | ||
| 65 | + e.printStackTrace(); | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + ); | ||
| 69 | +// try { | ||
| 70 | +// sseEmitter.send(SseEmitter.event().reconnectTime(5000)); | ||
| 71 | +// } catch (IOException e) { | ||
| 72 | +// e.printStackTrace(); | ||
| 73 | +// } | ||
| 74 | + LocalCache.CACHE.put(uid, sseEmitter); | ||
| 75 | + log.info("[{}]创建sse连接成功!", uid); | ||
| 76 | + return sseEmitter; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + @Override | ||
| 80 | + public void closeSse(String uid) { | ||
| 81 | + SseEmitter sse = (SseEmitter) LocalCache.CACHE.get(uid); | ||
| 82 | + if (sse != null) { | ||
| 83 | + sse.complete(); | ||
| 84 | + //移除 | ||
| 85 | + LocalCache.CACHE.remove(uid); | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + @Override | ||
| 90 | + public ChatResponse sseChat(String uid, ChatRequest chatRequest, ChatCompletion.Model model, CompleteCallback completeCallback) { | ||
| 91 | + if (ArrayUtil.isEmpty(chatRequest.getMsg())) { | ||
| 92 | + log.info("参数异常,msg为null", uid); | ||
| 93 | + throw new BaseException("参数异常,msg不能为空~"); | ||
| 94 | + } | ||
| 95 | +// String messageContext = (String) LocalCache.CACHE.get("msg" + uid); | ||
| 96 | + | ||
| 97 | +// if (StrUtil.isNotBlank(messageContext)) { | ||
| 98 | +// messages = JSONUtil.toList(messageContext, Message.class); | ||
| 99 | +// if (messages.size() >= 10) { | ||
| 100 | +// messages = messages.subList(1, 10); | ||
| 101 | +// } | ||
| 102 | +// Message currentMessage = Message.builder().content(chatRequest.getMsg()).role(Message.Role.USER).build(); | ||
| 103 | +// messages.add(currentMessage); | ||
| 104 | +// } else { | ||
| 105 | +// Message currentMessage = Message.builder().content(chatRequest.getMsg()).role(Message.Role.USER).build(); | ||
| 106 | +// messages.add(currentMessage); | ||
| 107 | +// } | ||
| 108 | + | ||
| 109 | + List<Message> messages = new ArrayList<>(); | ||
| 110 | + for(String msg:chatRequest.getMsg()) | ||
| 111 | + { | ||
| 112 | + Message currentMessage = Message.builder().content(msg).role(Message.Role.USER).build(); | ||
| 113 | + messages.add(currentMessage); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + | ||
| 117 | + SseEmitter sseEmitter = (SseEmitter) LocalCache.CACHE.get(uid); | ||
| 118 | + | ||
| 119 | + if (sseEmitter == null) { | ||
| 120 | + log.info("聊天消息推送失败uid:[{}],没有创建连接,请重试。", uid); | ||
| 121 | + throw new BaseException("聊天消息推送失败uid:[{}],没有创建连接,请重试。~"); | ||
| 122 | + } | ||
| 123 | + OpenAISSEEventSourceListener openAIEventSourceListener = new OpenAISSEEventSourceListener(sseEmitter); | ||
| 124 | + ChatCompletion completion = ChatCompletion | ||
| 125 | + .builder() | ||
| 126 | + .messages(messages) | ||
| 127 | + .model(model.getName()) | ||
| 128 | + .build(); | ||
| 129 | + openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener); | ||
| 130 | +// LocalCache.CACHE.put("msg" + uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT); | ||
| 131 | + ChatResponse response = new ChatResponse(); | ||
| 132 | + response.setQuestionTokens(completion.tokens()); | ||
| 133 | + if(null != completeCallback) | ||
| 134 | + { | ||
| 135 | + completeCallback.sseChatEnd(completion,openAIEventSourceListener); | ||
| 136 | + } | ||
| 137 | + return response; | ||
| 138 | + } | ||
| 139 | +} |
lh-jar/lh-jar-chatgpt/src/main/java/com/zhonglai/luhui/chatgpt/websocket/WebSocketServer.java
0 → 100644
| 1 | +package com.zhonglai.luhui.chatgpt.websocket; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 4 | +import cn.hutool.json.JSONUtil; | ||
| 5 | +import com.unfbx.chatgpt.OpenAiStreamClient; | ||
| 6 | +import com.unfbx.chatgpt.entity.chat.Message; | ||
| 7 | +import com.zhonglai.luhui.chatgpt.config.LocalCache; | ||
| 8 | +import com.zhonglai.luhui.chatgpt.listener.OpenAIWebSocketEventSourceListener; | ||
| 9 | +import lombok.extern.slf4j.Slf4j; | ||
| 10 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 11 | +import org.springframework.stereotype.Component; | ||
| 12 | + | ||
| 13 | +import javax.websocket.*; | ||
| 14 | +import javax.websocket.server.PathParam; | ||
| 15 | +import javax.websocket.server.ServerEndpoint; | ||
| 16 | +import java.util.ArrayList; | ||
| 17 | +import java.util.Collections; | ||
| 18 | +import java.util.List; | ||
| 19 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 20 | +import java.util.concurrent.CopyOnWriteArraySet; | ||
| 21 | + | ||
| 22 | +/** | ||
| 23 | + * 描述:websocket 服务端 | ||
| 24 | + * | ||
| 25 | + * @author https:www.unfbx.com | ||
| 26 | + * @date 2023-03-23 | ||
| 27 | + */ | ||
| 28 | +@Slf4j | ||
| 29 | +@Component | ||
| 30 | +@ServerEndpoint("/websocket/{uid}") | ||
| 31 | +public class WebSocketServer { | ||
| 32 | + | ||
| 33 | + private static OpenAiStreamClient openAiStreamClient; | ||
| 34 | + | ||
| 35 | + @Autowired | ||
| 36 | + public void setOrderService(OpenAiStreamClient openAiStreamClient) { | ||
| 37 | + this.openAiStreamClient = openAiStreamClient; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + //在线总数 | ||
| 41 | + private static int onlineCount; | ||
| 42 | + //当前会话 | ||
| 43 | + private Session session; | ||
| 44 | + //用户id -目前是按浏览器随机生成 | ||
| 45 | + private String uid; | ||
| 46 | + | ||
| 47 | + private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>(); | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 用来存放每个客户端对应的WebSocketServer对象 | ||
| 51 | + */ | ||
| 52 | + private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap(); | ||
| 53 | + | ||
| 54 | + /** | ||
| 55 | + * 为了保存在线用户信息,在方法中新建一个list存储一下【实际项目依据复杂度,可以存储到数据库或者缓存】 | ||
| 56 | + */ | ||
| 57 | + private final static List<Session> SESSIONS = Collections.synchronizedList(new ArrayList<>()); | ||
| 58 | + | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * 建立连接 | ||
| 62 | + * @param session | ||
| 63 | + * @param uid | ||
| 64 | + */ | ||
| 65 | + @OnOpen | ||
| 66 | + public void onOpen(Session session, @PathParam("uid") String uid) { | ||
| 67 | + this.session = session; | ||
| 68 | + this.uid = uid; | ||
| 69 | + webSocketSet.add(this); | ||
| 70 | + SESSIONS.add(session); | ||
| 71 | + if (webSocketMap.containsKey(uid)) { | ||
| 72 | + webSocketMap.remove(uid); | ||
| 73 | + webSocketMap.put(uid, this); | ||
| 74 | + } else { | ||
| 75 | + webSocketMap.put(uid, this); | ||
| 76 | + addOnlineCount(); | ||
| 77 | + } | ||
| 78 | + log.info("[连接ID:{}] 建立连接, 当前连接数:{}", this.uid, getOnlineCount()); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * 断开连接 | ||
| 83 | + */ | ||
| 84 | + @OnClose | ||
| 85 | + public void onClose() { | ||
| 86 | + webSocketSet.remove(this); | ||
| 87 | + if (webSocketMap.containsKey(uid)) { | ||
| 88 | + webSocketMap.remove(uid); | ||
| 89 | + subOnlineCount(); | ||
| 90 | + } | ||
| 91 | + log.info("[连接ID:{}] 断开连接, 当前连接数:{}", uid, getOnlineCount()); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + /** | ||
| 95 | + * 发送错误 | ||
| 96 | + * @param session | ||
| 97 | + * @param error | ||
| 98 | + */ | ||
| 99 | + @OnError | ||
| 100 | + public void onError(Session session, Throwable error) { | ||
| 101 | + log.info("[连接ID:{}] 错误原因:{}", this.uid, error.getMessage()); | ||
| 102 | + error.printStackTrace(); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + /** | ||
| 106 | + * 接收到客户端消息 | ||
| 107 | + * @param msg | ||
| 108 | + */ | ||
| 109 | + @OnMessage | ||
| 110 | + public void onMessage(String msg) { | ||
| 111 | + log.info("[连接ID:{}] 收到消息:{}", this.uid, msg); | ||
| 112 | + //接受参数 | ||
| 113 | + OpenAIWebSocketEventSourceListener eventSourceListener = new OpenAIWebSocketEventSourceListener(this.session); | ||
| 114 | + String messageContext = (String) LocalCache.CACHE.get(uid); | ||
| 115 | + List<Message> messages = new ArrayList<>(); | ||
| 116 | + if (StrUtil.isNotBlank(messageContext)) { | ||
| 117 | + messages = JSONUtil.toList(messageContext, Message.class); | ||
| 118 | + if (messages.size() >= 10) { | ||
| 119 | + messages = messages.subList(1, 10); | ||
| 120 | + } | ||
| 121 | + Message currentMessage = Message.builder().content(msg).role(Message.Role.USER).build(); | ||
| 122 | + messages.add(currentMessage); | ||
| 123 | + } else { | ||
| 124 | + Message currentMessage = Message.builder().content(msg).role(Message.Role.USER).build(); | ||
| 125 | + messages.add(currentMessage); | ||
| 126 | + } | ||
| 127 | + openAiStreamClient.streamChatCompletion(messages, eventSourceListener); | ||
| 128 | + LocalCache.CACHE.put(uid, JSONUtil.toJsonStr(messages), LocalCache.TIMEOUT); | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + | ||
| 132 | + /** | ||
| 133 | + * 获取当前连接数 | ||
| 134 | + * | ||
| 135 | + * @return | ||
| 136 | + */ | ||
| 137 | + public static synchronized int getOnlineCount() { | ||
| 138 | + return onlineCount; | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + /** | ||
| 142 | + * 当前连接数加一 | ||
| 143 | + */ | ||
| 144 | + public static synchronized void addOnlineCount() { | ||
| 145 | + WebSocketServer.onlineCount++; | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + /** | ||
| 149 | + * 当前连接数减一 | ||
| 150 | + */ | ||
| 151 | + public static synchronized void subOnlineCount() { | ||
| 152 | + WebSocketServer.onlineCount--; | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | +} | ||
| 156 | + |
| 1 | +.markdown-body{color-scheme:dark;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;margin:0;color:#c9d1d9;background-color:#0d1117;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body .octicon{display:inline-block;fill:currentColor;vertical-align:text-bottom}.markdown-body h1:hover .anchor .octicon-link:before,.markdown-body h2:hover .anchor .octicon-link:before,.markdown-body h3:hover .anchor .octicon-link:before,.markdown-body h4:hover .anchor .octicon-link:before,.markdown-body h5:hover .anchor .octicon-link:before,.markdown-body h6:hover .anchor .octicon-link:before{width:16px;height:16px;content:' ';display:inline-block;background-color:currentColor;-webkit-mask-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");mask-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>")}.markdown-body details,.markdown-body figcaption,.markdown-body figure{display:block}.markdown-body summary{display:list-item}.markdown-body [hidden]{display:none!important}.markdown-body a{background-color:transparent;color:#58a6ff;text-decoration:none}.markdown-body abbr[title]{border-bottom:none;text-decoration:underline dotted}.markdown-body b,.markdown-body strong{font-weight:600}.markdown-body dfn{font-style:italic}.markdown-body h1{margin:.67em 0;font-weight:600;padding-bottom:.3em;font-size:2em;border-bottom:1px solid #21262d}.markdown-body mark{background-color:rgba(187,128,9,.15);color:#c9d1d9}.markdown-body small{font-size:90%}.markdown-body sub,.markdown-body sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.markdown-body sub{bottom:-.25em}.markdown-body sup{top:-.5em}.markdown-body img{border-style:none;max-width:100%;box-sizing:content-box;background-color:#0d1117}.markdown-body code,.markdown-body kbd,.markdown-body pre,.markdown-body samp{font-family:monospace;font-size:1em}.markdown-body figure{margin:1em 40px}.markdown-body hr{box-sizing:content-box;overflow:hidden;background:0 0;border-bottom:1px solid #21262d;height:.25em;padding:0;margin:24px 0;background-color:#30363d;border:0}.markdown-body input{font:inherit;margin:0;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit}.markdown-body [type=button],.markdown-body [type=reset],.markdown-body [type=submit]{-webkit-appearance:button}.markdown-body [type=checkbox],.markdown-body [type=radio]{box-sizing:border-box;padding:0}.markdown-body [type=number]::-webkit-inner-spin-button,.markdown-body [type=number]::-webkit-outer-spin-button{height:auto}.markdown-body [type=search]::-webkit-search-cancel-button,.markdown-body [type=search]::-webkit-search-decoration{-webkit-appearance:none}.markdown-body ::-webkit-input-placeholder{color:inherit;opacity:.54}.markdown-body ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.markdown-body a:hover{text-decoration:underline}.markdown-body ::placeholder{color:#6e7681;opacity:1}.markdown-body hr::before{display:table;content:""}.markdown-body hr::after{display:table;clear:both;content:""}.markdown-body table{border-spacing:0;border-collapse:collapse;display:block;width:max-content;max-width:100%;overflow:auto}.markdown-body td,.markdown-body th{padding:0}.markdown-body details summary{cursor:pointer}.markdown-body details:not([open])>:not(summary){display:none!important}.markdown-body [role=button]:focus,.markdown-body a:focus,.markdown-body input[type=checkbox]:focus,.markdown-body input[type=radio]:focus{outline:2px solid #58a6ff;outline-offset:-2px;box-shadow:none}.markdown-body [role=button]:focus:not(:focus-visible),.markdown-body a:focus:not(:focus-visible),.markdown-body input[type=checkbox]:focus:not(:focus-visible),.markdown-body input[type=radio]:focus:not(:focus-visible){outline:solid 1px transparent}.markdown-body [role=button]:focus-visible,.markdown-body a:focus-visible,.markdown-body input[type=checkbox]:focus-visible,.markdown-body input[type=radio]:focus-visible{outline:2px solid #58a6ff;outline-offset:-2px;box-shadow:none}.markdown-body a:not([class]):focus,.markdown-body a:not([class]):focus-visible,.markdown-body input[type=checkbox]:focus,.markdown-body input[type=checkbox]:focus-visible,.markdown-body input[type=radio]:focus,.markdown-body input[type=radio]:focus-visible{outline-offset:0}.markdown-body kbd{display:inline-block;padding:3px 5px;font:11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;line-height:10px;color:#c9d1d9;vertical-align:middle;background-color:#161b22;border:solid 1px rgba(110,118,129,.4);border-bottom-color:rgba(110,118,129,.4);border-radius:6px;box-shadow:inset 0 -1px 0 rgba(110,118,129,.4)}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:600;line-height:1.25}.markdown-body h2{font-weight:600;padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid #21262d}.markdown-body h3{font-weight:600;font-size:1.25em}.markdown-body h4{font-weight:600;font-size:1em}.markdown-body h5{font-weight:600;font-size:.875em}.markdown-body h6{font-weight:600;font-size:.85em;color:#8b949e}.markdown-body p{margin-top:0;margin-bottom:10px}.markdown-body blockquote{margin:0;padding:0 1em;color:#8b949e;border-left:.25em solid #30363d}.markdown-body ol,.markdown-body ul{margin-top:0;margin-bottom:0;padding-left:2em}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ol ol ol,.markdown-body ol ul ol,.markdown-body ul ol ol,.markdown-body ul ul ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body code,.markdown-body samp,.markdown-body tt{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:12px}.markdown-body pre{margin-top:0;margin-bottom:0;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:12px;word-wrap:normal}.markdown-body .octicon{display:inline-block;overflow:visible!important;vertical-align:text-bottom;fill:currentColor}.markdown-body input::-webkit-inner-spin-button,.markdown-body input::-webkit-outer-spin-button{margin:0;-webkit-appearance:none;appearance:none}.markdown-body::before{display:table;content:""}.markdown-body::after{display:table;clear:both;content:""}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:#f85149}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:0}.markdown-body blockquote,.markdown-body details,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-top:0;margin-bottom:16px}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#c9d1d9;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 code,.markdown-body h1 tt,.markdown-body h2 code,.markdown-body h2 tt,.markdown-body h3 code,.markdown-body h3 tt,.markdown-body h4 code,.markdown-body h4 tt,.markdown-body h5 code,.markdown-body h5 tt,.markdown-body h6 code,.markdown-body h6 tt{padding:0 .2em;font-size:inherit}.markdown-body summary h1,.markdown-body summary h2,.markdown-body summary h3,.markdown-body summary h4,.markdown-body summary h5,.markdown-body summary h6{display:inline-block}.markdown-body summary h1 .anchor,.markdown-body summary h2 .anchor,.markdown-body summary h3 .anchor,.markdown-body summary h4 .anchor,.markdown-body summary h5 .anchor,.markdown-body summary h6 .anchor{margin-left:-40px}.markdown-body summary h1,.markdown-body summary h2{padding-bottom:0;border-bottom:0}.markdown-body ol.no-list,.markdown-body ul.no-list{padding:0;list-style-type:none}.markdown-body ol[type=a]{list-style-type:lower-alpha}.markdown-body ol[type=A]{list-style-type:upper-alpha}.markdown-body ol[type=i]{list-style-type:lower-roman}.markdown-body ol[type=I]{list-style-type:upper-roman}.markdown-body ol[type="1"]{list-style-type:decimal}.markdown-body div>ol:not([type]){list-style-type:decimal}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:600}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table th{font-weight:600}.markdown-body table td,.markdown-body table th{padding:6px 13px;border:1px solid #30363d}.markdown-body table tr{background-color:#0d1117;border-top:1px solid #21262d}.markdown-body table tr:nth-child(2n){background-color:#161b22}.markdown-body table img{background-color:transparent}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{max-width:none;vertical-align:text-top;background-color:transparent}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #30363d}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{display:block;padding:5px 0 0;clear:both;color:#c9d1d9}.markdown-body span.align-center{display:block;overflow:hidden;clear:both}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{display:block;overflow:hidden;clear:both}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{padding:.2em .4em;margin:0;font-size:85%;white-space:break-spaces;background-color:rgba(110,118,129,.4);border-radius:6px}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body samp{font-size:85%}.markdown-body pre code{font-size:100%}.markdown-body pre>code{padding:0;margin:0;word-break:normal;white-space:pre;background:0 0;border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#161b22;border-radius:6px}.markdown-body pre code,.markdown-body pre tt{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body .csv-data td,.markdown-body .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:#0d1117;border:0}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{font-weight:600;background:#161b22;border-top:0}.markdown-body [data-footnote-ref]::before{content:"["}.markdown-body [data-footnote-ref]::after{content:"]"}.markdown-body .footnotes{font-size:12px;color:#8b949e;border-top:1px solid #30363d}.markdown-body .footnotes ol{padding-left:16px}.markdown-body .footnotes ol ul{display:inline-block;padding-left:16px;margin-top:16px}.markdown-body .footnotes li{position:relative}.markdown-body .footnotes li:target::before{position:absolute;top:-8px;right:-8px;bottom:-8px;left:-24px;pointer-events:none;content:"";border:2px solid #1f6feb;border-radius:6px}.markdown-body .footnotes li:target{color:#c9d1d9}.markdown-body .footnotes .data-footnote-backref g-emoji{font-family:monospace}.markdown-body .pl-c{color:#8b949e}.markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:#79c0ff}.markdown-body .pl-e,.markdown-body .pl-en{color:#d2a8ff}.markdown-body .pl-s .pl-s1,.markdown-body .pl-smi{color:#c9d1d9}.markdown-body .pl-ent{color:#7ee787}.markdown-body .pl-k{color:#ff7b72}.markdown-body .pl-pds,.markdown-body .pl-s,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sra,.markdown-body .pl-sr .pl-sre{color:#a5d6ff}.markdown-body .pl-smw,.markdown-body .pl-v{color:#ffa657}.markdown-body .pl-bu{color:#f85149}.markdown-body .pl-ii{color:#f0f6fc;background-color:#8e1519}.markdown-body .pl-c2{color:#f0f6fc;background-color:#b62324}.markdown-body .pl-sr .pl-cce{font-weight:700;color:#7ee787}.markdown-body .pl-ml{color:#f2cc60}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{font-weight:700;color:#1f6feb}.markdown-body .pl-mi{font-style:italic;color:#c9d1d9}.markdown-body .pl-mb{font-weight:700;color:#c9d1d9}.markdown-body .pl-md{color:#ffdcd7;background-color:#67060c}.markdown-body .pl-mi1{color:#aff5b4;background-color:#033a16}.markdown-body .pl-mc{color:#ffdfb6;background-color:#5a1e02}.markdown-body .pl-mi2{color:#c9d1d9;background-color:#1158c7}.markdown-body .pl-mdr{font-weight:700;color:#d2a8ff}.markdown-body .pl-ba{color:#8b949e}.markdown-body .pl-sg{color:#484f58}.markdown-body .pl-corl{text-decoration:underline;color:#a5d6ff}.markdown-body g-emoji{display:inline-block;min-width:1ch;font-family:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1em;font-style:normal!important;font-weight:400;line-height:1;vertical-align:-.075em}.markdown-body g-emoji img{width:1em;height:1em}.markdown-body .task-list-item{list-style-type:none}.markdown-body .task-list-item label{font-weight:400}.markdown-body .task-list-item.enabled label{cursor:pointer}.markdown-body .task-list-item+.task-list-item{margin-top:4px}.markdown-body .task-list-item .handle{display:none}.markdown-body .task-list-item-checkbox{margin:0 .2em .25em -1.4em;vertical-align:middle}.markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}.markdown-body .contains-task-list{position:relative}.markdown-body .contains-task-list:focus-within .task-list-item-convert-container,.markdown-body .contains-task-list:hover .task-list-item-convert-container{display:block;width:auto;height:24px;overflow:visible;clip:auto}.markdown-body ::-webkit-calendar-picker-indicator{filter:invert(50%)} |
1.3 KB
| 1 | +/** @license | ||
| 2 | + * eventsource.js | ||
| 3 | + * Available under MIT License (MIT) | ||
| 4 | + * https://github.com/Yaffle/EventSource/ | ||
| 5 | + */ | ||
| 6 | +!function(e){"use strict";var r,H=e.setTimeout,N=e.clearTimeout,j=e.XMLHttpRequest,o=e.XDomainRequest,t=e.ActiveXObject,n=e.EventSource,i=e.document,w=e.Promise,d=e.fetch,a=e.Response,h=e.TextDecoder,s=e.TextEncoder,p=e.AbortController;function c(){this.bitsNeeded=0,this.codePoint=0}"undefined"==typeof window||void 0===i||"readyState"in i||null!=i.body||(i.readyState="loading",window.addEventListener("load",function(e){i.readyState="complete"},!1)),null==j&&null!=t&&(j=function(){return new t("Microsoft.XMLHTTP")}),null==Object.create&&(Object.create=function(e){function t(){}return t.prototype=e,new t}),Date.now||(Date.now=function(){return(new Date).getTime()}),null==p&&(r=d,d=function(e,t){var n=t.signal;return r(e,{headers:t.headers,credentials:t.credentials,cache:t.cache}).then(function(e){var t=e.body.getReader();return n._reader=t,n._aborted&&n._reader.cancel(),{status:e.status,statusText:e.statusText,headers:e.headers,body:{getReader:function(){return t}}}})},p=function(){this.signal={_reader:null,_aborted:!1},this.abort=function(){null!=this.signal._reader&&this.signal._reader.cancel(),this.signal._aborted=!0}}),c.prototype.decode=function(e){function t(e,t,n){if(1===n)return 128>>t<=e&&e<<t<=2047;if(2===n)return 2048>>t<=e&&e<<t<=55295||57344>>t<=e&&e<<t<=65535;if(3===n)return 65536>>t<=e&&e<<t<=1114111;throw new Error}function n(e,t){if(6===e)return 15<t>>6?3:31<t?2:1;if(12===e)return 15<t?3:2;if(18===e)return 3;throw new Error}for(var r="",o=this.bitsNeeded,i=this.codePoint,a=0;a<e.length;a+=1){var s=e[a];0!==o&&(s<128||191<s||!t(i<<6|63&s,o-6,n(o,i)))&&(o=0,i=65533,r+=String.fromCharCode(i)),0===o?(i=0<=s&&s<=127?(o=0,s):192<=s&&s<=223?(o=6,31&s):224<=s&&s<=239?(o=12,15&s):240<=s&&s<=247?(o=18,7&s):(o=0,65533),0===o||t(i,o,n(o,i))||(o=0,i=65533)):(o-=6,i=i<<6|63&s),0===o&&(i<=65535?r+=String.fromCharCode(i):r=(r+=String.fromCharCode(55296+(i-65535-1>>10)))+String.fromCharCode(56320+(i-65535-1&1023)))}return this.bitsNeeded=o,this.codePoint=i,r};function u(){}null!=h&&null!=s&&function(){try{return"test"===(new h).decode((new s).encode("test"),{stream:!0})}catch(e){}return!1}()||(h=c);function I(e){this.withCredentials=!1,this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=u,this.onload=u,this.onerror=u,this.onreadystatechange=u,this._contentType="",this._xhr=e,this._sendTimeout=0,this._abort=u}function l(e){return e.replace(/[A-Z]/g,function(e){return String.fromCharCode(e.charCodeAt(0)+32)})}function f(e){for(var t=Object.create(null),n=e.split("\r\n"),r=0;r<n.length;r+=1){var o=n[r].split(": "),i=o.shift(),o=o.join(": ");t[l(i)]=o}this._map=t}function P(){}function y(e){this._headers=e}function L(){}function M(){this._listeners=Object.create(null)}function b(e){H(function(){throw e},0)}function v(e){this.type=e,this.target=void 0}function $(e,t){v.call(this,e),this.data=t.data,this.lastEventId=t.lastEventId}function q(e,t){v.call(this,e),this.status=t.status,this.statusText=t.statusText,this.headers=t.headers}function F(e,t){v.call(this,e),this.error=t.error}I.prototype.open=function(e,t){this._abort(!0);var o=this,i=this._xhr,a=1,n=0,r=(this._abort=function(e){0!==o._sendTimeout&&(N(o._sendTimeout),o._sendTimeout=0),1!==a&&2!==a&&3!==a||(a=4,i.onload=u,i.onerror=u,i.onabort=u,i.onprogress=u,i.onreadystatechange=u,i.abort(),0!==n&&(N(n),n=0),e||(o.readyState=4,o.onabort(null),o.onreadystatechange())),a=0},function(){if(1===a){var t=0,n="",r=void 0;if("contentType"in i)t=200,n="OK",r=i.contentType;else try{t=i.status,n=i.statusText,r=i.getResponseHeader("Content-Type")}catch(e){n="",r=void(t=0)}0!==t&&(a=2,o.readyState=2,o.status=t,o.statusText=n,o._contentType=r,o.onreadystatechange())}}),s=function(){if(r(),2===a||3===a){a=3;var e="";try{e=i.responseText}catch(e){}o.readyState=3,o.responseText=e,o.onprogress()}},c=function(e,t){if(null!=t&&null!=t.preventDefault||(t={preventDefault:u}),s(),1===a||2===a||3===a){if(a=4,0!==n&&(N(n),n=0),o.readyState=4,"load"===e)o.onload(t);else if("error"===e)o.onerror(t);else{if("abort"!==e)throw new TypeError;o.onabort(t)}o.onreadystatechange()}},l=function(){n=H(function(){l()},500),3===i.readyState&&s()};"onload"in i&&(i.onload=function(e){c("load",e)}),"onerror"in i&&(i.onerror=function(e){c("error",e)}),"onabort"in i&&(i.onabort=function(e){c("abort",e)}),"onprogress"in i&&(i.onprogress=s),"onreadystatechange"in i&&(i.onreadystatechange=function(e){e=e,null!=i&&(4===i.readyState?"onload"in i&&"onerror"in i&&"onabort"in i||c(""===i.responseText?"error":"load",e):3===i.readyState?"onprogress"in i||s():2===i.readyState&&r())}),!("contentType"in i)&&"ontimeout"in j.prototype||(t+=(-1===t.indexOf("?")?"?":"&")+"padding=true"),i.open(e,t,!0),"readyState"in i&&(n=H(function(){l()},0))},I.prototype.abort=function(){this._abort(!1)},I.prototype.getResponseHeader=function(e){return this._contentType},I.prototype.setRequestHeader=function(e,t){var n=this._xhr;"setRequestHeader"in n&&n.setRequestHeader(e,t)},I.prototype.getAllResponseHeaders=function(){return null!=this._xhr.getAllResponseHeaders&&this._xhr.getAllResponseHeaders()||""},I.prototype.send=function(){var e;if("ontimeout"in j.prototype&&("sendAsBinary"in j.prototype||"mozAnon"in j.prototype)||null==i||null==i.readyState||"complete"===i.readyState){var t=this._xhr;"withCredentials"in t&&(t.withCredentials=this.withCredentials);try{t.send(void 0)}catch(e){throw e}}else(e=this)._sendTimeout=H(function(){e._sendTimeout=0,e.send()},4)},f.prototype.get=function(e){return this._map[l(e)]},null!=j&&null==j.HEADERS_RECEIVED&&(j.HEADERS_RECEIVED=2),P.prototype.open=function(o,i,t,n,e,r,a){o.open("GET",e);var s,c=0;for(s in o.onprogress=function(){var e=o.responseText.slice(c);c+=e.length,t(e)},o.onerror=function(e){e.preventDefault(),n(new Error("NetworkError"))},o.onload=function(){n(null)},o.onabort=function(){n(null)},o.onreadystatechange=function(){var e,t,n,r;o.readyState===j.HEADERS_RECEIVED&&(e=o.status,t=o.statusText,n=o.getResponseHeader("Content-Type"),r=o.getAllResponseHeaders(),i(e,t,n,new f(r)))},o.withCredentials=r,a)Object.prototype.hasOwnProperty.call(a,s)&&o.setRequestHeader(s,a[s]);return o.send(),o},y.prototype.get=function(e){return this._headers.get(e)},L.prototype.open=function(e,t,o,n,r,i,a){var s=null,c=new p,l=c.signal,u=new h;return d(r,{headers:a,credentials:i?"include":"same-origin",signal:l,cache:"no-store"}).then(function(e){return s=e.body.getReader(),t(e.status,e.statusText,e.headers.get("Content-Type"),new y(e.headers)),new w(function(t,n){function r(){s.read().then(function(e){e.done?t(void 0):(e=u.decode(e.value,{stream:!0}),o(e),r())}).catch(function(e){n(e)})}r()})}).catch(function(e){if("AbortError"!==e.name)return e}).then(function(e){n(e)}),{abort:function(){null!=s&&s.cancel(),c.abort()}}},M.prototype.dispatchEvent=function(e){var t=(e.target=this)._listeners[e.type];if(null!=t)for(var n=t.length,r=0;r<n;r+=1){var o=t[r];try{"function"==typeof o.handleEvent?o.handleEvent(e):o.call(this,e)}catch(e){b(e)}}},M.prototype.addEventListener=function(e,t){e=String(e);for(var n=this._listeners,r=n[e],o=(null==r&&(n[e]=r=[]),!1),i=0;i<r.length;i+=1)r[i]===t&&(o=!0);o||r.push(t)},M.prototype.removeEventListener=function(e,t){e=String(e);var n=this._listeners,r=n[e];if(null!=r){for(var o=[],i=0;i<r.length;i+=1)r[i]!==t&&o.push(r[i]);0===o.length?delete n[e]:n[e]=o}},$.prototype=Object.create(v.prototype),q.prototype=Object.create(v.prototype),F.prototype=Object.create(v.prototype);var X=-1,G=0,V=1,B=2,k=-1,z=0,K=1,J=2,W=3,Y=/^text\/event\-stream(;.*)?$/i,E=1e3,m=18e6,Q=function(e,t){e=null==e?t:parseInt(e,10);return U(e=e!=e?t:e)},U=function(e){return Math.min(Math.max(e,E),m)},Z=function(e,t,n){try{"function"==typeof t&&t.call(e,n)}catch(e){b(e)}};function g(e,t){function i(e,t,n,r){var o;m===G&&(200===e&&null!=n&&Y.test(n)?(m=V,y=Date.now(),f=d,c.readyState=V,o=new q("open",{status:e,statusText:t,headers:r}),c.dispatchEvent(o),Z(c,c.onopen,o)):(200!==e?t=t&&t.replace(/\s+/g," "):null!=n&&n.replace(/\s+/g," "),O(),o=new q("error",{status:e,statusText:t,headers:r}),c.dispatchEvent(o),Z(c,c.onerror,o)))}function a(e){if(m===V){for(var t=-1,n=0;n<e.length;n+=1)(a=e.charCodeAt(n))!=="\n".charCodeAt(0)&&a!=="\r".charCodeAt(0)||(t=n);var r=(-1!==t?S:"")+e.slice(0,t+1);S=(-1===t?S:"")+e.slice(t+1),""!==e&&(y=Date.now(),v+=e.length);for(var o=0;o<r.length;o+=1){var i,a=r.charCodeAt(o);if(x===k&&a==="\n".charCodeAt(0))x=z;else if(x===k&&(x=z),a==="\r".charCodeAt(0)||a==="\n".charCodeAt(0)){if(x!==z&&(x===K&&(R=o+1),s=r.slice(A,R-1),i=r.slice(R+(R<o&&r.charCodeAt(R)===" ".charCodeAt(0)?1:0),o),"data"===s?C=C+"\n"+i:"id"===s?T=i:"event"===s?_=i:"retry"===s?(d=Q(i,d),f=d):"heartbeatTimeout"===s&&(h=Q(i,h),0!==E&&(N(E),E=H(function(){D()},h)))),x===z){if(""!==C){p=T;var s=new $(_=""===_?"message":_,{data:C.slice(1),lastEventId:T});if(c.dispatchEvent(s),"open"===_?Z(c,c.onopen,s):"message"===_?Z(c,c.onmessage,s):"error"===_&&Z(c,c.onerror,s),m===B)return}_=C=""}x=a==="\r".charCodeAt(0)?k:z}else x===z&&(A=o,x=K),x===K?a===":".charCodeAt(0)&&(R=o+1,x=J):x===J&&(x=W)}}}function s(e){m!==V&&m!==G||(m=X,0!==E&&(N(E),E=0),E=H(function(){D()},f),f=U(Math.min(16*d,2*f)),c.readyState=G,e=new F("error",{error:e}),c.dispatchEvent(e),Z(c,c.onerror,e))}var c,l,u,d,h,p,f,y,v,n,g,w,b,E,m,C,T,_,S,x,A,R,O,D;M.call(this),t=t||{},this.onopen=void 0,this.onmessage=void 0,this.onerror=void 0,this.url=void 0,this.readyState=void 0,this.withCredentials=void 0,this.headers=void 0,this._close=void 0,c=this,l=e,e=t,l=String(l),t=Boolean(e.withCredentials),u=e.lastEventIdQueryParameterName||"lastEventId",d=U(1e3),h=Q(e.heartbeatTimeout,45e3),p="",f=d,y=!1,v=0,n=e.headers||{},e=e.Transport,g=ee&&null==e?void 0:new I(new(null!=e?e:null!=j&&"withCredentials"in j.prototype||null==o?j:o)),w=new(null!=e&&"string"!=typeof e?e:null==g?L:P),b=void 0,m=X,S=_=T=C="",x=z,R=A=E=0,O=function(){m=B,null!=b&&(b.abort(),b=void 0),0!==E&&(N(E),E=0),c.readyState=B},D=function(){if(E=0,m!==X)y||null==b?(e=Math.max((y||Date.now())+h-Date.now(),1),y=!1,E=H(function(){D()},e)):(s(new Error("No activity within "+h+" milliseconds. "+(m===G?"No response received.":v+" chars received.")+" Reconnecting.")),null!=b&&(b.abort(),b=void 0));else{y=!1,v=0,E=H(function(){D()},h),m=G,T=p,S=_=C="",R=A=0,x=z;var e=l,t=("data:"!==l.slice(0,5)&&"blob:"!==l.slice(0,5)&&""!==p&&(e=-1===(t=l.indexOf("?"))?l:l.slice(0,t+1)+l.slice(t+1).replace(/(?:^|&)([^=&]*)(?:=[^&]*)?/g,function(e,t){return t===u?"":e}),e+=(-1===l.indexOf("?")?"?":"&")+u+"="+encodeURIComponent(p)),c.withCredentials),n={Accept:"text/event-stream"},r=c.headers;if(null!=r)for(var o in r)Object.prototype.hasOwnProperty.call(r,o)&&(n[o]=r[o]);try{b=w.open(g,i,a,s,e,t,n)}catch(e){throw O(),e}}},c.url=l,c.readyState=G,c.withCredentials=t,c.headers=n,c._close=O,D()}var ee=null!=d&&null!=a&&"body"in a.prototype;(g.prototype=Object.create(M.prototype)).CONNECTING=G,g.prototype.OPEN=V,g.prototype.CLOSED=B,g.prototype.close=function(){this._close()},g.CONNECTING=G,g.OPEN=V,g.CLOSED=B,g.prototype.withCredentials=void 0;var C=n;null==j||null!=n&&"withCredentials"in n.prototype||(C=g),a=function(e){e.EventSourcePolyfill=g,e.NativeEventSource=n,e.EventSource=C},"object"==typeof module&&"object"==typeof module.exports?a(exports):"function"==typeof define&&define.amd?define(["exports"],a):a(e)}("undefined"==typeof globalThis?"undefined"!=typeof window?window:"undefined"!=typeof self?self:this:globalThis); |
| 1 | +/** | ||
| 2 | + * marked - a markdown parser | ||
| 3 | + * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) | ||
| 4 | + * https://github.com/chjj/marked | ||
| 5 | + */ | ||
| 6 | +(function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b";block.html=replace(block.html)("comment",/<!--[\s\S]*?-->/)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1","\\2")+"|"+block.list.source.replace("\\1","\\3")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm){if(this.options.tables){this.rules=block.tables}else{this.rules=block.gfm}}}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top,bq){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1){this.tokens.push({type:"space"})}}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]||""});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].split(/ *\| */)}this.tokens.push(item);continue}if(cap=this.rules.lheading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[2]==="="?1:2,text:cap[1]});continue}if(cap=this.rules.hr.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"hr"});continue}if(cap=this.rules.blockquote.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"blockquote_start"});cap=cap[0].replace(/^ *> ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i<l;i++){item=cap[i];space=item.length;item=item.replace(/^ *([*+-]|\d+\.) +/,"");if(~item.indexOf("\n ")){space-=item.length;item=!this.options.pedantic?item.replace(new RegExp("^ {1,"+space+"}","gm"),""):item.replace(/^ {1,4}/gm,"")}if(this.options.smartLists&&i!==l-1){b=block.bullet.exec(cap[i+1])[0];if(bull!==b&&!(bull.length>1&&b.length>1)){src=cap.slice(i+1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&(cap[1]==="pre"||cap[1]==="script"||cap[1]==="style"),text:cap[0]});continue}if(!bq&&top&&(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].replace(/^ *\| *| *\| *$/g,"").split(/ *\| */)}this.tokens.push(item);continue}if(top&&(cap=this.rules.paragraph.exec(src))){src=src.substring(cap[0].length);this.tokens.push({type:"paragraph",text:cap[1].charAt(cap[1].length-1)==="\n"?cap[1].slice(0,-1):cap[1]});continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"text",text:cap[0]});continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return this.tokens};var inline={escape:/^\\([\\`*{}\[\]()#+\-.!_>])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/};inline._inside=/(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;inline._href=/\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;if(!this.links){throw new Error("Tokens array requires a `links` property.")}if(this.options.gfm){if(this.options.breaks){this.rules=inline.breaks}else{this.rules=inline.gfm}}else if(this.options.pedantic){this.rules=inline.pedantic}}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^<a /i.test(cap[0])){this.inLink=true}else if(this.inLink&&/^<\/a>/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(cap[0]):escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.text(escape(this.smartypants(cap[0])));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/---/g,"鈥�").replace(/--/g,"鈥�").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1鈥�").replace(/'/g,"鈥�").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1鈥�").replace(/"/g,"鈥�").replace(/\.{3}/g,"鈥�")};InlineLexer.prototype.mangle=function(text){if(!this.options.mangle)return text;var out="",l=text.length,i=0,ch;for(;i<l;i++){ch=text.charCodeAt(i);if(Math.random()>.5){ch="x"+ch.toString(16)}out+="&#"+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return"<pre><code>"+(escaped?code:escape(code,true))+"\n</code></pre>"}return'<pre><code class="'+this.options.langPrefix+escape(lang,true)+'">'+(escaped?code:escape(code,true))+"\n</code></pre>\n"};Renderer.prototype.blockquote=function(quote){return"<blockquote>\n"+quote+"</blockquote>\n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"<h"+level+' id="'+this.options.headerPrefix+raw.toLowerCase().replace(/[^\w]+/g,"-")+'">'+text+"</h"+level+">\n"};Renderer.prototype.hr=function(){return this.options.xhtml?"<hr/>\n":"<hr>\n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">\n"+body+"</"+type+">\n"};Renderer.prototype.listitem=function(text){return"<li>"+text+"</li>\n"};Renderer.prototype.paragraph=function(text){return"<p>"+text+"</p>\n"};Renderer.prototype.table=function(header,body){return"<table>\n"+"<thead>\n"+header+"</thead>\n"+"<tbody>\n"+body+"</tbody>\n"+"</table>\n"};Renderer.prototype.tablerow=function(content){return"<tr>\n"+content+"</tr>\n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+"</"+type+">\n"};Renderer.prototype.strong=function(text){return"<strong>"+text+"</strong>"};Renderer.prototype.em=function(text){return"<em>"+text+"</em>"};Renderer.prototype.codespan=function(text){return"<code>"+text+"</code>"};Renderer.prototype.br=function(){return this.options.xhtml?"<br/>":"<br>"};Renderer.prototype.del=function(text){return"<del>"+text+"</del>"};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0||prot.indexOf("vbscript:")===0){return""}}var out='<a href="'+href+'"';if(title){out+=' title="'+title+'"'}out+=">"+text+"</a>";return out};Renderer.prototype.image=function(href,title,text){var out='<img src="'+href+'" alt="'+text+'"';if(title){out+=' title="'+title+'"'}out+=this.options.xhtml?"/>":">";return out};Renderer.prototype.text=function(text){return text};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="\n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i<this.token.header.length;i++){flags={header:true,align:this.token.align[i]};cell+=this.renderer.tablecell(this.inline.output(this.token.header[i]),{header:true,align:this.token.align[i]})}header+=this.renderer.tablerow(cell);for(i=0;i<this.token.cells.length;i++){row=this.token.cells[i];cell="";for(j=0;j<row.length;j++){cell+=this.renderer.tablecell(this.inline.output(row[j]),{header:false,align:this.token.align[j]})}body+=this.renderer.tablerow(cell)}return this.renderer.table(header,body)}case"blockquote_start":{var body="";while(this.next().type!=="blockquote_end"){body+=this.tok()}return this.renderer.blockquote(body)}case"list_start":{var body="",ordered=this.token.ordered;while(this.next().type!=="list_end"){body+=this.tok()}return this.renderer.list(body,ordered)}case"list_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.token.type==="text"?this.parseText():this.tok()}return this.renderer.listitem(body)}case"loose_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.tok()}return this.renderer.listitem(body)}case"html":{var html=!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;return this.renderer.html(html)}case"paragraph":{return this.renderer.paragraph(this.inline.output(this.token.text))}case"text":{return this.renderer.paragraph(this.parseText())}}};function escape(html,encode){return html.replace(!encode?/&(?!#?\w+;)/g:/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function unescape(html){return html.replace(/&([#\w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=1,target,key;for(;i<arguments.length;i++){target=arguments[i];for(key in target){if(Object.prototype.hasOwnProperty.call(target,key)){obj[key]=target[key]}}}return obj}function marked(src,opt,callback){if(callback||typeof opt==="function"){if(!callback){callback=opt;opt=null}opt=merge({},marked.defaults,opt||{});var highlight=opt.highlight,tokens,pending,i=0;try{tokens=Lexer.lex(src,opt)}catch(e){return callback(e)}pending=tokens.length;var done=function(err){if(err){opt.highlight=highlight;return callback(err)}var out;try{out=Parser.parse(tokens,opt)}catch(e){err=e}opt.highlight=highlight;return err?callback(err):callback(null,out)};if(!highlight||highlight.length<3){return done()}delete opt.highlight;if(!pending)return done();for(;i<tokens.length;i++){(function(token){if(token.type!=="code"){return--pending||done()}return highlight(token.text,token.lang,function(err,code){if(err)return done(err);if(code==null||code===token.text){return--pending||done()}token.text=code;token.escaped=true;--pending||done()})})(tokens[i])}return}try{if(opt)opt=merge({},marked.defaults,opt);return Parser.parse(Lexer.lex(src,opt),opt)}catch(e){e.message+="\nPlease report this to https://github.com/chjj/marked.";if((opt||marked.defaults).silent){return"<p>An error occured:</p><pre>"+escape(e.message+"",true)+"</pre>"}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,sanitizer:null,mangle:true,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof module!=="undefined"&&typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}()); |
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html lang="en"> | ||
| 3 | +<link href="css/markdown.css" rel="stylesheet" type="text/css"/> | ||
| 4 | + | ||
| 5 | +<head> | ||
| 6 | + <meta charset="UTF-8"> | ||
| 7 | + <title>战损版ChatGPT-SSE实现流式输出</title> | ||
| 8 | + <link rel="icon" type="image/png" sizes="32x32" href="image/favicon-32x32.png"> | ||
| 9 | + <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script> | ||
| 10 | + <script src="js/markdown.min.js"></script> | ||
| 11 | + <script src="js/eventsource.min.js"></script> | ||
| 12 | + <script> | ||
| 13 | + function setText(text, uuid_str) { | ||
| 14 | + let content = document.getElementById(uuid_str) | ||
| 15 | + content.innerHTML = marked(text); | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + function uuid() { | ||
| 19 | + var s = []; | ||
| 20 | + var hexDigits = "0123456789abcdef"; | ||
| 21 | + for (var i = 0; i < 36; i++) { | ||
| 22 | + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); | ||
| 23 | + } | ||
| 24 | + s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010 | ||
| 25 | + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 | ||
| 26 | + s[8] = s[13] = s[18] = s[23] = "-"; | ||
| 27 | + | ||
| 28 | + var uuid = s.join(""); | ||
| 29 | + return uuid; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + window.onload = function () { | ||
| 33 | + let disconnectBtn = document.getElementById("disconnectSSE"); | ||
| 34 | + let messageElement = document.getElementById("message"); | ||
| 35 | + let chat = document.getElementById("chat"); | ||
| 36 | + let sse; | ||
| 37 | + let uid = window.localStorage.getItem("uid"); | ||
| 38 | + if (uid == null || uid == '' || uid == 'null') { | ||
| 39 | + uid = uuid(); | ||
| 40 | + } | ||
| 41 | + let text = ''; | ||
| 42 | + let uuid_str; | ||
| 43 | + // 设置本地存储 | ||
| 44 | + window.localStorage.setItem("uid", uid); | ||
| 45 | + // 回车事件 | ||
| 46 | + messageElement.onkeydown = function () { | ||
| 47 | + if (window.event.keyCode === 13) { | ||
| 48 | + if (!messageElement.value) { | ||
| 49 | + return; | ||
| 50 | + } | ||
| 51 | + uuid_str = uuid(); | ||
| 52 | + | ||
| 53 | + //创建sse | ||
| 54 | + const eventSource = new EventSourcePolyfill('/api/createSse', { | ||
| 55 | + headers: { | ||
| 56 | + 'uid': uid | ||
| 57 | + } | ||
| 58 | + }); | ||
| 59 | + | ||
| 60 | + eventSource.onopen = (event) => { | ||
| 61 | + console.log("开始输出后端返回值"); | ||
| 62 | + sse = event.target; | ||
| 63 | + }; | ||
| 64 | + eventSource.onmessage = (event) => { | ||
| 65 | + if (event.lastEventId == "[TOKENS]") { | ||
| 66 | + text = text + event.data; | ||
| 67 | + setText(text, uuid_str) | ||
| 68 | + text = '' | ||
| 69 | + return; | ||
| 70 | + } | ||
| 71 | + if (event.data == "[DONE]") { | ||
| 72 | + if (sse) { | ||
| 73 | + sse.close(); | ||
| 74 | + } | ||
| 75 | + return; | ||
| 76 | + } | ||
| 77 | + let json_data = JSON.parse(event.data) | ||
| 78 | + if (json_data.content == null || json_data.content == 'null') { | ||
| 79 | + return; | ||
| 80 | + } | ||
| 81 | + text = text + json_data.content; | ||
| 82 | + setText(text, uuid_str) | ||
| 83 | + }; | ||
| 84 | + eventSource.onerror = (event) => { | ||
| 85 | + console.log("onerror", event); | ||
| 86 | + alert("服务异常请重试并联系开发者!") | ||
| 87 | + if (event.readyState === EventSource.CLOSED) { | ||
| 88 | + console.log('connection is closed'); | ||
| 89 | + } else { | ||
| 90 | + console.log("Error occured", event); | ||
| 91 | + } | ||
| 92 | + event.target.close(); | ||
| 93 | + }; | ||
| 94 | + eventSource.addEventListener("customEventName", (event) => { | ||
| 95 | + console.log("Message id is " + event.lastEventId); | ||
| 96 | + }); | ||
| 97 | + eventSource.addEventListener("customEventName", (event) => { | ||
| 98 | + console.log("Message id is " + event.lastEventId); | ||
| 99 | + }); | ||
| 100 | + $.ajax({ | ||
| 101 | + type: 'post', | ||
| 102 | + url: '/api/chat', | ||
| 103 | + data: JSON.stringify({ | ||
| 104 | + 'msg': messageElement.value | ||
| 105 | + }), | ||
| 106 | + contentType: "application/json;charset=UTF-8", | ||
| 107 | + dataType: "json", | ||
| 108 | + headers: { | ||
| 109 | + "uid": uid, | ||
| 110 | + }, | ||
| 111 | + beforeSend: function (request) { | ||
| 112 | + | ||
| 113 | + }, | ||
| 114 | + success: function (result) { | ||
| 115 | + //新增问题框 | ||
| 116 | + chat.innerHTML += '<tr><td style="height: 30px;">' + messageElement.value + '<br/><br/> tokens:' + result.question_tokens + '</td></tr>'; | ||
| 117 | + messageElement.value = null | ||
| 118 | + //新增答案框 | ||
| 119 | + chat.innerHTML += '<tr><td><article id="' + uuid_str + '" class="markdown-body"></article></td></tr>'; | ||
| 120 | + }, | ||
| 121 | + complete: function () { | ||
| 122 | + }, | ||
| 123 | + error: function () { | ||
| 124 | + console.info("发送问题失败!"); | ||
| 125 | + } | ||
| 126 | + }) | ||
| 127 | + } | ||
| 128 | + }; | ||
| 129 | + | ||
| 130 | + disconnectBtn.onclick = function () { | ||
| 131 | + if (sse) { | ||
| 132 | + sse.close(); | ||
| 133 | + } | ||
| 134 | + }; | ||
| 135 | + | ||
| 136 | + }; | ||
| 137 | + </script> | ||
| 138 | +</head> | ||
| 139 | + | ||
| 140 | +<body> | ||
| 141 | +<div class="float-card"> | ||
| 142 | + <div class="float-card-item" id="disconnectSSE"> | ||
| 143 | + <a rel="noopener noreferrer">停止输出</a> | ||
| 144 | + </div> | ||
| 145 | +</div> | ||
| 146 | +<div class="input-card"> | ||
| 147 | + <div class="input-card-item"> | ||
| 148 | + <input id="message" placeholder="输入你的问题,回车结束......" type="text"> | ||
| 149 | + </div> | ||
| 150 | +</div> | ||
| 151 | +<div class="container" > | ||
| 152 | + <table border="1"> | ||
| 153 | + <tbody id="chat"> | ||
| 154 | + </tbody> | ||
| 155 | + </table> | ||
| 156 | +</div> | ||
| 157 | +</body> | ||
| 158 | +<style> | ||
| 159 | + .markdown-body { | ||
| 160 | + box-sizing: border-box; | ||
| 161 | + min-width: 200px; | ||
| 162 | + max-width: 980px; | ||
| 163 | + margin: 0 auto; | ||
| 164 | + padding: 45px; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + @media (max-width: 767px) { | ||
| 168 | + .markdown-body { | ||
| 169 | + padding: 15px; | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + input { | ||
| 174 | + height: 50px; | ||
| 175 | + width: 500px; | ||
| 176 | + font-size: 20px; | ||
| 177 | + background: no-repeat; | ||
| 178 | + color: #d0838e; | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + .container { | ||
| 182 | + width: 980px; | ||
| 183 | + border: 1px solid black; | ||
| 184 | + display: flex; | ||
| 185 | + flex-direction: column; | ||
| 186 | + margin-left: 150px; | ||
| 187 | + margin-top: 40px; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + .input-card { | ||
| 191 | + position: fixed; | ||
| 192 | + display: inline-block; | ||
| 193 | + right: 37%; | ||
| 194 | + top: 80%; | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + .input-card-item { | ||
| 198 | + display: flex; | ||
| 199 | + flex-direction: column; | ||
| 200 | + justify-content: center; | ||
| 201 | + align-items: center; | ||
| 202 | + margin-bottom: 16px; | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + .float-card { | ||
| 206 | + position: fixed; | ||
| 207 | + display: inline-block; | ||
| 208 | + right: 120px; | ||
| 209 | + top: 100px; | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + .float-card-item { | ||
| 213 | + display: flex; | ||
| 214 | + flex-direction: column; | ||
| 215 | + justify-content: center; | ||
| 216 | + align-items: center; | ||
| 217 | + width: 60px; | ||
| 218 | + height: 60px; | ||
| 219 | + border-radius: 50%; | ||
| 220 | + background-color: #ccccd6; | ||
| 221 | + margin-bottom: 16px; | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + .float-card-item:last-child { | ||
| 225 | + margin-bottom: 0px; | ||
| 226 | + background-color: #d0838e; | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + .float-card-item a { | ||
| 230 | + text-decoration: none; | ||
| 231 | + color: #594649; | ||
| 232 | + font-size: 13px; | ||
| 233 | + } | ||
| 234 | +</style> | ||
| 235 | +</html> |
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html lang="en"> | ||
| 3 | +<link href="../static/css/markdown.css" rel="stylesheet" type="text/css"/> | ||
| 4 | + | ||
| 5 | +<head> | ||
| 6 | + <meta charset="UTF-8"> | ||
| 7 | + <title>战损版ChatGPT</title> | ||
| 8 | + <link rel="icon" type="image/png" sizes="32x32" href="../static/image/favicon-32x32.png"> | ||
| 9 | + <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> | ||
| 10 | + <script src="../static/js/markdown.min.js"></script> | ||
| 11 | + <script> | ||
| 12 | + function setText(text, uuid_str) { | ||
| 13 | + let content = document.getElementById(uuid_str) | ||
| 14 | + content.innerHTML = marked(text); | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + function uuid() { | ||
| 18 | + var s = []; | ||
| 19 | + var hexDigits = "0123456789abcdef"; | ||
| 20 | + for (var i = 0; i < 36; i++) { | ||
| 21 | + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); | ||
| 22 | + } | ||
| 23 | + s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010 | ||
| 24 | + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 | ||
| 25 | + s[8] = s[13] = s[18] = s[23] = "-"; | ||
| 26 | + | ||
| 27 | + var uuid = s.join(""); | ||
| 28 | + return uuid; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + window.onload = function () { | ||
| 33 | + let disconnectBtn = document.getElementById("disconnectSSE"); | ||
| 34 | + let messageElement = document.getElementById("message"); | ||
| 35 | + let chat = document.getElementById("chat"); | ||
| 36 | + let sse; | ||
| 37 | + // 回车事件 | ||
| 38 | + messageElement.onkeydown = function () { | ||
| 39 | + if (window.event.keyCode === 13) { | ||
| 40 | + if (!messageElement.value) { | ||
| 41 | + return; | ||
| 42 | + } | ||
| 43 | + let text = ''; | ||
| 44 | + let uuid_str = uuid(); | ||
| 45 | + const eventSource = new EventSource('http://localhost:8000/chat?message=' + messageElement.value); | ||
| 46 | + | ||
| 47 | + eventSource.onopen = (event) => { | ||
| 48 | + console.log("onopen", event.readyState, event.target); | ||
| 49 | + sse = event.target; | ||
| 50 | + //新增问题框 | ||
| 51 | + chat.innerHTML += '<tr><td style="height: 50px;">' + messageElement.value + '</td></tr>'; | ||
| 52 | + messageElement.value = null | ||
| 53 | + //新增答案框 | ||
| 54 | + chat.innerHTML += '<tr><td><article id="' + uuid_str + '" class="markdown-body"></article></td></tr>'; | ||
| 55 | + }; | ||
| 56 | + eventSource.onmessage = (event) => { | ||
| 57 | + if (event.data == "[DONE]") { | ||
| 58 | + text = ''; | ||
| 59 | + if (sse) { | ||
| 60 | + sse.close(); | ||
| 61 | + } | ||
| 62 | + return; | ||
| 63 | + } | ||
| 64 | + let json_data = JSON.parse(event.data) | ||
| 65 | + if (json_data.content == null || json_data.content == 'null') { | ||
| 66 | + text = ''; | ||
| 67 | + return; | ||
| 68 | + } | ||
| 69 | + text = text + json_data.content; | ||
| 70 | + setText(text, uuid_str) | ||
| 71 | + | ||
| 72 | + }; | ||
| 73 | + eventSource.onerror = (event) => { | ||
| 74 | + console.log("onerror", event); | ||
| 75 | + alert("服务异常请重试并联系开发者!") | ||
| 76 | + if (event.readyState === EventSource.CLOSED) { | ||
| 77 | + console.log('connection is closed'); | ||
| 78 | + } else { | ||
| 79 | + console.log("Error occured", event); | ||
| 80 | + } | ||
| 81 | + event.target.close(); | ||
| 82 | + }; | ||
| 83 | + eventSource.addEventListener("customEventName", (event) => { | ||
| 84 | + console.log("Message id is " + event.lastEventId); | ||
| 85 | + }); | ||
| 86 | + } | ||
| 87 | + }; | ||
| 88 | + | ||
| 89 | + disconnectBtn.onclick = function () { | ||
| 90 | + if (sse) { | ||
| 91 | + sse.close(); | ||
| 92 | + } | ||
| 93 | + }; | ||
| 94 | + | ||
| 95 | + }; | ||
| 96 | + </script> | ||
| 97 | +</head> | ||
| 98 | + | ||
| 99 | +<body> | ||
| 100 | +<!--<div class="float-card-item send-btn">--> | ||
| 101 | +<!-- <a id="connectSSE" rel="noopener noreferrer">发送请求</a>--> | ||
| 102 | +<!--</div>--> | ||
| 103 | +<!--<div class="float-card-item dis-btn">--> | ||
| 104 | +<!-- <a id="disconnectSSE" rel="noopener noreferrer">断开连接</a>--> | ||
| 105 | +<!--</div>--> | ||
| 106 | +<div class="float-card"> | ||
| 107 | + <div class="float-card-item"> | ||
| 108 | + <a href="https://www.unfbx.com" target="_blank" rel="noopener noreferrer">Website</a> | ||
| 109 | + </div> | ||
| 110 | + <div class="float-card-item"> | ||
| 111 | + <a href="https://github.com/Grt1228" target="_blank" rel="noopener noreferrer">Github</a> | ||
| 112 | + </div> | ||
| 113 | + <div class="float-card-item"> | ||
| 114 | + <a id="disconnectSSE" rel="noopener noreferrer">停止输出</a> | ||
| 115 | + </div> | ||
| 116 | +</div> | ||
| 117 | +<div class="input-card"> | ||
| 118 | + <div class="input-card-item"> | ||
| 119 | + <input id="message" placeholder="输入你的问题,回车结束......" type="text"> | ||
| 120 | + </div> | ||
| 121 | +</div> | ||
| 122 | +<div class="container"> | ||
| 123 | + <table border="1"> | ||
| 124 | + <tbody id="chat"> | ||
| 125 | + <tr> | ||
| 126 | + <td> | ||
| 127 | + <pre style="font-size: 15px"> | ||
| 128 | + 帮忙点个star吧<br/> | ||
| 129 | + 1、依赖ChatGPT开源Java SDK:<a rel="noopener noreferrer" href="https://github.com/Grt1228/chatgpt-java" | ||
| 130 | + target="_blank" style="font-size: 15px">https://github.com/Grt1228/chatgpt-java</a><br/> | ||
| 131 | + 2、本项目免费开源地址:<a rel="noopener noreferrer" href="https://github.com/Grt1228/chatgpt-steam-output" | ||
| 132 | + target="_blank" style="font-size: 15px">https://github.com/Grt1228/chatgpt-steam-output</a><br/> | ||
| 133 | + 3、默认保持连接5分钟,默认上下文保持10个,5分钟无请求上下文会话销毁 | ||
| 134 | + </pre> | ||
| 135 | + </td> | ||
| 136 | + </tr> | ||
| 137 | + </tbody> | ||
| 138 | + </table> | ||
| 139 | +</div> | ||
| 140 | + | ||
| 141 | + | ||
| 142 | +</body> | ||
| 143 | +<style> | ||
| 144 | + .markdown-body { | ||
| 145 | + box-sizing: border-box; | ||
| 146 | + min-width: 200px; | ||
| 147 | + max-width: 980px; | ||
| 148 | + margin: 0 auto; | ||
| 149 | + padding: 45px; | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + @media (max-width: 767px) { | ||
| 153 | + .markdown-body { | ||
| 154 | + padding: 15px; | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + input { | ||
| 159 | + height: 50px; | ||
| 160 | + width: 500px; | ||
| 161 | + font-size: 20px; | ||
| 162 | + background: url(10) no-repeat; | ||
| 163 | + color: #d0838e; | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + .container { | ||
| 167 | + width: 980px; | ||
| 168 | + border: 1px solid black; | ||
| 169 | + display: flex; | ||
| 170 | + flex-direction: column; | ||
| 171 | + margin-left: 150px; | ||
| 172 | + margin-top: 40px; | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + .input-card { | ||
| 176 | + position: fixed; | ||
| 177 | + display: inline-block; | ||
| 178 | + right: 37%; | ||
| 179 | + top: 80%; | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + .input-card-item { | ||
| 183 | + display: flex; | ||
| 184 | + flex-direction: column; | ||
| 185 | + justify-content: center; | ||
| 186 | + align-items: center; | ||
| 187 | + margin-bottom: 16px; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + .float-card { | ||
| 191 | + position: fixed; | ||
| 192 | + display: inline-block; | ||
| 193 | + right: 120px; | ||
| 194 | + top: 100px; | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + .float-card-item { | ||
| 198 | + display: flex; | ||
| 199 | + flex-direction: column; | ||
| 200 | + justify-content: center; | ||
| 201 | + align-items: center; | ||
| 202 | + width: 60px; | ||
| 203 | + height: 60px; | ||
| 204 | + border-radius: 50%; | ||
| 205 | + background-color: #ccccd6; | ||
| 206 | + margin-bottom: 16px; | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + .float-card-item:last-child { | ||
| 210 | + margin-bottom: 0px; | ||
| 211 | + background-color: #d0838e; | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + .float-card-item a { | ||
| 215 | + text-decoration: none; | ||
| 216 | + color: #594649; | ||
| 217 | + font-size: 13px; | ||
| 218 | + } | ||
| 219 | +</style> | ||
| 220 | +</html> |
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html lang="en"> | ||
| 3 | +<link href="css/markdown.css" rel="stylesheet" type="text/css"/> | ||
| 4 | + | ||
| 5 | +<head> | ||
| 6 | + <meta charset="UTF-8"> | ||
| 7 | + <title>战损版ChatGPT-WebSocket实现流式输出</title> | ||
| 8 | + <link rel="icon" type="image/png" sizes="32x32" href="image/favicon-32x32.png"> | ||
| 9 | + <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> | ||
| 10 | + <script src="js/markdown.min.js"></script> | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + <script> | ||
| 14 | + function setText(text, uuid_str) { | ||
| 15 | + let content = document.getElementById(uuid_str) | ||
| 16 | + content.innerHTML = marked(text); | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + function uuid() { | ||
| 20 | + var s = []; | ||
| 21 | + var hexDigits = "0123456789abcdef"; | ||
| 22 | + for (var i = 0; i < 36; i++) { | ||
| 23 | + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); | ||
| 24 | + } | ||
| 25 | + s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010 | ||
| 26 | + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 | ||
| 27 | + s[8] = s[13] = s[18] = s[23] = "-"; | ||
| 28 | + | ||
| 29 | + var uuid = s.join(""); | ||
| 30 | + return uuid; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + window.onload = function () { | ||
| 35 | + var socket | ||
| 36 | + let uuid_str = ''; | ||
| 37 | + let text = ''; | ||
| 38 | + let disconnectBtn = document.getElementById("disconnectSSE"); | ||
| 39 | + let messageElement = document.getElementById("message"); | ||
| 40 | + let chat = document.getElementById("chat"); | ||
| 41 | + let uid = window.localStorage.getItem("uid"); | ||
| 42 | + if (uid == null || uid == '' || uid == 'null') { | ||
| 43 | + uid = uuid(); | ||
| 44 | + } | ||
| 45 | + // 设置本地存储 | ||
| 46 | + window.localStorage.setItem("uid", uid); | ||
| 47 | + if (typeof (WebSocket) == "undefined") { | ||
| 48 | + console.log("您的浏览器不支持WebSocket"); | ||
| 49 | + } else { | ||
| 50 | + console.log("您的浏览器支持WebSocket"); | ||
| 51 | + //实现化WebSocket对象 | ||
| 52 | + //指定要连接的服务器地址与端口建立连接 | ||
| 53 | + //注意ws、wss使用不同的端口。我使用自签名的证书测试, | ||
| 54 | + //无法使用wss,浏览器打开WebSocket时报错 | ||
| 55 | + //ws对应http、wss对应https。 | ||
| 56 | + socket = new WebSocket("ws://localhost:8000/websocket/"+uid); | ||
| 57 | + //连接打开事件 | ||
| 58 | + socket.onopen = function () { | ||
| 59 | + console.log("Socket 已打开"); | ||
| 60 | + }; | ||
| 61 | + //收到消息事件 | ||
| 62 | + socket.onmessage = function (msg) { | ||
| 63 | + if (msg.data == "[DONE]") { | ||
| 64 | + text = ''; | ||
| 65 | + return; | ||
| 66 | + } | ||
| 67 | + let json_data = JSON.parse(msg.data) | ||
| 68 | + if (json_data.content == null || json_data.content == 'null') { | ||
| 69 | + text = ''; | ||
| 70 | + return; | ||
| 71 | + } | ||
| 72 | + text = text + json_data.content; | ||
| 73 | + setText(text, uuid_str) | ||
| 74 | + }; | ||
| 75 | + //连接关闭事件 | ||
| 76 | + socket.onclose = function () { | ||
| 77 | + console.log("Socket已关闭"); | ||
| 78 | + }; | ||
| 79 | + //发生了错误事件 | ||
| 80 | + socket.onerror = function () { | ||
| 81 | + alert("服务异常请重试并联系开发者!") | ||
| 82 | + } | ||
| 83 | + //窗口关闭时,关闭连接 | ||
| 84 | + window.unload = function () { | ||
| 85 | + socket.close(); | ||
| 86 | + }; | ||
| 87 | + } | ||
| 88 | + // 回车事件 | ||
| 89 | + messageElement.onkeydown = function () { | ||
| 90 | + if (window.event.keyCode === 13) { | ||
| 91 | + if (!messageElement.value) { | ||
| 92 | + return; | ||
| 93 | + } | ||
| 94 | + uuid_str = uuid(); | ||
| 95 | + socket.send(messageElement.value); | ||
| 96 | + //新增问题框 | ||
| 97 | + chat.innerHTML += '<tr><td style="height: 50px;">' + messageElement.value + '</td></tr>'; | ||
| 98 | + messageElement.value = null | ||
| 99 | + //新增答案框 | ||
| 100 | + chat.innerHTML += '<tr><td><article id="' + uuid_str + '" class="markdown-body"></article></td></tr>'; | ||
| 101 | + } | ||
| 102 | + }; | ||
| 103 | + | ||
| 104 | + disconnectBtn.onclick = function () { | ||
| 105 | + if (socket) { | ||
| 106 | + socket.close(); | ||
| 107 | + } | ||
| 108 | + }; | ||
| 109 | + | ||
| 110 | + }; | ||
| 111 | + </script> | ||
| 112 | +</head> | ||
| 113 | + | ||
| 114 | +<body> | ||
| 115 | +<!--<div class="float-card-item send-btn">--> | ||
| 116 | +<!-- <a id="connectSSE" rel="noopener noreferrer">发送请求</a>--> | ||
| 117 | +<!--</div>--> | ||
| 118 | +<!--<div class="float-card-item dis-btn">--> | ||
| 119 | +<!-- <a id="disconnectSSE" rel="noopener noreferrer">断开连接</a>--> | ||
| 120 | +<!--</div>--> | ||
| 121 | +<div class="float-card"> | ||
| 122 | + <div class="float-card-item"> | ||
| 123 | + <a href="https://www.unfbx.com" target="_blank" rel="noopener noreferrer">Website</a> | ||
| 124 | + </div> | ||
| 125 | + <div class="float-card-item"> | ||
| 126 | + <a href="https://github.com/Grt1228" target="_blank" rel="noopener noreferrer">Github</a> | ||
| 127 | + </div> | ||
| 128 | + <div class="float-card-item"> | ||
| 129 | + <a id="disconnectSSE" rel="noopener noreferrer">停止输出</a> | ||
| 130 | + </div> | ||
| 131 | +</div> | ||
| 132 | +<div class="input-card"> | ||
| 133 | + <div class="input-card-item"> | ||
| 134 | + <input id="message" placeholder="输入你的问题,回车结束......" type="text"> | ||
| 135 | + </div> | ||
| 136 | +</div> | ||
| 137 | +<div class="container"> | ||
| 138 | + <table border="1"> | ||
| 139 | + <tbody id="chat"> | ||
| 140 | + <tr> | ||
| 141 | + <td> | ||
| 142 | + <pre style="font-size: 15px"> | ||
| 143 | + 帮忙点个star吧<br/> | ||
| 144 | + 1、依赖ChatGPT开源Java SDK:<a rel="noopener noreferrer" href="https://github.com/Grt1228/chatgpt-java" | ||
| 145 | + target="_blank" style="font-size: 15px">https://github.com/Grt1228/chatgpt-java</a><br/> | ||
| 146 | + 2、本项目免费开源地址:<a rel="noopener noreferrer" href="https://github.com/Grt1228/chatgpt-steam-output" | ||
| 147 | + target="_blank" style="font-size: 15px">https://github.com/Grt1228/chatgpt-steam-output</a><br/> | ||
| 148 | + 3、默认保持连接5分钟,默认上下文保持10个,5分钟无请求上下文会话销毁。 | ||
| 149 | + </pre> | ||
| 150 | + </td> | ||
| 151 | + </tr> | ||
| 152 | + </tbody> | ||
| 153 | + </table> | ||
| 154 | +</div> | ||
| 155 | + | ||
| 156 | + | ||
| 157 | +</body> | ||
| 158 | +<style> | ||
| 159 | + .markdown-body { | ||
| 160 | + box-sizing: border-box; | ||
| 161 | + min-width: 200px; | ||
| 162 | + max-width: 980px; | ||
| 163 | + margin: 0 auto; | ||
| 164 | + padding: 45px; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + @media (max-width: 767px) { | ||
| 168 | + .markdown-body { | ||
| 169 | + padding: 15px; | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + input { | ||
| 174 | + height: 50px; | ||
| 175 | + width: 500px; | ||
| 176 | + font-size: 20px; | ||
| 177 | + background: url(10) no-repeat; | ||
| 178 | + color: #d0838e; | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + .container { | ||
| 182 | + width: 980px; | ||
| 183 | + border: 1px solid black; | ||
| 184 | + display: flex; | ||
| 185 | + flex-direction: column; | ||
| 186 | + margin-left: 150px; | ||
| 187 | + margin-top: 40px; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + .input-card { | ||
| 191 | + position: fixed; | ||
| 192 | + display: inline-block; | ||
| 193 | + right: 37%; | ||
| 194 | + top: 80%; | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + .input-card-item { | ||
| 198 | + display: flex; | ||
| 199 | + flex-direction: column; | ||
| 200 | + justify-content: center; | ||
| 201 | + align-items: center; | ||
| 202 | + margin-bottom: 16px; | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + .float-card { | ||
| 206 | + position: fixed; | ||
| 207 | + display: inline-block; | ||
| 208 | + right: 120px; | ||
| 209 | + top: 100px; | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + .float-card-item { | ||
| 213 | + display: flex; | ||
| 214 | + flex-direction: column; | ||
| 215 | + justify-content: center; | ||
| 216 | + align-items: center; | ||
| 217 | + width: 60px; | ||
| 218 | + height: 60px; | ||
| 219 | + border-radius: 50%; | ||
| 220 | + background-color: #ccccd6; | ||
| 221 | + margin-bottom: 16px; | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + .float-card-item:last-child { | ||
| 225 | + margin-bottom: 0px; | ||
| 226 | + background-color: #d0838e; | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + .float-card-item a { | ||
| 230 | + text-decoration: none; | ||
| 231 | + color: #594649; | ||
| 232 | + font-size: 13px; | ||
| 233 | + } | ||
| 234 | +</style> | ||
| 235 | +</html> |
lh-jar/lh-jar-rocketmq/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>Luhui</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + <relativePath>../../pom.xml</relativePath> | ||
| 11 | + </parent> | ||
| 12 | + | ||
| 13 | + <artifactId>lh-jar-rocketmq</artifactId> | ||
| 14 | + | ||
| 15 | + <properties> | ||
| 16 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 17 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 18 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 19 | + </properties> | ||
| 20 | + | ||
| 21 | + <dependencies> | ||
| 22 | + <dependency> | ||
| 23 | + <groupId>org.apache.rocketmq</groupId> | ||
| 24 | + <artifactId>rocketmq-spring-boot-starter</artifactId> | ||
| 25 | + </dependency> | ||
| 26 | + <dependency> | ||
| 27 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 28 | + <artifactId>ruoyi-common</artifactId> | ||
| 29 | + </dependency> | ||
| 30 | + </dependencies> | ||
| 31 | + | ||
| 32 | +</project> |
| 1 | -package com.ruoyi.system.rocketmq; | 1 | +package com.zhonglai.luhui.rocketmq.service; |
| 2 | 2 | ||
| 3 | import com.alibaba.fastjson.JSON; | 3 | import com.alibaba.fastjson.JSON; |
| 4 | import com.alibaba.fastjson.JSONObject; | 4 | import com.alibaba.fastjson.JSONObject; |
| 5 | +import com.ruoyi.common.core.domain.DeviceCommandApi; | ||
| 5 | import com.ruoyi.common.core.domain.Message; | 6 | import com.ruoyi.common.core.domain.Message; |
| 6 | import com.ruoyi.common.core.domain.MessageCode; | 7 | import com.ruoyi.common.core.domain.MessageCode; |
| 7 | -import com.ruoyi.common.core.domain.MessageCodeType; | ||
| 8 | -import com.ruoyi.system.dto.DeviceCommandApi; | ||
| 9 | import org.apache.rocketmq.client.exception.MQBrokerException; | 8 | import org.apache.rocketmq.client.exception.MQBrokerException; |
| 10 | import org.apache.rocketmq.client.exception.MQClientException; | 9 | import org.apache.rocketmq.client.exception.MQClientException; |
| 11 | import org.apache.rocketmq.client.exception.RequestTimeoutException; | 10 | import org.apache.rocketmq.client.exception.RequestTimeoutException; |
lh-jar/lh-jar-sys-service/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>Luhui</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + <relativePath>../../pom.xml</relativePath> | ||
| 11 | + </parent> | ||
| 12 | + | ||
| 13 | + <artifactId>lh-jar-sys-service</artifactId> | ||
| 14 | + | ||
| 15 | + <properties> | ||
| 16 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 17 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 18 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 19 | + </properties> | ||
| 20 | + <dependencies> | ||
| 21 | + <dependency> | ||
| 22 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 23 | + <artifactId>lh-common-datasource</artifactId> | ||
| 24 | + </dependency> | ||
| 25 | + <dependency> | ||
| 26 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 27 | + <artifactId>lh-public-dao</artifactId> | ||
| 28 | + </dependency> | ||
| 29 | + <dependency> | ||
| 30 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 31 | + <artifactId>lh-domain</artifactId> | ||
| 32 | + </dependency> | ||
| 33 | + <dependency> | ||
| 34 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 35 | + <artifactId>ruoyi-common-redis</artifactId> | ||
| 36 | + </dependency> | ||
| 37 | + | ||
| 38 | + </dependencies> | ||
| 39 | +</project> |
-
请 注册 或 登录 后发表评论