feat(home): 添加医生专属患者统计和菜单跳转功能

- 在HomeStatisticsDto中新增我的患者数量和待写病历数量字段
- 实现医生患者查询功能,支持按租户隔离数据
- 更新首页统计服务,为医生用户提供专属患者统计数据
- 添加菜单名称点击跳转功能,支持路由导航和外部链接打开
- 修复首页统计数据显示,确保医生看到正确的患者数量
- 添加医保日结结算相关实体、服务和前端页面
- 配置前端路由控制器,支持Vue Router History模式
This commit is contained in:
2026-02-02 16:28:31 +08:00
parent 5534a71c7d
commit 9ed43c9413
16 changed files with 1100 additions and 27 deletions

View File

@@ -0,0 +1,46 @@
package com.core.web.controller.common;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 前端路由 fallback 控制器
* 处理 Vue Router History 模式下的路由
*
* @author
*/
@Controller
public class FrontRouterController {
/**
* 处理前端路由,将所有前端路由请求转发到 index.html
*/
@RequestMapping(value = {
"/ybmanagement/**",
"/system/**",
"/monitor/**",
"/tool/**",
"/doctorstation/**",
"/features/**",
"/todo/**",
"/appoinmentmanage/**",
"/clinicmanagement/**",
"/medicationmanagement/**",
"/yb/**",
"/patient/**",
"/charge/**",
"/nurse/**",
"/pharmacy/**",
"/report/**",
"/document/**",
"/triage/**",
"/check/**",
"/lab/**",
"/financial/**",
"/crosssystem/**",
"/workflow/**"
})
public String index() {
return "forward:/index.html";
}
}

View File

@@ -0,0 +1,34 @@
package com.core.common.core.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 分页结果类
*
* @author
* @date 2026-02-02
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PageResult<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 数据列表
*/
private List<T> rows;
/**
* 总数
*/
private long total;
}

View File

@@ -59,4 +59,14 @@ public class HomeStatisticsDto {
* 待审核数量
*/
private Integer pendingApprovals;
/**
* 我的患者数量(医生专属)
*/
private Integer myPatients;
/**
* 待写病历数量
*/
private Integer pendingEmr;
}

View File

