生命体征数据

统一账户登录只能一个人
This commit is contained in:
zhaoyz 2024-04-10 09:24:16 +08:00
parent b4c11c9663
commit 8d176fc965
37 changed files with 385 additions and 307 deletions

View File

@ -1,5 +1,6 @@
package com.rax.auth.config;
import com.rax.admin.api.feign.RemoteClientDetailsService;
import com.rax.auth.support.CustomeOAuth2AccessTokenGenerator;
import com.rax.auth.support.core.CustomeOAuth2TokenCustomizer;
import com.rax.auth.support.core.RaxDaoAuthenticationProvider;
@ -37,11 +38,7 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.time.Duration;
import java.util.Arrays;
@Configuration
@ -60,6 +57,8 @@ public class AuthorizationServerConfiguration {
private final RaxLoginPreFilter raxLoginPreFilter;
private final RemoteClientDetailsService clientDetailsService;
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
@ -67,8 +66,6 @@ public class AuthorizationServerConfiguration {
RaxAuthenticationFailureEventHandler failureEventHandler) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
http.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(corsConfigurationSource()));
http.addFilterAfter(raxLoginPreFilter, UsernamePasswordAuthenticationFilter.class);
http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点
tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证Converter
@ -87,7 +84,7 @@ public class AuthorizationServerConfiguration {
.stream()
.map(AntPathRequestMatcher::new)
.toList()
.toArray(new AntPathRequestMatcher[] {});
.toArray(new AntPathRequestMatcher[]{});
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers)
.permitAll()
@ -105,6 +102,7 @@ public class AuthorizationServerConfiguration {
.authorizationServerSettings(
AuthorizationServerSettings.builder()/*.issuer(SecurityConstants.PROJECT_LICENSE)*/.build()),
Customizer.withDefaults());
DefaultSecurityFilterChain securityFilterChain = http.build();
// 注入自定义授权模式实现
@ -115,6 +113,7 @@ public class AuthorizationServerConfiguration {
/**
* 令牌生成规则实现 </br>
* client:username:uuid
*
* @return OAuth2TokenGenerator
*/
@Bean
@ -137,6 +136,7 @@ public class AuthorizationServerConfiguration {
/**
* request -> xToken 注入请求转换器
*
* @return DelegatingAuthenticationConverter
*/
private AuthenticationConverter accessTokenRequestConverter() {
@ -160,10 +160,10 @@ public class AuthorizationServerConfiguration {
OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
authenticationManager, authorizationService, oAuth2TokenGenerator());
authenticationManager, authorizationService, oAuth2TokenGenerator(), clientDetailsService);
OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
authenticationManager, authorizationService, oAuth2TokenGenerator());
authenticationManager, authorizationService, oAuth2TokenGenerator(), clientDetailsService);
// 处理 UsernamePasswordAuthenticationToken
http.authenticationProvider(new RaxDaoAuthenticationProvider());
@ -173,17 +173,4 @@ public class AuthorizationServerConfiguration {
http.authenticationProvider(resourceOwnerSmsAuthenticationProvider);
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.addAllowedOriginPattern("*");
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setMaxAge(Duration.ofHours(1));
source.registerCorsConfiguration("/static/**", configuration);
return source;
}
}

View File

