deason 5 жил өмнө
commit
120e077c83

+ 12 - 0
.gitignore

@@ -0,0 +1,12 @@
+.project
+.classpath
+.settings
+*.iml
+*.jar
+target/
+.idea/
+*test/
+logs/
+classes/
+
+

+ 1 - 0
README.md

@@ -0,0 +1 @@
+

+ 30 - 0
assembly.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+	<id>distribution</id>
+	<formats>
+		<format>zip</format>
+	</formats>
+	<fileSets>
+		<fileSet>
+			<directory>${project.basedir}/src/main/resources</directory>
+			<outputDirectory>/config</outputDirectory>
+		</fileSet>
+		<fileSet>
+			<directory>${project.basedir}/shell</directory>
+			<excludes>
+				<exclude>start.args</exclude>
+				<exclude>start.vmoptions</exclude>
+			</excludes>
+			<outputDirectory>/</outputDirectory>
+			<fileMode>0777</fileMode>
+		</fileSet>
+	</fileSets>
+	<dependencySets>
+		<dependencySet>
+			<useProjectArtifact>true</useProjectArtifact>
+			<outputDirectory>lib</outputDirectory>
+			<scope>runtime</scope>
+		</dependencySet>
+	</dependencySets>
+</assembly>

+ 18 - 0
jenkins-dev.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+pwd
+
+rm -rf ~/project/examcloud/examcloud-ws-distribution.zip
+rm -rf ~/project/examcloud/examcloud-ws/lib/
+
+cp target/examcloud-ws-distribution.zip ~/project/examcloud/
+
+cd  ~/project/examcloud/
+unzip -o examcloud-ws-distribution.zip
+
+cd examcloud-ws
+echo "--spring.profiles.active=dev --examcloud.startup.configCenterHost=localhost" > start.args
+echo "-server -Xms512m -Xmx512m  -XX:-UseGCOverheadLimit" > start.vmoptions
+
+bash stop.sh
+BUILD_ID=DONTKILLME
+bash start.sh 400

+ 4 - 0
jenkins-prod.sh

@@ -0,0 +1,4 @@
+#!/bin/bash
+pwd
+
+cp target/examcloud-ws-distribution.zip ~/packages

+ 18 - 0
jenkins-test.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+pwd
+
+rm -rf ~/project/examcloud/examcloud-ws-distribution.zip
+rm -rf ~/project/examcloud/examcloud-ws/lib/
+
+cp target/examcloud-ws-distribution.zip ~/project/examcloud/
+
+cd  ~/project/examcloud/
+unzip -o examcloud-ws-distribution.zip
+
+cd examcloud-ws
+echo "--spring.profiles.active=test --examcloud.startup.configCenterHost=localhost" > start.args
+echo "-server -Xms512m -Xmx512m  -XX:-UseGCOverheadLimit" > start.vmoptions
+
+bash stop.sh
+BUILD_ID=DONTKILLME
+bash start.sh 400

+ 77 - 0
pom.xml

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud</groupId>
+		<artifactId>examcloud-parent</artifactId>
+		<version>2019</version>
+	</parent>
+	<artifactId>examcloud-ws</artifactId>
+	<version>2019-SNAPSHOT</version>
+	<packaging>jar</packaging>
+
+	<dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud</groupId>
+			<artifactId>examcloud-ws-starter</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud.rpc</groupId>
+			<artifactId>examcloud-ws-api</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+        <dependency>
+            <groupId>cn.com.qmth.examcloud</groupId>
+            <artifactId>examcloud-support</artifactId>
+            <version>${examcloud.version}</version>
+        </dependency>
+    </dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifest>
+							<mainClass>cn.com.qmth.examcloud.ws.WebSocketApp</mainClass>
+							<addClasspath>true</addClasspath>
+							<classpathPrefix>./</classpathPrefix>
+						</manifest>
+						<manifestEntries>
+							<Class-Path>../config/</Class-Path>
+						</manifestEntries>
+					</archive>
+					<excludes>
+						<exclude>*.properties</exclude>
+						<exclude>*.xml </exclude>
+						<exclude>classpath.location</exclude>
+					</excludes>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<configuration>
+					<finalName>examcloud-ws</finalName>
+					<descriptors>
+						<descriptor>assembly.xml</descriptor>
+					</descriptors>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make-assembly</id>
+						<phase>install</phase>
+						<goals>
+							<goal>assembly</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