@@ -136,6 +136,7 @@ public class PatientInformationServiceImpl implements IPatientInformationService
// 获取登录者信息
LoginUser loginUser = SecurityUtils.getLoginUser();
Long userId = loginUser.getUserId();
Integer tenantId = loginUser.getTenantId().intValue();
// 先构建基础查询条件
QueryWrapper<PatientBaseInfoDto> queryWrapper = HisQueryUtils.buildQueryWrapper(
@@ -159,8 +160,9 @@ public class PatientInformationServiceImpl implements IPatientInformationService
if (practitioner != null) {
// 查询该医生作为接诊医生ADMITTER, code="1"和挂号医生REGISTRATION_DOCTOR, code="12"的所有就诊记录的患者ID
List<Long> doctorPatientIds = patientManageMapper.getPatientIdsByPractitionerId(
practitioner.getId(),
Arrays.asList(ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode()));
practitioner.getId(),
Arrays.asList(ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode()),
tenantId);
if (doctorPatientIds != null && !doctorPatientIds.isEmpty()) {
// 添加患者ID过滤条件 - 注意:这里使用列名而不是表别名

View File

@@ -64,8 +64,10 @@ public interface PatientManageMapper extends BaseMapper<Patient> {
*
* @param practitionerId 医生ID
* @param typeCodes 参与者类型代码列表
* @param tenantId 租户ID
* @return 患者ID列表
*/
List<Long> getPatientIdsByPractitionerId(@Param("practitionerId") Long practitionerId,
@Param("typeCodes") List<String> typeCodes);
List<Long> getPatientIdsByPractitionerId(@Param("practitionerId") Long practitionerId,
@Param("typeCodes") List<String> typeCodes,
@Param("tenantId") Integer tenantId);
}

View File

@@ -66,23 +66,34 @@ public class HomeStatisticsServiceImpl implements IHomeStatisticsService {
Practitioner practitioner = practitionerList != null && !practitionerList.isEmpty() ? practitionerList.get(0) : null;
int totalPatients = 0;
int myPatients = 0;
// 如果当前用户是医生,查询该医生接诊和被挂号的所有患者
if (practitioner != null) {
// 获取当前登录用户的租户ID
Integer tenantId = SecurityUtils.getLoginUser().getTenantId().intValue();
// 查询该医生作为接诊医生ADMITTER, code="1"和挂号医生REGISTRATION_DOCTOR, code="12"的所有就诊记录的患者ID
List<Long> doctorPatientIds = patientManageMapper.getPatientIdsByPractitionerId(
practitioner.getId(),
Arrays.asList(ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode()));
totalPatients = doctorPatientIds != null ? doctorPatientIds.size() : 0;
practitioner.getId(),
Arrays.asList(ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode()),
tenantId);
myPatients = doctorPatientIds != null ? doctorPatientIds.size() : 0;
// 对于医生,"我的患者"数量即为该医生负责的患者数量
statistics.setMyPatients(myPatients);
} else {
// 如果不是医生,查询所有患者(与患者管理页面逻辑保持一致)
LambdaQueryWrapper<Patient> patientQuery = new LambdaQueryWrapper<>();
patientQuery.eq(Patient::getDeleteFlag, "0");
List<Patient> patientList = patientService.list(patientQuery);
totalPatients = patientList != null ? patientList.size() : 0;
// 如果不是医生,"我的患者"数量为0
statistics.setMyPatients(0);
}
// 查询所有患者作为总患者数
LambdaQueryWrapper<Patient> patientQuery = new LambdaQueryWrapper<>();
patientQuery.eq(Patient::getDeleteFlag, "0");
List<Patient> patientList = patientService.list(patientQuery);
totalPatients = patientList != null ? patientList.size() : 0;
statistics.setTotalPatients(totalPatients);
// 查询昨日在院患者数量(暂时简化处理)

View File

@@ -3,6 +3,8 @@ package com.openhis.web.system.controller;
import com.core.common.core.domain.R;
import com.core.common.utils.SecurityUtils;
import com.openhis.web.doctorstation.appservice.IDoctorStationEmrAppService;
import com.openhis.web.dto.HomeStatisticsDto;
import com.openhis.web.service.IHomeStatisticsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
@@ -20,19 +22,21 @@ import org.springframework.web.bind.annotation.RestController;
public class HomeController {
private final IDoctorStationEmrAppService doctorStationEmrAppService;
private final IHomeStatisticsService homeStatisticsService;
@ApiOperation("获取首页统计数据")
@GetMapping("/statistics")
public R<?> getStatistics() {
// 这里可以返回各种统计数据
// 为了简化,我们只返回待写病历数量
// 获取基础统计数据
HomeStatisticsDto statisticsDto = homeStatisticsService.getHomeStatistics();
// 获取待写病历数量
Long userId = SecurityUtils.getLoginUser().getUserId();
R<?> pendingEmrCount = doctorStationEmrAppService.getPendingEmrCount(userId);
// 构建返回数据
java.util.Map<String, Object> data = new java.util.HashMap<>();
data.put("pendingEmr", pendingEmrCount.getData());
return R.ok(data);
// 将待写病历数量添加到统计数据
statisticsDto.setPendingEmr((Integer) pendingEmrCount.getData());
return R.ok(statisticsDto);
}
}

View File

@@ -136,13 +136,13 @@
<select id="getPatientIdsByPractitionerId" resultType="java.lang.Long">
SELECT DISTINCT enc.patient_id
FROM adm_encounter_participant AS ep
LEFT JOIN adm_encounter AS enc ON ep.encounter_id = enc.ID AND enc.delete_flag = '0'
INNER JOIN adm_encounter AS enc ON ep.encounter_id = enc.ID AND enc.delete_flag = '0'
INNER JOIN adm_patient AS pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
WHERE ep.delete_flag = '0'
AND ep.practitioner_id = #{practitionerId}
AND ep.tenant_id = 1
AND enc.tenant_id = 1
AND pt.tenant_id = 1
AND ep.tenant_id = #{tenantId}
AND enc.tenant_id = #{tenantId}
AND pt.tenant_id = #{tenantId}
<if test="typeCodes != null and !typeCodes.isEmpty()">
AND ep.type_code IN
<foreach collection="typeCodes" item="typeCode" open="(" separator="," close=")">

View File

@@ -0,0 +1,118 @@
package com.openhis.yb.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.Date;
/**
* 日结医保结算实体类
*
* @author
* @date 2026-02-02
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName("yb_day_end_settlement")
public class DayEndMedicalInsuranceSettlement extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 结算单号
*/
@TableField("settlement_no")
private String settlementNo;
/**
* 结算日期
*/
@TableField("settlement_date")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date settlementDate;
/**
* 结算类型 (daily, weekly, monthly)
*/
@TableField("settlement_type")
private String settlementType;
/**
* 医保类型 (城镇职工, 城乡居民等)
*/
@TableField("insurance_type")
private String insuranceType;
/**
* 总人次
*/
@TableField("total_visits")
private Integer totalVisits;
/**
* 总金额
*/
@TableField("total_amount")
private BigDecimal totalAmount;
/**
* 医保统筹支付金额
*/
@TableField("insurance_pay_amount")
private BigDecimal insurancePayAmount;
/**
* 个人账户支付金额
*/
@TableField("account_pay_amount")
private BigDecimal accountPayAmount;
/**
* 个人自付金额
*/
@TableField("personal_pay_amount")
private BigDecimal personalPayAmount;
/**
* 医保基金支付总额
*/
@TableField("fund_pay_sum_amount")
private BigDecimal fundPaySumAmount;
/**
* 状态 (0正常 1停用)
*/
@TableField("status")
private String status;
/**
* 操作员
*/
@TableField("operator")
private String operator;
/**
* 备注
*/
@TableField("remark")
private String remark;
}

View File

@@ -0,0 +1,18 @@
package com.openhis.yb.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.openhis.yb.domain.DayEndMedicalInsuranceSettlement;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* 日结医保结算Mapper接口
*
* @author
* @date 2026-02-02
*/
@Mapper
@Repository
public interface DayEndMedicalInsuranceSettlementMapper extends BaseMapper<DayEndMedicalInsuranceSettlement> {
}

View File

@@ -0,0 +1,74 @@
package com.openhis.yb.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.openhis.yb.domain.DayEndMedicalInsuranceSettlement;
import com.core.common.core.domain.PageResult;
import java.util.List;
/**
* 日结医保结算Service接口
*
* @author
* @date 2026-02-02
*/
public interface IDayEndMedicalInsuranceSettlementService extends IService<DayEndMedicalInsuranceSettlement> {
/**
* 查询日结医保结算
*
* @param id 日结医保结算ID
* @return 日结医保结算
*/
DayEndMedicalInsuranceSettlement selectDayEndMedicalInsuranceSettlementById(Long id);
/**
* 查询日结医保结算列表
*
* @param dayEndMedicalInsuranceSettlement 日结医保结算
* @return 日结医保结算集合
*/
List<DayEndMedicalInsuranceSettlement> selectDayEndMedicalInsuranceSettlementList(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement);
/**
* 新增日结医保结算
*
* @param dayEndMedicalInsuranceSettlement 日结医保结算
* @return 结果
*/
int insertDayEndMedicalInsuranceSettlement(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement);
/**
* 修改日结医保结算
*
* @param dayEndMedicalInsuranceSettlement 日结医保结算
* @return 结果
*/
int updateDayEndMedicalInsuranceSettlement(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement);
/**
* 批量删除日结医保结算
*
* @param ids 需要删除的日结医保结算ID
* @return 结果
*/
int deleteDayEndMedicalInsuranceSettlementByIds(Long[] ids);
/**
* 删除日结医保结算信息
*
* @param id 日结医保结算ID
* @return 结果
*/
int deleteDayEndMedicalInsuranceSettlementById(Long id);
/**
* 分页查询日结医保结算列表
*
* @param dayEndMedicalInsuranceSettlement 日结医保结算
* @param pageNum 页码
* @param pageSize 页面大小
* @return 分页结果
*/
PageResult<DayEndMedicalInsuranceSettlement> selectDayEndMedicalInsuranceSettlementPage(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement, int pageNum, int pageSize);
}

View File

@@ -0,0 +1,128 @@
package com.openhis.yb.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.core.common.core.domain.PageResult;
import com.openhis.yb.domain.DayEndMedicalInsuranceSettlement;
import com.openhis.yb.mapper.DayEndMedicalInsuranceSettlementMapper;
import com.openhis.yb.service.IDayEndMedicalInsuranceSettlementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
/**
* 日结医保结算Service业务层处理
*
* @author
* @date 2026-02-02
*/
@Service
public class DayEndMedicalInsuranceSettlementServiceImpl extends ServiceImpl<DayEndMedicalInsuranceSettlementMapper, DayEndMedicalInsuranceSettlement> implements IDayEndMedicalInsuranceSettlementService {
@Autowired
private DayEndMedicalInsuranceSettlementMapper dayEndMedicalInsuranceSettlementMapper;
/**
* 查询日结医保结算
*
* @param id 日结医保结算ID
* @return 日结医保结算
*/
@Override
public DayEndMedicalInsuranceSettlement selectDayEndMedicalInsuranceSettlementById(Long id) {
return dayEndMedicalInsuranceSettlementMapper.selectById(id);
}
/**
* 查询日结医保结算列表
*
* @param dayEndMedicalInsuranceSettlement 日结医保结算
* @return 日结医保结算
*/
@Override
public List<DayEndMedicalInsuranceSettlement> selectDayEndMedicalInsuranceSettlementList(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement) {
LambdaQueryWrapper<DayEndMedicalInsuranceSettlement> lqw = Wrappers.lambdaQuery();
lqw.like(dayEndMedicalInsuranceSettlement.getSettlementNo() != null, DayEndMedicalInsuranceSettlement::getSettlementNo, dayEndMedicalInsuranceSettlement.getSettlementNo());
lqw.eq(dayEndMedicalInsuranceSettlement.getSettlementDate() != null, DayEndMedicalInsuranceSettlement::getSettlementDate, dayEndMedicalInsuranceSettlement.getSettlementDate());
lqw.eq(dayEndMedicalInsuranceSettlement.getSettlementType() != null, DayEndMedicalInsuranceSettlement::getSettlementType, dayEndMedicalInsuranceSettlement.getSettlementType());
lqw.eq(dayEndMedicalInsuranceSettlement.getInsuranceType() != null, DayEndMedicalInsuranceSettlement::getInsuranceType, dayEndMedicalInsuranceSettlement.getInsuranceType());
lqw.eq(dayEndMedicalInsuranceSettlement.getStatus() != null, DayEndMedicalInsuranceSettlement::getStatus, dayEndMedicalInsuranceSettlement.getStatus());
lqw.orderByDesc(DayEndMedicalInsuranceSettlement::getCreateTime);
return dayEndMedicalInsuranceSettlementMapper.selectList(lqw);
}
/**
* 新增日结医保结算
*
* @param dayEndMedicalInsuranceSettlement 日结医保结算
* @return 结果
*/
@Override
public int insertDayEndMedicalInsuranceSettlement(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement) {
return dayEndMedicalInsuranceSettlementMapper.insert(dayEndMedicalInsuranceSettlement);
}
/**
* 修改日结医保结算
*
* @param dayEndMedicalInsuranceSettlement 日结医保结算
* @return 结果
*/
@Override
public int updateDayEndMedicalInsuranceSettlement(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement) {
return dayEndMedicalInsuranceSettlementMapper.updateById(dayEndMedicalInsuranceSettlement);
}
/**
* 批量删除日结医保结算
*
* @param ids 需要删除的日结医保结算ID
* @return 结果
*/
@Override
public int deleteDayEndMedicalInsuranceSettlementByIds(Long[] ids) {
return dayEndMedicalInsuranceSettlementMapper.deleteBatchIds(Arrays.asList(ids));
}
/**
* 删除日结医保结算信息
*
* @param id 日结医保结算ID
* @return 结果
*/
@Override
public int deleteDayEndMedicalInsuranceSettlementById(Long id) {
return dayEndMedicalInsuranceSettlementMapper.deleteById(id);
}
/**
* 分页查询日结医保结算列表
*
* @param dayEndMedicalInsuranceSettlement 日结医保结算
* @param pageNum 页码
* @param pageSize 页面大小
* @return 分页结果
*/
@Override
public PageResult<DayEndMedicalInsuranceSettlement> selectDayEndMedicalInsuranceSettlementPage(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement, int pageNum, int pageSize) {
LambdaQueryWrapper<DayEndMedicalInsuranceSettlement> lqw = Wrappers.lambdaQuery();
lqw.like(dayEndMedicalInsuranceSettlement.getSettlementNo() != null, DayEndMedicalInsuranceSettlement::getSettlementNo, dayEndMedicalInsuranceSettlement.getSettlementNo());
lqw.eq(dayEndMedicalInsuranceSettlement.getSettlementDate() != null, DayEndMedicalInsuranceSettlement::getSettlementDate, dayEndMedicalInsuranceSettlement.getSettlementDate());
lqw.eq(dayEndMedicalInsuranceSettlement.getSettlementType() != null, DayEndMedicalInsuranceSettlement::getSettlementType, dayEndMedicalInsuranceSettlement.getSettlementType());
lqw.eq(dayEndMedicalInsuranceSettlement.getInsuranceType() != null, DayEndMedicalInsuranceSettlement::getInsuranceType, dayEndMedicalInsuranceSettlement.getInsuranceType());
lqw.eq(dayEndMedicalInsuranceSettlement.getStatus() != null, DayEndMedicalInsuranceSettlement::getStatus, dayEndMedicalInsuranceSettlement.getStatus());
lqw.orderByDesc(DayEndMedicalInsuranceSettlement::getCreateTime);
Page<DayEndMedicalInsuranceSettlement> page = new Page<>(pageNum, pageSize);
Page<DayEndMedicalInsuranceSettlement> result = dayEndMedicalInsuranceSettlementMapper.selectPage(page, lqw);
return PageResult.<DayEndMedicalInsuranceSettlement>builder()
.rows(result.getRecords())
.total(result.getTotal())
.build();
}
}