Commit cb9461de authored by 高兆晨's avatar 高兆晨

新增 -> 香山医院公众号相关接口

1.在李处理的一年内历史就诊记录的表中查询“公众号医院”的三个历史科室展示
2.从“hpgp_ks_hot”表中查询展示“公众号医院”的科室(热门科室由医院提供,目前暂时用门诊量最大的三个科室代替)
3.按照standard_dept(=选择/机器人推荐的科室)、hospital_code(=公众号医院)、rank_score(=0)查询科室,选择有号的进行展示(一个医院中有多个同名科室,如果有号的超过3个那也只展示3个)
parent 80faa58c
......@@ -80,5 +80,13 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--纳里平台接口调用SDK(公众号用户信息接口)-->
<dependency>
<groupId>com.ngarihealth</groupId>
<artifactId>openapi-sdk-java</artifactId>
<version>1.0-RELEASE</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/openapi-sdk-java-1.0-RELEASE.jar</systemPath>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -136,6 +136,10 @@ public interface SecurityConstants {
* openid
*/
String DETAILS_APPID = "appid";
/**
* 医院代码
*/
String HOSPITAL_CODE = "hospitalCode";
/**
* 租户ID 字段
*/
......
......@@ -27,6 +27,11 @@ public enum LoginTypeEnum {
*/
SSB("ssb", "随申办token"),
/**
* 公众号香山中医医院
*/
WOA_XSZY("woa_xszy", "香山中医医院公众号"),
/**
* H5登录
*/
......@@ -51,4 +56,13 @@ public enum LoginTypeEnum {
* 描述
*/
private final String description;
public static LoginTypeEnum match(String type) {
for (LoginTypeEnum loginType : LoginTypeEnum.values()) {
if (loginType.getType().equals(type)) {
return loginType;
}
}
return null;
}
}
package cn.sh.stc.sict.cloud.common.core.dto;
import lombok.Data;
/**
* 公众号-香山中医医院用户信息
*/
@Data
public class XSZYUserInfo {
/**
* 用户ID
*/
private String userId;
/**
* 就诊人ID
*/
private String mpiId;
/**
* 姓名
*/
private String patientName;
/**
* 证件号
*/
private String certificate;
/**
* 证件类型
*/
private String certificateType;
/**
* 手机号
*/
private String mobile;
/**
* 医院编码
*/
private String hospitalCode;
}
package cn.sh.stc.sict.cloud.common.core.util;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.sh.stc.sict.cloud.common.core.constant.Constant;
import cn.sh.stc.sict.cloud.common.core.dto.XSZYUserInfo;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import ngari.openapi.Client;
import ngari.openapi.JSONResponseBean;
import ngari.openapi.Request;
import ngari.openapi.Response;
/**
* 医院公众号工具类
* <p>
* 注意事项:
* 1.医院编码是否传递
* 2.手机号必须有,作为用户身份的唯一标识
*/
@Slf4j
@UtilityClass
public class WoaUtil {
// 香山中医医院公众号用户信息获取接口地址(纳里)
private final static String XSZY_USER_URL = "https://openapi.ngarihealth.com/openapi/gateway";
// 接口授权信息
private final static String APP_KEY = "ngari635f7f4b0e980e63";
private final static String APP_SECRET = "0e980e634196fcde";
// 获取用户信息服务ID
private final static String SERVICE_ID = "openapi.authenticationService";
// 获取用户信息方法
private final static String METHOD = "apiGetUserInfo";
// 响应头请求唯一ID
private final static String RESPONSE_HEADER_REQUEST_ID = "X-Ca-Request-Id";
// 香山中医医院编码
private final static String XSZY_HOSPITAL_CODE = "42502905200";
/**
* 获取用户信息
*
* @param ngCode 纳里授权码
* @return
*/
public XSZYUserInfo getXszyUserInfo(String ngCode) {
//如果开启加密,则必填
String encodingAesKey = "";
Client client = new Client(XSZY_USER_URL, APP_KEY, APP_SECRET, encodingAesKey);
try {
// 入参说明[授权码,是否回调,拓展参数]
Request request = new Request(SERVICE_ID, METHOD, ListUtil.toList(ngCode, false, null));
Response response = client.execute(request);
if (response.isSuccess()) {
JSONResponseBean result = response.getJsonResponseBean();
XSZYUserInfo xszyUserInfo = Convert.convert(XSZYUserInfo.class, result.getBody());
// 设置医院编码
xszyUserInfo.setHospitalCode(XSZY_HOSPITAL_CODE);
return xszyUserInfo;
} else {
log.error("香山中医医院公众号token获取用户信息异常-1,requestId = [{}],caError = [{}],error = [{}]", response.getHeader(RESPONSE_HEADER_REQUEST_ID), response.getCaErrorMsg(), response.getErrorMessage());
return null;
}
} catch (Exception ex) {
log.error("香山中医医院公众号token获取用户信息异-2!");
log.error(ex.getMessage(), ex);
return null;
}
}
}
......@@ -19,56 +19,57 @@ import java.util.Map;
* @Date
*/
public class SictUserAuthenticationConverter implements UserAuthenticationConverter {
private static final String N_A = "N/A";
private static final String N_A = "N/A";
/**
* Extract information about the user to be used in an access token (i.e. for resource servers).
*
* @param authentication an authentication representing a user
* @return a map of key values representing the unique information about the user
*/
@Override
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
Map<String, Object> response = new LinkedHashMap<>();
response.put(USERNAME, authentication.getName());
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
}
return response;
}
/**
* Extract information about the user to be used in an access token (i.e. for resource servers).
*
* @param authentication an authentication representing a user
* @return a map of key values representing the unique information about the user
*/
@Override
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
Map<String, Object> response = new LinkedHashMap<>();
response.put(USERNAME, authentication.getName());
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
}
return response;
}
/**
* Inverse of {@link #convertUserAuthentication(Authentication)}. Extracts an Authentication from a map.
*
* @param map a map of user information
* @return an Authentication representing the user or null if there is none
*/
@Override
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME)) {
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
/**
* Inverse of {@link #convertUserAuthentication(Authentication)}. Extracts an Authentication from a map.
*
* @param map a map of user information
* @return an Authentication representing the user or null if there is none
*/
@Override
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME)) {
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
String username = (String) map.get(USERNAME);
Long id = (Long) map.get(SecurityConstants.DETAILS_USER_ID);
String name = (String) map.get(SecurityConstants.DETAILS_NAME);
String openid = (String) map.get(SecurityConstants.DETAILS_OPENID);
String appid = (String) map.get(SecurityConstants.DETAILS_APPID);
SictUser user = new SictUser(id, "", openid, appid, username, N_A, true
, true, true, true, authorities);
return new UsernamePasswordAuthenticationToken(user, N_A, authorities);
}
return null;
}
String username = (String) map.get(USERNAME);
Long id = (Long) map.get(SecurityConstants.DETAILS_USER_ID);
String name = (String) map.get(SecurityConstants.DETAILS_NAME);
String openid = (String) map.get(SecurityConstants.DETAILS_OPENID);
String appid = (String) map.get(SecurityConstants.DETAILS_APPID);
String hospitalCode = (String) map.get(SecurityConstants.HOSPITAL_CODE);
SictUser user = new SictUser(id, "", openid, appid, hospitalCode, username, N_A, true
, true, true, true, authorities);
return new UsernamePasswordAuthenticationToken(user, N_A, authorities);
}
return null;
}
private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
Object authorities = map.get(AUTHORITIES);
if (authorities instanceof String) {
return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
}
if (authorities instanceof Collection) {
return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
.collectionToCommaDelimitedString((Collection<?>) authorities));
}
throw new IllegalArgumentException("Authorities must be either a String or a Collection");
}
private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
Object authorities = map.get(AUTHORITIES);
if (authorities instanceof String) {
return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
}
if (authorities instanceof Collection) {
return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
.collectionToCommaDelimitedString((Collection<?>) authorities));
}
throw new IllegalArgumentException("Authorities must be either a String or a Collection");
}
}
......@@ -35,6 +35,13 @@ public class SictUser extends User {
@Setter
private String appId;
/**
* 医院公众号登陆的用户包含医院代码
*/
@Getter
@Setter
private String hospitalCode;
@Getter
@Setter
private transient String token;
......@@ -44,6 +51,7 @@ public class SictUser extends User {
* {@link DaoAuthenticationProvider}.
*
* @param id 用户ID
* @param hospitalCode 医院代码
* @param username the username presented to the
* <code>DaoAuthenticationProvider</code>
* @param password the password that should be presented to the
......@@ -62,6 +70,7 @@ public class SictUser extends User {
String phone,
String openId,
String appId,
String hospitalCode,
String username,
String password,
boolean enabled,
......@@ -74,5 +83,6 @@ public class SictUser extends User {
this.phone = phone;
this.openId = openId;
this.appId = appId;
this.hospitalCode = hospitalCode;
}
}
......@@ -129,15 +129,15 @@ public class SictUserDetailsServiceImpl implements SictUserDetailsService {
if (StrUtil.isBlank(user.getUserName())) {
user.setUserName(user.getId().toString());
}
String userName = user.getUserName() + StringPool.COLON + info.getAppId();
String password = SecurityConstants.BCRYPT + user.getPasswd();
// 登录失败5次锁定半小时
boolean nonLockd = !Constant.BYTE_YES.equals(user.getStatus());
return new SictUser(user.getId(), user.getPhone(), info.getOpenId(), info.getAppId(), userName, password, enabled,
return new SictUser(user.getId(), user.getPhone(), info.getOpenId(), info.getAppId(), info.getHospitalCode(), userName, password, enabled,
true, true, nonLockd, authorities);
}
private static String getAppId() {
String appId = "";
try {
......
......@@ -179,6 +179,7 @@ public class SecurityUtils {
current.setOpenId(user.getOpenId());
current.setAppId(user.getAppId());
current.setPhone(user.getPhone());
current.setHospitalCode(user.getHospitalCode());
}
current.setToken(token);
return current;
......
......@@ -171,6 +171,9 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
......
......@@ -19,6 +19,10 @@ public class CurrentUser implements Serializable {
private String appId;
private String idNum;
private String phone;
/**
* 医院公众号登陆的用户包含医院代码
*/
private String hospitalCode;
private Date createTime;
private Byte gender;
private Date registerTime;
......
......@@ -14,6 +14,11 @@ public class UserInfo {
private String openId;
private String appId;
/**
* 医院公众号登陆的用户包含医院代码
*/
private String hospitalCode;
/**
* 用户基本信息
*/
......
......@@ -10,8 +10,10 @@ import cn.sh.stc.sict.cloud.common.core.constant.RedisCacheConstant;
import cn.sh.stc.sict.cloud.common.core.constant.UserConstant;
import cn.sh.stc.sict.cloud.common.core.constant.enums.LoginTypeEnum;
import cn.sh.stc.sict.cloud.common.core.dto.WDUserInfo;
import cn.sh.stc.sict.cloud.common.core.dto.XSZYUserInfo;
import cn.sh.stc.sict.cloud.common.core.util.NumberUtil;
import cn.sh.stc.sict.cloud.common.core.util.SsbUtil;
import cn.sh.stc.sict.cloud.common.core.util.WoaUtil;
import cn.sh.stc.sict.cloud.upms.dao.SysUserBaseMapper;
import cn.sh.stc.sict.cloud.upms.dto.UserDTO;
import cn.sh.stc.sict.cloud.upms.dto.UserInfo;
......@@ -35,6 +37,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
......@@ -77,18 +80,81 @@ public class SysUserBaseServiceImpl extends ServiceImpl<SysUserBaseMapper, SysUs
log.error("=================> appId = {}, inStr = {}", appId, inStr);
// code = 011Eg6Ga1UOXeA0R5wHa16dkab2Eg6G7
String[] split = inStr.split(StringPool.AT);
if (LoginTypeEnum.SMS.getType().equals(split[0])) {
LoginTypeEnum loginType = Optional.ofNullable(LoginTypeEnum.match(split[0])).orElse(LoginTypeEnum.WECHAT);
switch (loginType) {
// 手机号登录
return this.getByPhone(split[2]);
} else if (LoginTypeEnum.SSB.getType().equals(split[0])) {
return this.getBySSBToken(split[0], split[2]);
} else {
case SMS:
return this.getByPhone(split[2]);
// 随身办登陆
case SSB:
return this.getBySSBToken(split[0], split[2]);
// 香山中医院公众号登陆
// inStr = 公众号标识符@公众号appId@token@hospitalCode
case WOA_XSZY:
return this.getByXszyWoa(split);
// 微信登录
WxOAuth2AccessToken token = wxMpService.getOAuth2Service().getAccessToken(split[2]);
return this.getByOpenId(token.getOpenId());
default:
WxOAuth2AccessToken token = wxMpService.getOAuth2Service().getAccessToken(split[2]);
return this.getByOpenId(token.getOpenId());
}
}
/**
* 获取香山中医医院公众号的用户信息
*
* @param infoArray 登陆信息 ["公众号标识符", "公众号appId", "token"]
* @return
*/
private UserInfo getByXszyWoa(String[] infoArray) {
String source = infoArray[0];
String token = infoArray[2];
XSZYUserInfo xszyUserInfo = WoaUtil.getXszyUserInfo(token);
log.error("woa.login.xszyUser = {}", JSONUtil.toJsonStr(xszyUserInfo));
if (ObjectUtil.isNull(xszyUserInfo) || StrUtil.isBlank(xszyUserInfo.getUserId())) {
return null;
}
// 根据用户ID和手机号查询当前用户是否已注册过
LambdaQueryWrapper<SysUserBase> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUserBase::getOpenId, xszyUserInfo.getUserId())
.or()
.eq(SysUserBase::getPhone, xszyUserInfo.getMobile())
.orderByDesc(SysUserBase::getUpdateTime)
.last("limit 1");
SysUserBase user = this.getOne(wrapper);
// 未注册用户默认注册
if (user == null) {
user = new SysUserBase();
user.setUserName(xszyUserInfo.getMobile());
user.setOpenId(xszyUserInfo.getUserId());
user.setName(xszyUserInfo.getPatientName());
// 证件类型为1时,证件号为身份证号
if (Constant.STRING_YES.equals(xszyUserInfo.getCertificateType())) {
user.setIdCard(xszyUserInfo.getCertificate());
}
//user.setSex(userInfo.getGender());
user.setHeadimg(Constant.DEFAULT_AVATAR_IMG);
user.setSource(source);
user.setStatus(Constant.BYTE_NO);
user.setPasswd(ENCODER.encode(Constant.DEFAULT_PASSWORD));
user.setPhone(xszyUserInfo.getMobile());
this.save(user);
}
UserInfo info = new UserInfo();
info.setSysUserBase(user);
info.setOpenId(xszyUserInfo.getUserId());
// 设置香山中医医院医疗机构代码,后续查询只显示当前医疗机构下的查询结果
info.setHospitalCode(xszyUserInfo.getHospitalCode());
return info;
}
private UserInfo getBySSBToken(String source, String token) {
WDUserInfo wdUser = SsbUtil.getUserInfo(token);
log.error("ssb.login.wdUser = {}", JSONUtil.toJsonStr(wdUser));
......
......@@ -4,25 +4,22 @@ package cn.sh.stc.sict.theme.hpgp.controller.mobile;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.sh.stc.sict.cloud.common.core.util.R;
import cn.sh.stc.sict.cloud.common.security.util.SecurityUtils;
import cn.sh.stc.sict.cloud.upms.dto.CurrentUser;
import cn.sh.stc.sict.theme.hpgp.model.HpgpBusyIdlePrediction;
import cn.sh.stc.sict.theme.hpgp.service.HpHzjlService;
import cn.sh.stc.sict.theme.hphy.model.HpDocInfo;
import cn.sh.stc.sict.theme.hphy.model.HphyPatientBase;
import cn.sh.stc.sict.theme.hphy.service.HphyPatientBaseService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import cn.sh.stc.sict.cloud.common.core.util.R;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cn.sh.stc.sict.theme.hpgp.model.HpHzjl;
import cn.sh.stc.sict.theme.hpgp.service.HpHzjlService;
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.List;
/**
......@@ -56,7 +53,7 @@ public class HpHzjlController {
log.error("patient = {}", JSONUtil.toJsonStr(patient));
return new R().error("患者信息异常!");
}
List<HpgpBusyIdlePrediction> result = hpHzjlService.listByCertId(patient.getCertId());
List<HpgpBusyIdlePrediction> result = hpHzjlService.listByCertIdAndHosCode(patient.getCertId(), current.getHospitalCode());
return new R(result);
}
......@@ -75,7 +72,7 @@ public class HpHzjlController {
return new R().error("患者信息异常!");
}
List<HpDocInfo> result = hpHzjlService.listDoctorByCertId(patient.getCertId());
List<HpDocInfo> result = hpHzjlService.listDoctorByCertIdAndHosCode(patient.getCertId(), current.getHospitalCode());
return new R(result);
}
}
......@@ -2,11 +2,13 @@ package cn.sh.stc.sict.theme.hpgp.controller.mobile;
import cn.sh.stc.sict.cloud.common.core.util.R;
import cn.sh.stc.sict.cloud.common.security.util.SecurityUtils;
import cn.sh.stc.sict.theme.hpgp.service.HpgpBusyIdlePredictionService;
import com.baomidou.mybatisplus.extension.api.ApiController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
......@@ -18,6 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
* @author zhangly
* @since 2022-01-14 09:56:44
*/
@Slf4j
@Api(value = "[C] 黄浦全科导诊——忙闲预测维护接口", tags = "[C] 黄浦全科导诊——忙闲预测维护接口")
@RestController
@RequestMapping("/hpgp/c/busyidleprediction")
......@@ -29,8 +32,8 @@ public class HpgpBusyIdlePredictionController extends ApiController {
private final HpgpBusyIdlePredictionService hpgpBusyIdlePredictionService;
@GetMapping
public R<?> busyIdlePrediction(@ApiParam("标准科室名称") @RequestParam("deptName") String deptName){
return new R<>().success(hpgpBusyIdlePredictionService.busyIdlePrediction(deptName));
public R<?> busyIdlePrediction(@ApiParam("标准科室名称") @RequestParam("deptName") String deptName) {
return new R<>().success(hpgpBusyIdlePredictionService.busyIdlePrediction(SecurityUtils.getCurrentUser().getHospitalCode(), deptName));
}
}
......@@ -16,9 +16,13 @@ import java.util.List;
public interface HpgpDepartmentRankMapper extends BaseMapper<HpgpDepartmentRank> {
/**
* 根据标准科室名称查询医院科室排名
* @param deptName 标准科室名称
* @param size 查询记录数 默认3条
*
* @param hospitalCode 医疗机构代码
* @param deptName 标准科室名称
* @param size 查询记录数 默认3条
* @return
*/
List<DeptRankVO> getRankByStandardDept(@Param("deptName") String deptName, @Param("size") int size);
List<DeptRankVO> getRankByHosAndStandardDept(@Param("hospitalCode") String hospitalCode,
@Param("deptName") String deptName,
@Param("size") int size);
}
package cn.sh.stc.sict.theme.hpgp.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import cn.sh.stc.sict.theme.hpgp.model.HpgpKsHot;
/**
* (HpgpKsHot)表数据库访问层
*
* @author gao
* @since 2022-11-07 10:33:21
*/
public interface HpgpKsHotMapper extends BaseMapper<HpgpKsHot> {
}
package cn.sh.stc.sict.theme.hpgp.model;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* (HpgpKsHot)表实体类
*
* @author makejava
* @since 2022-11-07 10:33:21
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class HpgpKsHot extends Model<HpgpKsHot> {
//医疗结构代码(ALL表示全区)
@ApiModelProperty(hidden=false, value="医疗结构代码(ALL表示全区)")
private String gzh;
@ApiModelProperty(hidden=false, value="标准科室")
private String standardDept;
@ApiModelProperty(hidden=false, value="医院代码")
private String hospitalCode;
@ApiModelProperty(hidden=false, value="医院名称")
private String hospitalName;
@ApiModelProperty(hidden=false, value="一级科室代码")
private String oneDeptCode;
@ApiModelProperty(hidden=false, value="科室代码")
private String deptCode;
@ApiModelProperty(hidden=false, value="科室名称")
private String deptName;
//科室排名
@ApiModelProperty(hidden=false, value="科室排名")
private byte rankScore;
}
package cn.sh.stc.sict.theme.hpgp.service;
import cn.sh.stc.sict.theme.hpgp.model.HpHzjl;
import cn.sh.stc.sict.theme.hpgp.model.HpgpBusyIdlePrediction;
import cn.sh.stc.sict.theme.hphy.model.HpDocInfo;
import com.baomidou.mybatisplus.extension.service.IService;
import cn.sh.stc.sict.theme.hpgp.model.HpHzjl;
import java.util.List;
......@@ -15,7 +15,22 @@ import java.util.List;
*/
public interface HpHzjlService extends IService<HpHzjl> {
List<HpgpBusyIdlePrediction> listByCertId(String certId);
/**
* 根据身份证号和医院查找最后三次不同科室就诊记录,并计算忙闲
* 如果未找到,则推送三个热门科室
*
* @param certId 身份证号
* @param hospCode 医疗机构代码(医院编码)
* @return
*/
List<HpgpBusyIdlePrediction> listByCertIdAndHosCode(String certId,
String hospCode);
List<HpDocInfo> listDoctorByCertId(String certId);
/**
* @param certId 身份证号
* @param hospCode 医疗机构代码(医院编码)
* @return
*/
List<HpDocInfo> listDoctorByCertIdAndHosCode(String certId,
String hospCode);
}
......@@ -19,10 +19,14 @@ public interface HpgpBusyIdlePredictionService extends IService<HpgpBusyIdlePred
/**
* 忙闲预测
* @param deptName 标准科室名称
* 按科室名称查询全区排名前3的科室
* 查询当前医院有号的3个科室
*
* @param hospitalCode 医疗结构代码(医院编码)
* @param deptName 标准科室名称
* @return 忙闲医院科室
*/
List<HpgpBusyIdlePrediction> busyIdlePrediction(String deptName);
List<HpgpBusyIdlePrediction> busyIdlePrediction(String hospitalCode, String deptName);
void statisticResourceInfo(HpgpDepartmentRank dept, Date startDate, Date endDate, Byte flag);
......
package cn.sh.stc.sict.theme.hpgp.service;
import cn.sh.stc.sict.theme.hpgp.model.HpgpKsHot;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* (HpgpKsHot)表服务接口
*
* @author gao
* @since 2022-11-07 10:33:21
*/
public interface HpgpKsHotService extends IService<HpgpKsHot> {
/**
* 根据医疗机构代码查询热门科室
*
* @param yljgdm 医疗机构代码
* @return
*/
List<HpgpKsHot> listHotDept(String yljgdm);
}
......@@ -4,20 +4,23 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.sh.stc.sict.theme.hpgp.dao.HpHzjlMapper;
import cn.sh.stc.sict.theme.hpgp.model.HpHzjl;
import cn.sh.stc.sict.theme.hpgp.model.HpgpBusyIdlePrediction;
import cn.sh.stc.sict.theme.hpgp.model.HpgpDepartmentRank;
import cn.sh.stc.sict.theme.hpgp.model.HpgpKsHot;
import cn.sh.stc.sict.theme.hpgp.service.HpHzjlService;
import cn.sh.stc.sict.theme.hpgp.service.HpgpBusyIdlePredictionService;
import cn.sh.stc.sict.theme.hpgp.service.HpgpDepartmentRankService;
import cn.sh.stc.sict.theme.hpgp.service.HpgpKsHotService;
import cn.sh.stc.sict.theme.hphy.model.HpDocInfo;
import cn.sh.stc.sict.theme.hphy.model.HphyDoctor;
import cn.sh.stc.sict.theme.hphy.service.HpDocInfoService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.sh.stc.sict.theme.hpgp.dao.HpHzjlMapper;
import cn.sh.stc.sict.theme.hpgp.model.HpHzjl;
import cn.sh.stc.sict.theme.hpgp.service.HpHzjlService;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
......@@ -36,22 +39,26 @@ public class HpHzjlServiceImpl extends ServiceImpl<HpHzjlMapper, HpHzjl> impleme
private final HpgpDepartmentRankService hpgpDepartmentRankService;
private final HpgpBusyIdlePredictionService hpgpBusyIdlePredictionService;
private final HpDocInfoService hpDocInfoService;
private final HpgpKsHotService hpgpKsHotService;
/**
* 根据身份证号查找最后三次不同科室就诊记录,并计算忙闲
* 如果未找到,则推送三个热门科室
*
* @param certId
* @param certId 身份证号
* @param hospitalCode 医疗机构代码(医院编码)
* @return
*/
@Override
public List<HpgpBusyIdlePrediction> listByCertId(String certId) {
public List<HpgpBusyIdlePrediction> listByCertIdAndHosCode(String certId,
String hospitalCode) {
List<HpgpBusyIdlePrediction> result = new ArrayList<>();
DateTime startTime = DateUtil.tomorrow();
DateTime endTime = DateUtil.offsetDay(startTime, 6);
LambdaQueryWrapper<HpHzjl> wrapper = Wrappers.lambdaQuery();
wrapper.eq(HpHzjl::getZjhm, certId)
.eq(StrUtil.isNotBlank(hospitalCode), HpHzjl::getYljgdm, hospitalCode)
.groupBy(HpHzjl::getJzksbm)
.orderByDesc(HpHzjl::getJzksrq)
.last("limit 10");
......@@ -70,9 +77,13 @@ public class HpHzjlServiceImpl extends ServiceImpl<HpHzjlMapper, HpHzjl> impleme
result.addAll(hpgpBusyIdlePredictionService.listByDeptRank(dept, startTime, endTime));
}
}
// 历史科室查询结果为空时,采用热点科室数据
if (CollUtil.isEmpty(result)) {
List<HpgpDepartmentRank> rankList = hpgpDepartmentRankService.listHotDept();
rankList.forEach(rank -> {
List<HpgpKsHot> hotList = hpgpKsHotService.listHotDept(hospitalCode);
hotList.forEach(hot -> {
HpgpDepartmentRank rank = new HpgpDepartmentRank();
BeanUtils.copyProperties(hot, rank);
result.addAll(hpgpBusyIdlePredictionService.listByDeptRank(rank, startTime, endTime));
});
}
......@@ -81,9 +92,11 @@ public class HpHzjlServiceImpl extends ServiceImpl<HpHzjlMapper, HpHzjl> impleme
}
@Override
public List<HpDocInfo> listDoctorByCertId(String certId) {
public List<HpDocInfo> listDoctorByCertIdAndHosCode(String certId,
String hospitalCode) {
LambdaQueryWrapper<HpHzjl> wrapper = Wrappers.lambdaQuery();
wrapper.eq(HpHzjl::getZjhm, certId)
.eq(StrUtil.isNotBlank(hospitalCode), HpHzjl::getYljgdm, hospitalCode)
.groupBy(HpHzjl::getYljgdm, HpHzjl::getJzksbm, HpHzjl::getZzysgh)
.orderByDesc(HpHzjl::getJzksrq)
.last("limit 10");
......@@ -97,7 +110,7 @@ public class HpHzjlServiceImpl extends ServiceImpl<HpHzjlMapper, HpHzjl> impleme
.eq(HpDocInfo::getResourceName, r.getZzysxm())
.last("limit 1");
HpDocInfo one = hpDocInfoService.getOne(dw);
if(ObjectUtil.isNotNull(one)){
if (ObjectUtil.isNotNull(one)) {
doctorList.add(one);
}
});
......
......@@ -22,7 +22,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.*;
......@@ -42,24 +41,85 @@ public class HpgpBusyIdlePredictionServiceImpl extends ServiceImpl<HpgpBusyIdleP
private final HpDocInfoService hpDocInfoService;
@Override
public List<HpgpBusyIdlePrediction> busyIdlePrediction(String deptName) {
// 查询科室排名
// 组装医院代码、科室代码
// 查询医院忙闲
List<DeptRankVO> rankList = departmentRankMapper.getRankByStandardDept(deptName, 3);
public List<HpgpBusyIdlePrediction> busyIdlePrediction(String hospitalCode,
String deptName) {
// 查询当前医院有号的三个科室
if (StrUtil.isNotBlank(hospitalCode)) {
return busyIdlePredictionByHosCodeAndDeptName(hospitalCode, deptName);
}
// 在科室排名表中查询排名前三的科室展示
return busyIdlePredictionByDeptName(deptName);
}
/**
* 按科室名称查询全区排名前3的科室
*
* @param deptName 科室名称
* @return HpgpBusyIdlePrediction {@link HpgpBusyIdlePrediction}
*/
private List<HpgpBusyIdlePrediction> busyIdlePredictionByDeptName(String deptName) {
List<DeptRankVO> rankList = departmentRankMapper.getRankByHosAndStandardDept(null, deptName, 3);
if (CollectionUtil.isEmpty(rankList)) {
return null;
}
return busyIdlePrediction(rankList);
}
/**
* 查询当前医院有号的三个科室
*
* @param hospitalCode 医院编码
* @param deptName 部门名称
* @return HpgpBusyIdlePrediction {@link HpgpBusyIdlePrediction}
*/
private List<HpgpBusyIdlePrediction> busyIdlePredictionByHosCodeAndDeptName(String hospitalCode,
String deptName) {
List<DeptRankVO> rankList = departmentRankMapper.getRankByHosAndStandardDept(hospitalCode, deptName, 0);
if (CollectionUtil.isEmpty(rankList)) {
return null;
}
List<HpgpBusyIdlePrediction> hpgpBusyIdlePredictionList = busyIdlePrediction(rankList);
// 当前医院近七天有号的三个科室
List<String> deptCodeList = hpgpBusyIdlePredictionList.stream()
// 按科室分组,并统计每个科室可预约号源总量
.collect(Collectors.groupingBy(HpgpBusyIdlePrediction::getDeptCode,
Collectors.summingInt(item -> Optional.ofNullable(item.getRemainNum()).orElse(0))))
.entrySet().stream()
// 按可预约号源进行排序,取排名前三的科室
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.map(Map.Entry::getKey)
.limit(3)
.collect(Collectors.toList());
return hpgpBusyIdlePredictionList.stream().filter(item -> deptCodeList.contains(item.getDeptCode())).collect(Collectors.toList());
}
/**
* 按医院和科室详情查询近一周各科室忙闲情况
*
* @param deptList 科室集
* @return HpgpBusyIdlePrediction {@link HpgpBusyIdlePrediction}
*/
private List<HpgpBusyIdlePrediction> busyIdlePrediction(List<DeptRankVO> deptList) {
DateTime startTime = DateUtil.tomorrow();
DateTime endTime = DateUtil.offsetDay(startTime, 6);
List<HpgpBusyIdlePrediction> result = new ArrayList<>();
rankList.forEach(rank -> {
deptList.forEach(rank -> {
// 查询医院忙闲
result.addAll(this.listByDeptRank(rank, startTime, endTime));
});
return result;
}
private HpgpBusyIdlePrediction getNoneSourcePredictionInfo(HpgpDepartmentRank rank, Date i) {
HpgpBusyIdlePrediction vo = new HpgpBusyIdlePrediction();
vo.setDeptCode(rank.getDeptCode());
......
package cn.sh.stc.sict.theme.hpgp.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.sh.stc.sict.theme.hpgp.dao.HpgpKsHotMapper;
import cn.sh.stc.sict.theme.hpgp.model.HpgpKsHot;
import cn.sh.stc.sict.theme.hpgp.service.HpgpKsHotService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* (HpgpKsHot)表服务实现类
*
* @author gao
* @since 2022-11-07 10:33:21
*/
@Service("hpgpKsHotService")
public class HpgpKsHotServiceImpl extends ServiceImpl<HpgpKsHotMapper, HpgpKsHot> implements HpgpKsHotService {
@Override
public List<HpgpKsHot> listHotDept(String yljgdm) {
LambdaQueryWrapper<HpgpKsHot> wrapper = Wrappers.lambdaQuery();
wrapper.eq(HpgpKsHot::getGzh, StrUtil.isBlank(yljgdm) ? "ALL" : yljgdm)
.orderByAsc(HpgpKsHot::getRankScore)
.last("limit 3");
return this.list(wrapper);
}
}
......@@ -8,9 +8,11 @@ import cn.sh.stc.sict.cloud.common.core.constant.Constant;
import cn.sh.stc.sict.cloud.common.core.constant.enums.LoginTypeEnum;
import cn.sh.stc.sict.cloud.common.core.dto.WDUserCardInfo;
import cn.sh.stc.sict.cloud.common.core.dto.WDUserInfo;
import cn.sh.stc.sict.cloud.common.core.dto.XSZYUserInfo;
import cn.sh.stc.sict.cloud.common.core.util.NumberUtil;
import cn.sh.stc.sict.cloud.common.core.util.R;
import cn.sh.stc.sict.cloud.common.core.util.SsbUtil;
import cn.sh.stc.sict.cloud.common.core.util.WoaUtil;
import cn.sh.stc.sict.cloud.common.log.annotation.SysLog;
import cn.sh.stc.sict.cloud.common.security.util.SecurityUtils;
import cn.sh.stc.sict.cloud.upms.dto.CurrentUser;
......@@ -143,7 +145,7 @@ public class HphyPatientBaseController {
// 根据手机号去重
if (ObjectUtil.isNull(base) || NumberUtil.isNullOrZero(base.getId())) {
base = hphyPatientBaseService.getByPhone(current.getPhone(), true);
if(NumberUtil.isNotNullOrZero(base.getId())){
if (ObjectUtil.isNotNull(base) && NumberUtil.isNotNullOrZero(base.getId())) {
HphyPatientBase update = new HphyPatientBase();
update.setOpenId(current.getOpenId());
update.setId(base.getId());
......@@ -151,6 +153,7 @@ public class HphyPatientBaseController {
}
}
if (ObjectUtil.isNull(base) || NumberUtil.isNullOrZero(base.getId())) {
// 随身办
if (LoginTypeEnum.SSB.getType().equals(source)) {
WDUserInfo userInfo = SsbUtil.getUserInfo(token);
log.error("wdUser = {}", userInfo);
......@@ -158,6 +161,12 @@ public class HphyPatientBaseController {
base = hphyPatientBaseService.saveSSbInfo(current, userInfo, cardList);
hpPatientCardService.save(base, cardList);
}
// 香山中医医院公众号
if (LoginTypeEnum.WOA_XSZY.getType().equals(source)) {
XSZYUserInfo xszyUserInfo = WoaUtil.getXszyUserInfo(token);
log.error("woaXszyUser = {}", xszyUserInfo);
base = hphyPatientBaseService.saveWoaXszyInfo(current, xszyUserInfo);
}
}
if (ObjectUtil.isNull(base) || NumberUtil.isNullOrZero(base.getId())) {
......
......@@ -2,6 +2,7 @@ package cn.sh.stc.sict.theme.hphy.service;
import cn.sh.stc.sict.cloud.common.core.dto.WDUserCardInfo;
import cn.sh.stc.sict.cloud.common.core.dto.WDUserInfo;
import cn.sh.stc.sict.cloud.common.core.dto.XSZYUserInfo;
import cn.sh.stc.sict.cloud.upms.dto.CurrentUser;
import com.baomidou.mybatisplus.extension.service.IService;
import cn.sh.stc.sict.theme.hphy.model.HphyPatientBase;
......@@ -21,6 +22,15 @@ public interface HphyPatientBaseService extends IService<HphyPatientBase> {
HphyPatientBase saveSSbInfo(CurrentUser current, WDUserInfo userInfo, List<WDUserCardInfo> cardList);
/**
* 保存香山中医医院公众号用户
*
* @param current 当前用户
* @param userInfo 公众号用户详情
* @return XSZYUserInfo {@link XSZYUserInfo}
*/
HphyPatientBase saveWoaXszyInfo(CurrentUser current, XSZYUserInfo userInfo);
void savePatient(HphyPatientBase patient);
HphyPatientBase getCurrentBase();
......
......@@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.sh.stc.sict.cloud.common.core.constant.Constant;
import cn.sh.stc.sict.cloud.common.core.dto.WDUserCardInfo;
import cn.sh.stc.sict.cloud.common.core.dto.WDUserInfo;
import cn.sh.stc.sict.cloud.common.core.dto.XSZYUserInfo;
import cn.sh.stc.sict.cloud.common.core.util.NumberUtil;
import cn.sh.stc.sict.cloud.common.security.util.SecurityUtils;
import cn.sh.stc.sict.cloud.upms.dto.CurrentUser;
......@@ -69,6 +70,39 @@ public class HphyPatientBaseServiceImpl extends ServiceImpl<HphyPatientBaseMappe
return null;
}
/**
* 保存香山中医医院公众号用户
*
* @param current 当前用户
* @param userInfo 公众号用户详情
* @return XSZYUserInfo {@link XSZYUserInfo}
*/
@Override
public HphyPatientBase saveWoaXszyInfo(CurrentUser current, XSZYUserInfo userInfo) {
if (ObjectUtil.isNotNull(userInfo)) {
HphyPatientBase base = new HphyPatientBase();
base.setUserId(current.getId());
base.setOpenId(current.getOpenId());
base.setPhone(userInfo.getMobile());
base.setName(userInfo.getPatientName());
if (Constant.STRING_YES.equals(userInfo.getCertificateType())) {
base.setCertId(userInfo.getCertificate());
}
base.setVerifyStatus(Constant.STRING_YES);
// 身份证号合法,设置年龄、性别、实名认证标志
if (IdcardUtil.isValidCard(base.getCertId())) {
String certId = base.getCertId();
base.setAge(String.valueOf(IdcardUtil.getAgeByIdCard(certId)));
base.setGender(Constant.INT_YES.equals(IdcardUtil.getGenderByIdCard(certId)) ? "男" : "女");
}
this.save(base);
return base;
}
return null;
}
@Override
public void savePatient(HphyPatientBase patient) {
HphyPatientBase base = this.getByCertId(patient.getCertId());
......
......@@ -13,7 +13,7 @@
<sql id="Base_Column_List"> standard_dept , hospital_code, hospital_name, dept_code, dept_name, rank_score </sql>
<select id="getRankByStandardDept" resultType="cn.sh.stc.sict.theme.hpgp.vo.DeptRankVO">
<select id="getRankByHosAndStandardDept" resultType="cn.sh.stc.sict.theme.hpgp.vo.DeptRankVO">
select dr.standard_dept standardDept,
dr.hospital_code hospitalCode,
dr.hospital_name hospitalName,
......@@ -22,10 +22,25 @@
dr.rank_score rankScore,
d.one_dept_code oneDeptCode,
d.dept_code subDeptCode
from hpgp_department_rank dr
join hp_dept_info d on (dr.hospital_code = d.hos_org_code and dr.one_dept_code = d.one_dept_code and dr.dept_code = d.dept_code)
where dr.standard_dept = #{deptName}
from hpgp_department_rank_1028 dr
join hp_dept_info_copy1 d on (dr.hospital_code = d.hos_org_code and dr.one_dept_code = d.one_dept_code and dr.dept_code = d.dept_code)
<where>
<choose>
<when test="hospitalCode != null and hospitalCode != ''">
AND dr.hospital_code = #{hospitalCode}
AND dr.rank_score = 0
</when>
<otherwise>
AND dr.rank_score != 0
</otherwise>
</choose>
<if test="deptName != null and deptName != ''">
AND dr.standard_dept = #{deptName}
</if>
</where>
order by dr.rank_score asc
limit #{size}
<if test="size != null and size != 0">
limit #{size}
</if>
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.sh.stc.sict.theme.hpgp.dao.HpgpKsHotMapper">
<resultMap type="cn.sh.stc.sict.theme.hpgp.model.HpgpKsHot" id="HpgpKsHotMap">
<result property="gzh" column="gzh"/>
<result property="standardDept" column="standard_dept"/>
<result property="hospitalCode" column="hospital_code"/>
<result property="hospitalName" column="hospital_name"/>
<result property="oneDeptCode" column="one_dept_code"/>
<result property="deptCode" column="dept_code"/>
<result property="deptName" column="dept_name"/>
<result property="rankScore" column="rank_score"/>
</resultMap>
<sql id="Base_Column_List">
gzh, standard_dept, hospital_code, hospital_name, one_dept_code, dept_code, dept_name, rank_score </sql>
</mapper>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment