移动护理总结:

项目架构:

使用JFinal 为基础 JBolt为框架进行开发,前后端分类

官网:

JFinal-JBolt极速开发平台

JFinal 极速开发

数据库:

  • mysql 8.0
  • SQL Server 2008

构建工具:

maven:

image-20220620105742436

版本控制:

git+腾讯工蜂

服务器部署

ubuntu

接口管理

Apifox

内网穿透

花生壳构建测试环境

image-20220622091558740

APIFOX配置测试环境:

image-20220622091633498

项目结构:

image-20220620105302042

image-20220622092617327

接口层级目录是以Controller层的包层级进行管理的

数据库文档设计

操作手册:

飞书链接: https://yzqhs04oeu.feishu.cn/docs/doccnmxOlulCDYM7icNY4MdTZhg 密码: wnJW

数据库接口文档:

飞书链接: https://yzqhs04oeu.feishu.cn/docs/doccnpYLJh255P67hsnWCzsnm0f 密码: jwQU

开发环境及流程配置:

项目需要映射SQLServer 与 项目端口地址:

image-20220620155609730

配置测试环境:

image-20220620155650748

image-20220620155704006

开发流程:

根据业务需求,商量初始界面之后,进行mock数据,就是先将所需要的json数据模拟出来,这样前后端都可以快速进行开发。通过APIFox工具可以构建

image-20220620160621903

前端将请求切换至mock 域名,就可以拿到数据了 mock.js语法

image-20220620161029665

Mock数据的使用:

image-20220526210035043

https://www.apifox.cn/help/app/mock/mock-custom-scripts/#

搭建测试环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package Config;

import com.jfinal.config.Constants;
import com.jfinal.config.JFinalConfig;
import com.jfinal.config.Plugins;
import com.jfinal.plugin.IPlugin;
import org.junit.After;
import org.junit.Before;

public class JunitFinalTest {
private Constants constants;
private Plugins plugins;


/**
* 通过配置类启动jfinal插件等
*/
@Before
public void initConfig() {
try {
String configClass = "cn.jbolt.common.config.MainConfig";
Class<?> clazz = Class.forName(configClass);
JFinalConfig jfinalConfig = (JFinalConfig) clazz.newInstance();
constants = new Constants();
jfinalConfig.configConstant(constants);
plugins = new Plugins();
jfinalConfig.configPlugin(plugins);
for (IPlugin plug : plugins.getPluginList()) {
plug.start();
}
jfinalConfig.afterJFinalStart();
System.out.println("\n==JunitFinalTest Start==================\n");
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 停止jfinal插件
*/
@After
public void endConfig() {
System.out.println("\n==JunitFinalTest End====================");
if (plugins != null) {
for (IPlugin plug : plugins.getPluginList()) {
plug.stop();
}
}
}

}

写完接口之后能够进行启动测试,使用JunitFinal进行测试。

image-20220622092832158

image-20220622092853490

用户登录设计:

用户登录基本功能:

需求

用户需要token验证,存在时效性

需要不能重复登录

需要记录用户设置的手机,所有推送只会在用户最后登录的手机上显示


登录设计:

1. 普通令牌

普通令牌是SpringSecurityOauth2给客户端颁发的一个无含义的令牌,在令牌发布时,SpringSecurityOauth2将用户信息存储到程序指定的存储位置,并用普通令牌唯一标识这个存储信息,当用户再次携带令牌访问时,SpringSecurityOauth2会根据令牌查询用户信息,进而实现权限角色的限制。

普通令牌需要一个存储用户信息的地方,这个地方可以内存,也可以是数据库(Redis、Mysql)。

基于数据库存储(以Redis为例)

①基于Redis存储用户信息的方式,认证服务器将用户信息存储到指定的Redis数据库中
②当资源服务获取到access_token时,会到Redis中获取用户信息
③在微服务场景下适用

2. JWT令牌

jwt令牌的方式就无需数据库的介入,jwt令牌中就包含着用户的信息,SpringSecurityOauth在发布令牌时,会将用户信息放入JWT令牌中,用户拿着JWT令牌时,SpringSecurityOauth从中获取到用户信息,实现用户权限的控制。

jwt不需要后端进行存储。
①基于JWT令牌的认证服务器,用户信息存储到令牌中
②当资源服务获取到access_token后,会解析这个jwt类型的access_token,从中会获取到用户信息

③微服务场景下也不适用

对比相应的业务,护理app有较高的安全认证需求,因为之前使用jwt+redis进行登录,因要求不需要redis ,因此采用jwt +用户在线表的方式进行登录,jwt的负载中记录用户id 和cid信息 ,再拦截其中进行解析用户信息,判断是否有效,数据库中记录过期时间,定时任务每个一分钟清理过期用户

用户登录分为两个部分,token签发以及认证

数据库在线用户表:

image-20220620164812523

用户登录模块

  • 进行密码校验
  • 判断用户是否在线,如果在线,判断设备号是否是相同的。如果不同,则是异端登录,重新生成token,并将状态设置为异端登录
  • 如果没有在线,生成token ,并将用户信息返回给前端。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    /**
* @param userName 用户名
* @param userPassword 用户密码
* @param cid 用户设备唯一标识
* @description: 登录成功
* @date: 2022/6/17
*/
public HttpResult login(String userName, String userPassword, String cid, HttpServletRequest httpServletRequest) {
if (StringUtils.isBlank(userName)) {
LOG.error(" {} - {}", "userName", ":参数不能为空");
return HttpResult.fail(ResultCodeEnum.REQUEST_PARAMETER_ISNULL_ERROR);
}
if (StringUtils.isBlank(userPassword)) {
LOG.error(" {} - {}", "userPassword", ":密码不能为空");
return HttpResult.fail(ResultCodeEnum.REQUEST_PARAMETER_ISNULL_ERROR);
}
if (StringUtils.isBlank(cid)) {
LOG.error(" {} - {}", "cid", ":CID不能为空");
return HttpResult.fail(ResultCodeEnum.REQUEST_PARAMETER_CID_ERROR);
}

ViewTdnisStaffDict viewTdnisStaffDict = staffDictMapper.selectStaffByUserName(userName);
// 用户密码错误
if (viewTdnisStaffDict == null || viewTdnisStaffDict.getMobilepassword() == null) {
return HttpResult.fail(ResultCodeEnum.LOGIN_USERNAME_ERROR);
}
// 用户名错误
if (!userPassword.equals(viewTdnisStaffDict.getMobilepassword())) {
return HttpResult.fail(ResultCodeEnum.LOGIN_PASSWORD_ERROR);
}

String ip = IpUtil.getIp(httpServletRequest);
// 判断用户是否是异端登录,用户处于在线状态
StaffDto staffDto = StaffDto.adapt(viewTdnisStaffDict);

// 判断用户是否在登录中
MobStaffCid mobStaffCid = findFirst(Kv.by("staff_name", userName));
// 用户在登录中
if (mobStaffCid != null && ((mobStaffCid.getOnlineState() != JBoltUserOnlineState.OFFLINE.getValue()))) {
if (!mobStaffCid.equals(cid)) {
// 异端顶替下线
mobStaffCid.setMobileCid(cid).setOnlineState(JBoltUserOnlineState.TERMINAL_OFFLINE.getValue()).setOfflineTime(new Date());
mobStaffCid.setExpirationTime(jwtTokenUtil.generateExpirationDate());
mobStaffCid.update();
Map<String, String> tokenMap = refreshToken(viewTdnisStaffDict, cid);
staffDto.setTokenMap(tokenMap);
return HttpResult.success(staffDto);
}

}
mobStaffCid = new MobStaffCid().setLoginIp(ip).setLoginTime(new Date()).setMobileCid(cid).setStaffId(viewTdnisStaffDict.getId()).setExpirationTime(jwtTokenUtil.generateExpirationDate());
mobStaffCid.setOnlineState(JBoltUserOnlineState.ONLINE.getValue()).setStaffName(userName);
mobStaffCid.save();
Map<String, String> tokenMap = refreshToken(viewTdnisStaffDict, cid);
staffDto.setTokenMap(tokenMap);
return HttpResult.success(staffDto);
}

JWT工具类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package medical.appsupport.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import medical.model.mssql.bean.ViewTdnisStaffDict;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;


public class JwtTokenUtil {
// JWT所面向的用户
private static final String CLAIM_KEY_ID = "sub";
//
private static final String CLAIM_KEY_CREATED = "created";

// JWT的超期限时间(60*60*24) 24小时60分钟60秒1000毫秒
private String secret ="cgygyfyuasacyufvyoiawjdoiijwadjpoidjjiojijhi";
private Integer expiration= MsgUtil.TOKEN_EXPIRATION_TIME;


public String generateTokenByUserDetail(ViewTdnisStaffDict staffDict ,String cid) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_ID, staffDict.getId());
claims.put(CLAIM_KEY_CREATED, new Date());
claims.put("cid",cid);
return generateToken(claims);
}
/**
* 根据荷载生成JWT TOKEN
*
* @param claims
* @return
*/
public String generateToken(Map<String, Object> claims) {

// SecretKey key = generalKey();
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}



/**
* 从token中获取登录名,如果没有就返回空值
* @param token
* @return
*/
public String getStaffIdFromToken(String token) {
String userId;
try {
Claims claims = getClaimsFormToken(token);
userId = claims.getSubject();
} catch (Exception e) {
userId = null;
}
return userId;
}
/**
* 判断是否能够刷新
*
* @param token
* @return
*/
public boolean conRefresh(String token) {
return !isTokenExpired(token);
}
/**
* 刷新token
*
* @return
*/
public String refreshToken(String token) {
Claims claims = getClaimsFormToken(token);
// 添加创建时间
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);

}
/**
* 判断token是否失效
*
* @param token
* @return
*/
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date());
}
/**
* 从token中获取过期时间
*
* @param token
* @return
*/
private Date getExpiredDateFromToken(String token) {
// 获取过期时间
Claims claimsFormToken = getClaimsFormToken(token);
return claimsFormToken.getExpiration();
}

public int calLastTime(String token){
long nowDate = new Date().getTime();
long startDateTime = getExpiredDateFromToken(token).getTime();
return (int) ((startDateTime - nowDate) / 1000);

}

/**
* 从token中获取荷载
*
* @param token
* @return
*/
public Claims getClaimsFormToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
return claims ;
}
return claims;
}

/**
* 生成token失效时间
*
* @return
*/
public Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration*1000 );
}


}

拦截器进行拦截流程

  • 判断用户是否有效

  • 获取用户信息

  • 查询用户是否处在登录中

  • 查询数据库判断是否是有效状态(可以管理端强制退出)

  • 判断是否是同一设备名,如不是账户已在其他登录,是否重新登录

  • 如果在活跃时间类,重新生成token,返回前端,自动延长时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package medical.appsupport.Interceptor;

import cn.jbolt.base.JBoltUserOnlineState;
import cn.jbolt.common.util.DateUtil;
import com.jfinal.aop.Aop;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import com.jfinal.log.Log;
import io.jsonwebtoken.Claims;
import medical.appsupport.common.HttpResult;
import medical.appsupport.common.ResultCodeEnum;
import medical.appsupport.redis.CacheKeyConstants;
import medical.appsupport.redis.RedisService;
import medical.appsupport.service.user.UserCidServiceImpl;
import medical.appsupport.util.JwtTokenUtil;
import medical.appsupport.util.MsgUtil;
import medical.model.mysql.MobStaffCid;

import java.util.Date;


/**
* @author
* @description: 进行token校验
* @date: 2022/4/3
*/
public class AuthInterceptor implements Interceptor {

JwtTokenUtil jwtTokenUtil = Aop.get(JwtTokenUtil.class);
public static final Log LOG = Log.getLog(AuthInterceptor.class);
private String tokenHead = "Bearer";
UserCidServiceImpl userCidService = Aop.get(UserCidServiceImpl.class);

/**
* 排除url前缀,不需要进行校验的url
*/

private String[] excluede;

public AuthInterceptor(String... prefix) {
int len = prefix.length;
excluede = new String[len];
for (int i = 0; i < len; i++) {
excluede[i] = prefix[i];
}
}

@Override
public void intercept(Invocation inv) {

// 1、比较排除前缀是否匹配,匹配直接放行
String actionKey = inv.getActionKey();
Boolean flag = true;

for (String s : excluede) {
if (actionKey.startsWith(s)) {
flag = false;
break;
}
}
Controller controller = inv.getController();
if (flag) {
inv.invoke();
return;
}
// 获取请求头中的token

String token = controller.getRequest().getHeader("Authorization");
System.out.println("token = " + token);
// 没有携带请求头
if (token == null || !token.startsWith(this.tokenHead)) {
controller.renderJson(HttpResult.fail(ResultCodeEnum.LOGIN_INTERCEPTOR_CHECK_TOKEN));
// inv.invoke();
return;
}
String authToken = token.substring(this.tokenHead.length());

// 解析token中的id,利用id获取
Claims claimsFormToken = jwtTokenUtil.getClaimsFormToken(authToken);
if (claimsFormToken == null) {
controller.renderJson(HttpResult.fail(ResultCodeEnum.LOGIN_INTERCEPTOR_CHECK_TOKEN));
return;
}
// 判断是否过期
Date expiration = claimsFormToken.getExpiration();
Date now = new Date();
if (expiration.getTime()<now.getTime()) {
controller.renderJson(HttpResult.fail(ResultCodeEnum.LOGIN_INTERCEPTOR_FAILURE_TOKEN));
}
String staffId = jwtTokenUtil.getStaffIdFromToken(authToken);
String cid = (String) claimsFormToken.get("cid");

if (staffId == null || cid == null) {
controller.renderJson(HttpResult.fail(ResultCodeEnum.LOGIN_INTERCEPTOR_CHECK_TOKEN));
return;
}
MobStaffCid mobStaffCid = userCidService.findByStaffId(staffId);

/**
* @description: 用户在线
*/
if (mobStaffCid == null || mobStaffCid.getOnlineState() == JBoltUserOnlineState.OFFLINE.getValue()) {
controller.renderJson(HttpResult.fail(ResultCodeEnum.LOGIN_INTERCEPTOR_CHECK_TOKEN));
return;
}
/**
* @description: 用户已在异端登录,是否重新登录 异端下线通知
* @date: 2022/6/18
*/
if (mobStaffCid.getOnlineState() == JBoltUserOnlineState.TERMINAL_OFFLINE.getValue() && !mobStaffCid.getMobileCid().equals(cid)) {
controller.renderJson(HttpResult.fail(ResultCodeEnum.LOGIN_DISTANCE_LOGIN));
return;
}
// 无状态延长时间,当小于设置时间会自动签发token给前端,延长登录时间
System.out.println("jwtTokenUtil.calLastTime(authToken) = " + jwtTokenUtil.calLastTime(authToken));
if (jwtTokenUtil.calLastTime(authToken) < MsgUtil.TOKEN_REFRESH_TIME) {
String refreshToken = jwtTokenUtil.refreshToken(authToken);
// redisService.saveCache(token,MsgUtil.TOKEN_EXPIRATION_TIME, CacheKeyConstants.USER_LOGIN,refreshToken);
controller.getResponse().setHeader("refreshToken", refreshToken);
mobStaffCid.setExpirationTime(jwtTokenUtil.generateExpirationDate());
mobStaffCid.update();
inv.invoke();
return;
}
inv.invoke();
}
}

开启拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 配置全局拦截器
*/
@Override
public void configInterceptor(Interceptors me) {
me.addGlobalActionInterceptor(new JBoltExceptionGlobalInterceptor());
me.addGlobalActionInterceptor(new JBoltOnlineUserGlobalInterceptor());
me.addGlobalActionInterceptor(new SessionInViewInterceptor());
String [] interceptUrl = {"/memon","/file","/advice","/vital","/pat"};
me.add(new AuthInterceptor(interceptUrl));
//二开配置扩展全局拦截器
ExtendProjectConfig.configInterceptor(me);
}