+ 1 - 0
shell/start.args

@@ -0,0 +1 @@
+--spring.profiles.active=dev

+ 38 - 0
shell/start.sh

@@ -0,0 +1,38 @@
+#!/bin/bash
+
+FILE_PATH=$(cd `dirname $0`; pwd)
+APP_VERSION=`cat $FILE_PATH/version`
+APP_MAIN_JAR="examcloud-ws-"$APP_VERSION"-SNAPSHOT.jar"
+
+JAVA_OPTS=`cat $FILE_PATH/start.vmoptions`
+APP_ARGS=`cat $FILE_PATH/start.args`
+
+PID_LIST=`ps -ef|grep $APP_MAIN_JAR|grep java|awk '{print $2}'`
+
+if [ ! -z "$PID_LIST" ]; then
+    echo "[ERROR] : APP is already running!"
+    exit -1
+fi
+
+if [ "$1" ];then
+    echo "startupCode:"$1;
+else
+    echo "[ERROR] : no arguments"
+    exit -1
+fi
+
+APP_ARGS=$APP_ARGS" --examcloud.startup.startupCode="$1
+
+echo "java options:"
+echo "$JAVA_OPTS"
+echo "args:"
+echo "$APP_ARGS"
+    
+nohup java $JAVA_OPTS -jar $FILE_PATH/lib/$APP_MAIN_JAR $APP_ARGS >/dev/null 2>&1 &
+
+echo "starting......"
+
+exit 0
+
+
+

+ 1 - 0
shell/start.vmoptions

@@ -0,0 +1 @@
+-server -Xms512m -Xmx512m

+ 18 - 0
shell/stop.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+
+FILE_PATH=$(cd `dirname $0`; pwd)
+APP_VERSION=`cat $FILE_PATH/version`
+APP_MAIN_JAR="examcloud-ws-"$APP_VERSION"-SNAPSHOT.jar"
+
+PID_LIST=`ps -ef|grep $APP_MAIN_JAR|grep java|awk '{print $2}'`
+
+if [ ! -z "$PID_LIST" ]; then
+    echo "Runnable jar is $APP_MAIN_JAR."
+    for PID in $PID_LIST 
+    do
+        kill -9 $PID
+    done
+    echo "stopped !"
+fi
+
+exit 0

+ 1 - 0
shell/version

@@ -0,0 +1 @@
+2019

+ 12 - 0
src/main/java/cn/com/qmth/examcloud/ws/Tester.java

@@ -0,0 +1,12 @@
+package cn.com.qmth.examcloud.ws;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class Tester {
+
+	public void test() {
+
+	}
+
+}

+ 52 - 0
src/main/java/cn/com/qmth/examcloud/ws/WebSocketApp.java

@@ -0,0 +1,52 @@
+package cn.com.qmth.examcloud.ws;
+
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+import cn.com.qmth.examcloud.web.bootstrap.AppBootstrap;
+import cn.com.qmth.examcloud.web.support.SpringContextHolder;
+
+@SpringBootApplication
+@Configuration
+@EnableDiscoveryClient
+@ComponentScan(basePackages = {"cn.com.qmth"})
+@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
+public class WebSocketApp {
+
+	static {
+		String runtimeLevel = System.getProperty("log.commonLevel");
+		if (null == runtimeLevel) {
+			System.setProperty("log.commonLevel", "DEBUG");
+		}
+
+	}
+
+	/**
+	 * main
+	 *
+	 * @author WANGWEI
+	 * @param args
+	 * @throws Exception
+	 */
+	public static void main(String[] args) throws Exception {
+
+		AppBootstrap.run(WebSocketApp.class, args);
+
+		test();
+	}
+
+	/**
+	 * 测试方法
+	 *
+	 * @author WANGWEI
+	 */
+	private static void test() {
+		Tester tester = SpringContextHolder.getBean(Tester.class);
+		tester.test();
+	}
+
+}