@ -1,5 +1,6 @@
package com.rax.auth.endpoint;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.TemporalAccessorUtil;
import cn.hutool.core.map.MapUtil;
@ -170,6 +171,9 @@ public class RaxTokenEndpoint {
cacheManager.getCache(CacheConstants.USER_DETAILS).evictIfPresent(authorization.getPrincipalName());
// 清空access token
authorizationService.remove(authorization);
String username = Base64.encode(authorization.getPrincipalName());
redisTemplate.delete("rax_logged_in::" + username + "::a::");
redisTemplate.delete("rax_logged_in::" + username + "::c::");
// 处理自定义退出事件保存相关日志
SpringContextHolder.publishEvent(new LogoutSuccessEvent(new PreAuthenticatedAuthenticationToken(
authorization.getPrincipalName(), authorization.getRegisteredClientId())));

View File

@ -1,11 +1,22 @@
package com.rax.auth.support.base;
import cn.hutool.core.codec.Base64;
import cn.hutool.extra.spring.SpringUtil;
import com.rax.admin.api.entity.SysOauthClientDetails;
import com.rax.admin.api.feign.RemoteClientDetailsService;
import com.rax.common.core.constant.SecurityConstants;
import com.rax.common.core.util.R;
import com.rax.common.core.util.RedisUtils;
import com.rax.common.core.util.SpringContextHolder;
import com.rax.common.security.service.RaxUser;
import com.rax.common.security.util.OAuth2ErrorCodesExpand;
import com.rax.common.security.util.ScopeException;
import jakarta.annotation.Resource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
@ -23,17 +34,17 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.security.Principal;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* @author jumuning
* @description
*
* 处理自定义授权
* @description 处理自定义授权
*/
public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OAuth2ResourceOwnerBaseAuthenticationToken>
implements AuthenticationProvider {
@ -53,21 +64,29 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
@Deprecated
private Supplier<String> refreshTokenGenerator;
private static final String LOGGED_IN = "rax_logged_in::";
private final RemoteClientDetailsService clientDetailsService;
/**
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
* provided parameters.
*
* @param authorizationService the authorization service
* @param tokenGenerator the token generator
* @since 0.2.3
*/
public OAuth2ResourceOwnerBaseAuthenticationProvider(AuthenticationManager authenticationManager,
OAuth2AuthorizationService authorizationService,
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
RemoteClientDetailsService clientDetailsService
) {
Assert.notNull(authorizationService, "authorizationService cannot be null");
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
this.authenticationManager = authenticationManager;
this.authorizationService = authorizationService;
this.tokenGenerator = tokenGenerator;
this.clientDetailsService = clientDetailsService;
// 国际化配置
this.messages = new MessageSourceAccessor(SpringUtil.getBean("securityMessageSource"), Locale.CHINA);
@ -83,6 +102,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
/**
* 当前provider是否支持此令牌类型
*
* @param authentication
* @return
*/
@ -91,6 +111,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
/**
* 当前的请求客户端是否支持此模式
*
* @param registeredClient
*/
public abstract void checkClient(RegisteredClient registeredClient);
@ -98,6 +119,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
/**
* Performs authentication with the same contract as
* {@link AuthenticationManager#authenticate(Authentication)} .
*
* @param authentication the authentication request object.
* @return a fully authenticated object including credentials. May return
* <code>null</code> if the <code>AuthenticationProvider</code> is unable to support
@ -126,8 +148,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
}
}
authorizedScopes = new LinkedHashSet<>(resouceOwnerBaseAuthentication.getScopes());
}
else {
} else {
authorizedScopes = new LinkedHashSet<>();
}
@ -177,8 +198,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
// 0.4.0 新增的方法
.authorizedScopes(authorizedScopes)
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
}
else {
} else {
authorizationBuilder.id(accessToken.getTokenValue()).accessToken(accessToken);
}
@ -192,8 +212,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getRefreshTokenTimeToLive());
refreshToken = new OAuth2RefreshToken(this.refreshTokenGenerator.get(), issuedAt, expiresAt);
}
else {
} else {
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
@ -208,6 +227,8 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
OAuth2Authorization authorization = authorizationBuilder.build();
checkIsLogin(authorization);
this.authorizationService.save(authorization);
LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken");
@ -215,8 +236,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken,
refreshToken, Objects.requireNonNull(authorization.getAccessToken().getClaims()));
}
catch (Exception ex) {
} catch (Exception ex) {
LOGGER.error("problem in authenticate", ex);
throw oAuth2AuthenticationException(authentication, (AuthenticationException) ex);
}
@ -225,6 +245,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
/**
* 登录异常转换为oauth2异常
*
* @param authentication 身份验证
* @param authenticationException 身份验证异常
* @return {@link OAuth2AuthenticationException}
@ -233,7 +254,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
AuthenticationException authenticationException) {
if (authenticationException instanceof UsernameNotFoundException) {
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USERNAME_NOT_FOUND,
this.messages.getMessage("JdbcDaoImpl.notFound", new Object[] { authentication.getName() },
this.messages.getMessage("JdbcDaoImpl.notFound", new Object[]{authentication.getName()},
"Username {0} not found"),
""));
}
@ -284,4 +305,23 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
}
/**
* 新登录用户会把老登录用户给踢下线
*/
private void checkIsLogin(OAuth2Authorization authorization) {
String username = Base64.encode(authorization.getPrincipalName());
RedisTemplate<String, String> redisTemplate = SpringContextHolder.getBean(RedisTemplate.class);
String previousAccessToken = redisTemplate.opsForValue().get(LOGGED_IN + username + "::a::");
String previousRefreshToken = redisTemplate.opsForValue().get(LOGGED_IN + username + "::c::");
String accessToken = authorization.getAccessToken().getToken().getTokenValue();
String refreshToken = authorization.getRefreshToken().getToken().getTokenValue();
if (StringUtils.hasText(previousAccessToken)) {
redisTemplate.delete("token::access_token::" + previousAccessToken);
redisTemplate.delete("token::refresh_token::" + previousRefreshToken);
}
R<SysOauthClientDetails> clientDetailsById = clientDetailsService.getClientDetailsById(authorization.getRegisteredClientId(), SecurityConstants.FROM_IN);
redisTemplate.opsForValue().set(LOGGED_IN + username + "::a::", accessToken, clientDetailsById.getData().getAccessTokenValidity(), TimeUnit.SECONDS);
redisTemplate.opsForValue().set(LOGGED_IN + username + "::c::", refreshToken, clientDetailsById.getData().getRefreshTokenValidity(), TimeUnit.SECONDS);
}
}

View File

@ -1,5 +1,6 @@
package com.rax.auth.support.password;
import com.rax.admin.api.feign.RemoteClientDetailsService;
import com.rax.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -28,6 +29,7 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider
/**
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
* provided parameters.
*
* @param authenticationManager
* @param authorizationService the authorization service
* @param tokenGenerator the token generator
@ -35,8 +37,9 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider
*/
public OAuth2ResourceOwnerPasswordAuthenticationProvider(AuthenticationManager authenticationManager,
OAuth2AuthorizationService authorizationService,
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
super(authenticationManager, authorizationService, tokenGenerator);
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
RemoteClientDetailsService clientDetailsService) {
super(authenticationManager, authorizationService, tokenGenerator, clientDetailsService);
}
@Override

View File

@ -1,5 +1,6 @@
package com.rax.auth.support.sms;
import com.rax.admin.api.feign.RemoteClientDetailsService;
import com.rax.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationProvider;
import com.rax.common.core.constant.SecurityConstants;
import org.apache.logging.log4j.LogManager;
@ -19,7 +20,7 @@ import java.util.Map;
/**
* @author lengleng
* @date date
*
* <p>
* 短信登录的核心处理
*/
public class OAuth2ResourceOwnerSmsAuthenticationProvider
@ -30,6 +31,7 @@ public class OAuth2ResourceOwnerSmsAuthenticationProvider
/**
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
* provided parameters.
*
* @param authenticationManager
* @param authorizationService the authorization service
* @param tokenGenerator the token generator
@ -37,8 +39,9 @@ public class OAuth2ResourceOwnerSmsAuthenticationProvider
*/
public OAuth2ResourceOwnerSmsAuthenticationProvider(AuthenticationManager authenticationManager,
OAuth2AuthorizationService authorizationService,
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
super(authenticationManager, authorizationService, tokenGenerator);
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
RemoteClientDetailsService clientDetailsService) {
super(authenticationManager, authorizationService, tokenGenerator, clientDetailsService);
}
@Override

View File

@ -33,7 +33,6 @@ import java.util.*;
import java.util.regex.Pattern;
/**
* @author lengleng
* @date 2020-03-11
* <p>
* 资源服务器对外直接暴露URL,如果设置contex-path 要特殊处理

View File

@ -59,6 +59,11 @@ public class OpenAPIDefinition extends OpenAPI implements InitializingBean, Appl
private ApplicationContext applicationContext;
/**
*
* @param swaggerProperties
* @return
*/
private SecurityScheme securityScheme(SwaggerProperties swaggerProperties) {
OAuthFlow clientCredential = new OAuthFlow();
clientCredential.setTokenUrl(swaggerProperties.getTokenUrl());

View File

@ -17,11 +17,11 @@
<modules>
<module>common-bom</module>
<module>common-core</module>
<module>common-datasource</module>
<!--<module>common-datasource</module>-->
<module>common-log</module>
<module>common-mybatis</module>
<module>common-oss</module>
<module>common-seata</module>
<!--<module>common-seata</module>-->
<module>common-security</module>
<module>common-feign</module>
<module>common-swagger</module>

View File

@ -623,6 +623,8 @@ CREATE TABLE `sys_user` (
`wx_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信登录openId',
`mini_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '小程序openId',
`qq_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'QQ openId',
`sex` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '性别',
`hospital_id` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '医院id',
PRIMARY KEY (`user_id`) USING BTREE,
KEY `user_wx_openid` (`wx_openid`) USING BTREE,
KEY `user_qq_openid` (`qq_openid`) USING BTREE,

View File

@ -89,7 +89,7 @@
<modules>
<!-- 代码生成模块 -->
<module>codegen</module>
<!--<module>codegen</module>-->
<!-- Quartz模块 -->
<module>quartz</module>
<!-- 身份验证模块 -->

View File

@ -36,7 +36,7 @@ public class SysLog implements Serializable {
* 日志类型
*/
@NotBlank(message = "日志类型不能为空")
@ExcelProperty("日志类型0-正常 9-错误")
@ExcelProperty("日志类型0-正常 9-错误 1-添加 2-删除 3-编辑")
@Schema(description = "日志类型")
private String logType;

View File

@ -148,6 +148,6 @@ public class SysUser implements Serializable {
private String sex;
@Schema(description = "医院")
private String hospital;
private String hospitalId;
}

View File

@ -49,7 +49,7 @@ public class SysHospitalController {
return sysHospitalService.getHospitalList(name, offset, limit);
}
@RequestMapping("/getHospitalList")
@PostMapping("/getHospitalList")
R<List> getHospitalList(String name) {
return sysHospitalService.getHospitalList(name);
}

View File

@ -39,6 +39,7 @@ public class SysLogController {
/**
* 简单分页查询
*
* @param page 分页对象
* @param sysLog 系统日志
* @return
@ -50,6 +51,7 @@ public class SysLogController {
/**
* 批量删除日志
*
* @param ids ID
* @return success/false
*/
@ -61,6 +63,7 @@ public class SysLogController {
/**
* 插入日志
*
* @param sysLog 日志实体
* @return success/false
*/
@ -72,6 +75,7 @@ public class SysLogController {
/**
* 导出excel 表格
*
* @param sysLog 查询条件
* @return
*/
@ -82,4 +86,9 @@ public class SysLogController {
return sysLogService.list(Wrappers.query(sysLog));
}
@PostMapping("/getMonthlyLogCount")
public R getMonthlyLogCount(String startTime, String endTime) {
return R.ok(sysLogService.getMonthlyLogCount(startTime, endTime));
}
}

View File

@ -3,16 +3,17 @@ package com.rax.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.rax.admin.api.entity.SysLog;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* <p>
* 日志表 Mapper 接口
* </p>
*
* @author lengleng
* @since 2017-11-20
*/
@Mapper
public interface SysLogMapper extends BaseMapper<SysLog> {
List<Map> getMonthlyLogCount(@Param("startTime") String startTime, @Param("endTime") String endTime);
}

View File

@ -5,6 +5,9 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.rax.admin.api.dto.SysLogDTO;
import com.rax.admin.api.entity.SysLog;
import java.util.List;
import java.util.Map;
/**
* <p>
* 日志表 服务类
@ -30,4 +33,6 @@ public interface SysLogService extends IService<SysLog> {
*/
Boolean saveLog(SysLog sysLog);
List<Map> getMonthlyLogCount(String startTime, String endTime);
}

View File

@ -13,6 +13,9 @@ import com.rax.admin.service.SysLogService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
/**
* <p>
* 日志表 服务实现类
@ -52,4 +55,8 @@ public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> impleme
return Boolean.TRUE;
}
@Override
public List<Map> getMonthlyLogCount(String startTime, String endTime) {
return baseMapper.getMonthlyLogCount(startTime, endTime);
}
}

View File

@ -1,5 +1,5 @@
server:
port: 9999
port: 8888
servlet:
context-path: /admin
@ -17,7 +17,7 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://192.168.65.130:3306/rax_backend?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true
url: jdbc:mysql://192.168.65.130:3306/rax_backend?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true
# 定时任务属性配置
quartz:
properties:
@ -46,6 +46,7 @@ spring:
multipart:
max-file-size: 100MB
max-request-size: 100MB
# 静态资源文件
mvc:
static-path-pattern: /static/**
@ -79,10 +80,6 @@ security:
- /rax/**
- /hospital/getHospitalList
# 临时白名单
- /static/**
# - /topic/**
# - /front/**
# - /medicine/**
#--------------如下配置尽量不要变动-------------

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rax.admin.mapper.SysLogMapper">
<select id="getMonthlyLogCount" resultType="java.util.Map">
SELECT
date_format( create_time,'%Y-%m-%d') "date", COUNT(*) "count"
FROM `sys_log`
WHERE
create_time <![CDATA[ >= ]]> #{startTime} AND create_time <![CDATA[ <= ]]> #{endTime}
GROUP BY date_format(create_time,'%Y-%m-%d')
</select>
</mapper>

View File

@ -1,93 +0,0 @@
const stompClient = new StompJs.Client({
brokerURL: 'ws://localhost:9999/admin/rax/SurgeryData'
});
/*const testClient = new StompJs.Client({
brokerURL: "ws://localhost:8080/test-guide"
})*/
let user = ""
stompClient.onConnect = (frame) => {
setConnected(true);
console.log('Connected: ' + frame);
/*stompClient.subscribe('/topic/user/'+user+'/doctorMedicine', (greeting) => {
showGreeting(greeting.body);
});*/
};
/*testClient.onConnect = (data) => {
console.log('Connected: ' + data);
testClient.subscribe('/topic/test', (greeting) => {
console.log(JSON.parse(greeting.body).content);
});
}*/
stompClient.onWebSocketError = (error) => {
console.error('Error with websocket', error);
};
stompClient.onStompError = (frame) => {
console.error('Broker reported error: ' + frame.headers['message']);
console.error('Additional details: ' + frame.body);
};
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
}
else {
$("#conversation").hide();
}
$("#greetings").html("");
}
function connect() {
stompClient.activate();
// testClient.activate();
}
function disconnect() {
sendNameToServer('stop');
stompClient.deactivate();
// testClient.deactivate();
setConnected(false);
console.log("Disconnected");
}
function sendName() {
user = Math.random()
$("#user-id").val(user)
sendNameToServer();
}
function sendNameToServer(status) {
/*stompClient.publish({
destination: "/front/doctorMedicine",
body: status ? status : $("#name").val()
});*/
stompClient.publish({
destination: "/front/doctorMedicine",
body: JSON.stringify({'status': status, "db": "test", user})
});
stompClient.subscribe('/topic/user/'+user+'/doctorMedicine', (greeting) => {
showGreeting(greeting.body);
});
/*testClient.publish({
destination: "/app/test-hello",
body: JSON.stringify({'name': "test-hello"})
})*/
}
function showGreeting(message) {
$("#greetings").append("<tr><td>" + message + "</td></tr>");
}
$(function () {
$("form").on('submit', (e) => e.preventDefault());
$( "#connect" ).click(() => connect());
$( "#disconnect" ).click(() => disconnect());
$( "#send" ).click(() => sendName());
});

View File

@ -1,53 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Hello WebSocket</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link href="/admin/static/html/main.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@7.0.0/bundles/stomp.umd.min.js"></script>
<script src="/admin/static/html/app.js"></script>
</head>
<body>
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
enabled. Please enable
Javascript and reload this page!</h2></noscript>
<div id="main-content" class="container">
<div class="row">
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="connect">WebSocket connection:</label>
<button id="connect" class="btn btn-default" type="submit">Connect</button>
<button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
</button>
</div>
</form>
</div>
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="name">What is your name?</label>
<input type="text" id="name" class="form-control" placeholder="Your name here...">
<span id="user-id"></span>
</div>
<button id="send" class="btn btn-default" type="submit">Send</button>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="conversation" class="table table-striped">
<thead>
<tr>
<th>Greetings</th>
</tr>
</thead>
<tbody id="greetings">
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,14 +0,0 @@
body {
background-color: #f5f5f5;
}
#main-content {
max-width: 940px;
padding: 2em 3em;
margin: 0 auto 20px;
background-color: #fff;
border: 1px solid #e5e5e5;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -44,17 +44,15 @@ public class WSChannelInterceptor implements ChannelInterceptor {
String token = accessToken.get(0);
OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
if (StompCommand.CONNECT.equals(accessor.getCommand()) || StompCommand.SEND.equals(accessor.getCommand())) {
if (authorization == null) {
throw new AccessDeniedException("Access is denied");
}
}
}
if (StompCommand.ABORT.equals(accessor.getCommand())) {
System.out.println("StompCommand.ABORT");
} else if (StompCommand.DISCONNECT.equals(accessor.getCommand())
|| StompCommand.UNSUBSCRIBE.equals(accessor.getCommand())) {
if (StompCommand.DISCONNECT.equals(accessor.getCommand())
|| StompCommand.UNSUBSCRIBE.equals(accessor.getCommand()) || StompCommand.ABORT.equals(accessor.getCommand())) {
String simpSessionId = (String) accessor.getHeader("simpSessionId");
vitalSignTimer.stopTimerTaskMongo(simpSessionId);
chatService.stopTimerTaskMongo(simpSessionId);

View File

@ -2,6 +2,7 @@ package com.rax.vital.medicine.controller;
import com.alibaba.fastjson.JSONObject;
import com.rax.vital.medicine.service.ChatService;
import com.rax.vital.util.DatabaseNameUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.handler.annotation.MessageMapping;
@ -11,6 +12,8 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.stereotype.Controller;
import java.util.HashMap;
@Controller
public class ChatController {
@ -27,9 +30,16 @@ public class ChatController {
if (authorization != null) {
String username = authorization.getPrincipalName();
String simpSessionId = messageHeaders.get("simpSessionId", String.class);
String db = params.getString("db");
// 病人名
String patientName = params.getString("patientName");
// 病人身份证
String idNum = params.getString("idNum");
// yyyyMMdd
String date = params.getString("date");
String databaseName = DatabaseNameUtil.encrypt(patientName) + "_" + DatabaseNameUtil.encrypt(idNum) + "_" + date;
// 消息内容
String msg = params.getString("msg");
chatService.sendMessage(db, username, simpSessionId, msg);
chatService.sendMessage(databaseName, username, simpSessionId, msg);
} else {
throw new AccessDeniedException("Access is denied");
}

View File

@ -2,17 +2,21 @@ package com.rax.vital.medicine.controller;
import com.alibaba.fastjson.JSONObject;
import com.rax.vital.timer.VitalSignTimer;
import com.rax.vital.util.DatabaseNameUtil;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.stereotype.Controller;
import org.springframework.util.LinkedMultiValueMap;
import java.util.ArrayList;
import java.util.List;
/**
* 用药
@ -32,14 +36,44 @@ public class MedicineController {
@MessageMapping("/getSurgeryData")
public void doctorMedicine(MessageHeaders messageHeaders, String body) {
JSONObject params = JSONObject.parseObject(body);
OAuth2Authorization authorization = authorizationService.findByToken(params.getString("token"), OAuth2TokenType.ACCESS_TOKEN);
if (authorization != null) {
LinkedMultiValueMap nativeHeaders = (LinkedMultiValueMap) messageHeaders.get("nativeHeaders");
ArrayList tokenList = (ArrayList) nativeHeaders.get("token");
String token = (String) tokenList.get(0);
OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
String username = authorization.getPrincipalName();
String simpSessionId = messageHeaders.get("simpSessionId", String.class);
vitalSignTimer.createAndSendMessageMongo(params.getString("db"), username, simpSessionId);
} else {
throw new AccessDeniedException("Access is denied");
JSONObject params = JSONObject.parseObject(body);
// 病人名
String patientName = params.getString("patientName");
// 病人身份证
String idNum = params.getString("idNum");
// yyyyMMdd
String date = params.getString("date");
String databaseName = DatabaseNameUtil.encrypt(patientName) + "_" + DatabaseNameUtil.encrypt(idNum) + "_" + date;
vitalSignTimer.createAndSendMessageMongo(databaseName, username, simpSessionId);
}
@MessageMapping("/changeAIFlag")
public void changeAIFlag(MessageHeaders messageHeaders, String body) {
LinkedMultiValueMap nativeHeaders = (LinkedMultiValueMap) messageHeaders.get("nativeHeaders");
ArrayList tokenList = (ArrayList) nativeHeaders.get("token");
String token = (String) tokenList.get(0);
OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
String username = authorization.getPrincipalName();
String simpSessionId = messageHeaders.get("simpSessionId", String.class);
JSONObject params = JSONObject.parseObject(body);
// 病人名
String patientName = params.getString("patientName");
// 病人身份证
String idNum = params.getString("idNum");
// yyyyMMdd
String date = params.getString("date");
String databaseName = DatabaseNameUtil.encrypt(patientName) + "_" + DatabaseNameUtil.encrypt(idNum) + "_" + date;
vitalSignTimer.changeAIFlag(databaseName, username, simpSessionId, params.getString("flag"),
params.getString("medicine"), params.getString("value"));
}
}

View File

@ -1,7 +1,6 @@
package com.rax.vital.medicine.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.rax.vital.medicine.entity.AIMedicine;
import com.rax.common.core.util.R;
import org.springframework.data.mongodb.core.MongoTemplate;
import java.sql.Connection;
@ -15,4 +14,6 @@ public interface AIMedicineService {
List<Map> getAIMedicine(MongoTemplate template);
List<Map> getAIMedicine(Connection connection);
void changeAIFlagMedicine(MongoTemplate template, String flag, String medicine, String value);
}

View File

@ -1,8 +1,6 @@
package com.rax.vital.medicine.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rax.vital.medicine.entity.AIMedicine;
import com.rax.vital.medicine.mapper.AIMedicineMapper;
import com.mongodb.BasicDBObject;
import com.rax.vital.medicine.service.AIMedicineService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -10,11 +8,13 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -28,11 +28,24 @@ import java.util.Map;
@AllArgsConstructor
public class AIMedicineServiceImpl implements AIMedicineService {
private static final Map<String, String> doctorMedicineKeyMap = new HashMap() {
{
put("丙泊酚", "丙泊酚");
put("舒芬太尼", "舒芬太尼");
put("瑞芬太尼", "瑞芬太尼");
put("顺阿曲库胺", "顺阿曲库胺");
put("尼卡地平", "尼卡地平");
put("艾司洛尔", "艾司洛尔");
put("麻黄素", "麻黄素");
put("阿托品", "阿托品");
}
};
@Override
public List<Map> getAIMedicine(MongoTemplate template) {
Query query = new Query();
query.limit(1);
query.with(Sort.by(Sort.Order.desc("_id")));
query.with(Sort.by(Sort.Order.desc("Time")));
List<Map> aiMedicines = template.find(query, Map.class, "AIMedicineTable");
return aiMedicines;
}
@ -63,4 +76,27 @@ public class AIMedicineServiceImpl implements AIMedicineService {
}
return medicineList;
}
@Override
public void changeAIFlagMedicine(MongoTemplate template, String flag, String medicine, String value) {
BasicDBObject obj = new BasicDBObject();
obj.put("Flag", flag);
obj.put("Time", LocalDateTime.now());
obj.put("ConvertFlag", "2".equals(flag) ? 1 : 0);
template.insert(obj, "aiflagtable");
if (StringUtils.hasText(value) && StringUtils.hasText(medicine)) {
BasicDBObject medicineObj = new BasicDBObject();
for (String key : doctorMedicineKeyMap.keySet()) {
if (key.equals(medicine)) {
medicineObj.put(medicine, value);
} else {
medicineObj.put(key, 0);
}
}
medicineObj.put("phase", "");
medicineObj.put("Time", LocalDateTime.now());
template.insert(medicineObj, "doctormedicinetable");
}
}
}

View File

@ -32,7 +32,7 @@ public class DoctorMedicineServiceImpl implements DoctorMedicineService {
public List<Map> getDocMedicine(MongoTemplate template) {
Query query = new Query();
query.limit(1);
query.with(Sort.by(Sort.Order.desc("_id")));
query.with(Sort.by(Sort.Order.desc("Time")));
List<Map> doctorMedicineTable = template.find(query, Map.class, "DoctorMedicineTable");
return doctorMedicineTable;
}

View File

@ -31,7 +31,7 @@ public class RevulsionServiceImpl implements RevulsionService {
public List<Map> getRevulsionServiceList(MongoTemplate template) {
Query query = new Query();
query.limit(1);
query.with(Sort.by(Sort.Order.desc("_id")));
query.with(Sort.by(Sort.Order.desc("Time")));
List<Map> revulsionTable = template.find(query, Map.class, "RevulsionTable");
return revulsionTable;
}

View File

@ -38,8 +38,24 @@ public class VitalSignServiceImpl implements VitalSignsService {
public List<Map> getVitalSignsList(MongoTemplate template) {
Query query = new Query();
query.limit(1);
query.with(Sort.by(Sort.Order.desc("_id")));
query.with(Sort.by(Sort.Order.desc("Time")));
List<Map> vitalList = template.find(query, Map.class, "featureTable");
if (vitalList != null && vitalList.size() > 0) {
Map map = vitalList.get(0);
Long bis = (Long) map.get("BIS");
map.put("BIS_except", bis <= 40 || bis >= 60);
Long hr = (Long) map.get("HR");
map.put("HR_except", hr <= 50 || hr >= 80);
Long sbp = (Long) map.get("SBP");
map.put("SBP_except", sbp <= 90 || sbp >= 120);
Long dbp = (Long) map.get("DBP");
map.put("DBP_except", dbp <= 60 || dbp >= 90);
Float st = (Float) map.get("ST");
map.put("ST_except", st <= -0.2 || st >= 0.2);
Long etCO2 = (Long) map.get("EtCO2");
map.put("EtCO2_except", etCO2 <= 30 || etCO2 >= 45);
}
return vitalList;
}
@ -48,7 +64,7 @@ public class VitalSignServiceImpl implements VitalSignsService {
List<Map> vitalList = new ArrayList<>();
try {
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT id, phase, bis, hr, sbp, dbp, st, temp, spo2, etco2, ppg, abg, tof, time FROM `featuretable` ORDER BY time DESC LIMIT 1;");
ResultSet resultSet = statement.executeQuery("SELECT id, Phase, BIS, HR, SBP, DBP, ST, TEMP, SPO2, EtCO2, PPG, ABG, TOF, Time FROM `featuretable` ORDER BY time DESC LIMIT 1;");
while (resultSet.next()) {
Map vital = new HashMap();
vital.put("id", resultSet.getString("id"));

View File

@ -38,16 +38,16 @@ public class VitalSignTimer {
private final RevulsionService revulsionService;
// mongoDB定时任务容器
private static final Map<String, TimerTask> timerMongoTaskMap = new HashMap<>(300);
private static volatile Map<String, TimerTask> timerMongoTaskMap = new HashMap<>(300);
// mongoDB链接工具类容器
private static final Map<String, MongoDBSource> mongoDBSourceMap = new HashMap<>(300);
private static volatile Map<String, MongoDBSource> mongoDBSourceMap = new HashMap<>(300);
// mysql定时任务容器
private static final Map<String, TimerTask> timerMysqlTaskMap = new HashMap<>(300);
private static volatile Map<String, TimerTask> timerMysqlTaskMap = new HashMap<>(300);
// mysql链接容器
private static final Map<String, MySQLSource> mysqlConnectionMap = new HashMap(300);
private static volatile Map<String, MySQLSource> mysqlConnectionMap = new HashMap(300);
// MongoDB的地址
@Value("${vital-sign.mongodb.host}")
@ -73,12 +73,21 @@ public class VitalSignTimer {
@Value("${vital-sign.mysql.password}")
private String mysqlPassword;
private static volatile Map<String, String> masterControlMap = new HashMap(300);
/**
* 根据当前用户和患者数据库进行查询生命体征和用药信息并推送数据库类型是MongoDB
*
* @author zhaoyz
*/
public void createAndSendMessageMongo(String database, String username, String simpSessionId) {
synchronized (username) {
if (!masterControlMap.containsKey(database)) {
masterControlMap.put(database, username);
}
}
TimerTask task = timerMongoTaskMap.get(simpSessionId);
if (task != null) {
return;
@ -110,7 +119,6 @@ public class VitalSignTimer {
result.put("flags", flags);
simpMessagingTemplate.convertAndSendToUser(username + ":" + database, "/surgeryData", result);
}
};
// 定时任务设置1秒
@ -200,4 +208,31 @@ public class VitalSignTimer {
}
}
public void changeAIFlag(String database, String username, String simpSessionId, String flag, String medicine, String value) {
synchronized (username) {
if (masterControlMap.containsKey(database) && masterControlMap.get(database).equals(username)) {
MongoDBSource mongoDBSource = mongoDBSourceMap.get(simpSessionId);
if (mongoDBSource == null) {
mongoDBSource = new MongoDBSource(mongoDBHost, mongoPassword, mongoUsername, database);
mongoDBSourceMap.put(simpSessionId, mongoDBSource);
mongoDBSource.open();
}
MongoTemplate template = mongoDBSource.getTemplate();
aiMedicineService.changeAIFlagMedicine(template, flag, medicine, value);
HashMap<String, Object> result = new HashMap();
result.put("status", 0);
result.put("flag", flag);
result.put("msg", "");
simpMessagingTemplate.convertAndSendToUser(username + ":" + database, "/medicineData", result);
} else {
HashMap<String, Object> result = new HashMap();
result.put("status", 1);
result.put("msg", "不是主控人员");
simpMessagingTemplate.convertAndSendToUser(username + ":" + database, "/medicineData", result);
}
}
}
}

View File

@ -0,0 +1,34 @@
package com.rax.vital.util;
import java.util.Base64;
import org.springframework.util.StringUtils;
public class DatabaseNameUtil {
private static final String key = "ruianxing112232323";
public static String decrypt(String data) {
Base64.Decoder decoder = Base64.getDecoder();
return new String(xor(decoder.decode(data)));
}
public static String encrypt(String data) {
if (StringUtils.hasText(data)) {
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(xor(data.getBytes()));
}
return null;
}
private static byte[] xor(byte[] dataBytes) {
byte[] result = new byte[dataBytes.length];
byte[] keyBytes = DatabaseNameUtil.key.getBytes();
int keyLen = keyBytes.length;
for (int i = 0; i < dataBytes.length; i++) {
result[i] = (byte) ((dataBytes[i]) ^ (keyBytes[i % keyLen]));
}
return result;
}
}