定时任务扫描,清理离线用户:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 清理离线用户信息
*/
public void deleteOfflineAndExpirationUser() {
dao().each(ou -> {
ou.delete();
return true;
}, selectSql().notEqQM("online_state").toSql(), JBoltUserOnlineState.ONLINE.getValue());

dao().each(ou -> {
ou.delete();
return true;
}, selectSql().eqQM("online_state").le("expiration_time", toDateTime(DateUtil.format(new Date(), DateUtil.YMDHMS))).toSql(), JBoltUserOnlineState.ONLINE.getValue());
}

定时扫描离线用户:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class JBoltOnlineUserClearTask implements ITask {
private static final Log LOG = Log.getLog(JBoltOnlineUserClearTask.class);

@Override
public void run() {
OnlineUserService service=Aop.get(OnlineUserService.class);
UserServiceImpl userService = Aop.get(UserServiceImpl.class);
if(!MainConfig.DEV_MODE) {
LOG.info("定时任务:每隔一分钟秒清理一次离线和过期用户 - 开始执行");
}
service.deleteOfflineAndExpirationUser();
userService.deleteOfflineAndExpirationUser();
if(!MainConfig.DEV_MODE) {
LOG.info("定时任务:每隔一分钟清理一次离线和过期用户 - 执行完毕");
}
}

@Override
public void stop() {
if(!MainConfig.DEV_MODE) {
LOG.info("stop 完成一次定时清理任务:每隔一分钟清理一次离线和过期用户信息");
}
}

}

利用Ehcache缓存各科室数据

Ehcache

EhCache - 简书 (jianshu.com)

Ehcache 简介:

​ EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点。是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。另外Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Ehcache),但本身不直接提供缓存功能的实现。它支持注解方式使用缓存,非常方便。

通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。简单的说就是将数据调用到内存中,然后从内存中读取,从而大大提高读取速度。

Ehcache的特点:

(1)快速简单,具有多种缓存策略

(2)缓存数据有两级为内存和磁盘,缓存数据会在虚拟机重启的过程中写入磁盘

(3)可以通过RMI、可插入API等方式进行分布式缓存

(4)具有缓存和缓存管理器的侦听接口

(5)支持多缓存管理器实例,以及一个实例的多个缓存区域。并提供Hibernate的缓存实现

Ehcache配置:

Ehcache支持通过xml文件和API两种方式进行配置。

xml方式
Ehcache的CacheManager构造函数或工厂方法被调用时,会默认加载classpath下名为ehcache.xml的配置文件。如果加载失败,会加载Ehcache jar包中的ehcache-failsafe.xml文件,这个文件中含有简单的默认配置。

ehcache.xml配置参数说明

name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。
timeToIdleSeconds:置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:缓存数据的生存时间(TTL),也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
maxEntriesLocalDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
overflowToDisk:内存不足时,是否启用磁盘缓存。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否在VM重启时存储硬盘的缓存数据。默认值是false。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。

 timeToLiveSeconds –>当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清除;即缓存自创建日期起能够存活的最长时间,单位为秒(s)

  timeToIdleSeconds –> 当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清空;即缓存被创建后,最后一次访问时间到缓存失效之时,两者之间的间隔,单位为秒(s)

  timeToLiveSeconds必须大于timeToIdleSeconds才有意义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ehcache.xml的一个范例

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

<!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir/ehcache"/>

<!-- 默认缓存 -->
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>

<cache name="userCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="3"
timeToLiveSeconds="3"
maxEntriesLocalDisk="10000000"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
</ehcache
API方式

xml配置的参数也可以直接通过编程方式来动态的进行配置(dynamicConfig没有设为false)。

  • jfianl
1
2
3
4
5
6
7
8
9
10
Cache cache = manager.getCache("sampleCache"); 
CacheConfiguration config = cache.getCacheConfiguration();
config.setTimeToIdleSeconds(60);
config.setTimeToLiveSeconds(120);
config.setmaxEntriesLocalHeap(10000);
config.setmaxEntriesLocalDisk(1000000);
也可以通过disableDynamicFeatures()方式关闭动态配置开关。配置以后你将无法再以编程方式配置参数。

Cache cache = manager.getCache("sampleCache");
cache.disableDynamicFeatures();
  • ​ 其他
1
2
3
4
5
6
7
CacheManager manager = CacheManager.newInstance("src/config/ehcache.xml");

Ehcache cache = new Cache("testCache", 5000, false, false, 5, 2);

cacheManager.addCache(cache);


测试用例:

(37条消息) ehcache缓存过期时间和注解的使用。shuixiou1的博客-CSDN博客ehcache过期时间

需求:

在app中,需要查询在院的科室患者,因此,可以在用户第一次查询时,将用户信息保存在缓存中 。

为方便管理建立一个工具类,将需要缓存的数据放在该类中(使用了缓存一定要考虑清除缓存):

MyCacheKit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package medical.appsupport.util;

import cn.jbolt.common.config.Msg;
import cn.jbolt.common.util.CamelCaseUtil;
import com.google.common.base.Joiner;
import com.google.common.collect.LinkedListMultimap;
import com.jfinal.aop.Aop;
import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.DbTemplate;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.ehcache.CacheKit;
import com.jfinal.plugin.ehcache.IDataLoader;
import medical.appsupport.dao.impl.DeptDictMapperImpl;
import medical.appsupport.dao.impl.DoctorAdviceMapperImpl;
import medical.appsupport.dao.impl.PatsInHospitalMapperImpl;
import medical.appsupport.redis.CacheKeyConstants;
import medical.appsupport.vo.DoctorAdviceParam;
import medical.appsupport.vo.PatientPageQuery;
import medical.appsupport.vo.StaffParam;
import medical.model.mssql.bean.ViewTdnisDeptDict;

import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

public class MyCacheKit {
public static final MyCacheKit me = new MyCacheKit();
public static final String MOB_CACHE_NAME = "jbolt_cache";
DeptDictMapperImpl deptDictMapper = Aop.get(DeptDictMapperImpl.class);
DoctorAdviceMapperImpl doctorAdviceMapper = Aop.get(DoctorAdviceMapperImpl.class);
PatsInHospitalMapperImpl patsInHospitalMapper = Aop.get(PatsInHospitalMapperImpl.class);
// 模糊查询获取用户的指定的数据
public static void removeCacheByPrefix(String prefixFirst, String prefixSecond) {
List<String> keys = CacheKit.getKeys(MsgUtil.CACHE_KEY_NAME_PAGE_PATNAME);
keys.stream().filter(keyName -> keyName.startsWith(prefixFirst) || keyName.startsWith(prefixSecond)).collect(Collectors.toSet()).forEach(element -> CacheKit.remove(Msg.CACHE_KEY_NAME_PAGEPATNAME, element));
}

/**
* 自动拼接字符串
*
* @param prefix
* @param keys
* @return
*/
public String generateCacheKey(CacheKeyConstants prefix, String... keys) {
String key = prefix.name();
if (keys != null && keys.length > 0) {
key += "_" + Joiner.on("_").join(keys);
}
return key;
}

/**
* @description: 首页生成主键信息
* @author
* @date: 2022/6/1
*/
public String generateCacheKeyPage(CacheKeyConstants prefix, StaffParam staffParam, PatientPageQuery patientPageQuery) {
return generateCacheKey(prefix, staffParam.getDeptCode() + MsgUtil.SPACE_CHARACTER, patientPageQuery.getIsConcerned() + MsgUtil.SPACE_CHARACTER, patientPageQuery.getPatientCondition() + MsgUtil.SPACE_CHARACTER, patientPageQuery.getState() + MsgUtil.SPACE_CHARACTER, patientPageQuery.getNursingClass() + MsgUtil.SPACE_CHARACTER, patientPageQuery.getQueryString() + MsgUtil.SPACE_CHARACTER, patientPageQuery.getPageNo() + MsgUtil.SPACE_CHARACTER, patientPageQuery.getPageSize() + MsgUtil.SPACE_CHARACTER);
}

/**
* @description: 缓存科室信息
* @date: 2022/6/1
*/
public HashMap<String, ViewTdnisDeptDict> getAllDept() {
return CacheKit.get(MsgUtil.CACHE_KEY_NAME_PAT_ADVICE, generateCacheKey(CacheKeyConstants.ADVICE, "deptCode"), new IDataLoader() {
@Override
public Object load() {
return deptDictMapper.selectDeptInfoByCache();
}
});
}

/**
* @param doctorAdviceParam 医嘱传参数据
* @return LinkedListMultimap<String, Record>
* @description 获取缓存的医嘱数据, 存储还缓存了,需要考虑医嘱,从map中移除数据 ,当执行医嘱时需要删除缓存数据
*/
public LinkedListMultimap<String, Record> selectAdviceMultimapByCache(DoctorAdviceParam doctorAdviceParam) {
return CacheKit.get(MOB_CACHE_NAME, generateCacheKey(CacheKeyConstants.ADVICE, doctorAdviceParam.getPatientID()), new IDataLoader() {
@Override
public Object load() {
return doctorAdviceMapper.selectAdviceMultimap(doctorAdviceParam);
}
});
}

/**
* @param patientId 病人id
* @description
*/
public void removePatAdviceByCache(String patientId) {
if (StrKit.notBlank(patientId)) {
CacheKit.remove(MOB_CACHE_NAME, generateCacheKey(CacheKeyConstants.ADVICE, patientId));
}
}

/**
* @description: 根据科室号获取所有的数据,显示为所有显示病床的信息
* 首先从缓存中查询,如果有直接获取数据,如果没有,那么就查询数据库,查询完之后存储至缓存中,有效期为五分钟,根据缓存名字查看配置
* @param deptCode 科室号
* @param queryString 模糊查询
*/
public List<Record> selectAllPatsByDeptCode(String deptCode, String queryString){
String key;
if (StrKit.notBlank(queryString)&&StrKit.notBlank(deptCode)){
key = MyCacheKit.me.generateCacheKey(CacheKeyConstants.DEPT, deptCode,queryString);
}else {
key = MyCacheKit.me.generateCacheKey(CacheKeyConstants.DEPT, deptCode);
}
return CacheKit.get(MsgUtil.CACHE_KEY_NAME_PAGE_PATNAME,key, new IDataLoader() {
@Override
public Object load() {
return patsInHospitalMapper.selectAllPatsByDeptCode(deptCode,queryString);
}
});
}






}


枚举类:保存键值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public enum CacheKeyConstants {
SYSTEM_ACLS,
USER_ACLS,
USER_LOGIN,
PAGE_HOME,
PAGE_HOME_COUNT,
CONCERN_PAT,
CONCERN_PAT_COUNT,
DEPT,
STATISTICAL,
MENU,
MOB,
ADVICE
;


@Override
public String toString() {
return super.toString();
}
}

image-20220620170413907

工具类设计:

单例设计模式

单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)。就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

就是类在内存中只能存在一个实例对象

单例模式的结构

单例模式的主要有以下角色:

  • 单例类。只能创建一个实例的类
  • 访问类。使用单例类

单例模式的实现

单例设计模式分类两种:

  • 饿汉式:类加载就会导致该单实例对象被创建

  • 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建

饿汉式


所谓饿汉式,就是直接创建出类的实例化,然后用private私有化,对外只用静态方法暴露。

静态变量

步骤

  1. 构造器私有化
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法
优点 缺点
写法简单,在类装载的时完成实例化,避免了线程同步问题 类装载时完成实例化,没有达到LazyLoading的效果,若该实例从未使用,则会造成内存浪费

写单例模式的步骤: 以下是饿汉式的写法:

(1)必须在该类中,自己先创建出一个对象,私有化;

(2)私有化自身的构造器,防止外界通过构造器创建新的对象。

(3)向外暴露一个公共静态方法用于获取自身的对象;

1
2
3
4
5
6
7
8
9
10
11
public class MyUtil{
// 必须在该类中,自己先New一个对象出来
private static MyUtil me = new MyUtil<>();
// 私有化构造器
private MyUtil(){
}
// 向外暴露一个公共的静态方法
public static MyUtil getInstance(){
return me;
}

该方式在成员变量MyUtil类型的静态变量,并创建MyUtil类的对象instance。

MyUtil对象是随着类的加载而创建的。如果类对象比较大的话会造成内存的浪费

静态代码块

Singleton.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {

//私有构造方法
private Singleton() {}

//声明Singleton类型的变量
private static Singleton instance; //null

//在静态代码块中进行赋值
static {
instance = new Singleton();
}

//对外提供获取该类对象的方法
public static Singleton getInstance() {
return instance;
}

1
2
3
4
5
6
7
8
9
10
public class Client {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();

Singleton instance1 = Singleton.getInstance();

//判断两次获取到的Singleton对象是否是同一个对象
System.out.println(instance == instance1);
}
}

该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。存在内存浪费的问题

懒汉式:

线程不安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class StringUtil {

// 私有构造方法
private StringUtil(){}

// 在成员位置创建该类

private static StringUtil instance;

// 对外提供静态方法
public static StringUtil getInstance(){
if (instance == null){
instance = new StringUtil();
}
return instance;
}

成员变量位置声明 StringUtil 类型的成员变量,并没有进行对象的赋值操作

单调用getInstance()方法时候获取StringUtil 创建 StringUtil 类的对象,这样就实现了懒加载的效果

但是,如果是多线程环境,就会出现线程安全的问题

方式二:线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public class StringUtil {

// 私有构造方法
private StringUtil(){}

// 声明StringUtild的变量,只是进行变量的声明,并没有进行赋值
private static StringUtil instance;

// 对外提供访问方式
public static synchronized StringUtil getInstance(){// 加了synchronized ,线程就安全了
// 判断instance 是否是null,如果是null,说明还没有创建 StringUtil类的对象
// 如果没有,创建一个并返回,如果有,直接返回
if (instance == null){
// 进入了锁也会进入判断
instance = new StringUtil();
}
return instance;
}

实现了懒加载效果,但是在synchronized关键字,导致方法的执行效果特别低

可以看出,只有在初始化instance的时候才会出现线程安全的问题,一旦初始化之后就不存在了

方式三: (双重锁检验)

从上个样例中可以看出,绝大部分操作都是读操作,读操作的线程是安全的,所以没有必要让每个线程都必须持有锁才能调用该方法,所以需要调整加锁的时机

双重检查锁机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class StringUtil {

// 私有构造方法
private StringUtil(){}

// 声明StringUtild的变量,只是进行变量的声明,并没有进行赋值
private volatile static StringUtil instance;

// 对外提供访问方式
public static StringUtil getInstance(){
// 第一次判断时,如果instance不为null,不进入抢锁阶段,直接返回实例
if (instance == null){
synchronized (StringUtil.class){
// 抢到锁之后再次进行判断是否为null
if (instance ==null){
instance = new StringUtil();
}
}
}
return instance;
}


双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,

在多线程的情况下,可能会出现空指针问题: JVM在实例化对象的时候会进行优化和指令重排序操作,

只需要使用 volatile 关键字

volatile 关键字可以保证可见性和有序性

静态内部类

静态内部类实现单例模式,是借助了类加载器加载类的特性实现的,类加载器在加载类时使用了synchronized关键字.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SingletonDemo {

public static void main(String[] args) {
SingletonDemo sd1 = getInstance();
SingletonDemo sd2 = getInstance();
System.out.println(sd1.equals(sd2));
}

private SingletonDemo(){};

private static class SingletonHolder{
private static SingletonDemo INSTANCE = new SingletonDemo();

}

public static SingletonDemo getInstance(){
return SingletonHolder.INSTANCE;
}
}

输出为:

1
true

关于sqlserver二开数据源设置:

填写二开数据源

image-20220620171501668

切换数据源:

1
2
3
4
5
6
7
8

// 获取数据库名
public DbPro getMSSQLPro() {
return Db.use(MSSQL_NAME);
}



数据库分页操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @param pageSize 一页总数
* @param pageNumber 第几页
* @param columnName 需要查询的字段
* @param max 最大值
* @param min 最小值
* @description: 获取医嘱信息
* @date: 2022/6/19
*/
public Page<Record> selectVitalInfoPage(Integer pageSize, Integer pageNumber, String columnName, Integer max, Integer min,String deptCode) {

Kv kv = Kv.by("columnName", columnName).setIfNotNull("maxValue", max).setIfNotNull("minValue", min).setIfNotBlank("deptCode",deptCode);
return mssqlDb.getMSSQLPro().paginate(pageNumber, pageSize, mssqlDb.getMSSQLPro().getSqlPara("medical.vital.selectVitalInfoPage", kv));
}

分页数据类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
* Copyright (c) 2011-2021, James Zhan 詹波 (jfinal@126.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.jfinal.plugin.activerecord;

import java.io.Serializable;
import java.util.List;

/**
* Page is the result of Model.paginate(......) or Db.paginate(......)
*/
public class Page<T> implements Serializable {

private static final long serialVersionUID = -7102129155309986923L;

private List<T> list; // list result of this page
private int pageNumber; // page number
private int pageSize; // result amount of this page
private int totalPage; // total page
private int totalRow; // total row

/**
* Constructor.
* @param list the list of paginate result
* @param pageNumber the page number
* @param pageSize the page size
* @param totalPage the total page of paginate
* @param totalRow the total row of paginate
*/
public Page(List<T> list, int pageNumber, int pageSize, int totalPage, int totalRow) {
this.list = list;
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.totalPage = totalPage;
this.totalRow = totalRow;
}

public Page() {

}

/**
* Return list of this page.
*/
public List<T> getList() {
return list;
}

public void setList(List<T> list) {
this.list = list;
}

/**
* Return page number.
*/
public int getPageNumber() {
return pageNumber;
}

public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}

/**
* Return page size.
*/
public int getPageSize() {
return pageSize;
}

public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}

/**
* Return total page.
*/
public int getTotalPage() {
return totalPage;
}

public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}

/**
* Return total row.
*/
public int getTotalRow() {
return totalRow;
}

public void setTotalRow(int totalRow) {
this.totalRow = totalRow;
}

public boolean isFirstPage() {
return pageNumber == 1;
}

public boolean isLastPage() {
return pageNumber >= totalPage;
}

public String toString() {
StringBuilder msg = new StringBuilder();
msg.append("pageNumber : ").append(pageNumber);
msg.append("\npageSize : ").append(pageSize);
msg.append("\ntotalPage : ").append(totalPage);
msg.append("\ntotalRow : ").append(totalRow);
return msg.toString();
}
}


自定义注解实现参数校验,并处理异常

注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。

导入hiberbate框架:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- validator -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${validation-api.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate.version}</version>
</dependency>

自定义注解

ParamException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ParamException extends RuntimeException{

public ParamException(){
super();
}
public ParamException(String message){
super(message);
}
public ParamException(String message, Throwable cause) {
super(message, cause);
}

public ParamException(Throwable cause) {
super(cause);
}

protected ParamException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

ExceptionConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package medical.appsupport.Interceptor;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;


//用来修饰自定义注解的生命周期
@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface ExceptionConfig {
Class<? extends Exception>[] value();
}

定义拦截器,拦截指定注解:

ParaExceptionInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package medical.appsupport.Interceptor;


import com.google.common.collect.Lists;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.log.Log;
import medical.appsupport.common.HttpResult;
import medical.appsupport.exception.ParamException;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class ParaExceptionInterceptor implements Interceptor {
private static final Log LOG = Log.getLog(ParaExceptionInterceptor.class);
private static List<Class<? extends Exception>> getConfigWithExceptionConfig(Invocation inv) {
List<Class<? extends Exception>> DEFAULT_EXCPTIONS;
DEFAULT_EXCPTIONS = Lists.newArrayList(ParamException.class);
ExceptionConfig config = inv.getMethod().getAnnotation(ExceptionConfig.class);
if (config == null)
config = inv.getTarget().getClass().getAnnotation(ExceptionConfig.class);
if (config != null) {
Class<? extends Exception>[] value = config.value();
return Arrays.asList(value);
}
return DEFAULT_EXCPTIONS;
}

@Override
public void intercept(Invocation inv) {
try {
inv.invoke();
} catch (Exception e) {
// 若返回值类型不是Ret则将异常继续往上抛
Class<?> returnType = inv.getMethod().getReturnType();
if (!(returnType.equals(HttpResult.class))) {
throw e;
}
List<Class<? extends Exception>> exceptionClasses = getConfigWithExceptionConfig(inv);
for (Class<? extends Exception> exceptionClass : exceptionClasses) {
if (Objects.equals(e.getClass(), exceptionClass) || Objects.equals(e.getClass().getSuperclass(), exceptionClass)) {
LOG.error("{}-{} - {}{}",inv.getMethod().getDeclaringClass(),inv.getMethod().getName(), " 错误:", e.getMessage());
inv.setReturnValue(HttpResult.fail(4000,e.getMessage()));
return;
}
}
throw e;
}
}
}

获取拦截的错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package medical.appsupport.util;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import medical.appsupport.exception.ParamException;
import org.apache.commons.collections.MapUtils;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.*;
import java.util.stream.Collectors;

public class BeanValidator {

private static ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();

public static <T> Map<String, String> validate(T t, Class... groups) {
Validator validator = validatorFactory.getValidator();
Set validateResult = validator.validate(t, groups);
if (validateResult.isEmpty()) {
return Collections.emptyMap();
} else {
LinkedHashMap errors = Maps.newLinkedHashMap();
Iterator iterator = validateResult.iterator();
while (iterator.hasNext()) {
ConstraintViolation violation = (ConstraintViolation)iterator.next();

errors.put(violation.getPropertyPath().toString(), violation.getMessage());
}

return errors;
}
}

public static Map<String, String> validateList(Collection<?> collection) {
Preconditions.checkNotNull(collection);
Iterator iterator = collection.iterator();
Map errors;
do {
if (!iterator.hasNext()) {
return Collections.emptyMap();
}
Object object = iterator.next();
errors = validate(object, new Class[0]);
} while (errors.isEmpty());

return errors;
}

public static Map<String, String> validateObject(Object first, Object... objects) {
if (objects != null && objects.length > 0) {
return validateList(Lists.asList(first, objects));
} else {
return validate(first, new Class[0]);
}
}

public static void check(Object param) throws ParamException {
Map<String, String> map = BeanValidator.validateObject(param);
if (MapUtils.isNotEmpty(map)) {
String collect = map.values().stream().collect(Collectors.joining(";"));
throw new ParamException(collect);
}
}
}

在需要的类上添加vo上添加注解,在需要校验的方法类进行

image-20220620181531632

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DoctorAdviceParam {


String adviceStatue;//执行状态
String repeatIndicator;//临时 长期
String administration;//用药方式
@NotBlank(message = "病人ID不能为空")
String patientID;//病人ID
@NotBlank(message = "病区号deptCode不能为空")
String deptCode;//病区Id号 String deptCode;//病区Id号
String type ;//病人主页进行筛选
@NotBlank(message = "筛选开始时间startTime不能为空")
String startTime;//筛选开始时间
@NotBlank(message = "筛选开始时间endTime不能为空")
String endTime;//筛选结束时间
String temporary;//临时 0
String LongTime; //长期 1
// 是否选择关注病人
String isConcerned;
String staffId;
}

Java 反射详解 - YSOcean - 博客园 (cnblogs.com)

前后端跨域问题解决

后端如何解决跨域问题_CrazySnail_x的博客-CSDN博客_后端跨域

image-20220620183525246

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package medical.appsupport.Interceptor;

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;

import javax.servlet.http.HttpServletResponse;

public class CorsInterceptor implements Interceptor {
@Override
public void intercept(Invocation inv) {
HttpServletResponse response = inv.getController().getResponse();
String origin=inv.getController().getRequest().getHeader("Origin");
String method = inv.getController().getRequest().getMethod();
response.setContentType("application/json;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST,GET, OPTIONS, DELETE, HEAD");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");
inv.invoke();
}
}

利用定时任务拆解医嘱:

定时任务扫描类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class AdviceSplitTask  implements ITask {
private static final Log LOG = Log.getLog(AdviceSplitTask.class);

@Override
public void stop() {
LOG.debug("定时任务:每隔5分钟扫描需要拆分的医嘱 - 结束执行");
}

@Override
public void run() {
// 生成查询日志
LOG.debug("定时任务:每隔5分钟扫描需要拆分的医嘱 - 开启执行");
DoctorAdviceMapperImpl doctorAdviceMapper = Aop.get(DoctorAdviceMapperImpl.class);
MobDoctorAdviceSplitService mobDoctorAdviceSplitService = new MobDoctorAdviceSplitService();
List<Record> records = doctorAdviceMapper.selectFluidAdvice();
mobDoctorAdviceSplitService.splitAdvice(records);
}
}

配置自动调度

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 配置自动调度插件
* @param me
*/
private void configCron4jPlugin(Plugins me) {
Cron4jPlugin cron4jPlugin = new Cron4jPlugin();
cron4jPlugin.addTask("0-59/1 * * * *", new JBoltOnlineUserClearTask());
cron4jPlugin.addTask("0-59/1 * * * *", new MemoInformTask());
cron4jPlugin.addTask("0-59/20 * * * *", new DeptStaticTask());
cron4jPlugin.addTask("0-59/5 * * * *",new AdviceSplitTask());
me.add(cron4jPlugin);
}

编写查询sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#sql("selectFluidAdvice")
SELECT a.D_ZDBH AS Patient_ID,a.姓名 PAT_NAME,b.ID AS VISIT_ID,a.当前病床 as BED_NO,
a.住院号 AS INP_NO, b.D_HM AS ORDER_NO,
b.D_PYH AS ORDER_NO_SUB, b.D_CDBH AS ORDER_CODE, b.D_CDMC AS ORDER_TEXT,
b.D_KSKS AS DEPT_CODE, b.D_SPEC AS ITEM_SPEC, b.D_AREA AS ITEM_SPEC_ADDRESS,
b.D_PC AS FREQUENCY, b.D_CYL AS DOSSAGE, b.D_XFSL AS DRUGAMOUNT, b.D_CYLDW AS DOSAGE_UNITS,
b.D_YF AS ADMINISTRATION, b.D_DEL AS ORDER_STATUS, b.D_XMBH AS ORDER_CLASS, D_LCBZ AS REPEAT_INDICATOR,
(CASE WHEN d_yf LIKE '%皮试%' THEN '1' ELSE '0' END) AS IS_SKIN, isNUll((CASE WHEN (D_XFRQ IS NULL OR D_XFSJ IS NULL) THEN NULL ELSE CONVERT(varchar(30), CONVERT(varchar(10), D_XFRQ, 120) + ' ' + CONVERT(varchar(8), D_XFSJ, 8), 120) END),'') AS START_DATE_TIME,
isNUll((CASE WHEN (D_TZRQ IS NULL OR D_TZSJ IS NULL) THEN NULL ELSE CONVERT(varchar(30), CONVERT(varchar(10), D_TZRQ, 120) + ' ' + CONVERT(varchar(8), d_tzsj, 8), 120) END),'') AS STOP_DATE_TIME,
b.D_QSBH AS DOCTOR_ID, isNUll(b.D_FSQM,'') AS NURSE_ID,
b.D_QRCZYBH AS VERIFYPASS_TIME,isNUll(b.D_JCCZYXM ,'') AS STOP_DOCTOR_NAME,isNUll(D_JCCZYBH,'') AS STOP_DOCTOR_BH,pc.次数 as PC_COUNT,pc.名称 PC_NAME,pc.备注 PC_MEMON
FROM [pyyzyy]. dbo.YY_BRZY AS a INNER JOIN [pyyzyy].dbo.JD_DCMX AS b ON a.D_ZDBH = b.D_DCBH inner join [pyyzyy].dbo.YY_PCDM as pc on b.D_PC = pc.名称
where b.D_XFRQ >= #("'"+dateTime+"'") and b.D_XFRQ <= #("'"+dateTime+"'") and CAST(D_XFSJ AS TIME) BETWEEN #("'"+beforeTime+"'") AND #("'"+nowTime+"'") and (b.D_YF like '%输液%' or b.D_YF like '%滴注%' or b.D_YF like '%口服%' or b.D_YF like '%注射%' ) and (D_LCBZ = 'C' or D_LCBZ ='L') and b.D_DEL !='T'
#end

每隔5分钟进行一次调度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    /**
* @description: 获取输液医嘱
* @date: 2022/5/21
*/
@Override
public List<Record> selectFluidAdvice() {
Date date = new Date();
List<Record> adviceInfo = mssqlDb.getMSSQLPro().template("medical.advice.selectFluidAdvice", Kv.by("dateTime", DateUtil.getCurrentTime(DateUtil.FORMAT_DATE, date))
.set("beforeTime", DateUtil.subtractTime(DateUtil.FORMAT_TIME, 5 * 60 * 1000, date))
.set("nowTime", DateUtil.getCurrentTime(DateUtil.FORMAT_TIME, date))
).find();
if (adviceInfo == null) {
return Collections.emptyList();
}
// 大小写转换
CamelCaseUtil.keyToCamelCase(adviceInfo);
// 这里没有办法进行映射
return adviceInfo;
}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @description: 进行时间相减
* @date: 2022/5/28
*/
public static String subtractTime(String format ,int amount,Date date) {
if (format == null || format.trim().equals("")) {
sdf.applyPattern(FORMAT_DATE_TIME);
} else {
sdf.applyPattern(format);
}
return sdf.format((date.getTime()-amount));
}

定时任务处理病区统计:

每隔二十分钟进行一次病区统计,将统计的数据存放至缓存中,提高并发量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import com.jfinal.aop.Aop;
import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.cron4j.ITask;
import medical.appsupport.dao.StaffDictMapper;
import medical.appsupport.dao.impl.StaffDictMapperImpl;
import medical.appsupport.service.pat.impl.PatServiceImpl;

import java.util.List;

public class DeptStaticTask implements ITask {
private static final Log LOG = Log.getLog(DeptStaticTask.class);
@Override
public void run() {
StaffDictMapper staffDictMapper = Aop.get(StaffDictMapperImpl.class);
LOG.debug("定时任务:每隔20分钟进行病区统计 - 开始执行");
List<Record> records = staffDictMapper.selectDeptList();
PatServiceImpl patService = Aop.get(PatServiceImpl.class);
records.forEach(deptCode->{
patService.selectPatStaticByTask(deptCode.get("deptCode"));
});
}


@Override
public void stop() {
// 生成查询日志
LOG.debug("定时任务:统计结束 - 结束执行");
}
}

1
2
3
4
5
6
7
8
9
10
11
/**
* @description: 定时统计每个科室的数据
* @author
* @date: 2022/5/12
*/
public void selectPatStaticByTask(String deptCode) {
Map<String, Object> map = selectPatStaticData(deptCode);
map.put("lastUpdateTime", DateUtil.format(new Date(), DateUtil.HMS));
map.put("interval", "二十分钟");
CacheKit.put(MsgUtil.CACHE_KEY_NAME_STATISTICS, deptCode, map);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
package cn.jbolt.common.util;

import java.sql.Time;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import com.jfinal.kit.StrKit;

/**
* 日期工具类
* @author Michael
*
*/
public class DateUtil {
public static final String YMDE = "yyyy-MM-dd EEEE";
public static final String YMD = "yyyy-MM-dd";
public static final String HMS = "HH:mm:ss";
public static final String HM = "HH:mm";
public static final String YMDHMSS = "yyyy-MM-dd HH:mm:ss.SSS";
public static final String YMDHMSS2 = "yyyyMMddHHmmssSSS";
public static final String YMDHMS = "yyyy-MM-dd HH:mm:ss";
public static final String YMDHM = "yyyy-MM-dd HH:mm";
public static final String MDHM = "MM-dd HH:mm";
public static final String MD = "MM-dd";
/**
* 取小值
*/
public static final int FLOOR=1;
/**
* 取大值
*/
public static final int CEIL=2;
/**
* 四舍五入
*/
public static final int ROUND=3;


public static Date getNow(){
return Calendar.getInstance().getTime();
}
public static String getNowStr(){
return format(getNow(), YMD);
}
public static String getNowStr(String pattern){
return format(getNow(), pattern);
}

public static String format(Date date, String pattern) {
if (date == null) {
return "";
}
SimpleDateFormat sdf = new SimpleDateFormat("",Locale.CHINA);
sdf.applyPattern(pattern);
return sdf.format(date);
}
public static String formatWithT(Date date, String pattern) {
String value=format(date, pattern);
if(StrKit.isBlank(value)) {return "";}
return value.replace(" ", "T");
}

public static Date getNowDate() {
return new Date();
}

public static int thisMonthMaxDate(Date date){
Calendar cal = Calendar.getInstance();
cal.clear();
cal.setTime(date);
return cal.getActualMaximum(Calendar.DATE);
}


/**
* 设置之间为0时0分0秒
* @param date
* @return
*/
public static Date HHmmssTo000000(Date date) {
if(date==null) {return null;}
Calendar cal = getCalendarByDate(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
/**
*
* 获取当前时间(精确到分)的字符串
* @return 返回时间字符串yyyy-MM-dd HH:mm
*/
public static String getTheTimeInMinutes() {
SimpleDateFormat time = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date date = new Date();
return time.format(date);
}
/**
* 设置之间为0时0分0秒
* @param date
* @return
*/
public static String HHmmssTo000000Str(Date date) {
if(date==null) {return null;}
Calendar cal = getCalendarByDate(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return format(cal.getTime(), YMDHMS);
}

/**
* 设置之间为23时59分59秒
* @param date
* @return
*/
public static Date HHmmssTo235959(Date date) {
if(date==null) {return null;}
Calendar cal = getCalendarByDate(date);
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MILLISECOND, 999);
return cal.getTime();
}
/**
* 设置之间为23时59分59秒
* @param date
* @return
*/
public static String HHmmssTo235959Str(Date date) {
if(date==null) {return null;}
Calendar cal = getCalendarByDate(date);
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MILLISECOND, 999);
return format(cal.getTime(), YMDHMS);
}

/**
* 判断是否是星期一
*
* @param date
* @return
*/
public static boolean isMonday(Date date) {
return getCalendarByDate(date).get(Calendar.DAY_OF_WEEK) == 2;
}

/**
* 判断是否是一个月的第一天
*
* @param date
* @return
*/
public static boolean isFirstDayOfTheMonth(Date date) {
return isFirstDayOfTheMonth(getCalendarByDate(date));
}

/**
* 判断是否是一个月的第一天
*
* @param date
* @return
*/
public static boolean isFirstDayOfTheMonth(Calendar date) {
return date.get(Calendar.DAY_OF_MONTH) == 1;
}

/**
* 判断是否是一个季度的第一天
*
* @param date
* @return
*/
public static boolean isFirstDayOfTheQuarter(Date date) {
Calendar cal = getCalendarByDate(date);
int month = cal.get(Calendar.MONTH) + 1;
if (month != 1 && month != 4 && month != 7 && month != 10) {
return false;
}
return isFirstDayOfTheMonth(date);
}

/**
* 判断是否是当前年的第一天
* @param date
* @return
*/
public static boolean isFirstDayOfTheYear(Date date) {
Calendar cal = getCalendarByDate(date);
int month = cal.get(Calendar.MONTH) + 1;// 得到月份
return month == 1 && isFirstDayOfTheMonth(date);
}

/**
* 得到季度
* @param cal
* @return
*/
public static int getQuarterNumber(Calendar cal) {
int month = cal.get(Calendar.MONTH) + 1;
if (month >= 1 && month <= 3) {
return 1;
}
if (month >= 4 && month <= 6) {
return 2;
}
if (month >= 7 && month <= 9) {
return 3;
}
if (month >= 10 && month <= 12) {
return 4;
}
return 0;
}

public static Calendar getCalendarByDate(Date date) {
Calendar cal = Calendar.getInstance();
cal.clear();
cal.setTime(date);
return cal;
}
/**
* 上周周一
* @param date
* @return
*/
public static Date lastWeekMonday(Date date) {
Date thisWeekMonday=thisWeekMonday(date);
Calendar cal = getCalendarByDate(thisWeekMonday);
cal.add(Calendar.DAY_OF_MONTH, -7);
return cal.getTime();
}
public static String getWeek(Date date){
SimpleDateFormat dateFm = new SimpleDateFormat("EEEE");
return dateFm.format(date);
}
public static String getWeek2(Date date){
String[] weekDays = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
if (w < 0){
w = 0;
}
return weekDays[w];
}
/**
* 上周星期天
* @param date
* @return
*/
public static Date lastWeekSunday(Date date) {
Date thisWeekSunday=thisWeekSunday(date);
Calendar cal = getCalendarByDate(thisWeekSunday);
cal.add(Calendar.DAY_OF_MONTH, -7);
return cal.getTime();
}

/**
* 上个月第一天
* @param date
* @return
*/
public static Date lastMonthFirstDay(Date date) {
Calendar cal = getCalendarByDate(date);
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) - 1);
cal.set(Calendar.DAY_OF_MONTH, 1);
return cal.getTime();
}

/**
* 上个月最后一天
* @param date
* @return
*/
public static Date lastMonthLastDay(Date date) {
Calendar cal = getCalendarByDate(date);
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) - 1);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DATE));
return cal.getTime();
}