+ 92 - 0
src/main/java/cn/com/qmth/examcloud/ws/api/provider/WsCloudServiceProvider.java

@@ -0,0 +1,92 @@
+package cn.com.qmth.examcloud.ws.api.provider;
+
+import javax.websocket.Session;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.ThreadContext;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.commons.util.ThreadLocalUtil;
+import cn.com.qmth.examcloud.ws.api.WsCloudService;
+import cn.com.qmth.examcloud.ws.api.request.SendTextReq;
+import cn.com.qmth.examcloud.ws.api.response.SendTextResp;
+import cn.com.qmth.examcloud.ws.starter.core.MessageOut;
+import cn.com.qmth.examcloud.ws.starter.core.SessionInfo;
+import cn.com.qmth.examcloud.ws.starter.core.WebSocketHelper;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@Api(tags = "websocket服务")
+@RestController
+@RequestMapping("api/cloud/ws")
+public class WsCloudServiceProvider implements WsCloudService {
+
+	private static final long serialVersionUID = 4784116669909937047L;
+
+	@ApiOperation(value = "发送消息")
+	@PostMapping("sendText")
+	@Override
+	public SendTextResp sendText(@RequestBody SendTextReq req) {
+
+		Long rootOrgId = req.getRootOrgId();
+		UserType userType = req.getUserType();
+		Long userId = req.getUserId();
+		String path = req.getPath();
+		String eventId = req.getEventId();
+		String content = req.getContent();
+
+		User user = new User();
+		user.setUserType(userType);
+		user.setUserId(userId);
+		user.setRootOrgId(rootOrgId);
+		String key = user.buildKey();
+
+		Session session = WebSocketHelper.getSession(path, key);
+
+		if (null == session) {
+			throw new StatusException("100001", "no ws session about path [" + path + "]");
+		}
+
+		SessionInfo sessionInfo = WebSocketHelper.getSessionInfo(session);
+
+		if (null == sessionInfo) {
+			throw new StatusException("100002", "no ws session info");
+		}
+
+		MessageOut out = new MessageOut(path, sessionInfo.getSessionId());
+		out.setEventId(eventId);
+
+		JsonElement jsonEl = null;
+		if (StringUtils.isNotBlank(content)) {
+			try {
+				jsonEl = JsonParser.parseString(content);
+			} catch (JsonSyntaxException e) {
+				throw new StatusException("100003", "message is not a json string");
+			}
+		}
+
+		out.setContent(jsonEl);
+
+		try {
+			ThreadContext.put("TRACE_ID", sessionInfo.getSessionId());
+			WebSocketHelper.sendText(session, path, out);
+		} finally {
+			ThreadContext.put("TRACE_ID", ThreadLocalUtil.getTraceId());
+		}
+
+		SendTextResp resp = new SendTextResp();
+		resp.setSessionId(sessionInfo.getSessionId());
+		return resp;
+	}
+
+}

+ 131 - 0
src/main/java/cn/com/qmth/examcloud/ws/config/ExamCloudResourceManager.java