/**
* 上个季度第一个月
* @param date
* @return
*/
public static int lastQuarterFirstMonth(Date date) {
Calendar cal = getCalendarByDate(date);
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) - 3);
return cal.get(Calendar.MONTH);
}

/**
* 上个季度最后一个月
* @param date
* @return
*/
public static int lastQuarterLastMonth(Date date) {
Calendar cal = getCalendarByDate(date);
cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) - 1);
return cal.get(Calendar.MONTH);
}

/**
* 昨天
* @return
*/
public static Calendar getYesterdayCalendar() {
Calendar c = Calendar.getInstance();
c.add(Calendar.DATE, -1);
return c;
}

/**
* 昨天
* @return
*/
public static Date getYesterday() {
return getYesterdayCalendar().getTime();
}

/**
* 判断是否是昨天
*
* @param finishTime
* @return
*/
public static boolean isYesterday(Date finishTime) {
Calendar yesterday = DateUtil.getYesterdayCalendar();
Calendar finishday = Calendar.getInstance();
finishday.setTime(finishTime);
boolean result = yesterday.get(Calendar.YEAR) == finishday
.get(Calendar.YEAR)
&& yesterday.get(Calendar.MONTH) == finishday
.get(Calendar.MONTH)
&& yesterday.get(Calendar.DATE) == finishday.get(Calendar.DATE);
return result;
}

/**
* 判断不是今天
*
* @param c
* @return
*/
public static boolean isNotToday(Calendar c) {
Calendar today = Calendar.getInstance();
boolean result = today.get(Calendar.YEAR) != c.get(Calendar.YEAR)
|| today.get(Calendar.MONTH) != c.get(Calendar.MONTH)
|| today.get(Calendar.DATE) != c.get(Calendar.DATE);
return result;
}

/**
* 本周一
* @param date
* @return
*/
public static Date thisWeekMonday(Date date) {
if(isMonday(date)){return date;}
Calendar c = getCalendarByDate(date);
while (true) {
c.set(Calendar.DAY_OF_MONTH, c.get(Calendar.DAY_OF_MONTH) - 1);
if (isMonday(c.getTime())) {
return c.getTime();
}
}
}
/**
* 本周日
* @param date
* @return
*/
public static Date thisWeekSunday(Date date) {
if(isSunday(date)){return date;}
Calendar c = getCalendarByDate(date);
while (true) {
c.set(Calendar.DAY_OF_MONTH, c.get(Calendar.DAY_OF_MONTH) + 1);
if (isSunday(c.getTime())) {
return c.getTime();
}
}
}

/**
* 昨天
* @param c
* @return
*/
public static Calendar getYesterday(Calendar c) {
Calendar c2 = getCalendarByDate(c.getTime());
c2.add(Calendar.DATE, -1);
return c2;
}

/**
* 本年第一天
* @param date
* @return
*/
public static Date thisYearFirstDay(Date date) {
Calendar c = getCalendarByDate(date);
c.set(Calendar.MONTH, 0);
c.set(Calendar.DATE, 1);
return c.getTime();
}
/**
* 本年最后一天
* @param date
* @return
*/
public static Date thisYearLastDay(Date date) {
Calendar c = getCalendarByDate(date);
c.set(Calendar.MONTH, 11);
c.set(Calendar.DATE, 31);
return c.getTime();
}
/**
* 本月第一天
* @param date
* @return
*/
public static Date thisMonthFirstDay(Date date) {
Calendar c = getCalendarByDate(date);
c.set(Calendar.DATE, 1);
return c.getTime();
}
/**
* 本季度第一天
* @param date
* @return
*/
public static Date thisQuarterFirstDay(Date date) {
int firstMonth=thisQuarterFirstMonth(date);
Calendar c = getCalendarByDate(date);
c.set(Calendar.MONTH, firstMonth-1);
c.set(Calendar.DATE, 1);
return c.getTime();
}

/**
* 本季度第一个月
* @param date
* @return
*/
public static int thisQuarterFirstMonth(Date date) {
Calendar c = getCalendarByDate(date);
int q=getQuarterNumber(c);
int month=1;
switch (q) {
case 1:
month=1;
break;
case 2:
month=4;
break;
case 3:
month=7;
break;
case 4:
month=10;
break;
}
return month;
}

/**
* 本季度最后一个月
* @param date
* @return
*/
public static int thisQuarterLastMonth(Date date) {
Calendar c = getCalendarByDate(date);
int q=getQuarterNumber(c);
int month=1;
switch (q) {
case 1:
month=3;
break;
case 2:
month=6;
break;
case 3:
month=9;
break;
case 4:
month=12;
break;
}
return month;
}

/**
* 周末
* @param calWhichDay
* @return
*/
public static boolean isSunday(Date calWhichDay) {
return getCalendarByDate(calWhichDay).get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY;
}

/**
* 月最后一天
* @param calWhichDay
* @return
*/
public static boolean isLastDayOfTheMonth(Date calWhichDay) {
Calendar c = getCalendarByDate(calWhichDay);
c.add(Calendar.DATE, 1);
if (c.get(Calendar.DATE) == 1) {
return true;
}
return false;
}

/**
* 季度最后一天
* @param calWhichDay
* @return
*/
public static boolean isLastDayOfTheQuarter(Date calWhichDay) {
if (!isLastDayOfTheMonth(calWhichDay)) {
return false;
}
int currentMonth = calWhichDay.getMonth() + 1;
if (currentMonth == 3 || currentMonth == 6 || currentMonth == 9
|| currentMonth == 12) {
return true;
}
return false;
}

/**
* 年最后一天
* @param calWhichDay
* @return
*/
public static boolean isLastDayOfTheYear(Date calWhichDay) {
Calendar c = getCalendarByDate(calWhichDay);
if (c.get(Calendar.MONTH) + 1 == 12 && c.get(Calendar.DATE) == 31) {
return true;
}
return false;
}

/**
* 获得一年前的时间
*
* @param now
* @return
*/
public static Date get365Before(Date now) {
Calendar cal = getCalendarByDate(now);
cal.add(Calendar.YEAR, -1);
return cal.getTime();
}

/**
* 指定年龄开始日期
* @param age
* @return
*/
public static String getByAgeStart(int age) {
Calendar now = Calendar.getInstance();
now.set(Calendar.MONTH, 0);
now.set(Calendar.DATE, 1);
now.add(Calendar.YEAR, -age);
return format(now.getTime(), "yyyy-MM-dd");
}
/**
* 指定年龄结束日期
* @param age
* @return
*/
public static String getByAgeEnd(int age) {
Calendar now = Calendar.getInstance();
now.set(Calendar.MONTH, 11);
now.set(Calendar.DATE, 31);
now.add(Calendar.YEAR, -age);
return format(now.getTime(), "yyyy-MM-dd");
}
/**
* 将字符串转为时间
* @param date
* @param format
* @return
*/
public static Calendar getFromStr(String date,String format){
Calendar cal=Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat(format);
try {
cal.setTime(sdf.parse(date));
} catch (ParseException e) {
e.printStackTrace();
return null;
}
return cal;
}
public static Date toDate(Date date){
return date;
}
public static Date toDate(String date){
return getDate(date);
}
public static Date toDate(String date,String pattern){
return getDate(date,pattern);
}
/**
* 将字符串转为时间
* @param date
* @param format
* @return
*/
public static Date getDate(String date){
if(date==null||date.isEmpty()){
return null;
}
String pattern = null ;

//TODO 换成使用正则表达式 对常用的日期类型都做支持
if(date.contains("T")){
date=date.replaceAll("T", " ");
}
if (date.contains(":")) {
if(StringUtil.count(date, ':')==2){
pattern=DateUtil.YMDHMS;
}else if(StringUtil.count(date, ':')==1){
pattern=DateUtil.YMDHM;
}
} else {
pattern=DateUtil.YMD;
}

SimpleDateFormat sdf = new SimpleDateFormat(pattern);
try {
return sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* 将字符串转为时间
* @param date
* @param format
* @return
*/
public static Date getDate(String date,String format){
if(StrKit.isBlank(date)) {
return null;
}
if(date.contains("T")){
date=date.replaceAll("T", " ");
}
if(format==null){
return getDate(date);
}
SimpleDateFormat sdf = new SimpleDateFormat(format);
try {
return sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* 得到几天后的时间
* @param minute
* @return
*/
public static Date getDaysAfter(Date date,int day) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DATE, day);
return calendar.getTime();
}
/**
* 得到几天后的时间
* @param minute
* @return
*/
public static Date getDaysAfter(int day) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, day);
return calendar.getTime();
}
/**
* 获得几分钟后的时间
* @param minute
* @return
*/
public static Date getMinutesAfter(int minute) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, minute);
return calendar.getTime();
}
/**
* 获得几秒后的时间
* @param seconds
* @return
*/
public static Date getSecondsAfter(int seconds) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, seconds);
return calendar.getTime();
}
/**
* 获得几秒前的时间
* @param seconds
* @return
*/
public static Date getSecondsBefore(int seconds) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, -seconds);
return calendar.getTime();
}
/**
* 获得几秒后的时间
* @param date
* @param seconds
* @return
*/
public static Date getSecondsAfter(Date date,int seconds) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.SECOND, seconds);
return calendar.getTime();
}
/**
* 获得几秒前的时间
* @param date
* @param seconds
* @return
*/
public static Date getSecondsBefore(Date date,int seconds) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.SECOND, -seconds);
return calendar.getTime();
}

/**
* 获得几分钟后的时间
* @param date
* @param minute
* @return
*/
public static Date getMinutesAfter(Date date,int minute) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MINUTE, minute);
return calendar.getTime();
}


/**
* 获得几小时后的时间
* @param minute
* @return
*/
public static Date getHourAfter(int hour) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR_OF_DAY, hour);
return calendar.getTime();
}

/**
* 获得几小时后的时间
* @param date
* @param minute
* @return
*/
public static Date getHourAfter(Date date,int hour) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.HOUR_OF_DAY, hour);
return calendar.getTime();
}
/**
* 得到几天后的时间
* @param minute
* @return
*/
public static Date getDaysAgo(int day) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -day);
return calendar.getTime();
}
/**
* 得到几天后的时间
* @param minute
* @return
*/
public static Date getDaysAgo(Date date,int day) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DATE, -day);
return calendar.getTime();
}
/**
* 获得几分钟前的时间
* @param minute
* @return
*/
public static Date getMinutesAgo(int minute) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, -minute);
return calendar.getTime();
}
/**
* 获得几分钟前的时间
* @param date
* @param minute
* @return
*/
public static Date getMinutesAgo(Date date,int minute) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.MINUTE, -minute);
return calendar.getTime();
}


/**
* 获得几小时前的时间
* @param minute
* @return
*/
public static Date getHourAgo(int hour) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR_OF_DAY, -hour);
return calendar.getTime();
}
/**
* 获得几小时前的时间
* @param date
* @param minute
* @return
*/
public static Date getHourAgo(Date date,int hour) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.HOUR_OF_DAY, -hour);
return calendar.getTime();
}