@@ -0,0 +1,131 @@
+package cn.com.qmth.examcloud.ws.config;
+
+import java.util.List;
+import java.util.Set;
+
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.AppCacheBean;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.common.collect.Sets;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.AccessApp;
+import cn.com.qmth.examcloud.api.commons.security.bean.Role;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserType;
+import cn.com.qmth.examcloud.api.commons.security.enums.RoleMeta;
+import cn.com.qmth.examcloud.commons.util.PropertiesUtil;
+import cn.com.qmth.examcloud.commons.util.RegExpUtil;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import cn.com.qmth.examcloud.web.security.ResourceManager;
+import cn.com.qmth.examcloud.web.support.ApiInfo;
+
+/**
+ * 资源管理器
+ *
+ * @author WANGWEI
+ * @date 2019年2月18日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@Component
+public class ExamCloudResourceManager implements ResourceManager {
+
+	@Autowired
+	RedisClient redisClient;
+
+	static {
+		PropertiesUtil.loadFromResource("security.properties");
+	}
+
+	@Override
+	public AccessApp getAccessApp(Long appId) {
+		AppCacheBean appCacheBean = CacheHelper.getApp(appId);
+		AccessApp app = new AccessApp();
+		app.setAppId(appCacheBean.getId());
+		app.setAppCode(appCacheBean.getCode());
+		app.setAppName(appCacheBean.getName());
+		app.setSecretKey(appCacheBean.getSecretKey());
+		app.setTimeRange(appCacheBean.getTimeRange());
+		return app;
+	}
+
+	@Override
+	public boolean isNaked(ApiInfo apiInfo, String mapping) {
+		if (null == apiInfo) {
+			return true;
+		}
+
+		if (mapping.matches(".*swagger.*")) {
+			return true;
+		}
+
+		if (null != apiInfo) {
+			if (apiInfo.isNaked()) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	@Override
+	public boolean hasPermission(User user, ApiInfo apiInfo, String mapping) {
+
+		// 学生鉴权
+		if (user.getUserType().equals(UserType.STUDENT)) {
+			String key = "[s]" + mapping;
+			return PropertiesUtil.getBoolean(key, false);
+		}
+
+		List<Role> roleList = user.getRoleList();
+
+		if (CollectionUtils.isEmpty(roleList)) {
+			return false;
+		}
+
+		for (Role role : roleList) {
+			if (role.getRoleCode().equals(RoleMeta.SUPER_ADMIN.name())) {
+				return true;
+			}
+		}
+
+		// 权限组集合
+		String privilegeGroups = PropertiesUtil.getString(mapping);
+		if (StringUtils.isBlank(privilegeGroups)) {
+			return true;
+		}
+
+		// 用户权限集合
+		Set<String> rolePrivilegeList = Sets.newHashSet();
+		Long rootOrgId = user.getRootOrgId();
+		for (Role role : roleList) {
+			String key = "$_P_" + rootOrgId + "_" + role.getRoleId();
+			String rolePrivileges = redisClient.get(key, String.class);
+
+			List<String> rpList = RegExpUtil.findAll(rolePrivileges, "\\w+");
+			rolePrivilegeList.addAll(rpList);
+		}
+
+		List<String> privilegeGroupList = RegExpUtil.findAll(privilegeGroups, "[^\\;]+");
+
+		for (String pg : privilegeGroupList) {
+			pg = pg.trim();
+			if (StringUtils.isBlank(pg)) {
+				continue;
+			}
+
+			List<String> pList = RegExpUtil.findAll(pg, "[^\\,]+");
+			if (rolePrivilegeList.containsAll(pList)) {
+				return true;
+			} else {
+				continue;
+			}
+		}
+
+		return false;
+	}
+
+}

+ 47 - 0
src/main/java/cn/com/qmth/examcloud/ws/config/ExamCloudWebMvcConfigurer.java

@@ -0,0 +1,47 @@
+package cn.com.qmth.examcloud.ws.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import cn.com.qmth.examcloud.web.interceptor.FirstInterceptor;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import cn.com.qmth.examcloud.web.security.RequestPermissionInterceptor;
+import cn.com.qmth.examcloud.web.security.ResourceManager;
+import cn.com.qmth.examcloud.web.security.RpcInterceptor;
+
+/**
+ * WebMvcConfigurer
+ *
+ * @author WANGWEI
+ * @date 2019年1月30日
+ * @Copyright (c) 2018-2020 WANGWEI [QQ:522080330] All Rights Reserved.
+ */
+@Configuration
+public class ExamCloudWebMvcConfigurer implements WebMvcConfigurer {
+
+	@Autowired
+	ResourceManager resourceManager;
+
+	@Autowired
+	RedisClient redisClient;
+
+	@Override
+	public void addInterceptors(InterceptorRegistry registry) {
+		registry.addInterceptor(new FirstInterceptor()).addPathPatterns("/**");
+		registry.addInterceptor(new RpcInterceptor(resourceManager)).addPathPatterns("/**");
+
+		RequestPermissionInterceptor permissionInterceptor = new RequestPermissionInterceptor(
+				resourceManager, redisClient);
+		registry.addInterceptor(permissionInterceptor).addPathPatterns("/**");
+	}
+
+	@Override
+	public void addCorsMappings(CorsRegistry registry) {
+		registry.addMapping("/**").allowedOrigins("*").allowCredentials(false).allowedMethods("*")
+				.maxAge(3600);
+	}
+
+}

+ 18 - 0
src/main/java/cn/com/qmth/examcloud/ws/handler/FaceBiopsyHandler.java

@@ -0,0 +1,18 @@
+package cn.com.qmth.examcloud.ws.handler;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.ws.starter.core.MessageHandler;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+@Component
+@MessageHandler("faceBiopsy")
+public class FaceBiopsyHandler {
+
+    @MessageHandler
+    public Map<String, Object> message(User user) {
+        return null;
+    }
+
+}

+ 60 - 0
src/main/java/cn/com/qmth/examcloud/ws/handler/FileAnswerHandler.java

@@ -0,0 +1,60 @@
+package cn.com.qmth.examcloud.ws.handler;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.ws.api.enums.WebSocketEventType;
+import cn.com.qmth.examcloud.ws.handler.bean.FileAnswerBean;
+import cn.com.qmth.examcloud.ws.starter.core.Message;
+import cn.com.qmth.examcloud.ws.starter.core.MessageHandler;
+import com.mysql.cj.util.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+@MessageHandler("fileAnswer")
+public class FileAnswerHandler {
+
+	@MessageHandler
+	public Map<String,Object> message(User user, @Message FileAnswerBean fileAnswerBean) {
+		try {
+			//事件类型
+			String eventType = fileAnswerBean.getEventType();
+
+			if (StringUtils.isNullOrEmpty(eventType)) {
+				return buildSystemErrorMap("无效的消息请求");
+			}
+			if (eventType.equals(WebSocketEventType.HEARTBEAT.toString())) {
+				return buildHeartbeatMessage(eventType);
+			} else {
+				return buildSystemErrorMap("无效的消息请求");
+			}
+		} catch (Exception e) {
+			return buildSystemErrorMap("无效的消息请求,详情:" + e.getMessage());
+		}
+	}
+
+	/**
+	 * 构建系统错误消息体
+	 *
+	 * @param message
+	 */
+	private Map<String,Object> buildSystemErrorMap(String message) {
+		Map<String,Object> errorMap = new HashMap<>();
+		errorMap.put("eventType", WebSocketEventType.SYSTEM_ERROR.toString());
+		errorMap.put("errorMessage", message);
+		return errorMap;
+	}
+
+	/**
+	 * 构建心跳消息体
+	 * @param eventType
+	 * @return
+	 */
+	private Map<String,Object> buildHeartbeatMessage(String eventType) {
+		Map<String,Object> heartbeatMap = new HashMap<>();
+		heartbeatMap.put("eventType", eventType);
+		return heartbeatMap;
+	}
+
+}

+ 21 - 0
src/main/java/cn/com/qmth/examcloud/ws/handler/TestHandler2.java