/**
* 判断两个日期是否同一天
* @param a
* @param b
* @return
*/
public static boolean isSameDay(Calendar a, Calendar b) {
if(a.get(Calendar.YEAR)!=b.get(Calendar.YEAR)){
return false;
}
if(a.get(Calendar.MONTH)!=b.get(Calendar.MONTH)){
return false;
}
if(a.get(Calendar.DATE)!=b.get(Calendar.DATE)){
return false;
}
return true;
}
/**
* 是前天
* @param calWhichDay
* @return
*/
public static boolean isTheDayBeforeYesterday(Date calWhichDay) {
Calendar c=Calendar.getInstance();
c.setTime(calWhichDay);

Calendar c2=Calendar.getInstance();
c2.add(Calendar.DATE, -2);

boolean result = c.get(Calendar.YEAR)==c2.get(Calendar.YEAR)&&
c.get(Calendar.MONTH)==c2.get(Calendar.MONTH)&&
c.get(Calendar.DATE)==c2.get(Calendar.DATE);

return result;
}
/**
* 昨天或更近的时间
* @param calWhichDay
* @return
*/
public static boolean isYesterdayOrMoreRecent(Date calWhichDay) {
return DateUtil.HHmmssTo000000(calWhichDay).compareTo(DateUtil.HHmmssTo000000(DateUtil.getYesterdayCalendar().getTime()))>=0;
}
/**
* 计算两个日期之间相差的天数
* @param smdate 较小的时间
* @param bdate 较大的时间
* @return 相差天数
* @throws ParseException
*/
public static int daysBetween(Date aDate,Date bDate){
Long time=(bDate.getTime()-aDate.getTime())/(1000*3600*24);
return time.intValue();
}
/**
* 计算两个日期之间相差的天数
* @param smdate 较小的时间
* @param bdate 较大的时间
* @return 相差天数
* @throws ParseException
*/
public static int daysBetween(Date aDate,Date bDate,int mode){
double time=(bDate.getTime()-aDate.getTime())*1.0/(1000*3600*24);
if(mode==FLOOR){
return (int) Math.floor(time);
}else if(mode==CEIL){
return (int)Math.ceil(time);
}else if(mode==ROUND){
return (int)Math.round(time);
}
return (int)time;
}
/**
* 本月最后一天
* @param date
* @return
*/
public static Date thisMonthLastDay(Date date){
Calendar c = getCalendarByDate(nextMonthFirstDay(date));
c.add(Calendar.DAY_OF_MONTH, -1);
return c.getTime();
}
/**
* 本季度最后一天
* @param date
* @return
*/
public static Date thisQuarterLastDay(Date date){
int lastMonth=thisQuarterLastMonth(date);
Calendar c = getCalendarByDate(date);
c.set(Calendar.MONTH, lastMonth-1);
return thisMonthLastDay(c.getTime());
}
/**
* 下个月的第一天
* @param date
* @return
*/
public static Date nextMonthFirstDay(Date date){
Calendar c = getCalendarByDate(date);
c.add(Calendar.MONTH, 1);
c.set(Calendar.DATE, 1);
return c.getTime();
}
/**
* 明天
* @param date
* @return
*/
public static Date tomorrow(Date date){
Calendar c = getCalendarByDate(date);
c.add(Calendar.DATE, 1);
return c.getTime();
}
/**
* 昨天
* @param date
* @return
*/
public static Date yesterday(Date date){
Calendar c = getCalendarByDate(date);
c.add(Calendar.DATE, -1);
return c.getTime();
}

/**
* 下周一
* @param date
* @return
*/
public static Date nextWeekMonday(Date date){
Date thisWeekMonday=DateUtil.thisWeekMonday(date);
Calendar c = getCalendarByDate(thisWeekMonday);
c.add(Calendar.DAY_OF_MONTH, 7);
return c.getTime();
}
/**
* 得到指定日期所在周的所有天
* @param date
* @return
*/
public static Date[] getOneWeekAllDays(Date date){
Date[] dates=new Date[7];
Date thisWeekMonday=DateUtil.thisWeekMonday(date);
dates[0]=thisWeekMonday;
dates[1]=tomorrow(dates[0]);
dates[2]=tomorrow(dates[1]);
dates[3]=tomorrow(dates[2]);
dates[4]=tomorrow(dates[3]);
dates[5]=tomorrow(dates[4]);
dates[6]=tomorrow(dates[5]);
return dates;
}
/**
* 下周日
* @param date
* @return
*/
public static Date nextWeekSunday(Date date){
Date thisWeekSunday=DateUtil.thisWeekSunday(date);
Calendar c = getCalendarByDate(thisWeekSunday);
c.add(Calendar.DAY_OF_MONTH, 7);
return c.getTime();
}
/**
* 得到一年最后一天
* @param year
* @return
*/
public static Date getYearLastDay(int year){
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(Calendar.YEAR, year);
calendar.roll(Calendar.DAY_OF_YEAR, -1);
return calendar.getTime();
}
/**
* 得到一年第一天
* @param year
* @return
*/
public static Date getYearFirstDay(int year){
Calendar calendar=Calendar.getInstance();
calendar.clear();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.DAY_OF_YEAR, 1);
return calendar.getTime();
}

public static String getTime(Time time) {
if(time==null){return null;}
return time.toString();
}

public static Time getTime(String time) {
if(time==null){return null;}
if (time.contains(":")) {
String[] strs=time.split(":");
if(StringUtil.count(time, ':')==2){
return new Time(Integer.parseInt(strs[0]), Integer.parseInt(strs[1]), Integer.parseInt(strs[2]));
}else if(StringUtil.count(time, ':')==1){
return new Time(Integer.parseInt(strs[0]), Integer.parseInt(strs[1]), 0);
}
}
return null;
}



/**
* 得到周期内的所有日期
* @param startDate
* @param endDate
* @return
*/
public static List<Date> getContainDays(Date startDate,Date endDate){
List<Date> dates = new ArrayList<Date>();
Calendar start = Calendar.getInstance();
start.setTime(startDate);
start.set(Calendar.HOUR, 1);
start.set(Calendar.MINUTE, 1);
start.set(Calendar.SECOND, 1);
start.set(Calendar.MILLISECOND, 1);
Calendar end = Calendar.getInstance();
end.setTime(endDate);
end.set(Calendar.HOUR, 1);
end.set(Calendar.MINUTE, 1);
end.set(Calendar.SECOND, 1);
end.set(Calendar.MILLISECOND, 2);

while(start.before(end)){
dates.add(start.getTime());
start.add(Calendar.DATE, 1);
}
return dates;
}



public static void main(String[] args) {
// Date[] dates=getOneWeekAllDays(new Date());
// for(Date d:dates){
// System.out.println(format(d, "yyyy-MM-dd"));
// }
// System.out.println("上周一:"+format(lastWeekMonday(new Date()), "yyyy-MM-dd"));
// System.out.println("上周日:"+format(lastWeekSunday(new Date()), "yyyy-MM-dd"));
// System.out.println("本周一:"+format(thisWeekMonday(new Date()), "yyyy-MM-dd"));
// System.out.println("本周日:"+format(thisWeekSunday(new Date()), "yyyy-MM-dd"));
// System.out.println("下周一:"+format(nextWeekMonday(new Date()), "yyyy-MM-dd"));
// System.out.println("下周日:"+format(nextWeekSunday(new Date()), "yyyy-MM-dd"));
// System.out.println("今年第一天:"+format(getYearFirstDay(2017), "yyyy-MM-dd"));
// System.out.println("今年最后一天:"+format(getYearLastDay(2017), "yyyy-MM-dd"));
// System.out.println("季度第一天:"+format(thisQuarterFirstDay(new Date()), "yyyy-MM-dd"));
// System.out.println("季度最后一天:"+format(thisQuarterLastDay(new Date()), "yyyy-MM-dd"));
// System.out.println("年第一天:"+format(thisYearFirstDay(new Date()), "yyyy-MM-dd"));
// System.out.println("年最后一天:"+format(thisYearLastDay(new Date()), "yyyy-MM-dd"));
//
//
//
//
// Calendar start = Calendar.getInstance();
// Calendar end = Calendar.getInstance();
// end.add(Calendar.DATE, 2);
// List<Date> ds =getContainDays(start.getTime(), end.getTime());
// for(Date d:ds){
// System.out.println(d);
// }

//
// Calendar cal = Calendar.getInstance();
// cal.add(Calendar.YEAR, -1);
// cal.set(Calendar.MONTH, 11);
// cal.set(Calendar.DATE, 31);
// WeekBean bean = getThisWeek(cal.getTime());
// System.out.println(bean.getYear()+":"+bean.getWeek()+bean.getStartDay()+":"+bean.getEndDay());
//


System.out.println(getAge(getDate("1989-03-04")));

}

public static int getAge(Date birthDay) {
Calendar cal = Calendar.getInstance();

if (cal.before(birthDay)) {
throw new IllegalArgumentException(
"The birthDay is before Now.It's unbelievable!");
}
int yearNow = cal.get(Calendar.YEAR);
int monthNow = cal.get(Calendar.MONTH);
int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH);
cal.setTime(birthDay);

int yearBirth = cal.get(Calendar.YEAR);
int monthBirth = cal.get(Calendar.MONTH);
int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);

int age = yearNow - yearBirth;

if (monthNow <= monthBirth) {
if (monthNow == monthBirth) {
if (dayOfMonthNow < dayOfMonthBirth) age--;
}else{
age--;
}
}
return age;
}
/**
* 获取距今耗时
* @param startTime
* @return
*/
public static String getUsedTimeStr(Date startTime) {
return getTimeDiff(startTime, getNow());
}

/**
* 计算两个时间差
*/
public static String getTimeDiff(Date endDate, Date nowDate)
{
long nd = 1000 * 24 * 60 * 60;
long nh = 1000 * 60 * 60;
long nm = 1000 * 60;
// long ns = 1000;
// 获得两个时间的毫秒时间差异
long diff = endDate.getTime() - nowDate.getTime();
// 计算差多少天
long day = diff / nd;
// 计算差多少小时
long hour = diff % nd / nh;
// 计算差多少分钟
long min = diff % nd % nh / nm;
// 计算差多少秒//输出结果
// long sec = diff % nd % nh % nm / ns;
return day + "天" + hour + "小时" + min + "分钟";
}


}

备忘录:

使用unipush进行个推服务

官网

uni-app官网 (dcloud.net.cn)

  • 通知类型表
  • 通知日志表

每隔一分钟进行扫描数据库,并进行通知,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public void run() {
MemoService memoService = Aop.get(MemoService.class);
MemoMsgLogService memoMsgLogService = Aop.get(MemoMsgLogService.class);
LOG.debug("定时任务:每隔一分钟查询需要通知的用户 - 开始执行");
List<MobMemo> mobMemoList = memoService.selectAlarmMobList();
if(mobMemoList !=null && mobMemoList.size()!=0){
LOG.debug("定时任务:需要执行数 - "+ mobMemoList.size());
for (int i = 0; i < mobMemoList.size(); i++) {
if (i==mobMemoList.size()){
return;
}
MobMsgLog memoMsgLog = memoMsgLogService.saveMemoMsgLog(mobMemoList.get(i));
PushApiUtils.PushMemon(mobMemoList.get(i),memoMsgLog);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public static void PushMemon(MobMemo mobMemo, MobMsgLog memoMsgLog) {


//根据cid进行单推
PushDTO<Audience> pushDTO = new PushDTO<Audience>();
// 设置推送参数
pushDTO.setRequestId(System.currentTimeMillis() + "");
/**** 设置个推通道参数 *****/
PushMessage pushMessage = new PushMessage();
pushDTO.setPushMessage(pushMessage);
GTNotification notification = new GTNotification();
pushMessage.setNotification(notification);
notification.setTitle(mobMemo.getTitle());
notification.setBody(mobMemo.getContent());
notification.setClickType("payload");
notification.setChannelLevel("3");
JSONObject jsonObject = new JSONObject();
jsonObject.put("id",mobMemo.getId());
jsonObject.put("memoMsgLogId",memoMsgLog.getId());
notification.setPayload(jsonObject.toJSONString());
notification.setUrl("https://www.getui.com");
/**** 设置个推通道参数,更多参数请查看文档或对象源码 *****/
/*设置接收人信息*/
Audience audience = new Audience();
pushDTO.setAudience(audience);
audience.addCid(mobMemo.getStr("mobile_cid"));
/*设置接收人信息结束*/
/**** 设置厂商相关参数,更多参数请查看文档或对象源码 ****/

// 进行cid单推
ApiResult<Map<String, Map<String, String>>> apiResult = pushApi.pushToSingleByCid(pushDTO);
if (apiResult.isSuccess()) {
// success
System.out.println(apiResult.getData());
} else {
// failed
System.out.println("code:" + apiResult.getCode() + ", msg: " + apiResult.getMsg());
}
}

获取备忘录的历史消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    /**
* @description: 获取备忘录的历史信息
* @date: 2022/6/9
*/
public HttpResult selectMemoMsg(Integer staffId, Integer pageNumber, Integer pageSize){
if (notOk(staffId)||notOk(pageNumber)||notOk(pageSize)){
return HttpResult.fail(ResultCodeEnum.REQUEST_PARAMETER);
}
List<Record> mobMsgLogs = null;
Integer total = 0;
try {
Sql sql = selectSql().select("a.id,a.associate_id,a.state,a.alarm_time,b.content,b.title,b.user_id ,c.msg_type").from("mob_msg_log","a")
.innerJoin("mob_memo","b","a.associate_id = b.id").innerJoin("mob_msg_type","c","a.msg_type_id = c.id")
.eq("b.user_id",staffId).eq("b.is_vaild",0).eq("is_del",0).orderBy("alarm_time","desc").page(pageNumber,pageSize);
Sql totalSql = selectSql().count("a.id").from("mob_msg_log", "a").innerJoin("mob_memo", "b", "a.associate_id = b.id").eq("b.user_id", staffId).eq("b.is_vaild", 0).eq("is_del", 0);
Future<List<Record>> findFuture = threadPoolExecutor.submit(() -> Db.find(sql.toSql()));
Future<Integer> totalFuture = threadPoolExecutor.submit(() -> queryInt(totalSql.toSql()));
// 计算总数
mobMsgLogs = findFuture.get();
// 计算总数
total = totalFuture.get();

}catch (Exception e){
LOG.debug(" selectMemoMsg sql异常{}",e.getMessage());
}
if (mobMsgLogs.isEmpty()){
mobMsgLogs = Collections.emptyList();
}
return HttpResult.success(Ret.by("memoList",mobMsgLogs).set("total",total));
}

已读未读设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @description: 计算没有计算的读过的信息
* @param staffId 职工号id
* @date: 2022/6/9
*/
public HttpResult countUnreadMemoMessage(Integer staffId){
if (notOk(staffId)){
return HttpResult.fail(ResultCodeEnum.REQUEST_PARAMETER);
}
Sql sql = selectSql().count("a.id").from("mob_msg_log", "a").innerJoin("mob_memo", "b", "a.associate_id = b.id")
.eq("b.user_id", staffId).eq("b.is_vaild", 0).eq("a.state", 0);
Integer memonCount = queryInt(sql.toSql());
return HttpResult.success(Ret.by("unRead",memonCount));
}

生命体征配置:

配置表中写入用户需要查询的数据,记录接口数据等

image-20220620195129157

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Page<LoginLog> paginateAdminList(Integer pageNumber, Integer pageSize, String keywords, Date startTime,
Date endTime) {
Kv paras=Kv.create();
if(StrKit.notBlank(keywords)){
keywords=keywords.trim();
paras.set("username",columnLike(keywords));
}
if(isOk(startTime)){
paras.set("create_time >=",toDateTime(DateUtil.HHmmssTo000000Str(startTime)));
}
if(isOk(endTime)){
paras.set("create_time <=",toDateTime(DateUtil.HHmmssTo235959Str(endTime)));
}

return paginate(paras, "id", "desc", pageNumber, pageSize, true);
}

医嘱筛选

image-20220620205037839

1
2
3
4
5
6
7
8
9
10
/**
* @description: 此处需要进行缓存处理
* @date: 2022/5/13
*/
@Override
public LinkedListMultimap<String, Record> selectAdviceMultimap(DoctorAdviceParam doctorAdviceParam) {
List<Record> records = selectAllAdviceByPatId(doctorAdviceParam.getPatientID());
LinkedListMultimap<String, Record> multimap = handlerAdviceReplace(records);
return multimap;
}

对医嘱数据进行替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    /**
* @param adviceList 病人医嘱列表
* @description: 对病人字段进行处理替换操作, 在此方法中自行修改需要替换的数据,并进行分组操作
* @date: 2022/5/13
*/
private LinkedListMultimap<String, Record> handlerAdviceReplace(List<Record> adviceList) {
if (adviceList == null || adviceList.isEmpty()) {
return null;
}
LinkedListMultimap<String, Record> groupByStartDate = LinkedListMultimap.create();
;
for (int i = 0; i < adviceList.size(); i++) {
Record tempAdvice = adviceList.get(i);
// 替换用药方式
tempAdvice.set("administration", MyUtil.getInstance().administrationConvert(tempAdvice.get("administration")));
// 替换医嘱状态
tempAdvice.set("orderStatus", MyUtil.getInstance().orderStatusConvert(tempAdvice.get("orderStatus")));
tempAdvice.set("repeatIndicator", MyUtil.getInstance().repeatIndicatorConvert(tempAdvice.get("repeatIndicator")));
// 按日期进行分组
String s = MyUtil.getInstance().startDateTimeConvert(tempAdvice.getStr("startDateTime"));
groupByStartDate.put(s, tempAdvice);
}
return groupByStartDate;
}

获取元数据:

1
2
3
4
5
6
7
8
9
10
11
/**
* @description: 此处需要进行缓存处理
* @date: 2022/5/13
*/
@Override
public LinkedListMultimap<String, Record> selectAdviceMultimap(DoctorAdviceParam doctorAdviceParam) {
List<Record> records = selectAllAdviceByPatId(doctorAdviceParam.getPatientID());
LinkedListMultimap<String, Record> multimap = handlerAdviceReplace(records);
return multimap;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//    通过日期获取指定的医嘱信息
@Override
public HttpResult selectAdviceByStartTime(DoctorAdviceParam doctorAdviceParam) {


Multimap<String, Record> adviceList = doctorAdviceMapper.selectAdviceMultimap(doctorAdviceParam);
if (adviceList == null || adviceList.isEmpty()) {
HttpResult.success(Ret.by("doctorAdvice", Collections.emptyList()));
}
List<Record> metadata = (List<Record>) adviceList.get(doctorAdviceParam.getStartTime().trim());
return HttpResult.success(Ret.by("doctorAdvice", adviceFilter(doctorAdviceParam, metadata)));
}

/**

按需筛选医嘱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    /**
* @param doctorAdviceParam 医嘱对象
* @param metadata 需要筛选的元数据
* @description: 医嘱筛选器, 执行页面筛选
* @date: 2022/5/13
*/
private List<Record> adviceFilter(DoctorAdviceParam doctorAdviceParam, List<Record> metadata) {
List<Record> collect = metadata.stream().filter(administrationFilter -> {
// 判断是否需要进行用药方式筛选
if (StringUtil.isNotBlank(doctorAdviceParam.getAdministration())) {
AdviceValue tempAdviceValue = administrationFilter.get("administration");
return doctorAdviceParam.getAdministration().equals(tempAdviceValue.getText());
} else {
// 如果为空则放行
return true;
}
}).filter(adviceStatue -> {
// 判断是否进行 医嘱状态筛选
if (StringUtil.isNotBlank(doctorAdviceParam.getAdviceStatue())) {
AdviceValue tempAdviceValue = adviceStatue.get("orderStatus");
return doctorAdviceParam.getAdviceStatue().equals(tempAdviceValue.getValue());
} else {
return true;
}
}).filter(repeatIndicator -> {
// 判断是否进行 医嘱类型,长期还是短期筛选
if (StringUtil.isNotBlank(doctorAdviceParam.getRepeatIndicator())) {
AdviceValue tempAdviceValue = repeatIndicator.get("repeatIndicator");
return doctorAdviceParam.getRepeatIndicator().equals(tempAdviceValue.getValue());
} else {
return true;
}
}).collect(Collectors.toList());
return collect;
}

医嘱执行

医嘱执行的目的是,通过医嘱医嘱执行接口,将dbo.JD_DCMX 中患者的医嘱进行修改

如果是 长期医嘱 修改字段

D_KDCZYBH(关联用户id的编号)

D_KDCZYXM(姓名)

D_ZXSJ(时间)

如果是短期医嘱

修改

D_ZXQM(进行签名操作)

D_ZXSJ(时间)

短期医嘱进行双签名操作

双签名操作更新时间以及 对D_ZXQM 字段进行拼接操作 。

问题一:

1、医嘱执行与签名是不需要修改的医嘱的状态嘛,只需要填上执行人/签名即可,那我怎么进行筛选呢?

通过判断写入字段是否为空嘛

image-20220514165437709

image-20220514165611554

2、医嘱执行业务只有以下操作吗

长期医嘱进行的是执行操作,只有一次,短期医嘱进行执行与签名操作,存在两次操作(一次进行执行操作,第二次进行签名操作)

3、通过什么判断医嘱执行与未执行

医嘱未执行

未执行:ORDER_STATUS = G

长期已执行: D_KDCZYBH(关联用户id的编号)、 D_KDCZYXM(姓名) 不为空值

短期已执行: D_ZXQM(进行签名操作)、D_ZXSJ(时间) 不为空值

4、签名与未签名医嘱筛选

V:未签名

未签名: (ORDER_STATUS= V )&& (D_ZXQM ==null)

双签名:(ORDER_STATUS= V )&& (D_ZXQM !=null)

病人主页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 SELECT    
a.D_ZDBH AS PATIENT_ID, a.住院次数 AS VISIT_ID, a.住院号 AS INP_NO, '' AS BABY_ID, a.姓名 AS NAME, a.性别 AS SEX, a.当前科别代码 AS DEPT_CODE, '' AS WARD_CODE,
a.当前病床 AS BED_NO, a.出生日期 AS DATE_OF_BIRTH, (CASE WHEN (入院时 IS NOT NULL AND 入院时 <> '' AND 入院分 IS NOT NULL AND 入院分 <> '') THEN dateadd(MINUTE, 入院分,
DATEADD(HOUR, 入院时, 入院日期)) ELSE 入院日期 END) AS ADMISSION_DATE_TIME, '' AS ADM_WARD_DATE_TIME, a.疾病分类 AS DIAGNOSIS, a.护理 AS NURSING_CLASS,
a.危重级别 AS PATIENT_CONDITION, a.收费类别 AS CHARGE_TYPE, c.消费金额 AS TOTAL_COSTS, b.预缴金额 AS PRE_PAYMENTS, b.预缴金额 - c.消费金额 AS BILL_YE, a.饮食 AS DIET,
a.主治医生工号 AS DOCTOR_IN_CHARGE, a.住院标志 AS SATUS, a.过敏史 AS ALLERGY, a.电话号码,d.Name as doctorName
FROM
[pyyzyy].dbo.YY_BRZY AS a INNER JOIN
(SELECT D_ZDBH, SUM(D_YFJE) AS 预缴金额
FROM
[pyyzyy].dbo.JD_KRYJ
GROUP BY D_ZDBH) AS b
ON a.D_ZDBH = b.D_ZDBH INNER JOIN
(SELECT D_DCBH, SUM(D_XFJE) AS 消费金额
FROM [pyyzyy].dbo.JD_DCMX
WHERE (D_DEL = 'F')
GROUP BY D_DCBH) AS c ON a.D_ZDBH = c.D_DCBH
left join [pyyzyy].dbo.Employe as d
on a.主治医生工号 = d.UserCode
where a.D_ZDBH = 'ZYFCBF4CD765'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public List<JSONObject> findAllPatByAssociateId(List<String> patIds, String deptCode) {
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
Connection connection = null;
List<JSONObject> res = new ArrayList<>();

StringBuffer sql = new StringBuffer();
sql.append("select ").append(" a.D_ZDBH AS PATIENT_ID,a.姓名 AS NAME, (CASE WHEN (入院时 IS NOT NULL AND 入院时 <> '' AND 入院分 IS NOT NULL AND 入院分 <> '') THEN dateadd(MINUTE, 入院分, DATEADD(HOUR, 入院时, 入院日期)) ELSE 入院日期 END) AS ADMISSION_DATE_TIME ");
if (patIds == null || patIds.size() == 0) {
sql.append(", 'false' as isConcern ");
} else {
String s = StringUtil.getInCond(patIds);
sql.append(", (CASE when a.D_ZDBH IN (").append(s).append(" ) then 'true' else 'false' end) as isConcern ");
}
sql.append(" from [pyyzyy].dbo.YY_BRZY AS a ");
sql.append(" where a.当前科别代码 = ").append(deptCode).append(" and a.住院标志 = 1 ")
.append(" ORDER BY isConcern desc,ADMISSION_DATE_TIME desc");
try {
connection = dbPro.getConfig().getDataSource().getConnection();

// 对sql进行预编译
preparedStatement = connection.prepareStatement(sql.toString());
resultSet = preparedStatement.executeQuery();
if (connection != null) {
while (resultSet.next()) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("patientId", resultSet.getObject("PATIENT_ID"));
jsonObject.put("name", resultSet.getObject("NAME"));
jsonObject.put("admissionDateTime", resultSet.getObject("ADMISSION_DATE_TIME"));
jsonObject.put("isConcern", resultSet.getBoolean("isConcern"));
res.add(jsonObject);
}
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
dbPro.getConfig().close(resultSet, preparedStatement, connection);
}
if (res != null && res.size() != 0) {
return res;
}
return Collections.emptyList();
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

/**
* @description: 根据用户的的patient_id 获取病人数据,首页显示
* @author
* @date: 2022/5/7
*/
public PatInfoDto selectPatInfo(String patientId) {
Record patInfo = mssqlDb.getMSSQLPro().template("medical.patsInhospital.selectPatInfo", Kv.by("patientId",patientId)).findFirst();
if (patInfo.getColumns() == null) {
return null;
}
// 这里没有办法进行映射
String doctorName = patInfo.getStr("doctorName");
PatInfoDto patInfoDto = BeanUtil.fillBeanWithMap(patInfo.getColumns(), new PatInfoDto(), true, false);
patInfoDto.setDoctorName(doctorName);
return patInfoDto;
}


要求业务一:

我们现在进行医嘱执行、签名、停止三个功能

长期、短期医嘱

不清楚如何筛选是否进行执行、签名

1、对长期、临时、当天医嘱、有效长期医嘱进行分类(按时间排序)

获取个人医嘱的所有分类 ,进入医嘱筛选分类器

2、在一段时间间隔内,对多人能够进行长期 临时 进行筛选(加入时间、和病人个人)

​ 获取个人缓存的时间,进行长期或者临时筛选

3、在指定的日期内进行查找所有的数据,并按要求字段进行筛选显示,获取指定日期, 进入分类筛选器操作 。

  • 共性,都共用一个数据源,并在此基础上进行筛选操作
  • 构造三个不同的分类器,对源数据进行分类

缓存清楚操作,三个业务

与以往对比:

能够从大批量中查找时间+分类时间

主要是分类的时间

查看每个人的数据

查询数据: 对数据进行

数据清洗

分类器:传入,构造分类规则, 分类器中进行筛选

回写数据:

image-20220512212136450

L 临时 c 长期

image-20220512212334319

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
长期临时 医嘱类型

**/

select D_LCBZ,COUNT(D_LCBZ) from [PYYZYY].dbo.JD_DCMX group by D_LCBZ;

/**

医嘱状态
**/
select D_DEL,COUNT(D_DEL) from [PYYZYY].dbo.JD_DCMX group by D_DEL;

医嘱停止:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
USE [pyyzyy]
GO
/****** Object: StoredProcedure [dbo].[YYHL_YZHX] Script Date: 05/09/2022 09:15:46 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[YYHL_TZYZHX](@PATIENT_ID varchar(30),@INP_NO varchar(30),@VISIT_ID varchar(30),@ORDER_NO varchar(30),@OPERATOR_ID varchar(30), @OPERATOR_NAME varchar(30),@OPERATOR_DATE varchar(30),@RESULT int output)
as
begin
declare @sql varchar(300)
declare @D_LCBZ varchar(30)
declare @D_ZDBH varchar(30)
declare @D_TZRQ varchar(30)
declare @D_TZSJ varchar(30)
set @D_TZRQ = CONVERT(varchar(100),CONVERT(datetime,@OPERATOR_DATE),23)
set @D_TZSJ = CONVERT(varchar(5),CONVERT(datetime,@OPERATOR_DATE),108)
declare @error int =0
set @RESULT = '0'
select @D_ZDBH = D_ZDBH from YY_BRZY where 住院号 = @INP_NO;
begin transaction
if @D_ZDBH = @PATIENT_ID
Begin
update JD_DCMX set D_TZRQ = @D_TZRQ,D_TZSJ = @D_TZSJ,D_LCBZ = 'T',D_JCSJ = @OPERATOR_DATE,D_JCCZYBH=@OPERATOR_ID,D_JCCZYXM=@OPERATOR_NAME where ID = @VISIT_ID;
set @error+=@@ERROR;
End
if(@error<>0)
begin
rollback transaction
end
else begin
set @RESULT = '1';
commit transaction
end
Return @RESULT
end

/*医嘱停止回写存储过程,参数与医嘱执行一致
declare @RESULT varchar(20)
exec YYHL_TZYZHX @PATIENT_ID = 'ZYF98AE9C088',@INP_NO = '20070988',@VISIT_ID = '9710224',@ORDER_NO ='4',@OPERATOR_ID = '035',@OPERATOR_NAME = '刘珊',@OPERATOR_DATE = '2022-05-08 02:00:00',@RESULT = @RESULT output
select @RESULT
*/

医嘱停止sql

1
2
3
4
5
6
7
8
9
10

/**
医嘱停止测试
**/

select top 100 *,D_DEL , ID,D_TZRQ,D_TZSJ,D_TZSJ,D_LCBZ,D_JCSJ, D_JCCZYBH,D_LCBZ ,D_JCCZYXM from [PYYZYY].dbo.JD_DCMX

select top 100 a.D_ZDBH, a.住院号,D_TZRQ,D_LCBZ,D_JCSJ,D_JCCZYBH,D_JCCZYXM
from [pyyzyy].dbo.YY_BRZY as a inner join [PYYZYY].dbo.JD_DCMX as b on a.D_ZDBH = b.D_DCBH where a.D_ZDBH = 'ZY0000160919' and
b.ID = 9878038

测试样例

1
2
3
4
5
6
7
8
9
10
11
12
13
@org.junit.Test
public void TestUpdateAdviceToStop(){
AdviceExecuteParam adviceExecuteParam = new AdviceExecuteParam();
adviceExecuteParam.setInpNo("20070988");
adviceExecuteParam.setOperatorName("刘珊");
adviceExecuteParam.setPatientId("ZYF98AE9C088");
adviceExecuteParam.setVisitId("9710224");
adviceExecuteParam.setOperatorDate("2022-05-08 02:00:00");
adviceExecuteParam.setOrderNo("4");
adviceExecuteParam.setOperatorId("035");
DoctorAdviceMapperImpl doctorAdviceMapper = new DoctorAdviceMapperImpl();
doctorAdviceMapper.updateAdviceToStop(adviceExecuteParam);
}

image-20220512211847532

修改两个时间,并将状态设置未 T

三测单回写

采写时间回写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
USE [pyyzyy]
GO
/****** Object: StoredProcedure [dbo].[YYHL_CXSJHX] Script Date: 05/05/2022 16:05:55 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[YYHL_CXSJHX](@PATIENT_ID varchar(30),@TEST_NO varchar(30),@OPERATOR_ID varchar(30),@OPERATOR_NAME varchar(30),@OPERATOR_DATE varchar(30),@RESULT int output)
as
begin
declare @MXID int
declare @error int =0
set @RESULT = '0'
declare @count int;
declare @i int;
select * into #temp from(select ID,ROW_NUMBER() over(order by ID) as row from JD_DCMX where D_JYBH = @TEST_NO and D_DEL = 'F') b
select @count = COUNT(1) from #temp;
set @i = 1;
begin transaction
while (@count >= @i)
Begin
select @MXID = ID from #temp where row = @i;
update JD_DCMX set D_JCSJ = @OPERATOR_DATE,D_JCCZYBH = @OPERATOR_ID,D_JCCZYXM = @OPERATOR_NAME where D_DCBH = @PATIENT_ID and ID = @MXID and D_JYBH = @TEST_NO;
set @error+=@@ERROR;
set @i = @i + 1;
End
if (@error = 0)
Begin
update EmrNew.dbo.BREMR set 采集时间 = @OPERATOR_DATE,采集人编号 = @OPERATOR_ID,采集人 = @OPERATOR_NAME where 电子申请单号 = @TEST_NO;
set @error+=@@ERROR;
set @RESULT = '1';
End
drop table #temp;
if(@error<>0)
begin
rollback transaction
end
else begin
commit transaction
end
Return @RESULT
end


/*采血时间回写参数不够,按示例参数传值
declare @RESULT varchar(20)
exec YYHL_CXSJHX @PATIENT_ID = 'ZYF98AE9C088',@TEST_NO = '202012180001',@OPERATOR_ID = '180',@OPERATOR_NAME ='刘丹',@OPERATOR_DATE = '2020-12-18 03:34:16',@RESULT = @RESULT output
select @RESULT
*/

双签名回写

存储过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

USE [pyyzyy]
GO
/****** Object: StoredProcedure [dbo].[YYHL_YZHX] Script Date: 05/05/2022 11:42:48 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[PRC_YDHL_UPDATE_EXECINFO](@PATIENT_ID varchar(30),@INP_NO varchar(30),@VISIT_ID varchar(30),@ORDER_NO varchar(30),@OPERATOR_ID varchar(30), @OPERATOR_NAME varchar(30),@OPERATOR_DATE varchar(30),@RESULT int output)
as
begin
declare @sql varchar(300)
declare @D_LCBZ varchar(30)
declare @D_ZDBH varchar(30)
declare @D_ZXQM varchar(100)
declare @error int =0
set @RESULT = '0'
select @D_ZDBH = D_ZDBH from YY_BRZY where 住院号 = @INP_NO;
begin transaction
if @D_ZDBH = @PATIENT_ID
Begin
select @D_LCBZ = D_LCBZ from JD_DCMX where ID = @VISIT_ID;
IF @D_LCBZ = 'L'
Begin
select @D_ZXQM = D_ZXQM from JD_DCMX where D_DCBH = @PATIENT_ID and ID = @VISIT_ID;
set @D_ZXQM = @D_ZXQM + ',' + @OPERATOR_NAME
update JD_DCMX set D_ZXQM = @D_ZXQM,D_ZXSJ = @OPERATOR_DATE where D_DCBH = @PATIENT_ID and ID = @VISIT_ID;
set @error+=@@ERROR;
set @RESULT = '1';
End
End
if(@error<>0)
begin
rollback transaction
end
else begin
commit transaction
end
Return @RESULT
end

/*文档中的参数不能支持操作,按医嘱回传的参数传递,双签名只有临时医嘱有,其他医嘱不要使用该功能
declare @RESULT varchar(20)
exec PRC_YDHL_UPDATE_EXECINFO @PATIENT_ID = 'ZYF98AE9C088',@INP_NO = '20070988',@VISIT_ID = '9707640',@ORDER_NO ='4',@OPERATOR_ID = '035',@OPERATOR_NAME = '刘珊',@OPERATOR_DATE = '12-18 02:00',@RESULT = @RESULT output
select @RESULT
*/

存储过程检验

1
2
3
4
5
6
7
8
9
/**

医嘱双签名操作,只有短期医嘱 D_LCBZ =L 进行双签名操作
**/
select top 100 D_DEL , ID,D_ZXQM,D_DCBH,D_TZRQ,D_TZSJ,D_TZSJ,D_LCBZ,D_JCSJ, D_JCCZYBH,D_LCBZ ,D_JCCZYXM from [PYYZYY].dbo.JD_DCMX

select top 100 a.D_ZDBH, a.住院号,D_ZXQM, D_ZXSJ,b.ID,D_LCBZ
from [pyyzyy].dbo.YY_BRZY as a inner join [PYYZYY].dbo.JD_DCMX as b on a.D_ZDBH = b.D_DCBH where
b.ID =9926997

未签名之前

image-20220512205517828

测试结果

更新失败:

当医嘱不是短期医嘱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@org.junit.Test
public void updateAdviceToDoubleSignature(){
AdviceExecuteParam adviceExecuteParam = new AdviceExecuteParam();
adviceExecuteParam.setInpNo("00187162");
adviceExecuteParam.setOperatorName("刘珊");
adviceExecuteParam.setPatientId("ZY0000187162");
adviceExecuteParam.setVisitId("9926997");
adviceExecuteParam.setOperatorDate("2022-05-12 02:00:00");
adviceExecuteParam.setOrderNo("4");
adviceExecuteParam.setOperatorId("035");
DoctorAdviceMapperImpl doctorAdviceMapper = new DoctorAdviceMapperImpl();
doctorAdviceMapper.updateAdviceToDoubleSignature(adviceExecuteParam);
}

image-20220512211035673

image-20220512210955241

更新成功:

image-20220512211457683

医嘱执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
USE [pyyzyy]
GO
/****** Object: StoredProcedure [dbo].[YYHL_YZHX] Script Date: 05/05/2022 11:33:59 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[YYHL_YZHX](@PATIENT_ID varchar(30),@INP_NO varchar(30),@VISIT_ID varchar(30),@ORDER_NO varchar(30),@OPERATOR_ID varchar(30), @OPERATOR_NAME varchar(30),@OPERATOR_DATE varchar(30),@RESULT int output)
as
begin
declare @sql varchar(300)
declare @D_LCBZ varchar(30)
declare @D_ZDBH varchar(30)
declare @error int =0
set @RESULT = '0'
select @D_ZDBH = D_ZDBH from YY_BRZY where 住院号 = @INP_NO;
begin transaction
if @D_ZDBH = @PATIENT_ID
Begin
select @D_LCBZ = D_LCBZ from JD_DCMX where ID = @VISIT_ID;
IF @D_LCBZ = 'C'
Begin
update JD_DCMX set D_KDCZYBH = @OPERATOR_ID,D_KDCZYXM = @OPERATOR_NAME,D_ZXSJ = @OPERATOR_DATE where D_DCBH = @PATIENT_ID and ID = @VISIT_ID;
set @error+=@@ERROR;
set @RESULT = '1';
End
Else IF @D_LCBZ = 'L'
Begin
update JD_DCMX set D_ZXQM = @OPERATOR_NAME,D_ZXSJ = @OPERATOR_DATE where D_DCBH = @PATIENT_ID and ID = @VISIT_ID;
set @error+=@@ERROR;
set @RESULT = '1';
End
End
if(@error<>0)
begin
rollback transaction
end
else begin
commit transaction
end
Return @RESULT
end


/* 存储过程调用请按该参数值格式传参
declare @RESULT varchar(20)
exec YYHL_YZHX @PATIENT_ID = 'ZYF98AE9C088',@INP_NO = '20070988',@VISIT_ID = '9707640',@ORDER_NO ='4',@OPERATOR_ID = '035',@OPERATOR_NAME = '刘珊',@OPERATOR_DATE = '12-18 02:00',@RESULT = @RESULT output
select @RESULT
*/

短期医嘱执行:

1
2
3
select  top 100  a.D_ZDBH, a.住院号,D_ZXQM, D_ZXSJ,b.ID,D_LCBZ
from [pyyzyy].dbo.YY_BRZY as a inner join [PYYZYY].dbo.JD_DCMX as b on a.D_ZDBH = b.D_DCBH where
b.ID =9926997

image-20220512214041836

长期医嘱执行

没有修改之前

查询sql

1
2
3
4
5
  
select top 100 a.D_ZDBH, a.住院号,D_KDCZYBH,D_KDCZYXM,D_ZXSJ
from [pyyzyy].dbo.YY_BRZY as a inner join [PYYZYY].dbo.JD_DCMX as b on a.D_ZDBH = b.D_DCBH
where D_LCBZ = 'C' and D_DCBH = 'ZY0000188841' and b.ID = 9927089

image-20220512213918692

修改之后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public Boolean updateAdviceToExecute(AdviceExecuteParam adviceExecuteParam) {
Object execute = mssqlDb.getMSSQLPro().execute((connection) -> {
CallableStatement cs = connection.prepareCall("{call [dbo].[YYHL_YZHX] (?,?,?,?,?,?,?,?)}");
cs.setObject(1, adviceExecuteParam.getPatientId());
cs.setObject(2, adviceExecuteParam.getInpNo());
cs.setObject(3, adviceExecuteParam.getVisitId());
cs.setObject(4, adviceExecuteParam.getOrderNo());
cs.setObject(5, adviceExecuteParam.getOperatorId());
cs.setObject(6, adviceExecuteParam.getOperatorName());
cs.setObject(7, adviceExecuteParam.getOperatorDate());
cs.registerOutParameter(8, Types.INTEGER);
cs.execute();
Boolean isSuccess = cs.getBoolean(8);
System.out.println(isSuccess);
cs.close();
return isSuccess;
});
return (Boolean) execute;
}

image-20220512214828041

三测单回写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
USE [pyyzyy]
GO
/****** Object: StoredProcedure [dbo].[YYHL_JFHX] Script Date: 05/07/2022 09:57:46 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[YYHL_SCDHX](@PATIENT_ID varchar(30),@WardCode varchar(30),@Temperature varchar(30),@TemperatureScore varchar(30),@Breath varchar(30), @Pulse varchar(30),@HeartRate varchar(30),@PhysicalCooling varchar(30),@Defecate varchar(30),@Urine varchar(30),@Weight varchar(30),@Height varchar(30),@BloodPressure varchar(30),@Totalinput varchar(30),@TotalOutput varchar(30),@DrugAllergy varchar(30),@PostoperativeDays varchar(30),@Interrupt varchar(30),@Superscript varchar(30),@Subscript varchar(30),@OperatorID varchar(30),@OPeratorDate varchar(30),@Surgerydate varchar(30),@Revise varchar(30),@RESULT int output)
as
begin
declare @error int =0
declare @D_ZDBH varchar(30)
declare @床号 varchar(30)
declare @测量日期 datetime
declare @呼吸3 varchar(30)
declare @呼吸7 varchar(30)
declare @呼吸11 varchar(30)
declare @呼吸15 varchar(30)
declare @呼吸19 varchar(30)
declare @呼吸23 varchar(30)
declare @脉搏3 int
declare @脉搏7 int
declare @脉搏11 int
declare @脉搏15 int
declare @脉搏19 int
declare @脉搏23 int
declare @心率3 int
declare @心率7 int
declare @心率11 int
declare @心率15 int
declare @心率19 int
declare @心率23 int
declare @体温3 money
declare @体温7 money
declare @体温11 money
declare @体温15 money
declare @体温19 money
declare @体温23 money
declare @体温类型3 varchar(30)
declare @体温类型7 varchar(30)
declare @体温类型11 varchar(30)
declare @体温类型15 varchar(30)
declare @体温类型19 varchar(30)
declare @体温类型23 varchar(30)
declare @物理降温3 money
declare @物理降温7 money
declare @物理降温11 money
declare @物理降温15 money
declare @物理降温19 money
declare @物理降温23 money
declare @上标说明3 varchar(30)
declare @上标说明7 varchar(30)
declare @上标说明11 varchar(30)
declare @上标说明15 varchar(30)
declare @上标说明19 varchar(30)
declare @上标说明23 varchar(30)
declare @下标说明3 varchar(30)
declare @下标说明7 varchar(30)
declare @下标说明11 varchar(30)
declare @下标说明15 varchar(30)
declare @下标说明19 varchar(30)
declare @下标说明23 varchar(30)
declare @大便次数 varchar(30)
declare @小便 varchar(30)
declare @体重 varchar(30)
declare @血压 varchar(30)
declare @总入量 varchar(30)
declare @总出量 varchar(30)
declare @药物过敏 varchar(30)
declare @术后天数 varchar(30)
declare @中断3 int
declare @中断7 int
declare @中断11 int
declare @中断15 int
declare @中断19 int
declare @中断23 int
declare @新生儿 int
declare @一次手术日期 datetime
declare @身高 varchar(30)
set @RESULT = '0'
set @D_ZDBH = @PATIENT_ID
select @床号 = 当前病床 from YY_BRZY where D_ZDBH = @D_ZDBH
if(@Revise = '1')
begin
select @床号=床号,@测量日期=测量日期,@呼吸3=呼吸3,@呼吸7=呼吸7,@呼吸11=呼吸11,@呼吸15=呼吸15,@呼吸19=呼吸19,@呼吸23=呼吸23,@脉搏3=脉搏3,@脉搏7=脉搏7,@脉搏11=脉搏11,@脉搏15=脉搏15,@脉搏19=脉搏19,@脉搏23=脉搏23,@心率3=心率3,@心率7=心率7,@心率11=心率11,@心率15=心率15,@心率19=心率19,@心率23=心率23,@体温3=体温3,@体温7=体温7,@体温11=体温11,@体温15=体温15,@体温19=体温19,@体温23=体温23,@体温类型3=体温类型3,@体温类型7=体温类型7,@体温类型11=体温类型11,@体温类型15=体温类型15,@体温类型19=体温类型19,@体温类型23=体温类型23,@物理降温3=物理降温3,@物理降温7=物理降温7,@物理降温11=物理降温11,@物理降温15=物理降温15,@物理降温19=物理降温19,@物理降温23=物理降温23,@上标说明3=上标说明3,@上标说明7=上标说明7,@上标说明11=上标说明11,@上标说明15=上标说明15,@上标说明19=上标说明19,@上标说明23=上标说明23,@下标说明3=下标说明3,@下标说明7=下标说明7,@下标说明11=下标说明11,@下标说明15=下标说明15,@下标说明19=下标说明19,@下标说明23=下标说明23,@大便次数=大便次数,@小便=小便,@体重=体重,@血压=血压,@总入量=总入量,@总出量=总出量,@药物过敏=药物过敏,@术后天数=术后天数,@中断3=中断3,@中断7=中断7,@中断11=中断11,@中断15=中断15,@中断19=中断19,@中断23=中断23,@新生儿=新生儿,@一次手术日期=一次手术日期,@身高=身高 from EmrNew.dbo.ThirdTest where D_ZDBH = @D_ZDBH and ID = @OperatorID
end
if(@OPeratorDate = '3')
begin
set @呼吸3 = @Breath
set @脉搏3 = CONVERT(int,@Pulse)
set @心率3 = CONVERT(int,@HeartRate)
set @体温3 = CONVERT(money,@TemperatureScore)
set @体温类型3 = @Temperature
set @物理降温3 = CONVERT(money,@PhysicalCooling)
set @上标说明3 = @Superscript
set @下标说明3 = @Subscript
set @中断3 = CONVERT(int,@Interrupt)
end
else if(@OPeratorDate = '7')
begin
set @呼吸7 = @Breath
set @脉搏7 = CONVERT(int,@Pulse)
set @心率7 = CONVERT(int,@HeartRate)
set @体温7 = CONVERT(money,@TemperatureScore)
set @体温类型7 = @Temperature
set @物理降温7 = CONVERT(money,@PhysicalCooling)
set @上标说明7 = @Superscript
set @下标说明7 = @Subscript
set @中断7 = CONVERT(int,@Interrupt)
end
else if(@OPeratorDate = '11')
begin
set @呼吸11 = @Breath
set @脉搏11 = CONVERT(int,@Pulse)
set @心率11 = CONVERT(int,@HeartRate)
set @体温11 = CONVERT(money,@TemperatureScore)
set @体温类型11 = @Temperature
set @物理降温11 = CONVERT(money,@PhysicalCooling)
set @上标说明11 = @Superscript
set @下标说明11 = @Subscript
set @中断11 = CONVERT(int,@Interrupt)
end
else if(@OPeratorDate = '15')
begin
set @呼吸15 = @Breath
set @脉搏15 = CONVERT(int,@Pulse)
set @心率15 = CONVERT(int,@HeartRate)
set @体温15 = CONVERT(money,@TemperatureScore)
set @体温类型15 = @Temperature
set @物理降温15 = CONVERT(money,@PhysicalCooling)
set @上标说明15 = @Superscript
set @下标说明15 = @Subscript
set @中断15 = CONVERT(int,@Interrupt)
end
else if(@OPeratorDate = '19')
begin
set @呼吸19 = @Breath
set @脉搏19 = CONVERT(int,@Pulse)
set @心率19 = CONVERT(int,@HeartRate)
set @体温19 = CONVERT(money,@TemperatureScore)
set @体温类型19 = @Temperature
set @物理降温19 = CONVERT(money,@PhysicalCooling)
set @上标说明19 = @Superscript
set @下标说明19 = @Subscript
set @中断19 = CONVERT(int,@Interrupt)
end
else if(@OPeratorDate = '23')
begin
set @呼吸23 = @Breath
set @脉搏23 = CONVERT(int,@Pulse)
set @心率23 = CONVERT(int,@HeartRate)
set @体温23 = CONVERT(money,@TemperatureScore)
set @体温类型23 = @Temperature
set @物理降温23 = CONVERT(money,@PhysicalCooling)
set @上标说明23 = @Superscript
set @下标说明23 = @Subscript
set @中断23 = CONVERT(int,@Interrupt)
end
set @测量日期 = getdate()
set @大便次数 = @Defecate
set @小便 = @Urine
set @体重 = @Weight
set @血压 = @BloodPressure
set @总入量 = @Totalinput
set @总出量 = @TotalOutput
set @药物过敏 = @DrugAllergy
set @术后天数 = @PostoperativeDays
set @一次手术日期 = CONVERT(Datetime,@Surgerydate)
set @身高 = @Height
begin transaction
if(@Revise = '0')
begin
insert into EmrNew.dbo.ThirdTest(D_ZDBH,床号,测量日期,呼吸3,呼吸7,呼吸11,呼吸15,呼吸19,呼吸23,脉搏3,脉搏7,脉搏11,脉搏15,脉搏19,脉搏23,心率3,心率7,心率11,心率15,心率19,心率23,体温3,体温7,体温11,体温15,体温19,体温23,体温类型3,体温类型7,体温类型11,体温类型15,体温类型19,体温类型23,物理降温3,物理降温7,物理降温11,物理降温15,物理降温19,物理降温23,上标说明3,上标说明7,上标说明11,上标说明15,上标说明19,上标说明23,下标说明3,下标说明7,下标说明11,下标说明15,下标说明19,下标说明23,大便次数,小便,体重,血压,总入量,总出量,药物过敏,术后天数,中断3,中断7,中断11,中断15,中断19,中断23,新生儿,一次手术日期,身高) values(@D_ZDBH,@床号,@测量日期,@呼吸3,@呼吸7,@呼吸11,@呼吸15,@呼吸19,@呼吸23,@脉搏3,@脉搏7,@脉搏11,@脉搏15,@脉搏19,@脉搏23,@心率3,@心率7,@心率11,@心率15,@心率19,@心率23,@体温3,@体温7,@体温11,@体温15,@体温19,@体温23,@体温类型3,@体温类型7,@体温类型11,@体温类型15,@体温类型19,@体温类型23,@物理降温3,@物理降温7,@物理降温11,@物理降温15,@物理降温19,@物理降温23,@上标说明3,@上标说明7,@上标说明11,@上标说明15,@上标说明19,@上标说明23,@下标说明3,@下标说明7,@下标说明11,@下标说明15,@下标说明19,@下标说明23,@大便次数,@小便,@体重,@血压,@总入量,@总出量,@药物过敏,@术后天数,@中断3,@中断7,@中断11,@中断15,@中断19,@中断23,@新生儿,@一次手术日期,@身高); set @OperatorID = @@identity;set @error+=@@ERROR;
end
else if(@Revise = '1')
begin
update EmrNew.dbo.ThirdTest set 床号=@床号,测量日期=@测量日期,呼吸3=@呼吸3,呼吸7=@呼吸7,呼吸11=@呼吸11,呼吸15=@呼吸15,呼吸19=@呼吸19,呼吸23=@呼吸23,脉搏3=@脉搏3,脉搏7=@脉搏7,脉搏11=@脉搏11,脉搏15=@脉搏15,脉搏19=@脉搏19,脉搏23=@脉搏23,心率3=@心率3,心率7=@心率7,心率11=@心率11,心率15=@心率15,心率19=@心率19,心率23=@心率23,体温3=@体温3,体温7=@体温7,体温11=@体温11,体温15=@体温15,体温19=@体温19,体温23=@体温23,体温类型3=@体温类型3,体温类型7=@体温类型7,体温类型11=@体温类型11,体温类型15=@体温类型15,体温类型19=@体温类型19,体温类型23=@体温类型23,物理降温3=@物理降温3,物理降温7=@物理降温7,物理降温11=@物理降温11,物理降温15=@物理降温15,物理降温19=@物理降温19,物理降温23=@物理降温23,上标说明3=@上标说明3,上标说明7=@上标说明7,上标说明11=@上标说明11,上标说明15=@上标说明15,上标说明19=@上标说明19,上标说明23=@上标说明23,下标说明3=@下标说明3,下标说明7=@下标说明7,下标说明11=@下标说明11,下标说明15=@下标说明15,下标说明19=@下标说明19,下标说明23=@下标说明23,大便次数=@大便次数,小便=@小便,体重=@体重,血压=@血压,总入量=@总入量,总出量=@总出量,药物过敏=@药物过敏,术后天数=@术后天数,中断3=@中断3,中断7=@中断7,中断11=@中断11,中断15=@中断15,中断19=@中断19,中断23=@中断23,新生儿=@新生儿,一次手术日期=@一次手术日期,身高=@身高 where D_ZDBH = @D_ZDBH and ID = @OperatorID
end
if(@error = 0)
begin
set @RESULT = @OperatorID;
end
else begin
set @RESULT = '0';
end
if(@error<>0)
begin
rollback transaction
end
else begin
commit transaction
end
Return @RESULT
end

/*三测单回写存储过程,增加Surgerydate一次手术日期,参数,增加Revise状态参数(0为新增,1为修改),OperatorID参数改为三测单ID,新增时返回Op Result值为三测单ID,修改时使用OperatorID参数传入三测单ID
declare @RESULT varchar(20)
exec YYHL_SCDHX @PATIENT_ID = 'ZYF98AE9C088',@WardCode = '101',@Temperature = '1',@TemperatureScore = '1',@Breath = '1',@Pulse = '1',@HeartRate = '1',@PhysicalCooling = '1',@Defecate = '1',@Urine = '1',@Weight = '1',@Height = '1',@BloodPressure = '1',@Totalinput = '1',@TotalOutput = '1',@DrugAllergy = '1',@PostoperativeDays = '1',@Interrupt = '1',@Superscript = '1',@Subscript = '1',@OperatorID = '182311',@OPeratorDate = '3',@Surgerydate = '2022-05-07 00:00:00',@Revise = '1',@RESULT = @RESULT output
select @RESULT
*/

新增

image-20220515161243128

三测单查询视图:

1
2
3
4
5
6
7
8
9
SELECT b.姓名,a.D_ZDBH as 账单编码, b.住院号, b.住院次数, b.收费类别, b.性别, b.年龄, b.入院科别代码, b.入院科别, b.疾病分类, c.Name AS 主治医生,
b.入院时间, a.床号, a.测量日期, a.呼吸3, a.呼吸7, a.呼吸11, a.呼吸15, a.呼吸19, a.呼吸23, a.脉搏3, a.脉搏7, a.脉搏11,
a.脉搏15, a.脉搏19, a.脉搏23, a.心率3, a.心率7, a.心率11, a.心率15, a.心率19, a.心率23, a.体温3, a.体温7, a.体温11,
a.体温15,a.体温19, a.体温23, a.体温类型3, a.体温类型7, a.体温类型11, a.体温类型15, a.体温类型19, a.体温类型23, a.物理降温3,
a.物理降温7, a.物理降温11, a.物理降温15, a.物理降温19, a.物理降温23,a.上标说明3, a.上标说明7, a.上标说明11, a.上标说明15,
a.上标说明19, a.上标说明23, a.下标说明3, a.下标说明7, a.下标说明11, a.下标说明15, a.下标说明19, a.下标说明23, a.大便次数,
a.小便, a.体重, a.血压, a.总入量, a.总出量, a.药物过敏, a.术后天数, a.中断3, a.中断7, a.中断11, a.中断15, a.中断19, a.中断23,
a.新生儿, a.一次手术日期, a.身高 FROM EmrNew.dbo.ThirdTest AS a
INNER JOIN [pyyzyy].dbo.YY_BRZY AS b ON a.D_ZDBH = b.D_ZDBH INNER JOIN [pyyzyy].dbo.Employe AS c ON b.主治医生工号 = c.UserCode

image-20220514204723405

三测单VO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package medical.appsupport.vo;

import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;

import java.io.Serializable;

@Data
@ToString
@Accessors(chain = true)
public class TemperatureMonitorParam implements Serializable {


// 病人id ZYF98AE9C088
String patientId;
// 科室代码
String deptCode;
//体温类型3
String temperature;
//体温3
String temperatureScore;
//呼吸3
String breath;
//脉搏3
String pulse;
//心率3
String heartRate;
//物理降温3
String physicalCooling;
//大便次数
String defecate;
//小便
String urine;
//体重
String weight;
//血压
String bloodPressure;
//总入量
String totalinput;
// 总出量
String totalOutput;
//药物过敏
String drugAllergy;
//术后天数
String postoperativeDays;
//中断
String interrupt;
//上标说明3
String superscript;
// 下标说明
String subscript;
//OperatorID参数改为三测单ID
String operatorId;
//操作时间
String operatorDate;
//一次手术日期
String surgeryDate;

//revise状态参数(0为新增,1为修改),
String revise;
//身高
String height;


}

医嘱视图:

1
2
3
4
5
6
7
8
9
SELECT     a.D_ZDBH AS PatientID, b.ID AS VISIT_ID, a.住院号 AS INP_NO, '' AS BABY_ID, b.D_HM AS ORDER_NO, b.D_PYH AS ORDER_NO_SUB, b.D_CDBH AS ORDER_CODE, b.D_CDMC AS ORDER_TEXT, 
'' AS WARD_CODE, b.D_KSKS AS DEPT_CODE, b.D_SPEC AS ITEM_SPEC, b.D_AREA AS ITEM_SPEC_ADDRESS, b.D_PC AS FREQUENCY, b.D_CYL AS DOSSAGE, b.D_XFSL AS DRUGAMOUNT,
b.D_CYLDW AS DOSAGE_UNITS, b.D_YF AS ADMINISTRATION, b.D_DEL AS ORDER_STATUS, b.D_XMBH AS ORDER_CLASS, (CASE D_LCBZ WHEN 'C' THEN '1' ELSE '0' END)
AS REPEAT_INDICATOR, '' AS HIGH, '' AS ADVICE_FREQUENCY, '' AS ADVICE_LASTTIME, (CASE WHEN d_yf LIKE '%皮试%' THEN '1' ELSE '0' END) AS IS_SKIN, '' AS IS_SELF,
b.D_XFRQ AS START_DATE_TIME, (CASE WHEN (D_TZRQ IS NULL OR
D_TZSJ IS NULL) THEN NULL ELSE CONVERT(datetime, CONVERT(varchar(10), D_TZRQ, 120) + ' ' + CONVERT(varchar(8), d_tzsj, 8), 120) END) AS STOP_DATE_TIME, b.D_QSBH AS DOCTOR_ID,
b.D_FSQM AS NURSE_ID, b.D_QRCZYBH AS VERIFYPASS_TIME, b.D_CZYBH AS STOP_DOCTOR_ID, b.D_Memo AS FREQ_DETAIL, b.D_JYBH AS APP_NO
FROM dbo.YY_BRZY AS a INNER JOIN
dbo.JD_DCMX AS b ON a.D_ZDBH = b.D_DCBH

皮试结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
USE [pyyzyy]
GO
/****** Object: StoredProcedure [dbo].[YYHL_PSJGHX] Script Date: 05/06/2022 08:54:48 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[YYHL_PSJGHX](@PATIENT_ID varchar(30),@VISIT_ID varchar(30),@ORDER_NO varchar(30),@OPERATOR_ID varchar(30),@HYPOTEST varchar(30),@RESULT int output)
as
begin
declare @error int =0
set @RESULT = '0'
begin transaction
update JD_DCMX set UserC10 = @HYPOTEST where D_DCBH = @PATIENT_ID and ID = @VISIT_ID;
set @error+=@@ERROR;
if(@error<>0)
begin
set @RESULT = '0';
rollback transaction
end
else begin
set @RESULT = '1';
commit transaction
end
Return @RESULT
end

/*皮试结果回写,增加VISIT_ID参数传递医嘱ID,皮试结果做下拉框暂列以下4项(免试,皮试,皮试 (-),皮试 (+)),皮试结果下拉框最好做成可配置以防将来有修改
declare @RESULT varchar(20)
exec YYHL_PSJGHX @PATIENT_ID = 'ZYF98AE9C088',@VISIT_ID = '9707626',@ORDER_NO = '10',@OPERATOR_ID ='032',@HYPOTEST = '皮试',@RESULT = @RESULT output
select @RESULT
*/

病人查询页面:

页面填写查询信息

查询相应的信息:

修改查询执行页面,输入需要查询的信息

输液扫描:

使用姓名获取当前的医嘱

扫码查询医嘱:

  • 扫描瓶签

  • 更新瓶签数据

  • 更新定时任务

  • 修改扫描关注信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"patientId":"ZY0000188906",
"deptCode":"101",
"temperature":"1",
"temperatureScore":"1",
"breath":"1",
"pulse":"1",
"heartRate":"1",
"physicalCooling":"1",
"defecate":"1",
"urine":"1",
"weight":"1",
"bloodPressure":"1",
"totalinput":"11",
"totalOutput":"",
"drugAllergy":"1",
"postoperativeDays":"",
"interrupt":"",
"superscript":"",
"subscript":"",
"operatorId":"187763",
"operatorDate":"3",
"surgeryDate":"2022-04-23",
"revise":"1",
"height":"2",
}

使用视图进行查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* @param temperatureMonitorParam 三测单数据
* @return
* @description: 更新三测单数据
* @date: 2022/5/15
*/
@Override
public String updateTemperatureMonitor(TemperatureMonitorParam temperatureMonitorParam) {
Object execute = mssqlDb.getMSSQLPro().execute((connection) -> {
CallableStatement cs = connection.prepareCall("{call [dbo].[YYHL_SCDHX] (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}");
cs.setObject(1, temperatureMonitorParam.getPatientId());
cs.setObject(2, temperatureMonitorParam.getDeptCode());
cs.setObject(3, temperatureMonitorParam.getTemperature());
cs.setObject(4, temperatureMonitorParam.getTemperatureScore());
cs.setObject(5, temperatureMonitorParam.getBreath());
cs.setObject(6, temperatureMonitorParam.getPulse());
cs.setObject(7, temperatureMonitorParam.getHeartRate());
cs.setObject(8, temperatureMonitorParam.getPhysicalCooling());
cs.setObject(9, temperatureMonitorParam.getDefecate());
cs.setObject(10, temperatureMonitorParam.getUrine());
cs.setObject(11, temperatureMonitorParam.getWeight());
cs.setObject(12, temperatureMonitorParam.getHeight());
cs.setObject(13, temperatureMonitorParam.getBloodPressure());
cs.setObject(14, temperatureMonitorParam.getTotalinput());
cs.setObject(15, temperatureMonitorParam.getTotalOutput());
cs.setObject(16, temperatureMonitorParam.getDrugAllergy());
cs.setObject(17, temperatureMonitorParam.getPostoperativeDays());
cs.setObject(18, temperatureMonitorParam.getInterrupt());
cs.setObject(19, temperatureMonitorParam.getSuperscript());
cs.setObject(20, temperatureMonitorParam.getSubscript());
cs.setObject(21, temperatureMonitorParam.getOperatorId());
cs.setObject(22, temperatureMonitorParam.getOperatorDate());
cs.setObject(23, temperatureMonitorParam.getSurgeryDate());
cs.setObject(24, temperatureMonitorParam.getRevise());
cs.registerOutParameter(25, Types.INTEGER);
cs.execute();
String isSuccess = cs.getString(25);
cs.close();
return isSuccess;
});
return (String) execute;
}