@@ -0,0 +1,21 @@
+package cn.com.qmth.examcloud.ws.handler;
+
+import org.springframework.stereotype.Component;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.ws.handler.bean.TestBean;
+import cn.com.qmth.examcloud.ws.starter.core.Message;
+import cn.com.qmth.examcloud.ws.starter.core.MessageHandler;
+
+@Component
+@MessageHandler("test2")
+public class TestHandler2 {
+
+	@MessageHandler
+	public TestBean test(User user, @Message TestBean message) {
+		message.setS1(String.valueOf(System.currentTimeMillis()));
+		message.setS2(String.valueOf(System.currentTimeMillis()));
+		return message;
+	}
+
+}

+ 19 - 0
src/main/java/cn/com/qmth/examcloud/ws/handler/TestHandler3.java

@@ -0,0 +1,19 @@
+package cn.com.qmth.examcloud.ws.handler;
+
+import org.springframework.stereotype.Component;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.util.DateUtil;
+import cn.com.qmth.examcloud.ws.starter.core.Message;
+import cn.com.qmth.examcloud.ws.starter.core.MessageHandler;
+
+@Component
+@MessageHandler("test3")
+public class TestHandler3 {
+
+	@MessageHandler
+	public String test(User user, @Message String message) {
+		return DateUtil.chinaNow();
+	}
+
+}

+ 73 - 0
src/main/java/cn/com/qmth/examcloud/ws/handler/bean/FileAnswerBean.java

@@ -0,0 +1,73 @@
+package cn.com.qmth.examcloud.ws.handler.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import com.sun.org.apache.xpath.internal.operations.Bool;
+
+public class FileAnswerBean implements JsonSerializable {
+
+	private static final long serialVersionUID = -2201601873884785173L;
+	/**
+	 * 事件类型
+	 */
+	private String eventType;
+
+	/**
+	 * 题目序号
+	 */
+	private Integer order;
+
+	/**
+	 *是否为环境监测
+	 */
+	private Boolean testEnv;
+
+	/**
+	 * 传输文件类型(用于标识,通过二维码传输文件的类型)
+	 */
+	private String transferFileType;
+
+	/**
+	 * 考试记录id
+	 */
+	private Long examRecordDataId;
+
+	public String getEventType() {
+		return eventType;
+	}
+
+	public void setEventType(String eventType) {
+		this.eventType = eventType;
+	}
+
+	public Integer getOrder() {
+		return order;
+	}
+
+	public void setOrder(Integer order) {
+		this.order = order;
+	}
+
+	public Boolean getTestEnv() {
+		return testEnv;
+	}
+
+	public void setTestEnv(Boolean testEnv) {
+		this.testEnv = testEnv;
+	}
+
+	public String getTransferFileType() {
+		return transferFileType;
+	}
+
+	public void setTransferFileType(String transferFileType) {
+		this.transferFileType = transferFileType;
+	}
+
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+}

+ 25 - 0
src/main/java/cn/com/qmth/examcloud/ws/handler/bean/TestBean.java

@@ -0,0 +1,25 @@
+package cn.com.qmth.examcloud.ws.handler.bean;
+
+public class TestBean {
+
+	private String s1;
+
+	private String s2;
+
+	public String getS1() {
+		return s1;
+	}
+
+	public void setS1(String s1) {
+		this.s1 = s1;
+	}
+
+	public String getS2() {
+		return s2;
+	}
+
+	public void setS2(String s2) {
+		this.s2 = s2;
+	}
+
+}

+ 8 - 0
src/main/resources/application.properties

@@ -0,0 +1,8 @@
+spring.profiles.active=dev
+
+examcloud.startup.startupCode=8010
+examcloud.startup.configCenterHost=127.0.0.1
+examcloud.startup.configCenterPort=9999
+examcloud.startup.appCode=WS
+
+

+ 1 - 0
src/main/resources/classpath.location

@@ -0,0 +1 @@
+classpath 定位文件

+ 0 - 0
src/main/resources/limited.properties


+ 87 - 0
src/main/resources/log4j2.xml

@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN" monitorInterval="30">
+
+	<Properties>
+		<Property name="commonLevel" value="${sys:log.commonLevel}" />
+	</Properties>
+
+	<Appenders>
+		<!-- 控制台 日志 -->
+		<Console name="Console" target="SYSTEM_OUT">
+			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m | %l%n" />
+		</Console>
+		<!-- debug 日志 -->
+		<RollingFile name="DEBUG_APPENDER" fileName="./logs/debug/debug.log"
+			filePattern="./logs/debug/debug-%d{yyyy.MM.dd.HH}-%i.log">
+			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m | %l%n" />
+			<Policies>
+				<TimeBasedTriggeringPolicy interval="1" />
+				<SizeBasedTriggeringPolicy size="100 MB" />
+			</Policies>
+			<DefaultRolloverStrategy max="10000">
+				<Delete basePath="./logs/debug" maxDepth="1">
+					<IfFileName glob="debug-*.log">
+						<IfAccumulatedFileSize exceeds="2 GB" />
+					</IfFileName>
+				</Delete>
+			</DefaultRolloverStrategy>
+		</RollingFile>
+		<!-- WebSocket 日志 -->
+		<RollingFile name="WS_APPENDER" fileName="./logs/ws/ws.log"
+			filePattern="./logs/ws/ws-%d{yyyy.MM.dd.HH}-%i.log">
+			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m%n" />
+			<Policies>
+				<TimeBasedTriggeringPolicy interval="1" />
+				<SizeBasedTriggeringPolicy size="100 MB" />
+			</Policies>
+			<DefaultRolloverStrategy max="10000">
+				<Delete basePath="./logs/ws" maxDepth="1">
+					<IfFileName glob="ws-*.log">
+						<IfAccumulatedFileSize exceeds="2 GB" />
+					</IfFileName>
+				</Delete>
+			</DefaultRolloverStrategy>
+		</RollingFile>
+		<!-- 接口日志 -->
+		<RollingFile name="INTERFACE_APPENDER" fileName="./logs/interface/interface.log"
+			filePattern="./logs/interface/interface-%d{yyyy.MM.dd.HH}-%i.log">
+			<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}| %level | %X{TRACE_ID} - %X{CALLER} | %m%n" />
+			<Policies>
+				<TimeBasedTriggeringPolicy interval="1" />
+				<SizeBasedTriggeringPolicy size="100 MB" />
+			</Policies>
+			<DefaultRolloverStrategy max="10000">
+				<Delete basePath="./logs/interface" maxDepth="1">
+					<IfFileName glob="interface-*.log">
+						<IfAccumulatedFileSize exceeds="10 GB" />
+					</IfFileName>
+				</Delete>
+			</DefaultRolloverStrategy>
+		</RollingFile>
+	</Appenders>
+
+	<Loggers>
+		<Logger name="cn.com.qmth" level="${commonLevel}" additivity="false">
+			<AppenderRef ref="DEBUG_APPENDER" />
+			<AppenderRef ref="Console" />
+		</Logger>
+
+		<Logger name="WS_LOGGER" level="DEBUG" additivity="false">
+			<AppenderRef ref="WS_APPENDER" />
+			<AppenderRef ref="Console" />
+		</Logger>
+
+		<Logger name="INTERFACE_LOGGER" level="INFO" additivity="false">
+			<AppenderRef ref="INTERFACE_APPENDER" />
+			<AppenderRef ref="Console" />
+		</Logger>
+
+		<Logger name="cn.com.qmth.examcloud.web.actuator.ApiStatusInfoHolder" level="ERROR" />
+
+		<Root level="INFO">
+			<AppenderRef ref="Console" />
+			<AppenderRef ref="DEBUG_APPENDER" />
+		</Root>
+	</Loggers>
+
+</Configuration>

+ 1 - 0
src/main/resources/security.properties

@@ -0,0 +1 @@
+[s][api/ctr/ws][{path}][POST]=true