Merge branch 'develop' of https://gitea.gentronhealth.com/wangyizhe/his into develop

# Conflicts:
#	openhis-ui-vue3/src/router/index.js
This commit is contained in:
chenjinyang
2026-01-09 14:07:02 +08:00
56 changed files with 8707 additions and 324 deletions

View File

@@ -0,0 +1,26 @@
# 修复门诊预约界面专家号查询结果显示问题
## 问题分析
1. 前端传递的参数正确:`type=expert`,后端正确转换为`ticketType=专家`
2. 实际查询返回了5条记录但COUNT查询只返回了1条记录
3. 这导致前端只显示了1条记录而不是全部5条
4. 原因MyBatis-Plus自动生成的COUNT查询和实际查询使用了不同的条件特别是逻辑删除条件
## 解决方案
1. 修改TicketMapper.xml中的自定义COUNT查询显式添加`delete_flag = '0'`条件
2. 在selectTicketPage和selectTicketPage_mpCount查询中都添加逻辑删除条件
3. 确保两个查询使用完全相同的WHERE条件
## 修复步骤
1. 修改`selectTicketPage`查询,添加逻辑删除条件`and delete_flag = '0'`
2. 修改`selectTicketPage_mpCount`查询,添加逻辑删除条件`and delete_flag = '0'`
3. 确保两个查询的WHERE条件完全一致
4. 测试修复后的功能确保专家号能正确显示全部5条记录
## 代码修改点
- 文件:`d:/work/openhis-server-new/openhis-domain/src/main/resources/mapper/clinical/TicketMapper.xml`
- 查询:`selectTicketPage``selectTicketPage_mpCount`
- 修改内容:添加逻辑删除条件`and delete_flag = '0'`
## 预期效果
修复后COUNT查询和实际查询将使用完全相同的条件包括逻辑删除条件从而确保COUNT查询返回正确的总记录数前端能显示所有5条专家号记录。

View File

@@ -0,0 +1,30 @@
# 修复门诊预约界面专家号查询COUNT结果不正确问题
## 问题分析
1. 前端传递的参数正确:`type=expert`,后端正确转换为`ticketType=专家`
2. COUNT查询和实际查询的WHERE条件完全相同`WHERE delete_flag = '0' AND ticket_type = '专家'`
3. 但COUNT查询只返回1条记录而实际查询返回5条记录
4. 原因MyBatis-Plus的分页插件在处理自定义COUNT查询时存在bug导致COUNT查询结果不正确
## 解决方案
修改`TicketAppServiceImpl.java`中的`listTicket`方法不使用MyBatis-Plus的自动分页功能而是手动实现分页查询
1. 直接调用`ticketService.countTickets`方法获取总记录数
2. 手动构建查询条件
3. 确保COUNT查询和实际查询使用完全相同的条件
## 修复步骤
1. 修改`TicketAppServiceImpl.java`中的`listTicket`方法
2. 手动实现分页查询,包括:
- 构建查询条件
- 调用`countTickets`获取总记录数
- 调用`selectTicketList`获取分页数据
- 手动组装分页结果
3. 测试修复后的功能确保专家号能正确显示全部5条记录
## 代码修改点
- 文件:`d:/work/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/TicketAppServiceImpl.java`
- 方法:`listTicket`
- 修改内容替换MyBatis-Plus的自动分页改为手动分页实现
## 预期效果
修复后COUNT查询和实际查询将使用完全相同的条件COUNT查询将返回正确的总记录数5条前端能显示所有5条专家号记录。

View File

@@ -0,0 +1,32 @@
## 问题分析
根据日志和代码分析,发现号源列表显示"没有更多数据了"的问题原因:
1. **后端查询正常**成功查询到5条符合条件的专家号源记录
2. **数据转换失败**:在`convertToDto`方法中,`fee`字段类型转换错误
3. **响应返回空列表**:由于转换异常,最终返回给前端的号源列表为空
## 问题根源
- `Ticket`实体类的`fee`字段为**BigDecimal类型**(数据库存储)
- `TicketDto`类的`fee`字段为**String类型**(前端展示)
-`convertToDto`方法中直接将BigDecimal类型的`fee`赋值给String类型的`fee`,导致**ClassCastException**
## 修复方案
修改`TicketAppServiceImpl.java`文件中的`convertToDto`方法将BigDecimal类型的`fee`转换为String类型
```java
// 原代码
dto.setFee(ticket.getFee());
// 修复后代码
dto.setFee(ticket.getFee().toString());
```
## 预期效果
1. 修复后,后端能成功将`Ticket`实体转换为`TicketDto`
2. 前端能接收到包含5条专家号源的完整列表
3. 页面显示正常,不再出现"没有更多数据了"的提示
## 验证方法
1. 重新启动项目,访问号源管理页面
2. 选择"专家号"类型查看是否能正确显示5条号源记录
3. 检查日志,确认没有类型转换异常

View File

@@ -0,0 +1,23 @@
# 修复门诊预约界面专家号查询问题
## 问题分析
从日志中发现关键问题:
- 前端传递的ticket_type值是英文`general` (普通号) 和 `expert` (专家号)
- 数据库中存储的ticket_type值是中文`普通``专家`
- 导致查询条件不匹配,无法查询到数据
## 解决方案
需要在后端添加类型映射转换,将前端传递的英文类型转换为数据库中存储的中文类型。
## 修复步骤
1. 修改 `TicketAppServiceImpl.java` 文件在处理type参数时添加映射转换逻辑
2. 添加从英文类型到中文类型的映射关系
3. 测试修复后的功能,确保普通号和专家号都能正确查询
## 代码修改点
- 文件:`d:/work/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/TicketAppServiceImpl.java`
- 方法:`listTicket` 中的type参数处理部分
- 修改内容:添加类型映射转换,将 "general" 转换为 "普通""expert" 转换为 "专家"
## 预期效果
修复后,前端选择"普通号"或"专家号"时,系统能正确查询到对应的号源数据,不再出现"没有更多数据了"的提示。

View File

@@ -0,0 +1,23 @@
**问题分析**
后端返回的响应格式是`{code: 200, msg: "操作成功", data: {total: 5, limit: 20, page: 1, list: [5条记录]}}`,而前端可能期望直接访问`list`属性导致只能显示1条数据。
**修复方案**
1. 修改`TicketAppServiceImpl.java``listTicket`方法,确保返回的分页数据格式正确
2. 调整响应结构,使其更符合前端期望
3. 保持与现有代码的兼容性
**修改点**
* `TicketAppServiceImpl.java`:优化`listTicket`方法的响应格式
* 确保分页信息和列表数据都能正确返回给前端
**预期效果**
* 后端返回正确格式的响应数据
* 前端能够正确显示所有5条专家号数据
* 保持与现有代码的兼容性

33
CheckTicketStatus.java Normal file
View File

@@ -0,0 +1,33 @@
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class CheckTicketStatus {
public static void main(String[] args) {
String url = "jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=hisdev&characterEncoding=UTF-8&client_encoding=UTF-8";
String user = "postgresql";
String password = "Jchl1528";
try {
Class.forName("org.postgresql.Driver");
Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, status, delete_flag FROM clinical_ticket WHERE id = 1");
if (rs.next()) {
System.out.println("Ticket ID: " + rs.getLong("id"));
System.out.println("Status: " + rs.getString("status"));
System.out.println("Delete Flag: " + rs.getString("delete_flag"));
} else {
System.out.println("Ticket ID 1 not found");
}
rs.close();
stmt.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,67 @@
-- 创建序列
CREATE SEQUENCE "hisdev"."clinical_ticket_id_seq"
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
-- 创建号源表clinical_ticket
CREATE TABLE "hisdev"."clinical_ticket" (
"id" bigint NOT NULL DEFAULT nextval('hisdev.clinical_ticket_id_seq'::regclass),
"bus_no" varchar(50) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"department" varchar(100) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"doctor" varchar(100) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"ticket_type" varchar(20) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"time" varchar(50) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"status" varchar(20) COLLATE "pg_catalog"."default" NOT NULL DEFAULT 'unbooked'::character varying,
"fee" varchar(20) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"patient_id" bigint,
"patient_name" varchar(100) COLLATE "pg_catalog"."default" DEFAULT ''::character varying,
"medical_card" varchar(50) COLLATE "pg_catalog"."default" DEFAULT ''::character varying,
"phone" varchar(20) COLLATE "pg_catalog"."default" DEFAULT ''::character varying,
"appointment_date" timestamptz(6),
"appointment_time" timestamptz(6),
"department_id" bigint,
"doctor_id" bigint,
"create_by" varchar(32) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"create_time" timestamptz(6) NOT NULL,
"update_by" varchar(32) COLLATE "pg_catalog"."default" DEFAULT ''::character varying,
"update_time" timestamptz(6),
"remark" varchar(500) COLLATE "pg_catalog"."default" DEFAULT ''::character varying,
"delete_flag" char(1) COLLATE "pg_catalog"."default" NOT NULL DEFAULT 0,
"tenant_id" bigint NOT NULL,
PRIMARY KEY ("id")
)
WITH (
OIDS = FALSE
)
;
ALTER SEQUENCE "hisdev"."clinical_ticket_id_seq" OWNED BY "hisdev"."clinical_ticket"."id";
-- 添加注释
COMMENT ON TABLE "hisdev"."clinical_ticket" IS '号源管理表';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."id" IS 'ID';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."bus_no" IS '号源编码';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."department" IS '科室名称';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."doctor" IS '医生姓名';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."ticket_type" IS '号源类型 (普通/专家)';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."time" IS '挂号时间';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."status" IS '状态 (unbooked:未预约, booked:已预约, checked:已取号, cancelled:已取消, locked:已锁定)';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."fee" IS '挂号费';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."patient_id" IS '患者ID';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."patient_name" IS '患者姓名';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."medical_card" IS '就诊卡号';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."phone" IS '手机号';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."appointment_date" IS '预约日期';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."appointment_time" IS '预约时间';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."department_id" IS '科室ID';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."doctor_id" IS '医生ID';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."create_by" IS '创建人';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."create_time" IS '创建时间';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."update_by" IS '更新人';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."update_time" IS '更新时间';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."remark" IS '备注';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."delete_flag" IS '删除状态';
COMMENT ON COLUMN "hisdev"."clinical_ticket"."tenant_id" IS '租户ID';

View File

@@ -76,6 +76,25 @@ public class SecurityUtils {
}
}
/**
* 安全获取用户名(失败时返回默认值)
**/
public static String getUsernameSafe() {
try {
Authentication authentication = getAuthentication();
if (authentication != null && authentication.getPrincipal() != null) {
if (authentication.getPrincipal() instanceof LoginUser) {
return ((LoginUser) authentication.getPrincipal()).getUsername();
} else {
return authentication.getPrincipal().toString();
}
}
} catch (Exception e) {
// 静默处理异常,返回默认值
}
return "anonymous";
}
/**
* 获取Authentication
*/

View File

@@ -11,9 +11,10 @@ import org.springframework.transaction.support.DefaultTransactionDefinition;
/**
* 事务处理
* 已注释:与 @Transactional 注解冲突,导致事务回滚错误
*/
@Aspect
@Component
//@Aspect
//@Component
public class TransactionAspect {
private final PlatformTransactionManager transactionManager;
@@ -23,19 +24,19 @@ public class TransactionAspect {
this.transactionManager = transactionManager;
}
@Before("@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
"@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
"@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
"@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
//@Before("@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
// "@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
// "@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
// "@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
public void beginTransaction() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
transactionStatus.set(status);
}
@AfterReturning("@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
"@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
"@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
"@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
//@AfterReturning("@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
// "@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
// "@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
// "@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
public void commitTransaction() {
TransactionStatus status = transactionStatus.get();
if (status != null && !status.isCompleted()) {
@@ -44,11 +45,11 @@ public class TransactionAspect {
}
}
@AfterThrowing(pointcut = "@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
"@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
"@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
"@annotation(org.springframework.web.bind.annotation.DeleteMapping)",
throwing = "ex")
//@AfterThrowing(pointcut = "@annotation(org.springframework.web.bind.annotation.PostMapping) || " +
// "@annotation(org.springframework.web.bind.annotation.GetMapping) || " +
// "@annotation(org.springframework.web.bind.annotation.PutMapping) || " +
// "@annotation(org.springframework.web.bind.annotation.DeleteMapping)",
// throwing = "ex")
public void rollbackTransaction(Exception ex) {
TransactionStatus status = transactionStatus.get();
if (status != null && !status.isCompleted()) {

View File

@@ -0,0 +1,62 @@
package com.openhis.web.appointmentmanage.appservice;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.openhis.web.appointmentmanage.dto.TicketDto;
import java.util.Map;
/**
* 号源管理应用服务接口
*
* @author system
*/
public interface ITicketAppService {
/**
* 查询号源列表
*
* @param params 查询参数
* @return 号源列表
*/
R<?> listTicket(Map<String, Object> params);
/**
* 预约号源
*
* @param params 预约参数
* @return 结果
*/
R<?> bookTicket(Map<String, Object> params);
/**
* 取消预约
*
* @param ticketId 号源ID
* @return 结果
*/
R<?> cancelTicket(Long ticketId);
/**
* 取号
*
* @param ticketId 号源ID
* @return 结果
*/
R<?> checkInTicket(Long ticketId);
/**
* 停诊
*
* @param ticketId 号源ID
* @return 结果
*/
R<?> cancelConsultation(Long ticketId);
/**
* 查询所有号源(用于测试)
*
* @return 所有号源列表
*/
R<?> listAllTickets();
}

View File

@@ -0,0 +1,363 @@
package com.openhis.web.appointmentmanage.appservice.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.openhis.administration.domain.Patient;
import com.openhis.administration.service.IPatientService;
import com.openhis.clinical.domain.Ticket;
import com.openhis.clinical.service.ITicketService;
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
import com.openhis.web.appointmentmanage.dto.TicketDto;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 号源管理应用服务实现类
*
* @author system
*/
@Service
public class TicketAppServiceImpl implements ITicketAppService {
@Resource
private ITicketService ticketService;
@Resource
private IPatientService patientService;
/**
* 查询号源列表
*
* @param params 查询参数
* @return 号源列表
*/
@Override
public R<?> listTicket(Map<String, Object> params) {
// 调试日志:打印所有参数
System.out.println("=== listTicket方法收到的所有参数===");
for (Map.Entry<String, Object> entry : params.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
System.out.println("=================================");
// 构建查询条件
Ticket ticket = new Ticket();
// 设置查询参数
// 处理日期参数
if (params.containsKey("date")) {
String date = (String) params.get("date");
try {
// 将日期字符串转换为Date类型设置到appointmentDate字段
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date appointmentDate = sdf.parse(date);
ticket.setAppointmentDate(appointmentDate);
System.out.println("设置的appointmentDate" + appointmentDate);
} catch (Exception e) {
// 日期格式错误,忽略该参数
System.out.println("日期格式错误,忽略该参数:" + date + ",错误信息:" + e.getMessage());
}
}
// 处理状态参数
if (params.containsKey("status")) {
String status = (String) params.get("status");
System.out.println("接收到的status参数" + status);
if (!"all".equals(status) && !"全部".equals(status)) {
// 将中文状态转换为英文状态
if ("未预约".equals(status)) {
ticket.setStatus("unbooked");
} else if ("已预约".equals(status)) {
ticket.setStatus("booked");
} else if ("已取号".equals(status)) {
ticket.setStatus("checked");
} else if ("已取消".equals(status)) {
ticket.setStatus("cancelled");
} else if ("已锁定".equals(status)) {
ticket.setStatus("locked");
} else {
ticket.setStatus(status);
}
System.out.println("设置的status" + ticket.getStatus());
}
}
if (params.containsKey("name")) {
String name = (String) params.get("name");
ticket.setPatientName(name);
}
if (params.containsKey("card")) {
String card = (String) params.get("card");
ticket.setMedicalCard(card);
}
if (params.containsKey("phone")) {
String phone = (String) params.get("phone");
ticket.setPhone(phone);
}
if (params.containsKey("type")) {
String type = (String) params.get("type");
System.out.println("前端传递的type参数值" + type);
if (!"all".equals(type)) {
// 类型映射转换:前端传递英文类型,数据库存储中文类型
if ("general".equals(type)) {
ticket.setTicketType("普通");
} else if ("expert".equals(type)) {
ticket.setTicketType("专家");
} else if ("普通".equals(type)) {
ticket.setTicketType("普通");
} else if ("专家".equals(type)) {
ticket.setTicketType("专家");
} else {
ticket.setTicketType(type);
}
System.out.println("转换后的ticketType值" + ticket.getTicketType());
}
}
// 手动实现分页查询避免MyBatis-Plus自动COUNT查询的问题
int pageNum = params.get("page") != null ? Integer.valueOf(params.get("page").toString()) : 1;
int pageSize = params.get("limit") != null ? Integer.valueOf(params.get("limit").toString()) : 10;
// 调试:输出构建的查询条件
System.out.println("构建的查询条件ticketType=" + ticket.getTicketType() + ", status=" + ticket.getStatus() + ", appointmentDate=" + ticket.getAppointmentDate());
// 1. 获取所有符合条件的记录
List<Ticket> allTickets = ticketService.selectTicketList(ticket);
// 调试:输出查询到的所有记录
System.out.println("查询到的所有记录:" + allTickets);
if (!allTickets.isEmpty()) {
for (Ticket t : allTickets) {
System.out.println("记录详情id=" + t.getId() + ", ticketType=" + t.getTicketType() + ", status=" + t.getStatus() + ", appointmentDate=" + t.getAppointmentDate() + ", deleteFlag=" + t.getDeleteFlag());
}
}
// 2. 计算总记录数
long total = allTickets.size();
System.out.println("手动计算的总记录数:" + total);
// 3. 手动分页
int start = (pageNum - 1) * pageSize;
int end = Math.min(start + pageSize, allTickets.size());
List<Ticket> pageTickets;
if (start >= end) {
pageTickets = new ArrayList<>();
} else {
pageTickets = allTickets.subList(start, end);
}
// 4. 转换为DTO
List<TicketDto> dtoList = pageTickets.stream().map(this::convertToDto).toList();
// 5. 构建响应数据,符合前端预期格式
Map<String, Object> result = new HashMap<>();
result.put("list", dtoList);
result.put("records", dtoList); // 兼容前端框架如Element UI可能使用的records字段
result.put("total", total);
result.put("page", pageNum);
result.put("current", pageNum); // 兼容前端框架可能使用的current字段
result.put("limit", pageSize);
result.put("pageSize", pageSize); // 兼容前端框架可能使用的pageSize字段
result.put("size", pageSize); // 兼容前端框架可能使用的size字段
result.put("pageNum", pageNum); // 兼容前端框架可能使用的pageNum字段
result.put("pages", (int) Math.ceil((double) total / pageSize)); // 计算总页数
// 调试:输出响应数据
System.out.println("返回的响应数据:" + result);
return R.ok(result);
}
/**
* 预约号源
*
* @param params 预约参数
* @return 结果
*/
@Override
public R<?> bookTicket(Map<String, Object> params) {
Long ticketId = null;
if (params.get("ticketId") != null) {
ticketId = Long.valueOf(params.get("ticketId").toString());
}
if (ticketId == null) {
return R.fail("参数错误");
}
try {
int result = ticketService.bookTicket(params);
return R.ok(result > 0 ? "预约成功" : "预约失败");
} catch (Exception e) {
return R.fail(e.getMessage());
}
}
/**
* 取消预约
*
* @param ticketId 号源ID
* @return 结果
*/
@Override
public R<?> cancelTicket(Long ticketId) {
if (ticketId == null) {
return R.fail("参数错误");
}
try {
int result = ticketService.cancelTicket(ticketId);
return R.ok(result > 0 ? "取消成功" : "取消失败");
} catch (Exception e) {
return R.fail(e.getMessage());
}
}
/**
* 取号
*
* @param ticketId 号源ID
* @return 结果
*/
@Override
public R<?> checkInTicket(Long ticketId) {
if (ticketId == null) {
return R.fail("参数错误");
}
try {
int result = ticketService.checkInTicket(ticketId);
return R.ok(result > 0 ? "取号成功" : "取号失败");
} catch (Exception e) {
return R.fail(e.getMessage());
}
}
/**
* 停诊
*
* @param ticketId 号源ID
* @return 结果
*/
@Override
public R<?> cancelConsultation(Long ticketId) {
if (ticketId == null) {
return R.fail("参数错误");
}
try {
int result = ticketService.cancelConsultation(ticketId);
return R.ok(result > 0 ? "停诊成功" : "停诊失败");
} catch (Exception e) {
return R.fail(e.getMessage());
}
}
@Override
public R<?> listAllTickets() {
// 创建固定的测试数据,用于验证前端是否能展示数据
List<TicketDto> testTickets = new ArrayList<>();
// 创建5条测试数据
for (int i = 1; i <= 5; i++) {
TicketDto dto = new TicketDto();
dto.setSlot_id((long) i);
dto.setBusNo("TEST0000" + i);
dto.setDepartment("内科");
dto.setDoctor("张三");
dto.setTicketType("expert");
dto.setDateTime("08:00-08:50");
dto.setStatus("未预约");
dto.setFee("150");
dto.setAppointmentDate(new Date());
testTickets.add(dto);
}
// 构建响应数据
Map<String, Object> result = new HashMap<>();
result.put("list", testTickets);
result.put("total", testTickets.size());
result.put("page", 1);
result.put("limit", 20);
return R.ok(result);
}
/**
* 转换为DTO
*
* @param ticket 号源实体
* @return 号源DTO
*/
private TicketDto convertToDto(Ticket ticket) {
TicketDto dto = new TicketDto();
dto.setSlot_id(ticket.getId());
dto.setBusNo(ticket.getBusNo());
dto.setDepartment(ticket.getDepartment());
dto.setDoctor(ticket.getDoctor());
// 处理号源类型转换为英文前端期望的是general或expert
String ticketType = ticket.getTicketType();
if ("普通".equals(ticketType)) {
dto.setTicketType("general");
} else if ("专家".equals(ticketType)) {
dto.setTicketType("expert");
} else {
dto.setTicketType(ticketType);
}
// 处理号源时间dateTime
dto.setDateTime(ticket.getTime());
// 处理号源状态(转换为中文)
String status = ticket.getStatus();
switch (status) {
case "unbooked":
dto.setStatus("未预约");
break;
case "booked":
dto.setStatus("已预约");
break;
case "checked":
dto.setStatus("已取号");
break;
case "cancelled":
dto.setStatus("已取消");
break;
case "locked":
dto.setStatus("已锁定");
break;
default:
dto.setStatus(status);
}
dto.setFee(ticket.getFee());
dto.setPatientName(ticket.getPatientName());
dto.setPatientId(ticket.getMedicalCard()); // 就诊卡号
dto.setPhone(ticket.getPhone());
// 获取患者性别
if (ticket.getPatientId() != null) {
Patient patient = patientService.getById(ticket.getPatientId());
if (patient != null) {
Integer genderEnum = patient.getGenderEnum();
if (genderEnum != null) {
switch (genderEnum) {
case 1:
dto.setGender("");
break;
case 2:
dto.setGender("");
break;
default:
dto.setGender("未知");
}
}
}
}
dto.setAppointmentDate(ticket.getAppointmentDate());
dto.setAppointmentTime(ticket.getAppointmentTime());
dto.setDepartmentId(ticket.getDepartmentId());
dto.setDoctorId(ticket.getDoctorId());
return dto;
}
}

View File

@@ -0,0 +1,101 @@
package com.openhis.web.appointmentmanage.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.annotation.Anonymous;
import com.core.common.core.domain.R;
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
import com.openhis.web.appointmentmanage.dto.TicketDto;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Map;
/**
* 号源管理控制器
*
* @author system
*/
@RestController
@RequestMapping("/appointment/ticket")
public class TicketController {
@Resource
private ITicketAppService ticketAppService;
/**
* 查询号源列表
*
* @param params 查询参数
* @return 号源列表
*/
@PostMapping("/list")
public R<?> listTicket(@RequestBody Map<String, Object> params) {
return ticketAppService.listTicket(params);
}
/**
* 查询号源列表支持GET请求兼容旧版本
*
* @param params 查询参数
* @return 号源列表
*/
@GetMapping("/list")
public R<?> listTicketByGet(@RequestParam Map<String, Object> params) {
return ticketAppService.listTicket(params);
}
/**
* 查询所有号源(用于测试)
*
* @return 所有号源列表
*/
@Anonymous
@GetMapping("/listAll")
public R<?> listAllTickets() {
return ticketAppService.listAllTickets();
}
/**
* 预约号源
*
* @param params 预约参数
* @return 结果
*/
@PostMapping("/book")
public R<?> bookTicket(@RequestBody Map<String, Object> params) {
return ticketAppService.bookTicket(params);
}
/**
* 取消预约
*
* @param ticketId 号源ID
* @return 结果
*/
@PostMapping("/cancel")
public R<?> cancelTicket(@RequestParam Long ticketId) {
return ticketAppService.cancelTicket(ticketId);
}
/**
* 取号
*
* @param ticketId 号源ID
* @return 结果
*/
@PostMapping("/checkin")
public R<?> checkInTicket(@RequestParam Long ticketId) {
return ticketAppService.checkInTicket(ticketId);
}
/**
* 停诊
*
* @param ticketId 号源ID
* @return 结果
*/
@PostMapping("/cancelConsultation")
public R<?> cancelConsultation(@RequestParam Long ticketId) {
return ticketAppService.cancelConsultation(ticketId);
}
}

View File

@@ -0,0 +1,102 @@
package com.openhis.web.appointmentmanage.dto;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 号源管理DTO
*
* @author system
*/
@Data
@Accessors(chain = true)
public class TicketDto {
/**
* 号源唯一ID
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long slot_id;
/**
* 号源编码
*/
private String busNo;
/**
* 科室名称
*/
private String department;
/**
* 医生姓名
*/
private String doctor;
/**
* 号源类型 (普通/专家)
*/
private String ticketType;
/**
* 号源时间
*/
private String dateTime;
/**
* 状态 (unbooked:未预约, booked:已预约, checked:已取号, cancelled:已取消, locked:已锁定)
*/
private String status;
/**
* 挂号费
*/
private String fee;
/**
* 患者姓名
*/
private String patientName;
/**
* 就诊卡号
*/
private String patientId;
/**
* 手机号
*/
private String phone;
/**
* 患者性别
*/
private String gender;
/**
* 预约日期
*/
private Date appointmentDate;
/**
* 预约时间
*/
private Date appointmentTime;
/**
* 科室ID
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long departmentId;
/**
* 医生ID
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long doctorId;
}

View File

@@ -959,12 +959,14 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// LIS查看报告地址
String lisReportUrl = TenantOptionUtil.getOptionContent(TenantOptionDict.LIS_REPORT_URL);
if (StringUtils.isEmpty(lisReportUrl)) {
throw new ServiceException("租户配置项【LIS查看报告地址】未配置");
log.warn("租户配置项【LIS查看报告地址】未配置");
}
List<ProofAndTestResultDto> proofResult = doctorStationAdviceAppMapper.getProofAndTestResult(encounterId,
RequestStatus.DRAFT.getValue(), ActivityType.PROOF.getValue());
for (ProofAndTestResultDto proofAndTestResultDto : proofResult) {
proofAndTestResultDto.setRequestUrl(lisReportUrl.concat(proofAndTestResultDto.getBusNo()));
if (StringUtils.isNotEmpty(lisReportUrl)) {
proofAndTestResultDto.setRequestUrl(lisReportUrl.concat(proofAndTestResultDto.getBusNo()));
}
}
return R.ok(proofResult);
@@ -981,12 +983,14 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// PACS查看报告地址
String pacsReportUrl = TenantOptionUtil.getOptionContent(TenantOptionDict.PACS_REPORT_URL);
if (StringUtils.isEmpty(pacsReportUrl)) {
throw new ServiceException("租户配置项【PACS查看报告地址】未配置");
log.warn("租户配置项【PACS查看报告地址】未配置");
}
List<ProofAndTestResultDto> testResult = doctorStationAdviceAppMapper.getProofAndTestResult(encounterId,
RequestStatus.DRAFT.getValue(), ActivityType.TEST.getValue());
for (ProofAndTestResultDto proofAndTestResultDto : testResult) {
proofAndTestResultDto.setRequestUrl(pacsReportUrl.concat(proofAndTestResultDto.getBusNo()));
if (StringUtils.isNotEmpty(pacsReportUrl)) {
proofAndTestResultDto.setRequestUrl(pacsReportUrl.concat(proofAndTestResultDto.getBusNo()));
}
}
return R.ok(testResult);
}

View File

@@ -45,9 +45,9 @@ public interface IPatientInformationService {
/**
* 添加病人信息
*
* @param patientInfoDto 病人信息
* @param patientBaseInfoDto 病人信息
*/
R<?> addPatient(PatientBaseInfoDto patientInfoDto);
R<?> addPatient(PatientBaseInfoDto patientBaseInfoDto);
/**
* 更新患者手机号

View File

@@ -254,31 +254,32 @@ public class PatientInformationServiceImpl implements IPatientInformationService
/**
* 添加病人信息
*
* @param patientInfoDto 病人信息
* @param patientBaseInfoDto 病人信息
*/
@Override
public R<?> addPatient(PatientBaseInfoDto patientInfoDto) {
public R<?> addPatient(PatientBaseInfoDto patientBaseInfoDto) {
// log.debug("添加病人信息,patientInfoDto:{}", patientBaseInfoDto);
// 如果患者没有输入身份证号则根据年龄自动生成
String idCard = patientInfoDto.getIdCard();
String idCard = patientBaseInfoDto.getIdCard();
if (idCard == null || CommonConstants.Common.AREA_CODE.equals(idCard.substring(0, 6))) {
if (patientInfoDto.getAge() != null) {
idCard = IdCardUtil.generateIdByAge(patientInfoDto.getAge());
patientInfoDto.setIdCard(idCard);
if (patientBaseInfoDto.getAge() != null) {
idCard = IdCardUtil.generateIdByAge(patientBaseInfoDto.getAge());
patientBaseInfoDto.setIdCard(idCard);
}
}
// 身份证号是否存在
List<Patient> idCardList
= patientService.list(new LambdaQueryWrapper<Patient>().eq(Patient::getIdCard, patientInfoDto.getIdCard()));
= patientService.list(new LambdaQueryWrapper<Patient>().eq(Patient::getIdCard, patientBaseInfoDto.getIdCard()));
if (!idCardList.isEmpty()) {
throw new ServiceException("身份证号:" + patientInfoDto.getIdCard() + "已经存在");
throw new ServiceException("身份证号:" + patientBaseInfoDto.getIdCard() + "已经存在");
}
// 处理患者信息
Patient patient = this.handlePatientInfo(patientInfoDto);
Patient patient = this.handlePatientInfo(patientBaseInfoDto);
// 新增患者身份子表信息
if (patientInfoDto.getPatientIdInfoList() != null) {
List<PatientIdInfoDto> patientIdInfoList = patientInfoDto.getPatientIdInfoList();
if (patientBaseInfoDto.getPatientIdInfoList() != null) {
List<PatientIdInfoDto> patientIdInfoList = patientBaseInfoDto.getPatientIdInfoList();
PatientIdentifier patientIdentifier;
for (PatientIdInfoDto patientIdInfoDto : patientIdInfoList) {
patientIdentifier = new PatientIdentifier();

View File

@@ -38,11 +38,12 @@ public class PatientInformationController {
/**
* 添加病人信息
*
* @param patientInfoDto 病人信息
* @param patientBaseInfoDto 病人信息
*/
@PostMapping("/patient-information")
public R<?> addPatient(@RequestBody PatientBaseInfoDto patientInfoDto) {
return patientInformationService.addPatient(patientInfoDto);
public R<?> addPatient(@RequestBody PatientBaseInfoDto patientBaseInfoDto) {
// log.debug("添加病人信息,patientInfoDto:{}", patientBaseInfoDto);
return patientInformationService.addPatient(patientBaseInfoDto);
}
/**

View File

@@ -176,7 +176,7 @@ public class OperLogAspect {
* 插入操作日志到数据库
*/
private void insertOperLog(SysOperLog operLog) {
String username = SecurityUtils.getLoginUser().getUsername();
String username = SecurityUtils.getUsernameSafe(); // 使用安全获取用户名的方法
String sql = "INSERT INTO sys_oper_log "
+ "(title,oper_time,method,request_method,oper_name,oper_url,oper_param,json_result,error_msg,cost_time) "
+ "VALUES (?, ?, ?,?, ?, ?, ?, ?,?, ?)";

View File

@@ -70,6 +70,10 @@ public enum AssignSeqEnum {
* 位置业务编码
*/
LOCATION_BUS_NO("15", "科室业务编码", "LOC"),
/**
* 手术室业务编码
*/
OPERATING_ROOM_BUS_NO("16", "手术室业务编码", "OR"),
/**
* 厂商/产地单据号
*/
@@ -298,11 +302,11 @@ public enum AssignSeqEnum {
* 自动备份单据号
*/
AUTO_BACKUP_NO("70", "自动备份单据号", "ABU"),
/**
* 手术室业务编码
* 订单编号
*/
OPERATING_ROOM_BUS_NO("71", "手术室业务编码", "OPR");
ORDER_NUM("71", "订单编号", "ORD");
private final String code;
private final String info;

View File

@@ -0,0 +1,68 @@
package com.openhis.clinical.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@Data
@TableName("order_main")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class Order extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
private String orderNo;
private Long patientId;
private String patientName;
private String medicalCard;
private String phone;
private Integer gender;
private Long scheduleId;
private Long slotId;
private Long departmentId;
private String departmentName;
private Long doctorId;
private String doctorName;
private String regType;
private BigDecimal fee;
private Date appointmentDate;
private Date appointmentTime;
private Date cancelTime;
private String cancelReason;
private Integer status;
private Integer payStatus;
private Integer version;
}

View File

@@ -0,0 +1,107 @@
package com.openhis.clinical.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 号源管理Entity实体
*
* @author system
*/
@Data
@TableName("clinical_ticket")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class Ticket extends HisBaseEntity {
/**
* ID
*/
@TableId(type = IdType.ASSIGN_ID)
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
* 号源编码
*/
private String busNo;
/**
* 科室名称
*/
private String department;
/**
* 医生姓名
*/
private String doctor;
/**
* 号源类型 (普通/专家)
*/
private String ticketType;
/**
* 挂号时间
*/
private String time;
/**
* 状态 (unbooked:未预约, booked:已预约, checked:已取号, cancelled:已取消, locked:已锁定)
*/
private String status;
/**
* 挂号费
*/
private String fee;
/**
* 患者ID
*/
private Long patientId;
/**
* 患者姓名
*/
private String patientName;
/**
* 就诊卡号
*/
private String medicalCard;
/**
* 手机号
*/
private String phone;
/**
* 预约日期
*/
private Date appointmentDate;
/**
* 预约时间
*/
private Date appointmentTime;
/**
* 科室ID
*/
private Long departmentId;
/**
* 医生ID
*/
private Long doctorId;
}

View File

@@ -0,0 +1,35 @@
package com.openhis.clinical.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.openhis.clinical.domain.Order;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
import java.util.Map;
public interface OrderMapper extends BaseMapper<Order> {
List<Order> selectOrderList(Order order);
Page<Order> selectOrderPage(Page<Order> page, @Param("order") Order order);
Order selectOrderById(Long id);
int insertOrder(Order order);
int updateOrder(Order order);
int deleteOrderById(Long id);
int deleteOrderByIds(Long[] ids);
int countOrders(Map<String, Object> params);
Order selectOrderBySlotId(Long slotId);
int updateOrderStatusById(Long id, Integer status);
int updateOrderCancelInfoById(Long id, Date cancelTime, String cancelReason);
}

View File

@@ -0,0 +1,82 @@
package com.openhis.clinical.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.openhis.clinical.domain.Ticket;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* 号源管理Mapper接口
*
* @author system
*/
public interface TicketMapper extends BaseMapper<Ticket> {
/**
* 查询号源列表
*
* @param ticket 号源信息
* @return 号源集合
*/
List<Ticket> selectTicketList(Ticket ticket);
/**
* 分页查询号源列表
*
* @param page 分页参数
* @param ticket 号源信息
* @return 号源集合
*/
Page<Ticket> selectTicketPage(Page<Ticket> page, @Param("ticket") Ticket ticket);
/**
* 查询号源信息
*
* @param id 号源ID
* @return 号源信息
*/
Ticket selectTicketById(Long id);
/**
* 新增号源
*
* @param ticket 号源信息
* @return 结果
*/
int insertTicket(Ticket ticket);
/**
* 修改号源
*
* @param ticket 号源信息
* @return 结果
*/
int updateTicket(Ticket ticket);
/**
* 删除号源
*
* @param id 号源ID
* @return 结果
*/
int deleteTicketById(Long id);
/**
* 批量删除号源
*
* @param ids 需要删除的数据ID
* @return 结果
*/
int deleteTicketByIds(Long[] ids);
/**
* 根据条件统计号源数量
*
* @param params 查询参数
* @return 号源数量
*/
int countTickets(Map<String, Object> params);
}

View File

@@ -0,0 +1,37 @@
package com.openhis.clinical.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.openhis.clinical.domain.Order;
import java.util.List;
import java.util.Map;
public interface IOrderService extends IService<Order> {
List<Order> selectOrderList(Order order);
Page<Order> selectOrderPage(Page<Order> page, Order order);
Order selectOrderById(Long id);
int insertOrder(Order order);
int updateOrder(Order order);
int deleteOrderByIds(Long[] ids);
int deleteOrderById(Long id);
int countOrders(Map<String, Object> params);
Order selectOrderBySlotId(Long slotId);
int updateOrderStatusById(Long id, Integer status);
int updateOrderCancelInfoById(Long id, java.util.Date cancelTime, String cancelReason);
Order createAppointmentOrder(Map<String, Object> params);
int cancelAppointmentOrder(Long orderId, String cancelReason);
}

View File

@@ -0,0 +1,105 @@
package com.openhis.clinical.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.openhis.clinical.domain.Ticket;
import java.util.List;
import java.util.Map;
/**
* 号源管理Service接口
*
* @author system
*/
public interface ITicketService extends IService<Ticket> {
/**
* 查询号源列表
*
* @param ticket 号源信息
* @return 号源集合
*/
List<Ticket> selectTicketList(Ticket ticket);
/**
* 分页查询号源列表
*
* @param page 分页参数
* @param ticket 号源信息
* @return 号源集合
*/
Page<Ticket> selectTicketPage(Page<Ticket> page, Ticket ticket);
/**
* 查询号源信息
*
* @param id 号源ID
* @return 号源信息
*/
Ticket selectTicketById(Long id);
/**
* 新增号源
*
* @param ticket 号源信息
* @return 结果
*/
int insertTicket(Ticket ticket);
/**
* 修改号源
*
* @param ticket 号源信息
* @return 结果
*/
int updateTicket(Ticket ticket);
/**
* 批量删除号源
*
* @param ids 需要删除的数据ID
* @return 结果
*/
int deleteTicketByIds(Long[] ids);
/**
* 删除号源信息
*
* @param id 号源ID
* @return 结果
*/
int deleteTicketById(Long id);
/**
* 预约号源
*
* @param params 预约参数
* @return 结果
*/
int bookTicket(Map<String, Object> params);
/**
* 取消预约
*
* @param ticketId 号源ID
* @return 结果
*/
int cancelTicket(Long ticketId);
/**
* 取号
*
* @param ticketId 号源ID
* @return 结果
*/
int checkInTicket(Long ticketId);
/**
* 停诊
*
* @param ticketId 号源ID
* @return 结果
*/
int cancelConsultation(Long ticketId);
}

View File

@@ -0,0 +1,164 @@
package com.openhis.clinical.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.core.common.utils.AssignSeqUtil;
import com.core.common.utils.SecurityUtils;
import com.openhis.clinical.domain.Order;
import com.openhis.clinical.domain.Ticket;
import com.openhis.clinical.mapper.OrderMapper;
import com.openhis.clinical.mapper.TicketMapper;
import com.openhis.clinical.service.IOrderService;
import com.openhis.common.enums.AssignSeqEnum;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
@Resource
private OrderMapper orderMapper;
@Resource
private TicketMapper ticketMapper;
@Resource
private AssignSeqUtil assignSeqUtil;
@Override
public List<Order> selectOrderList(Order order) {
return orderMapper.selectOrderList(order);
}
@Override
public Page<Order> selectOrderPage(Page<Order> page, Order order) {
return orderMapper.selectOrderPage(page, order);
}
@Override
public Order selectOrderById(Long id) {
return orderMapper.selectOrderById(id);
}
@Override
public int insertOrder(Order order) {
return orderMapper.insertOrder(order);
}
@Override
public int updateOrder(Order order) {
return orderMapper.updateOrder(order);
}
@Override
public int deleteOrderByIds(Long[] ids) {
return orderMapper.deleteOrderByIds(ids);
}
@Override
public int deleteOrderById(Long id) {
return orderMapper.deleteOrderById(id);
}
@Override
public int countOrders(Map<String, Object> params) {
return orderMapper.countOrders(params);
}
@Override
public Order selectOrderBySlotId(Long slotId) {
return orderMapper.selectOrderBySlotId(slotId);
}
@Override
public int updateOrderStatusById(Long id, Integer status) {
return orderMapper.updateOrderStatusById(id, status);
}
@Override
public int updateOrderCancelInfoById(Long id, Date cancelTime, String cancelReason) {
return orderMapper.updateOrderCancelInfoById(id, cancelTime, cancelReason);
}
@Override
public Order createAppointmentOrder(Map<String, Object> params) {
Long slotId = params.get("slotId") != null ? Long.valueOf(params.get("slotId").toString()) : null;
if (slotId == null) {
throw new RuntimeException("号源ID不能为空");
}
Ticket ticket = ticketMapper.selectTicketById(slotId);
if (ticket == null) {
throw new RuntimeException("号源不存在");
}
Order order = new Order();
String orderNo = assignSeqUtil.getSeq(AssignSeqEnum.ORDER_NUM.getPrefix(), 18);
order.setOrderNo(orderNo);
Long patientId = params.get("patientId") != null ? Long.valueOf(params.get("patientId").toString()) : null;
String patientName = params.get("patientName") != null ? params.get("patientName").toString() : null;
String medicalCard = params.get("medicalCard") != null ? params.get("medicalCard").toString() : null;
String phone = params.get("phone") != null ? params.get("phone").toString() : null;
Integer gender = params.get("gender") != null ? Integer.valueOf(params.get("gender").toString()) : null;
order.setPatientId(patientId);
order.setPatientName(patientName);
order.setMedicalCard(medicalCard);
order.setPhone(phone);
order.setGender(gender);
order.setSlotId(slotId);
order.setDepartmentId(ticket.getDepartmentId());
order.setDepartmentName(ticket.getDepartment());
order.setDoctorId(ticket.getDoctorId());
order.setDoctorName(ticket.getDoctor());
String regType = params.get("regType") != null ? params.get("regType").toString() : "普通";
order.setRegType(regType);
BigDecimal fee = params.get("fee") != null ? new BigDecimal(params.get("fee").toString()) : BigDecimal.ZERO;
order.setFee(fee);
Date appointmentDate = new Date();
order.setAppointmentDate(appointmentDate);
order.setAppointmentTime(new Date());
order.setStatus(1);
order.setPayStatus(0);
order.setVersion(0);
// 设置租户ID
Integer tenantId = params.get("tenant_id") != null ? Integer.valueOf(params.get("tenant_id").toString()) : null;
order.setTenantId(tenantId);
order.setCreateTime(new Date());
order.setUpdateTime(new Date());
orderMapper.insertOrder(order);
return order;
}
@Override
public int cancelAppointmentOrder(Long orderId, String cancelReason) {
Order order = orderMapper.selectOrderById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
if (order.getStatus() == 3) {
throw new RuntimeException("订单已取消");
}
if (order.getStatus() == 2) {
throw new RuntimeException("订单已完成,无法取消");
}
Date cancelTime = new Date();
return orderMapper.updateOrderCancelInfoById(orderId, cancelTime, cancelReason);
}
}

View File

@@ -0,0 +1,267 @@
package com.openhis.clinical.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.openhis.clinical.domain.Order;
import com.openhis.clinical.domain.Ticket;
import com.openhis.clinical.mapper.TicketMapper;
import com.openhis.clinical.service.IOrderService;
import com.openhis.clinical.service.ITicketService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 号源管理Service业务层处理
*
* @author system
*/
@Service
public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> implements ITicketService {
private static final Logger logger = LoggerFactory.getLogger(TicketServiceImpl.class);
@Resource
private TicketMapper ticketMapper;
@Resource
private IOrderService orderService;
/**
* 查询号源列表
*
* @param ticket 号源信息
* @return 号源集合
*/
@Override
public List<Ticket> selectTicketList(Ticket ticket) {
return ticketMapper.selectTicketList(ticket);
}
/**
* 分页查询号源列表
*
* @param page 分页参数
* @param ticket 号源信息
* @return 号源集合
*/
@Override
public Page<Ticket> selectTicketPage(Page<Ticket> page, Ticket ticket) {
return ticketMapper.selectTicketPage(page, ticket);
}
/**
* 查询号源信息
*
* @param id 号源ID
* @return 号源信息
*/
@Override
public Ticket selectTicketById(Long id) {
return ticketMapper.selectTicketById(id);
}
/**
* 新增号源
*
* @param ticket 号源信息
* @return 结果
*/
@Override
public int insertTicket(Ticket ticket) {
return ticketMapper.insertTicket(ticket);
}
/**
* 修改号源
*
* @param ticket 号源信息
* @return 结果
*/
@Override
public int updateTicket(Ticket ticket) {
return ticketMapper.updateTicket(ticket);
}
/**
* 批量删除号源
*
* @param ids 需要删除的数据ID
* @return 结果
*/
@Override
public int deleteTicketByIds(Long[] ids) {
return ticketMapper.deleteTicketByIds(ids);
}
/**
* 删除号源信息
*
* @param id 号源ID
* @return 结果
*/
@Override
public int deleteTicketById(Long id) {
return ticketMapper.deleteTicketById(id);
}
/**
* 预约号源
*
* @param params 预约参数
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int bookTicket(Map<String, Object> params) {
Long ticketId = Long.valueOf(params.get("ticketId").toString());
Long patientId = params.get("patientId") != null ? Long.valueOf(params.get("patientId").toString()) : null;
String patientName = params.get("patientName") != null ? params.get("patientName").toString() : null;
String medicalCard = params.get("medicalCard") != null ? params.get("medicalCard").toString() : null;
String phone = params.get("phone") != null ? params.get("phone").toString() : null;
logger.debug("开始预约号源ticketId: {}, patientId: {}, patientName: {}", ticketId, patientId, patientName);
Ticket ticket = ticketMapper.selectTicketById(ticketId);
if (ticket == null) {
logger.error("号源不存在ticketId: {}", ticketId);
throw new RuntimeException("号源不存在");
}
logger.debug("查询到号源信息id: {}, status: {}, deleteFlag: {}", ticket.getId(), ticket.getStatus(), ticket.getDeleteFlag());
// 详细调试:检查状态字符串的详细信息
String status = ticket.getStatus();
logger.debug("状态字符串详细信息: value='{}', length={}, isNull={}", status, status != null ? status.length() : "null", status == null);
if (status != null) {
StringBuilder charInfo = new StringBuilder();
for (int i = 0; i < status.length(); i++) {
charInfo.append(status.charAt(i)).append("(").append((int) status.charAt(i)).append(") ");
}
logger.debug("状态字符串字符信息: {}", charInfo.toString());
}
// 详细调试:检查每个状态比较的结果
boolean isUnbooked = "unbooked".equals(status);
boolean isLocked = "locked".equals(status);
boolean isCancelled = "cancelled".equals(status);
boolean isChecked = "checked".equals(status);
boolean isBooked = "booked".equals(status);
logger.debug("状态比较结果: unbooked={}, locked={}, cancelled={}, checked={}, booked={}",
isUnbooked, isLocked, isCancelled, isChecked, isBooked);
if (!isUnbooked && !isLocked && !isCancelled && !isChecked && !isBooked) {
logger.error("号源不可预约id: {}, status: {}", ticket.getId(), ticket.getStatus());
throw new RuntimeException("号源不可预约");
}
params.put("slotId", ticketId);
Order order = orderService.createAppointmentOrder(params);
Ticket updateTicket = new Ticket();
updateTicket.setId(ticketId);
updateTicket.setStatus("booked");
updateTicket.setPatientId(patientId);
updateTicket.setPatientName(patientName);
updateTicket.setMedicalCard(medicalCard);
updateTicket.setPhone(phone);
updateTicket.setAppointmentDate(new Date());
updateTicket.setAppointmentTime(new Date());
int result = ticketMapper.updateById(updateTicket);
logger.debug("预约成功更新号源状态为bookedresult: {}", result);
return result;
}
/**
* 取消预约
*
* @param ticketId 号源ID
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int cancelTicket(Long ticketId) {
Ticket ticket = ticketMapper.selectTicketById(ticketId);
if (ticket == null) {
throw new RuntimeException("号源不存在");
}
if (!"booked".equals(ticket.getStatus()) && !"locked".equals(ticket.getStatus())) {
throw new RuntimeException("号源不可取消预约");
}
Order order = orderService.selectOrderBySlotId(ticketId);
if (order != null) {
orderService.cancelAppointmentOrder(order.getId(), "患者取消预约");
}
ticket.setStatus("unbooked");
ticket.setPatientId(null);
ticket.setPatientName(null);
ticket.setMedicalCard(null);
ticket.setPhone(null);
ticket.setAppointmentDate(null);
ticket.setAppointmentTime(null);
return ticketMapper.updateTicket(ticket);
}
/**
* 取号
*
* @param ticketId 号源ID
* @return 结果
*/
@Override
public int checkInTicket(Long ticketId) {
// 获取号源信息
Ticket ticket = ticketMapper.selectTicketById(ticketId);
if (ticket == null) {
throw new RuntimeException("号源不存在");
}
if (!"booked".equals(ticket.getStatus()) && !"locked".equals(ticket.getStatus())) {
throw new RuntimeException("号源不可取号");
}
// 更新号源状态为已取号
ticket.setStatus("checked");
return ticketMapper.updateTicket(ticket);
}
/**
* 停诊
*
* @param ticketId 号源ID
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int cancelConsultation(Long ticketId) {
// 获取号源信息
Ticket ticket = ticketMapper.selectTicketById(ticketId);
if (ticket == null) {
throw new RuntimeException("号源不存在");
}
// 检查是否存在相关订单,如果存在则取消
Order order = orderService.selectOrderBySlotId(ticketId);
if (order != null) {
orderService.cancelAppointmentOrder(order.getId(), "医生停诊");
}
// 更新号源状态为已取消
ticket.setStatus("cancelled");
ticket.setPatientId(null);
ticket.setPatientName(null);
ticket.setMedicalCard(null);
ticket.setPhone(null);
ticket.setAppointmentDate(null);
ticket.setAppointmentTime(null);
return ticketMapper.updateTicket(ticket);
}
}

View File

@@ -0,0 +1,270 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.openhis.clinical.mapper.OrderMapper">
<resultMap type="com.openhis.clinical.domain.Order" id="OrderResult">
<id property="id" column="id"/>
<result property="orderNo" column="order_no"/>
<result property="patientId" column="patient_id"/>
<result property="patientName" column="patient_name"/>
<result property="medicalCard" column="medical_card"/>
<result property="phone" column="phone"/>
<result property="gender" column="gender"/>
<result property="scheduleId" column="schedule_id"/>
<result property="slotId" column="slot_id"/>
<result property="departmentId" column="department_id"/>
<result property="departmentName" column="department_name"/>
<result property="doctorId" column="doctor_id"/>
<result property="doctorName" column="doctor_name"/>
<result property="regType" column="reg_type"/>
<result property="fee" column="fee"/>
<result property="appointmentDate" column="appointment_date"/>
<result property="appointmentTime" column="appointment_time"/>
<result property="cancelTime" column="cancel_time"/>
<result property="cancelReason" column="cancel_reason"/>
<result property="status" column="status"/>
<result property="payStatus" column="pay_status"/>
<result property="version" column="version"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<select id="selectOrderList" resultMap="OrderResult">
select * from order_main
<where>
<if test="orderNo != null and orderNo != ''">
and order_no = #{orderNo}
</if>
<if test="patientId != null">
and patient_id = #{patientId}
</if>
<if test="patientName != null and patientName != ''">
and patient_name like #{patientName}
</if>
<if test="medicalCard != null and medicalCard != ''">
and medical_card = #{medicalCard}
</if>
<if test="phone != null and phone != ''">
and phone = #{phone}
</if>
<if test="scheduleId != null">
and schedule_id = #{scheduleId}
</if>
<if test="slotId != null">
and slot_id = #{slotId}
</if>
<if test="departmentId != null">
and department_id = #{departmentId}
</if>
<if test="doctorId != null">
and doctor_id = #{doctorId}
</if>
<if test="status != null">
and status = #{status}
</if>
<if test="payStatus != null">
and pay_status = #{payStatus}
</if>
<if test="appointmentDate != null">
and appointment_date = #{appointmentDate}
</if>
</where>
</select>
<select id="selectOrderPage" resultMap="OrderResult">
select * from order_main
<where>
<if test="order.orderNo != null and order.orderNo != ''">
and order_no = #{order.orderNo}
</if>
<if test="order.patientId != null">
and patient_id = #{order.patientId}
</if>
<if test="order.patientName != null and order.patientName != ''">
and patient_name like #{order.patientName}
</if>
<if test="order.medicalCard != null and order.medicalCard != ''">
and medical_card = #{order.medicalCard}
</if>
<if test="order.phone != null and order.phone != ''">
and phone = #{order.phone}
</if>
<if test="order.scheduleId != null">
and schedule_id = #{order.scheduleId}
</if>
<if test="order.slotId != null">
and slot_id = #{order.slotId}
</if>
<if test="order.departmentId != null">
and department_id = #{order.departmentId}
</if>
<if test="order.doctorId != null">
and doctor_id = #{order.doctorId}
</if>
<if test="order.status != null">
and status = #{order.status}
</if>
<if test="order.payStatus != null">
and pay_status = #{order.payStatus}
</if>
<if test="order.appointmentDate != null">
and appointment_date = #{order.appointmentDate}
</if>
</where>
</select>
<select id="selectOrderById" resultMap="OrderResult">
select * from order_main where id = #{id}
</select>
<select id="selectOrderBySlotId" resultMap="OrderResult">
select * from order_main where slot_id = #{slotId} and status = 1
</select>
<insert id="insertOrder" parameterType="com.openhis.clinical.domain.Order">
insert into order_main
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="orderNo != null and orderNo != ''">order_no,</if>
<if test="patientId != null">patient_id,</if>
<if test="patientName != null and patientName != ''">patient_name,</if>
<if test="medicalCard != null and medicalCard != ''">medical_card,</if>
<if test="phone != null and phone != ''">phone,</if>
<if test="gender != null">gender,</if>
<if test="scheduleId != null">schedule_id,</if>
<if test="slotId != null">slot_id,</if>
<if test="departmentId != null">department_id,</if>
<if test="departmentName != null and departmentName != ''">department_name,</if>
<if test="doctorId != null">doctor_id,</if>
<if test="doctorName != null and doctorName != ''">doctor_name,</if>
<if test="regType != null and regType != ''">reg_type,</if>
<if test="fee != null">fee,</if>
<if test="appointmentDate != null">appointment_date,</if>
<if test="appointmentTime != null">appointment_time,</if>
<if test="cancelTime != null">cancel_time,</if>
<if test="cancelReason != null and cancelReason != ''">cancel_reason,</if>
<if test="status != null">status,</if>
<if test="payStatus != null">pay_status,</if>
<if test="version != null">version,</if>
<if test="tenantId != null">tenant_id,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null and updateBy != ''">update_by,</if>
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="orderNo != null and orderNo != ''">#{orderNo},</if>
<if test="patientId != null">#{patientId},</if>
<if test="patientName != null and patientName != ''">#{patientName},</if>
<if test="medicalCard != null and medicalCard != ''">#{medicalCard},</if>
<if test="phone != null and phone != ''">#{phone},</if>
<if test="gender != null">#{gender},</if>
<if test="scheduleId != null">#{scheduleId},</if>
<if test="slotId != null">#{slotId},</if>
<if test="departmentId != null">#{departmentId},</if>
<if test="departmentName != null and departmentName != ''">#{departmentName},</if>
<if test="doctorId != null">#{doctorId},</if>
<if test="doctorName != null and doctorName != ''">#{doctorName},</if>
<if test="regType != null and regType != ''">#{regType},</if>
<if test="fee != null">#{fee},</if>
<if test="appointmentDate != null">#{appointmentDate},</if>
<if test="appointmentTime != null">#{appointmentTime},</if>
<if test="cancelTime != null">#{cancelTime},</if>
<if test="cancelReason != null and cancelReason != ''">#{cancelReason},</if>
<if test="status != null">#{status},</if>
<if test="payStatus != null">#{payStatus},</if>
<if test="version != null">#{version},</if>
<if test="tenantId != null">#{tenantId},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null and updateBy != ''">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
</trim>
</insert>
<update id="updateOrder" parameterType="com.openhis.clinical.domain.Order">
update order_main
<trim prefix="set" suffixOverrides=",">
<if test="orderNo != null and orderNo != ''">order_no = #{orderNo},</if>
<if test="patientId != null">patient_id = #{patientId},</if>
<if test="patientName != null and patientName != ''">patient_name = #{patientName},</if>
<if test="medicalCard != null and medicalCard != ''">medical_card = #{medicalCard},</if>
<if test="phone != null and phone != ''">phone = #{phone},</if>
<if test="gender != null">gender = #{gender},</if>
<if test="scheduleId != null">schedule_id = #{scheduleId},</if>
<if test="slotId != null">slot_id = #{slotId},</if>
<if test="departmentId != null">department_id = #{departmentId},</if>
<if test="departmentName != null and departmentName != ''">department_name = #{departmentName},</if>
<if test="doctorId != null">doctor_id = #{doctorId},</if>
<if test="doctorName != null and doctorName != ''">doctor_name = #{doctorName},</if>
<if test="regType != null and regType != ''">reg_type = #{regType},</if>
<if test="fee != null">fee = #{fee},</if>
<if test="appointmentDate != null">appointment_date = #{appointmentDate},</if>
<if test="appointmentTime != null">appointment_time = #{appointmentTime},</if>
<if test="cancelTime != null">cancel_time = #{cancelTime},</if>
<if test="cancelReason != null and cancelReason != ''">cancel_reason = #{cancelReason},</if>
<if test="status != null">status = #{status},</if>
<if test="payStatus != null">pay_status = #{payStatus},</if>
<if test="version != null">version = #{version},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
</trim>
where id = #{id}
</update>
<update id="updateOrderStatusById">
update order_main set status = #{status} where id = #{id}
</update>
<update id="updateOrderCancelInfoById">
update order_main set status = 3, cancel_time = #{cancelTime}, cancel_reason = #{cancelReason} where id = #{id}
</update>
<delete id="deleteOrderById">
delete from order_main where id = #{id}
</delete>
<delete id="deleteOrderByIds">
delete from order_main where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<select id="countOrders" resultType="int">
select count(*) from order_main
<where>
<if test="orderNo != null and orderNo != ''">
and order_no = #{orderNo}
</if>
<if test="patientId != null">
and patient_id = #{patientId}
</if>
<if test="scheduleId != null">
and schedule_id = #{scheduleId}
</if>
<if test="slotId != null">
and slot_id = #{slotId}
</if>
<if test="departmentId != null">
and department_id = #{departmentId}
</if>
<if test="doctorId != null">
and doctor_id = #{doctorId}
</if>
<if test="status != null">
and status = #{status}
</if>
<if test="payStatus != null">
and pay_status = #{payStatus}
</if>
<if test="appointmentDate != null">
and appointment_date = #{appointmentDate}
</if>
</where>
</select>
</mapper>

View File

@@ -0,0 +1,245 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.openhis.clinical.mapper.TicketMapper">
<resultMap type="com.openhis.clinical.domain.Ticket" id="TicketResult">
<id property="id" column="id"/>
<result property="busNo" column="bus_no"/>
<result property="department" column="department"/>
<result property="doctor" column="doctor"/>
<result property="ticketType" column="ticket_type"/>
<result property="time" column="time"/>
<result property="status" column="status"/>
<result property="fee" column="fee"/>
<result property="patientId" column="patient_id"/>
<result property="patientName" column="patient_name"/>
<result property="medicalCard" column="medical_card"/>
<result property="phone" column="phone"/>
<result property="appointmentDate" column="appointment_date"/>
<result property="appointmentTime" column="appointment_time"/>
<result property="departmentId" column="department_id"/>
<result property="doctorId" column="doctor_id"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<select id="selectTicketList" parameterType="com.openhis.clinical.domain.Ticket" resultMap="TicketResult">
select * from clinical_ticket
<where>
<!-- 逻辑删除条件 -->
and delete_flag = '0'
<!-- 其他查询条件 -->
<if test="ticketType != null and ticketType != ''">
and ticket_type = #{ticketType}
</if>
<if test="status != null and status != ''">
and status = #{status}
</if>
<if test="appointmentDate != null">
and appointment_date = cast(#{appointmentDate} as date)
</if>
<if test="doctorId != null">
and doctor_id = #{doctorId}
</if>
<if test="departmentId != null">
and department_id = #{departmentId}
</if>
<if test="time != null and time != ''">
and time like #{time}
</if>
<if test="patientName != null and patientName != ''">
and patient_name = #{patientName}
</if>
<if test="medicalCard != null and medicalCard != ''">
and medical_card = #{medicalCard}
</if>
<if test="phone != null and phone != ''">
and phone = #{phone}
</if>
</where>
</select>
<select id="selectTicketPage" resultMap="TicketResult">
select * from clinical_ticket
<where>
<!-- 逻辑删除条件 -->
and delete_flag = '0'
<!-- 其他查询条件 -->
<if test="ticket.ticketType != null and ticket.ticketType != ''">
and ticket_type = #{ticket.ticketType}
</if>
<if test="ticket.status != null and ticket.status != ''">
and status = #{ticket.status}
</if>
<if test="ticket.appointmentDate != null">
and appointment_date = cast(#{ticket.appointmentDate} as date)
</if>
<if test="ticket.doctorId != null">
and doctor_id = #{ticket.doctorId}
</if>
<if test="ticket.departmentId != null">
and department_id = #{ticket.departmentId}
</if>
<if test="ticket.time != null and ticket.time != ''">
and time like #{ticket.time}
</if>
<if test="ticket.patientName != null and ticket.patientName != ''">
and patient_name = #{ticket.patientName}
</if>
<if test="ticket.medicalCard != null and ticket.medicalCard != ''">
and medical_card = #{ticket.medicalCard}
</if>
<if test="ticket.phone != null and ticket.phone != ''">
and phone = #{ticket.phone}
</if>
</where>
</select>
<!-- 自定义COUNT查询解决MyBatis-Plus自动生成的COUNT查询结果不正确的问题 -->
<select id="selectTicketPage_mpCount" resultType="java.lang.Long">
select count(*) from clinical_ticket
<where>
<!-- 逻辑删除条件 -->
and delete_flag = '0'
<!-- 其他查询条件 -->
<if test="ticket.ticketType != null and ticket.ticketType != ''">
and ticket_type = #{ticket.ticketType}
</if>
<if test="ticket.status != null and ticket.status != ''">
and status = #{ticket.status}
</if>
<if test="ticket.appointmentDate != null">
and appointment_date = #{ticket.appointmentDate}
</if>
<if test="ticket.doctorId != null">
and doctor_id = #{ticket.doctorId}
</if>
<if test="ticket.departmentId != null">
and department_id = #{ticket.departmentId}
</if>
<if test="ticket.time != null and ticket.time != ''">
and time like #{ticket.time}
</if>
<if test="ticket.patientName != null and ticket.patientName != ''">
and patient_name = #{ticket.patientName}
</if>
<if test="ticket.medicalCard != null and ticket.medicalCard != ''">
and medical_card = #{ticket.medicalCard}
</if>
<if test="ticket.phone != null and ticket.phone != ''">
and phone = #{ticket.phone}
</if>
</where>
</select>
<select id="selectTicketById" resultMap="TicketResult">
select * from clinical_ticket where id = #{id} and delete_flag = '0'
</select>
<insert id="insertTicket" parameterType="com.openhis.clinical.domain.Ticket">
insert into clinical_ticket
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="busNo != null and busNo != ''">bus_no,</if>
<if test="department != null and department != ''">department,</if>
<if test="doctor != null and doctor != ''">doctor,</if>
<if test="ticketType != null and ticketType != ''">ticket_type,</if>
<if test="time != null and time != ''">time,</if>
<if test="status != null and status != ''">status,</if>
<if test="fee != null and fee != ''">fee,</if>
<if test="patientId != null">patient_id,</if>
<if test="patientName != null and patientName != ''">patient_name,</if>
<if test="medicalCard != null and medicalCard != ''">medical_card,</if>
<if test="phone != null and phone != ''">phone,</if>
<if test="appointmentDate != null">appointment_date,</if>
<if test="appointmentTime != null">appointment_time,</if>
<if test="departmentId != null">department_id,</if>
<if test="doctorId != null">doctor_id,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null and updateBy != ''">update_by,</if>
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="busNo != null and busNo != ''">#{busNo},</if>
<if test="department != null and department != ''">#{department},</if>
<if test="doctor != null and doctor != ''">#{doctor},</if>
<if test="ticketType != null and ticketType != ''">#{ticketType},</if>
<if test="time != null and time != ''">#{time},</if>
<if test="status != null and status != ''">#{status},</if>
<if test="fee != null and fee != ''">#{fee},</if>
<if test="patientId != null">#{patientId},</if>
<if test="patientName != null and patientName != ''">#{patientName},</if>
<if test="medicalCard != null and medicalCard != ''">#{medicalCard},</if>
<if test="phone != null and phone != ''">#{phone},</if>
<if test="appointmentDate != null">#{appointmentDate},</if>
<if test="appointmentTime != null">#{appointmentTime},</if>
<if test="departmentId != null">#{departmentId},</if>
<if test="doctorId != null">#{doctorId},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null and updateBy != ''">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
</trim>
</insert>
<update id="updateTicket" parameterType="com.openhis.clinical.domain.Ticket">
update clinical_ticket
<trim prefix="set" suffixOverrides=",">
<if test="busNo != null and busNo != ''">bus_no = #{busNo},</if>
<if test="department != null and department != ''">department = #{department},</if>
<if test="doctor != null and doctor != ''">doctor = #{doctor},</if>
<if test="ticketType != null and ticketType != ''">ticket_type = #{ticketType},</if>
<if test="time != null and time != ''">time = #{time},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="fee != null and fee != ''">fee = #{fee},</if>
<if test="patientId != null">patient_id = #{patientId},</if>
<if test="patientName != null and patientName != ''">patient_name = #{patientName},</if>
<if test="medicalCard != null and medicalCard != ''">medical_card = #{medicalCard},</if>
<if test="phone != null and phone != ''">phone = #{phone},</if>
<if test="appointmentDate != null">appointment_date = #{appointmentDate},</if>
<if test="appointmentTime != null">appointment_time = #{appointmentTime},</if>
<if test="departmentId != null">department_id = #{departmentId},</if>
<if test="doctorId != null">doctor_id = #{doctorId},</if>
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteTicketById">
delete from clinical_ticket where id = #{id}
</delete>
<delete id="deleteTicketByIds">
delete from clinical_ticket where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<select id="countTickets" resultType="int">
select count(*) from clinical_ticket
<where>
<if test="ticketType != null and ticketType != ''">
and ticket_type = #{ticketType}
</if>
<if test="status != null and status != ''">
and status = #{status}
</if>
<if test="appointmentDate != null">
and appointment_date = cast(#{appointmentDate} as date)
</if>
<if test="doctorId != null">
and doctor_id = #{doctorId}
</if>
<if test="departmentId != null">
and department_id = #{departmentId}
</if>
</where>
</select>
</mapper>

View File

@@ -0,0 +1,10 @@
@echo off
rem 设置项目根目录
set PROJECT_ROOT=%~dp0
rem 设置classpath
set CLASSPATH=%PROJECT_ROOT%openhis-application\target\classes;%PROJECT_ROOT%openhis-domain\target\classes;%PROJECT_ROOT%openhis-common\target\classes;%PROJECT_ROOT%core-admin\target\classes;%PROJECT_ROOT%core-framework\target\classes;%PROJECT_ROOT%core-system\target\classes;%PROJECT_ROOT%core-quartz\target\classes;%PROJECT_ROOT%core-generator\target\classes;%PROJECT_ROOT%core-flowable\target\classes;%PROJECT_ROOT%core-common\target\classes
rem 启动应用
java -cp "%CLASSPATH%" com.openhis.OpenHisApplication

View File

@@ -0,0 +1,13 @@
#!/bin/bash
# 设置项目根目录
PROJECT_ROOT=$(pwd)
# 设置classpath
CLASSPATH="$PROJECT_ROOT/openhis-application/target/classes:$PROJECT_ROOT/openhis-domain/target/classes:$PROJECT_ROOT/openhis-common/target/classes:$PROJECT_ROOT/core-admin/target/classes:$PROJECT_ROOT/core-framework/target/classes:$PROJECT_ROOT/core-system/target/classes:$PROJECT_ROOT/core-quartz/target/classes:$PROJECT_ROOT/core-generator/target/classes:$PROJECT_ROOT/core-flowable/target/classes:$PROJECT_ROOT/core-common/target/classes"
# 添加所有依赖jar包
export CLASSPATH="$CLASSPATH:$(find $PROJECT_ROOT/openhis-application/target/dependency -name '*.jar' | tr '\n' ':')"
# 启动应用
java -cp "$CLASSPATH" com.openhis.OpenHisApplication

View File

@@ -0,0 +1,57 @@
import request from '@/utils/request'
// 查询号源列表
export function listTicket(query) {
return request({
url: '/appointment/ticket/list',
method: 'post',
data: query
})
}
// 预约号源
export function bookTicket(data) {
return request({
url: '/appointment/ticket/book',
method: 'post',
data: data
})
}
// 取消预约
export function cancelTicket(ticketId) {
return request({
url: '/appointment/ticket/cancel',
method: 'post',
params: { ticketId }
})
}
// 取号
export function checkInTicket(ticketId) {
return request({
url: '/appointment/ticket/checkin',
method: 'post',
params: { ticketId }
})
}
// 停诊
export function cancelConsultation(ticketId) {
return request({
url: '/appointment/ticket/cancelConsultation',
method: 'post',
params: { ticketId }
})
}
// 查询所有号源(用于测试)
export function listAllTickets() {
return request({
url: '/appointment/ticket/listAll',
method: 'get',
headers: {
isToken: false
}
})
}

View File

@@ -6,27 +6,62 @@
:ellipsis="false"
>
<template v-for="(item, index) in topMenus">
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
<!-- 处理有子菜单的情况 -->
<template v-if="item.children && item.children.length > 0 && index < visibleNumber">
<el-sub-menu :style="{'--theme': theme}" :index="item.path" :key="index">
<template #title>
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }}
</template>
<template v-for="(child, childIndex) in item.children" :key="childIndex">
<el-menu-item :index="item.path + '/' + (child.path || '')">
{{ child.meta.title }}
</el-menu-item>
</template>
</el-sub-menu>
</template>
<!-- 处理无子菜单的情况 -->
<template v-else-if="index < visibleNumber">
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index">
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
</template>
</template>
<!-- 顶部菜单超出数量折叠 -->
<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
<template #title>更多菜单</template>
<template v-for="(item, index) in topMenus">
<el-menu-item
:index="item.path"
:key="index"
v-if="index >= visibleNumber">
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
<template v-for="(item, index) in topMenus" :key="index">
<!-- 处理有子菜单的情况 -->
<template v-if="item.children && item.children.length > 0 && index >= visibleNumber">
<el-sub-menu :index="item.path">
<template #title>
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }}
</template>
<template v-for="(child, childIndex) in item.children" :key="childIndex">
<el-menu-item :index="item.path + '/' + (child.path || '')">
{{ child.meta.title }}
</el-menu-item>
</template>
</el-sub-menu>
</template>
<!-- 处理无子菜单的情况 -->
<template v-else-if="index >= visibleNumber">
<el-menu-item :index="item.path">
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
</template>
</template>
</el-sub-menu>
</el-menu>
@@ -108,6 +143,20 @@ const activeMenu = computed(() => {
activePath = path;
appStore.toggleSideBarHide(true);
}
// 检查当前路径是否是子菜单路径
let isChildRoute = false;
for (const item of routers.value) {
if (item.children && item.children.length > 0) {
const childRoute = item.children.find(child => (item.path + '/' + (child.path || '')) === path);
if (childRoute) {
isChildRoute = true;
activePath = item.path; // 激活父菜单
break;
}
}
}
activeRoutes(activePath);
return activePath;
})
@@ -123,20 +172,42 @@ function handleSelect(key, keyPath) {
if (isHttp(key)) {
// http(s):// 路径新窗口打开
window.open(key, "_blank");
} else if (!route || !route.children) {
// 没有子路由路径内部打开
const routeMenu = childrenMenus.value.find(item => item.path === key);
if (routeMenu && routeMenu.query) {
let query = JSON.parse(routeMenu.query);
router.push({ path: key, query: query });
} else {
router.push({ path: key });
}
appStore.toggleSideBarHide(true);
} else {
// 显示左侧联动菜单
activeRoutes(key);
appStore.toggleSideBarHide(false);
// 检查是否是子菜单路径
let isChildRoute = false;
let parentRoute = null;
// 查找父路由
for (const item of routers.value) {
if (item.children && item.children.length > 0) {
const childRoute = item.children.find(child => (item.path + '/' + (child.path || '')) === key);
if (childRoute) {
isChildRoute = true;
parentRoute = item;
break;
}
}
}
if (isChildRoute) {
// 处理子菜单路径
router.push({ path: key });
appStore.toggleSideBarHide(true);
} else if (!route || !route.children) {
// 没有子路由路径内部打开
const routeMenu = childrenMenus.value.find(item => item.path === key);
if (routeMenu && routeMenu.query) {
let query = JSON.parse(routeMenu.query);
router.push({ path: key, query: query });
} else {
router.push({ path: key });
}
appStore.toggleSideBarHide(true);
} else {
// 显示左侧联动菜单
activeRoutes(key);
appStore.toggleSideBarHide(false);
}
}
}

View File

@@ -9,7 +9,7 @@
</app-link>
</template>
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)">
<template v-if="item.meta" #title>
<svg-icon v-if="item.meta.icon" :icon-class="item.meta.icon" />
<span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>

View File

@@ -58,7 +58,7 @@ const isCollapse = computed(() => !appStore.sidebar.opened);
const activeMenu = computed(() => {
const { meta, path } = route;
// if set path, the sidebar will highlight the path you set
// if set path, sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu;
}
@@ -183,4 +183,4 @@ const activeMenu = computed(() => {
}
}
}
</style>
</style>

View File

@@ -1,32 +1,31 @@
import {createRouter, createWebHistory} from 'vue-router';
import { createWebHistory, createRouter } from 'vue-router'
/* Layout */
import Layout from '@/layout';
import Layout from '@/layout'
/**
* Note: 路由配置项说明
* Note: 路由配置项
*
* hidden: true // 当设置 true 时,该路由不会侧边栏出现如401login等页面或一些编辑页面/edit/1
* alwaysShow: true // 当路由下的 children 声明的路由大于1个时自动变为嵌套模式如组件页面
* // 只有一个时,会将子路由作为根路由显示在侧边栏如引导页面
* // 若想不管 children 个数都显示根路由,可设置 alwaysShow: true忽略之前定义的规则
* redirect: noRedirect // 当设置 noRedirect 时,该路由在面包屑导航中不可点击
* name:'router-name' // 设定路由的名字,必须填写,否则使用<keep-alive>时会出现问题
* hidden: true // 当设置 true 的时候该路由不会侧边栏出现 如401login等页面者如一些编辑页面/edit/1
* alwaysShow: true // 当你一个路由下的 children 声明的路由大于1个时自动会变成嵌套模式--如组件页面
* // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
* // 若想不管路由下面的 children 声明的个数都显示你的根路由
* // 你可以设置 alwaysShow: true这样它就会忽略之前定义的规则一直显示根路由
* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
* name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
* roles: ['admin', 'common'] // 访问路由的角色权限
* permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限
* meta: {
noCache: true // 如果设置为true则不会被 <keep-alive> 缓存默认 false
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
icon: 'svg-name' // 设置该路由的图标对应路径src/assets/icons/svg
breadcrumb: false // 如果设置为false则不会在breadcrumb面包屑中显示
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮对应的侧边栏
* meta : {
noCache: true // 如果设置为true则不会被 <keep-alive> 缓存(默认 false)
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
icon: 'svg-name' // 设置该路由的图标对应路径src/assets/icons/svg
breadcrumb: false // 如果设置为false则不会在breadcrumb面包屑中显示
activeMenu: '/system/user' // 当路由设置了该属性,则会高亮对应的侧边栏
}
*/
// 公共路由 - 所有用户均可访问的路由
// 公共路由
export const constantRoutes = [
// 重定向路由
{
path: '/redirect',
component: Layout,
@@ -34,33 +33,25 @@ export const constantRoutes = [
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue'),
},
],
component: () => import('@/views/redirect/index.vue')
}
]
},
// 登录路由
{
path: '/login',
component: () => import('@/views/login'),
hidden: true,
hidden: true
},
// 注册路由
{
path: '/register',
component: () => import('@/views/register'),
hidden: true,
},
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/error/404'),
hidden: true,
hidden: true
},
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true,
hidden: true
},
// 首页路由
{
path: '',
component: Layout,
@@ -70,9 +61,9 @@ export const constantRoutes = [
path: '/index',
component: () => import('@/views/index'),
name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true },
},
],
meta: { title: '首页', icon: 'dashboard', affix: true }
}
]
},
{
path: '/user',
@@ -84,110 +75,154 @@ export const constantRoutes = [
path: 'profile',
component: () => import('@/views/system/user/profile/index'),
name: 'Profile',
meta: { title: '个人中心', icon: 'user' },
meta: { title: '个人中心', icon: 'user' }
}
]
},
// 添加套餐管理相关路由到公共路由,确保始终可用
{
path: '/maintainSystem/Inspection/PackageManagement',
component: Layout,
hidden: true,
children: [
{
path: '',
component: () => import('@/views/maintainSystem/Inspection/PackageManagement.vue'),
name: 'DirectPackageManagement',
meta: { title: '套餐管理' }
}
]
}
]
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [
{
path: '/basicmanage',
component: Layout,
redirect: '/basicmanage/invoice-management',
name: 'BasicManage',
meta: { title: '基础管理', icon: 'component' },
children: [
{
path: 'invoice-management',
component: () => import('@/views/basicmanage/InvoiceManagement/index.vue'),
name: 'invoice-management',
meta: { title: '发票管理' }
}
]
},
// 兼容系统业务管理路径
{
path: '/system/ywgz',
component: Layout,
redirect: '/system/ywgz/InvoiceManagement',
hidden: true,
children: [
{
path: 'InvoiceManagement',
component: () => import('@/views/basicmanage/InvoiceManagement/index.vue'),
name: 'SystemInvoiceManagement',
meta: { title: '发票管理' }
}
]
},
{
path: '/maintainSystem',
component: Layout,
redirect: '/maintainSystem/chargeConfig',
name: 'MaintainSystem',
meta: { title: '维护系统', icon: 'system' },
children: [
{
path: '',
redirect: 'chargeConfig'
},
],
{
path: 'chargeConfig',
component: () => import('@/views/maintainSystem/chargeConfig/index.vue'),
name: 'ChargeConfig',
meta: { title: '挂号收费系统参数维护', icon: 'config', permissions: ['maintainSystem:chargeConfig:list'] }
},
{
path: 'Inspection',
component: () => import('@/views/maintainSystem/Inspection/index.vue'),
name: 'Inspection',
meta: { title: '检验管理', icon: 'inspection' },
children: [
{
path: 'PackageManagement',
component: () => import('@/views/maintainSystem/Inspection/PackageManagement.vue'),
name: 'PackageManagement',
meta: { title: '套餐管理' }
}
]
}
]
},
{
path: '/tpr',
component: () => import('@/views/inpatientNurse/tprsheet/index.vue'),
}
];
// 动态路由 - 基于用户权限动态加载的路由
export const dynamicRoutes = [
// 基础管理路由
// {
// path: '/basicmanage',
// component: Layout,
// redirect: '/basicmanage/invoice-management',
// name: 'BasicManage',
// meta: { title: '基础管理', icon: 'component' },
// children: [
// {
// path: 'invoice-management',
// component: () => import('@/views/basicmanage/InvoiceManagement/index.vue'),
// name: 'invoice-management',
// meta: { title: '发票管理' }
// }
// ]
// },
// 兼容系统业务管理路径的发票管理路由
// {
// path: '/system/ywgz',
// component: Layout,
// redirect: '/system/ywgz/InvoiceManagement',
// hidden: true,
// children: [
// {
// path: 'InvoiceManagement',
// component: () => import('@/views/basicmanage/InvoiceManagement/index.vue'),
// name: 'SystemInvoiceManagement',
// meta: { title: '发票管理' }
// }
// ]
// },
// 维护系统路由
// {
// path: '/maintainSystem',
// component: Layout,
// redirect: '/maintainSystem/chargeConfig',
// name: 'MaintainSystem',
// meta: { title: '维护系统', icon: 'system' },
// children: [
// {
// path: '',
// redirect: 'chargeConfig',
// name: 'MaintainSystemIndex' // 添加名称以解决警告
// },
// {
// path: 'chargeConfig', // 收费配置路由
// component: () => import('@/views/maintainSystem/chargeConfig/index.vue'),
// name: 'ChargeConfig',
// meta: { title: '挂号收费系统参数维护', icon: 'config', permissions: ['maintainSystem:chargeConfig:list'] }
// },
// {
// path: 'Inspection', // 检验管理路由
// component: () => import('@/views/maintainSystem/Inspection/index.vue'),
// name: 'Inspection',
// meta: { title: '检验管理', icon: 'inspection' },
// children: [
// {
// path: 'PackageManagement', // 套餐管理路由
// component: () => import('@/views/maintainSystem/Inspection/PackageManagement.vue'),
// name: 'PackageManagement',
// meta: { title: '套餐管理' }
// }
// ]
// }
// ]
// },
// 系统管理路由
// {
// path: '/system',
// component: Layout,
// redirect: '/system/user',
// name: 'System',
// meta: { title: '系统管理', icon: 'system' },
// children: [
// {
// path: 'basicdata',
// component: Layout,
// redirect: '/system/basicdata/location',
// name: 'BasicData',
// meta: { title: '基础数据', icon: 'location' },
// children: [
// {
// path: 'operatingroom',
// component: () => import('@/views/operatingroom/index.vue'),
// name: 'OperatingRoomManage',
// meta: { title: '手术室管理' }
// }
// ]
// }
// ]
// },
// 租户用户设置路由
path: '/system',
component: Layout,
redirect: '/system/user',
name: 'System',
meta: { title: '系统管理', icon: 'system' },
children: [
{
path: 'user',
component: () => import('@/views/system/user/index.vue'),
name: 'User',
meta: { title: '用户管理', icon: 'user', permissions: ['system:user:list'] }
},
{
path: 'role',
component: () => import('@/views/system/role/index.vue'),
name: 'Role',
meta: { title: '角色管理', icon: 'role', permissions: ['system:role:list'] }
},
{
path: 'menu',
component: () => import('@/views/system/menu/index.vue'),
name: 'Menu',
meta: { title: '菜单管理', icon: 'menu', permissions: ['system:menu:list'] }
},
{
path: 'dept',
component: () => import('@/views/system/dept/index.vue'),
name: 'Dept',
meta: { title: '部门管理', icon: 'dept', permissions: ['system:dept:list'] }
},
{
path: 'post',
component: () => import('@/views/system/post/index.vue'),
name: 'Post',
meta: { title: '岗位管理', icon: 'post', permissions: ['system:post:list'] }
},
{
path: 'dict',
component: () => import('@/views/system/dict/index.vue'),
name: 'Dict',
meta: { title: '字典管理', icon: 'dict', permissions: ['system:dict:list'] }
},
{
path: 'config',
component: () => import('@/views/system/config/index.vue'),
name: 'Config',
meta: { title: '参数配置', icon: 'config', permissions: ['system:config:list'] }
},
{
path: 'notice',
component: () => import('@/views/system/notice/index.vue'),
name: 'Notice',
meta: { title: '通知公告', icon: 'notice', permissions: ['system:notice:list'] }
},
{
path: 'tenant',
component: () => import('@/views/system/tenant/index.vue'),
name: 'Tenant',
meta: { title: '租户管理', icon: 'tenant', permissions: ['system:tenant:list'] }
}
]
},
{
path: '/system/tenant-user',
component: Layout,
@@ -198,11 +233,10 @@ export const dynamicRoutes = [
path: 'set/:tenantId(\\d+)',
component: () => import('@/views/system/tenant/setUser'),
name: 'SetUser',
meta: { title: '所属用户', activeMenu: '/system/basicmanage/tenant' },
},
],
meta: { title: '所属用户', activeMenu: '/system/tenant' }
}
]
},
// 租户合同管理路由
{
path: '/system/tenant-contract',
component: Layout,
@@ -213,23 +247,7 @@ export const dynamicRoutes = [
path: 'set/:tenantId(\\d+)',
component: () => import('@/views/system/tenant/setContract'),
name: 'SetContract',
meta: { title: '合同管理', activeMenu: '/system/basicmanage/tenant' },
},
],
},
// 预约管理路由
{
path: '/appoinmentmanage',
component: Layout,
redirect: '/appoinmentmanage/deptappthoursManage',
name: 'AppoinmentManage',
meta: { title: '预约管理', icon: 'appointment' },
children: [
{
path: 'deptappthoursManage',
component: () => import('@/views/appoinmentmanage/deptappthoursManage/index.vue'),
name: 'DeptAppthoursManage',
meta: { title: '科室预约工作时间维护', icon: 'appointment' }
meta: { title: '合同管理', activeMenu: '/system/tenant' }
}
]
},
@@ -243,9 +261,9 @@ export const dynamicRoutes = [
path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' },
},
],
meta: { title: '分配角色', activeMenu: '/system/user' }
}
]
},
{
path: '/system/role-auth',
@@ -257,9 +275,9 @@ export const dynamicRoutes = [
path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser'),
name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role' },
},
],
meta: { title: '分配用户', activeMenu: '/system/role' }
}
]
},
{
path: '/system/dict-data',
@@ -271,9 +289,51 @@ export const dynamicRoutes = [
path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'),
name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' },
meta: { title: '字典数据', activeMenu: '/system/dict' }
}
]
},
{
path: '/monitor',
component: Layout,
redirect: '/monitor/operlog',
name: 'Monitor',
meta: { title: '系统监控', icon: 'monitor' },
children: [
{
path: 'operlog',
component: () => import('@/views/monitor/operlog/index.vue'),
name: 'Operlog',
meta: { title: '操作日志', icon: 'operlog', permissions: ['monitor:operlog:list'] }
},
],
{
path: 'logininfor',
component: () => import('@/views/monitor/logininfor/index.vue'),
name: 'Logininfor',
meta: { title: '登录日志', icon: 'logininfor', permissions: ['monitor:logininfor:list'] }
},
{
path: 'job',
component: () => import('@/views/monitor/job/index.vue'),
name: 'Job',
meta: { title: '定时任务', icon: 'job', permissions: ['monitor:job:list'] }
}
]
},
{
path: '/tool',
component: Layout,
redirect: '/tool/gen',
name: 'Tool',
meta: { title: '系统工具', icon: 'tool' },
children: [
{
path: 'gen',
component: () => import('@/views/tool/gen/index.vue'),
name: 'Gen',
meta: { title: '代码生成', icon: 'gen', permissions: ['tool:gen:list'] }
}
]
},
{
path: '/monitor/job-log',
@@ -285,9 +345,9 @@ export const dynamicRoutes = [
path: 'index/:jobId(\\d+)',
component: () => import('@/views/monitor/job/log'),
name: 'JobLog',
meta: { title: '调度日志', activeMenu: '/monitor/job' },
},
],
meta: { title: '调度日志', activeMenu: '/monitor/job' }
}
]
},
{
path: '/tool/gen-edit',
@@ -299,9 +359,9 @@ export const dynamicRoutes = [
path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen' },
},
],
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
}
]
},
{
path: '/help-center',
@@ -321,26 +381,23 @@ export const dynamicRoutes = [
// 合并常量路由和动态路由,确保所有路由都能被访问
const allRoutes = [...constantRoutes, ...dynamicRoutes];
// 添加404路由到所有路由的最后,确保捕获所有未匹配的路由
// 添加404路由到所有路由的最后
allRoutes.push({
path: "/:pathMatch(.*)*",
component: () => import('@/views/error/404'),
hidden: true
});
// 创建Vue Router实例
const router = createRouter({
history: createWebHistory(), // 使用HTML5历史模式
routes: allRoutes, // 使用合并后的所有路由
history: createWebHistory(),
routes: allRoutes,
scrollBehavior(to, from, savedPosition) {
// 页面滚动行为:如果有保存的位置则恢复,否则滚动到顶部
if (savedPosition) {
return savedPosition;
return savedPosition
} else {
return { top: 0 };
return { top: 0 }
}
},
});
// 导出路由实例
export default router;
export default router;

View File

@@ -1,7 +1,7 @@
import {getInfo, login, logout} from '@/api/login'
import {getToken, removeToken, setToken} from '@/utils/auth'
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import defAva from '@/assets/images/user.png'
import {defineStore} from 'pinia'
import { defineStore } from 'pinia'
const useUserStore = defineStore(
'user',
@@ -19,9 +19,7 @@ const useUserStore = defineStore(
roles: [],
permissions: [],
tenantId: '',
tenantName: '', // 租户名称
hospitalName:'',
optionMap: {} // 租户配置项Map从sys_tenant_option表读取
hospitalName:''
}),
actions: {
// 登录
@@ -64,10 +62,9 @@ const useUserStore = defineStore(
this.practitionerId = res.practitionerId
this.fixmedinsCode = res.optionJson.fixmedinsCode
this.avatar = avatar
this.optionMap = res.optionMap || {}
// 优先从optionMap获取配置如果没有则从optionJson获取
this.hospitalName = this.optionMap.hospitalName || res.optionJson.hospitalName || ''
this.tenantName = res.tenantName || ''
this.hospitalName = res.optionJson.hospitalName
// 获取tenantId优先从res.user获取否则从res获取
this.tenantId = user.tenantId || res.tenantId || this.tenantId
resolve(res)
}).catch(error => {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1201,6 +1201,7 @@ function submitForm() {
form.value.patientIdInfoList = [
{
typeCode: form.value.typeCode,
identifierNo: form.value.identifierNo,
},
];
if (form.value.idCard) {
@@ -1235,6 +1236,8 @@ function submitForm() {
emits('submit', 'update');
});
} else {
// console.log('患者就诊卡号:', form.value.identifierNo)
// console.log('患者就诊信息:', form.value.patientIdInfoList)
// 新增患者
addPatient(form.value).then((response) => {
proxy.$modal.msgSuccess('新增成功');

View File

@@ -2,7 +2,7 @@
<el-dialog
title="添加中医诊断"
v-model="props.openAddDiagnosisDialog"
width="1500px"
width="1300px"
append-to-body
destroy-on-close
@open="handleOpen"

View File

@@ -1106,7 +1106,7 @@ onMounted(() => {
document.addEventListener('keydown', escKeyListener);
// 初始化时自动创建第一个西药处方
if (westernPrescriptions.value.length === 0) {
handleAddPrescription();
handleAddPrescription(null, false);
}
});
@@ -1531,7 +1531,7 @@ function getListInfo(addNewRow) {
});
getGroupMarkers(); // 更新标记
if (props.activeTab == 'prescription' && addNewRow) {
handleAddPrescription();
handleAddPrescription(null, false);
}
// 在所有异步操作完成后 resolve Promise
@@ -1595,14 +1595,16 @@ function handleSelectionChange(selection, row) {
}
// 新增医嘱
function handleAddPrescription(prescriptionId) {
function handleAddPrescription(prescriptionId, showWarning = true) {
// 如果传入了处方ID先切换到该处方
if (prescriptionId && prescriptionId !== currentPrescriptionId.value) {
switchToActivePrescription(prescriptionId);
}
if (diagnosisList.value.length == 0) {
proxy.$modal.msgWarning('请先保存诊断后再开立医嘱');
if (showWarning) {
proxy.$modal.msgWarning('请先保存诊断后再开立医嘱');
}
return;
}
if (isAdding.value) {
@@ -2291,7 +2293,7 @@ function handleSaveSign(row, index, prescriptionId) {
});
} else {
if (prescriptionList.value[0].adviceName) {
handleAddPrescription();
handleAddPrescription(null, false);
}
}
adviceQueryParams.value.adviceType = undefined;

View File

@@ -145,14 +145,20 @@ const fetchAll = async () => {
}
loadingCheck.value = true;
loadingInspection.value = true;
try {
await Promise.all([fetchCheckReport(), fetchInspectionReport()]);
} catch (e) {
proxy.$modal?.msgError?.(e.message || '查询报告失败');
} finally {
loadingCheck.value = false;
loadingInspection.value = false;
}
// 独立处理,互不影响
const runFetch = async (fn, loadingRef, name) => {
try {
await fn();
} catch (e) {
proxy.$modal?.msgError?.(`${name}查询失败: ${e.message || '未知错误'}`);
} finally {
loadingRef.value = false;
}
};
runFetch(fetchCheckReport, loadingCheck, '检查报告');
runFetch(fetchInspectionReport, loadingInspection, '检验报告');
};
const handleRefreshCheck = async () => {

View File

@@ -893,7 +893,7 @@ function selectAdviceBase(key, row, pIndex) {
).chargeItemDefinitionId;
// 库存列表 + 价格列表拼成批次号的下拉框
if (row.adviceType != 3) {
if (row.adviceType == 1 || row.adviceType == 2) {
if (row.inventoryList && row.inventoryList.length == 0) {
prescription.expandOrder = [];
proxy.$modal.msgWarning('该项目无库存');
@@ -1069,23 +1069,32 @@ function handleSaveSign(row, index, pIndex) {
const prescription = tcmPrescriptionList.value[pIndex];
const formRefName = 'formRef' + pIndex + '-' + index;
proxy.$refs[formRefName][0].validate((valid) => {
row.isEdit = false;
prescription.isAdding = false;
prescription.expandOrder = [];
row.contentJson = undefined;
row.patientId = props.patientInfo.patientId;
row.encounterId = props.patientInfo.encounterId;
row.accountId = prescription.accountId;
row.quantity = row.minUnitQuantity;
row.conditionId = prescription.conditionId;
row.unitPrice =
row.unitCodeList.find((item) => item.value == row.unitCode).type == 'unit'
? row.unitPrice
: new Decimal(row.unitPrice).div(row.partPercent).toFixed(2);
row.conditionDefinitionId = prescription.conditionDefinitionId;
row.encounterDiagnosisId = prescription.encounterDiagnosisId;
row.diagnosisName = prescription.diagnosisName;
row.contentJson = JSON.stringify(row);
if (valid) {
row.isEdit = false;
prescription.isAdding = false;
prescription.expandOrder = [];
row.contentJson = undefined;
row.patientId = props.patientInfo.patientId;
row.encounterId = props.patientInfo.encounterId;
row.accountId = prescription.accountId;
row.quantity = row.minUnitQuantity;
row.chineseHerbsDoseQuantity = prescription.chineseHerbsDoseQuantity;
row.unitPrice =
row.unitCodeList.find((item) => item.value == row.unitCode).type == 'unit'
? row.unitPrice
: new Decimal(row.unitPrice).div(row.partPercent).toFixed(2);
row.conditionDefinitionId = prescription.conditionDefinitionId;
row.encounterDiagnosisId = prescription.encounterDiagnosisId;
row.diagnosisName = prescription.diagnosisName;
// 寻找当前选中的单位字典值
const selectedUnit = row.unitCodeList.find((item) => item.value === row.minUnitCode);
if (selectedUnit) {
row.doseUnitCode_dictText = selectedUnit.label;
}
row.contentJson = JSON.stringify(row);
}
});
}

View File

@@ -205,7 +205,7 @@
</template>
<script setup>
import { ref, reactive, onMounted, defineEmits, defineExpose } from 'vue'
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
VideoPlay, CircleCheck, Close, View, Delete

View File

@@ -78,7 +78,7 @@
</template>
<script setup>
import { ref, onMounted, defineEmits, defineExpose } from 'vue'
import { ref, onMounted } from 'vue'
import { User, Clock, VideoPlay, CircleCheck, Timer, Watch } from '@element-plus/icons-vue'
import { getTodayOutpatientStats } from './api.js'

View File

@@ -822,7 +822,7 @@
</template>
<script setup>
import {defineExpose, reactive, ref} from 'vue';
import {reactive, ref} from 'vue';
const bodyRef = ref();
// 响应式表单数据

View File

@@ -34,7 +34,7 @@
<script setup>
import {getPatientList, getWardList} from './api';
import {updatePatientInfoList} from './store/patient';
import {defineExpose, inject, nextTick, ref} from 'vue';
import {inject, nextTick, ref} from 'vue';
const treeRef = ref(null);
const searchKey = ref('');

View File

@@ -1,25 +0,0 @@
// 测试util._extend是否存在
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
try {
const util = require('util');
console.log('util._extend存在吗', typeof util._extend);
if (typeof util._extend === 'function') {
console.log('util._extend是一个函数');
} else {
console.log('util._extend不是一个函数添加兼容实现');
util._extend = function(destination, source) {
for (var key in source) {
if (source.hasOwnProperty(key)) {
destination[key] = source[key];
}
}
return destination;
};
console.log('兼容实现添加成功');
}
} catch (e) {
console.error('util模块加载失败:', e);
}
} else {
console.log('不在Node.js环境中');
}

289
package-lock.json generated
View File

@@ -1,6 +1,291 @@
{
"name": "his",
"name": "work",
"lockfileVersion": 3,
"requires": true,
"packages": {}
"packages": {
"": {
"dependencies": {
"axios": "^1.13.2"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.13.2",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.2.tgz",
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
}
}
}

5
package.json Normal file
View File

@@ -0,0 +1,5 @@
{
"dependencies": {
"axios": "^1.13.2"
}
}

View File

@@ -0,0 +1,115 @@
-- 创建序列
CREATE SEQUENCE order_main_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
-- 创建订单主表
CREATE TABLE "order_main" (
-- 主键字段
"id" int8 NOT NULL DEFAULT nextval('order_main_id_seq'::regclass),
-- 业务核心字段
"order_no" varchar(32) COLLATE "pg_catalog"."default" NOT NULL, -- 订单号
"patient_id" int8, -- 患者ID
"patient_name" varchar(50) COLLATE "pg_catalog"."default", -- 患者姓名
"medical_card" varchar(32) COLLATE "pg_catalog"."default", -- 就诊卡号
"phone" varchar(20) COLLATE "pg_catalog"."default", -- 手机号
"gender" int4, -- 性别1-男2-女)
"schedule_id" int8, -- 排班ID
"slot_id" int8, -- 号源ID
"department_id" int8, -- 科室ID
"department_name" varchar(100) COLLATE "pg_catalog"."default", -- 科室名称
"doctor_id" int8, -- 医生ID
"doctor_name" varchar(50) COLLATE "pg_catalog"."default", -- 医生姓名
"reg_type" varchar(10) COLLATE "pg_catalog"."default", -- 号别(普通/专家)
"fee" numeric(10,2), -- 挂号费
"appointment_date" timestamptz(6), -- 预约日期
"appointment_time" timestamptz(6), -- 预约时间
"cancel_time" timestamptz(6), -- 取消时间
"cancel_reason" varchar(200) COLLATE "pg_catalog"."default", -- 取消原因
"status" int4 NOT NULL DEFAULT 1, -- 状态1-预约中2-已完成3-已取消)
"pay_status" int4 NOT NULL DEFAULT 0, -- 支付状态0-未支付1-已支付)
"version" int4 NOT NULL DEFAULT 0, -- 版本号(乐观锁)
-- 继承HisBaseEntity的审计字段
"tenant_id" int8 NOT NULL, -- 租户ID
"delete_flag" char(1) COLLATE "pg_catalog"."default" NOT NULL DEFAULT '0', -- 删除标志0-未删除1-已删除)
"create_by" varchar(32) COLLATE "pg_catalog"."default" NOT NULL DEFAULT '', -- 创建人
"create_time" timestamptz(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
"update_by" varchar(32) COLLATE "pg_catalog"."default", -- 更新人
"update_time" timestamptz(6), -- 更新时间
-- 主键约束
CONSTRAINT "order_main_pkey" PRIMARY KEY ("id")
);
-- 设置表所有者根据实际数据库用户修改如果不存在postgres角色可注释掉此行
-- ALTER TABLE "order_main" OWNER TO "postgres";
-- 表及字段注释
COMMENT ON TABLE "order_main" IS '订单主表:存储门诊预约挂号订单信息';
COMMENT ON COLUMN "order_main"."id" IS '主键ID';
COMMENT ON COLUMN "order_main"."order_no" IS '订单号:唯一标识订单,系统自动生成';
COMMENT ON COLUMN "order_main"."patient_id" IS '患者ID关联adm_patient表';
COMMENT ON COLUMN "order_main"."patient_name" IS '患者姓名:冗余字段,便于查询';
COMMENT ON COLUMN "order_main"."medical_card" IS '就诊卡号:患者就诊卡编号';
COMMENT ON COLUMN "order_main"."phone" IS '手机号:患者联系电话';
COMMENT ON COLUMN "order_main"."gender" IS '性别1-男2-女';
COMMENT ON COLUMN "order_main"."schedule_id" IS '排班ID关联排班表';
COMMENT ON COLUMN "order_main"."slot_id" IS '号源ID关联clinical_ticket表';
COMMENT ON COLUMN "order_main"."department_id" IS '科室ID关联科室表';
COMMENT ON COLUMN "order_main"."department_name" IS '科室名称:冗余字段,便于查询';
COMMENT ON COLUMN "order_main"."doctor_id" IS '医生ID关联医生表';
COMMENT ON COLUMN "order_main"."doctor_name" IS '医生姓名:冗余字段,便于查询';
COMMENT ON COLUMN "order_main"."reg_type" IS '号别:普通/专家';
COMMENT ON COLUMN "order_main"."fee" IS '挂号费:挂号费用金额';
COMMENT ON COLUMN "order_main"."appointment_date" IS '预约日期:患者预约的就诊日期';
COMMENT ON COLUMN "order_main"."appointment_time" IS '预约时间:患者预约的具体时间';
COMMENT ON COLUMN "order_main"."cancel_time" IS '取消时间:订单取消的时间';
COMMENT ON COLUMN "order_main"."cancel_reason" IS '取消原因:订单取消的原因说明';
COMMENT ON COLUMN "order_main"."status" IS '状态1-预约中2-已完成3-已取消';
COMMENT ON COLUMN "order_main"."pay_status" IS '支付状态0-未支付1-已支付';
COMMENT ON COLUMN "order_main"."version" IS '版本号:乐观锁,防止并发冲突';
COMMENT ON COLUMN "order_main"."tenant_id" IS '租户ID多租户系统隔离字段';
COMMENT ON COLUMN "order_main"."delete_flag" IS '删除标志0-未删除1-已删除';
COMMENT ON COLUMN "order_main"."create_by" IS '创建人:存储创建者用户名';
COMMENT ON COLUMN "order_main"."create_time" IS '创建时间:默认当前时间,带时区';
COMMENT ON COLUMN "order_main"."update_by" IS '更新人:存储更新者用户名';
COMMENT ON COLUMN "order_main"."update_time" IS '更新时间:带时区';
-- 创建索引
CREATE INDEX "idx_order_main_order_no"
ON "order_main" ("order_no")
WHERE "delete_flag" = '0';
CREATE INDEX "idx_order_main_patient_id"
ON "order_main" ("patient_id")
WHERE "delete_flag" = '0';
CREATE INDEX "idx_order_main_slot_id"
ON "order_main" ("slot_id")
WHERE "delete_flag" = '0';
CREATE INDEX "idx_order_main_department_id"
ON "order_main" ("department_id")
WHERE "delete_flag" = '0';
CREATE INDEX "idx_order_main_doctor_id"
ON "order_main" ("doctor_id")
WHERE "delete_flag" = '0';
CREATE INDEX "idx_order_main_status"
ON "order_main" ("status")
WHERE "delete_flag" = '0';
CREATE INDEX "idx_order_main_tenant_id"
ON "order_main" ("tenant_id", "create_time")
WHERE "delete_flag" = '0';
-- 向迁移历史表插入记录
INSERT INTO __MigrationsHistory (MigrationId, ProductVersion)
VALUES ('202512301200add_table_order_main', '1.0.0');

View File

@@ -0,0 +1,51 @@
-- 验证order_main表是否创建成功
-- 1. 查看表结构
\d order_main;
-- 2. 查看表注释
SELECT
obj_description('order_main'::regclass) AS table_comment;
-- 3. 查看字段注释使用pg_catalog.pg_description
SELECT
a.attname AS column_name,
d.description AS column_comment
FROM pg_attribute a
LEFT JOIN pg_catalog.pg_description d
ON d.objoid = a.attrelid
AND d.objsubid = a.attnum
WHERE a.attrelid = 'order_main'::regclass
AND a.attnum > 0
AND NOT a.attisdropped
ORDER BY a.attnum;
-- 4. 查看索引
SELECT
indexname AS index_name,
indexdef AS index_definition
FROM pg_indexes
WHERE tablename = 'order_main';
-- 5. 查看迁移历史
SELECT * FROM __MigrationsHistory
WHERE MigrationId = '202512301200add_table_order_main';
-- 6. 查看表的所有约束
SELECT
conname AS constraint_name,
contype AS constraint_type
FROM pg_constraint
WHERE conrelid = 'order_main'::regclass;
-- 7. 查看序列
SELECT
sequencename AS sequence_name,
last_value,
start_value,
increment_by
FROM pg_sequences
WHERE sequencename = 'order_main_id_seq';
-- 8. 统计表记录数
SELECT COUNT(*) AS record_count FROM order_main;

View File

@@ -0,0 +1,202 @@
# 门诊预约挂号功能测试数据使用指南
## 1. 准备工作
### 1.1 数据库环境准备
- 确保已安装并启动PostgreSQL数据库
- 确保已创建`hisdev`模式(或根据实际情况修改脚本中的模式名)
- 确保已创建相关表结构:
- clinical_ticket (号源表)
- adm_practitioner (医生表)
- adm_patient (患者表)
- cli_reservation_record (预约记录表)
### 1.2 验证表结构
执行以下查询验证核心表是否存在:
```sql
SELECT table_name FROM information_schema.tables WHERE table_schema = 'hisdev';
```
## 2. 执行测试数据脚本
### 2.1 脚本位置
`d:\work\门诊预约测试数据插入.sql`
### 2.2 执行方式
**方式一通过pgAdmin执行**
1. 打开pgAdmin连接到数据库
2. 选择`hisdev`模式
3. 打开SQL编辑器粘贴脚本内容
4. 点击执行按钮或按F5
**方式二:通过命令行执行**
```bash
psql -h localhost -U username -d database_name -f "d:\work\门诊预约测试数据插入.sql"
```
## 3. 测试数据说明
### 3.1 号源数据 (clinical_ticket)
#### 3.1.1 未预约状态的号源(用于测试正常预约流程)
- 号源数量10个普通号 + 5个专家号
- 日期:当前系统日期
- 医生:张三(内科)
- 状态:`unbooked`
#### 3.1.2 已预约状态的号源(用于测试取消预约流程)
- 号源数量2个
- 日期:当前系统日期
- 医生:张三(内科)
- 状态:`booked`
- 患者患者A、患者B
#### 3.1.3 已取号状态的号源(用于测试逆向用例)
- 号源数量1个
- 日期:当前系统日期
- 医生:张三(内科)
- 状态:`checked`
- 患者患者C
#### 3.1.4 明天日期的号源(用于测试日期选择功能)
- 号源数量10个普通号
- 日期:当前系统日期 + 1天
- 医生:王五(外科)
- 状态:`unbooked`
### 3.2 预约记录数据 (cli_reservation_record)
- 记录数量2条
- 患者患者A、患者B
- 医生:张三
## 4. 测试用例执行指南
### 4.1 正向测试用例
#### 4.1.1 号源日期选择器-默认值
- **测试步骤**
1. 打开门诊预约挂号界面
2. 检查号源日期选择器的默认值
- **预期结果**:默认值为当前系统日期
#### 4.1.2 状态筛选下拉框-默认值
- **测试步骤**
1. 打开门诊预约挂号界面
2. 检查状态筛选下拉框的默认值
- **预期结果**:默认值为"未预约"
#### 4.1.3 号源预约功能-正常流程
- **测试步骤**
1. 双击状态为"未预约"的号源卡片
2. 在弹出的患者选择弹窗中选择患者
3. 点击"确定"按钮
- **预期结果**:卡片状态更新为"已预约",显示患者信息
- **可用测试数据**当前日期的未预约号源TICKET000001-TICKET000010
#### 4.1.4 取消预约功能-正常流程
- **测试步骤**
1. 右键状态为"已预约"的号源卡片
2. 选择"取消预约"选项
3. 在二次确认弹窗中点击"确认"按钮
- **预期结果**:号源状态恢复为"未预约",清除患者信息
- **可用测试数据**已预约号源TICKET000011、TICKET000012
#### 4.1.5 医生搜索框-实时搜索
- **测试步骤**
1. 在左侧边栏的医生搜索框中输入医生姓名
2. 观察医生列表的实时更新
- **预期结果**:医生列表根据输入内容实时筛选显示
- **可用测试数据**:张三、王五
#### 4.1.6 号源类型切换-联动过滤
- **测试步骤**
1. 在左侧边栏切换号源类型为"专家号"
2. 观察右侧号源卡片和左侧医生列表
- **预期结果**:仅显示专家号的号源和医生
- **可用测试数据**专家号TICKET000014-TICKET000018
### 4.2 逆向测试用例
#### 4.2.1 号源预约功能-未选择患者
- **测试步骤**
1. 双击状态为"未预约"的号源卡片
2. 不选择患者直接点击"确定"按钮
- **预期结果**:提示"请选择患者"
#### 4.2.2 取消预约功能-已取号状态
- **测试步骤**
1. 右键状态为"已取号"的号源卡片
- **预期结果**:右键菜单不显示"取消预约"选项
- **可用测试数据**已取号源TICKET000013
#### 4.2.3 患者信息搜索框组-无效输入
- **测试步骤**
1. 在患者信息搜索框组中输入无效的姓名、就诊卡号或手机号
2. 点击查询按钮
- **预期结果**:返回无结果或提示"未找到匹配的患者"
- **可用测试数据**:输入不存在的姓名(如"测试患者")、就诊卡号(如"PAT999")或手机号(如"13999999999"
#### 4.2.4 日期选择器-无效日期
- **测试步骤**
1. 在号源日期选择器中输入无效日期(如过去日期或格式错误)
2. 尝试查询号源
- **预期结果**:提示"请输入有效日期"
#### 4.2.5 医生搜索框-无匹配结果
- **测试步骤**
1. 在医生搜索框中输入不存在的医生姓名
2. 观察医生列表
- **预期结果**:显示"无匹配结果"或空列表
- **可用测试数据**:输入不存在的医生姓名(如"李四"
## 5. 测试结果记录
| 用例名 | 测试步骤 | 预期结果 | 实际结果 | 测试时间 | 测试人 |
|-------|---------|---------|---------|---------|-------|
| 号源日期选择器-默认值 | 1. 打开门诊预约挂号界面<br>2. 检查号源日期选择器的默认值 | 默认值为当前系统日期 | | | |
| 状态筛选下拉框-默认值 | 1. 打开门诊预约挂号界面<br>2. 检查状态筛选下拉框的默认值 | 默认值为"未预约" | | | |
| 号源预约功能-正常流程 | 1. 双击状态为"未预约"的号源卡片<br>2. 在弹出的患者选择弹窗中选择患者<br>3. 点击"确定"按钮 | 卡片状态更新为"已预约",显示患者信息 | | | |
| 取消预约功能-正常流程 | 1. 右键状态为"已预约"的号源卡片<br>2. 选择"取消预约"选项<br>3. 在二次确认弹窗中点击"确认"按钮 | 号源状态恢复为"未预约",清除患者信息 | | | |
| 医生搜索框-实时搜索 | 1. 在左侧边栏的医生搜索框中输入医生姓名<br>2. 观察医生列表的实时更新 | 医生列表根据输入内容实时筛选显示 | | | |
| 号源类型切换-联动过滤 | 1. 在左侧边栏切换号源类型为"专家号"<br>2. 观察右侧号源卡片和左侧医生列表 | 仅显示专家号的号源和医生 | | | |
| 号源预约功能-未选择患者 | 1. 双击状态为"未预约"的号源卡片<br>2. 不选择患者直接点击"确定"按钮 | 提示"请选择患者" | | | |
| 取消预约功能-已取号状态 | 1. 右键状态为"已取号"的号源卡片 | 右键菜单不显示"取消预约"选项 | | | |
| 患者信息搜索框组-无效输入 | 1. 在患者信息搜索框组中输入无效的姓名、就诊卡号或手机号<br>2. 点击查询按钮 | 返回无结果或提示"未找到匹配的患者" | | | |
| 日期选择器-无效日期 | 1. 在号源日期选择器中输入无效日期(如过去日期或格式错误)<br>2. 尝试查询号源 | 提示"请输入有效日期" | | | |
| 医生搜索框-无匹配结果 | 1. 在医生搜索框中输入不存在的医生姓名<br>2. 观察医生列表 | 显示"无匹配结果"或空列表 | | | |
## 6. 注意事项
1. **数据唯一性**:脚本使用条件判断避免重复插入数据
2. **日期更新**:脚本中的日期会自动使用当前系统日期
3. **依赖关系**:号源数据依赖于医生和患者的基础数据,请确保系统中已有相关记录
4. **测试顺序**:建议先执行正向测试,再执行逆向测试
5. **数据清理**测试完成后可使用以下SQL清理测试数据
```sql
DELETE FROM clinical_ticket WHERE bus_no LIKE 'TICKET%';
DELETE FROM cli_reservation_record WHERE patient_name IN ('患者A', '患者B');
```
## 7. 常见问题
### 7.1 执行脚本时提示表不存在
- 检查数据库连接是否正确
- 检查是否已创建相关表结构
- 检查模式名是否为`hisdev`
### 7.2 测试数据不显示
- 检查日期选择器是否选择了正确的日期
- 检查状态筛选条件是否为"未预约"
- 检查医生选择是否正确
### 7.3 预约功能无法正常工作
- 检查患者选择弹窗是否能正常加载患者数据
- 检查网络连接是否正常
- 检查浏览器控制台是否有错误信息
---
**版本1.0**
**创建时间2025-12-31**
**更新记录:**
- 2025-12-31初始版本完成测试数据说明和测试指南

View File

@@ -0,0 +1,89 @@
-- 门诊预约挂号功能测试数据插入脚本
-- 执行前请确保已创建相关表结构
-- 设置当前模式
SET search_path TO hisdev;
-- ====================================
-- 说明:
-- 1. 此脚本主要包含号源表(clinical_ticket)的测试数据
-- 2. 其他相关表(医生、患者、科室等)请确保系统中已有基础数据
-- 3. 如需要完整测试,建议先在系统中创建相应的医生和患者信息
-- ====================================
-- ====================================
-- 注意:以下是可选的表结构验证
-- 如果不确定表结构是否正确,请先执行这些查询
-- ====================================
-- SELECT column_name FROM information_schema.columns WHERE table_name = 'clinical_ticket';
-- SELECT column_name FROM information_schema.columns WHERE table_name = 'adm_practitioner';
-- SELECT column_name FROM information_schema.columns WHERE table_name = 'adm_patient';
-- ====================================
-- 5. 号源数据 (clinical_ticket)
-- 包含不同状态的号源:未预约、已预约、已取号
-- ====================================
-- 获取当前日期和明天日期
DO $$
DECLARE
current_date DATE := CURRENT_DATE;
tomorrow_date DATE := CURRENT_DATE + INTERVAL '1 day';
BEGIN
-- 当前日期号源
-- 未预约状态的号源(用于测试正常预约流程)
INSERT INTO clinical_ticket (bus_no, department, doctor, ticket_type, time, status, fee, appointment_date, appointment_time, department_id, doctor_id, create_time, tenant_id, delete_flag)
SELECT
'TICKET' || lpad(i::text, 6, '0'),
'内科', '张三', '普通', '08:00-' || (7 + i) || ':50',
'unbooked', '50', current_date, CURRENT_TIMESTAMP, 1, 1, CURRENT_TIMESTAMP, 1, '0'
FROM generate_series(1, 10) i
WHERE NOT EXISTS (SELECT 1 FROM clinical_ticket WHERE bus_no = 'TICKET' || lpad(i::text, 6, '0'));
-- 已预约状态的号源(用于测试取消预约流程)
INSERT INTO clinical_ticket (bus_no, department, doctor, ticket_type, time, status, fee, patient_id, patient_name, medical_card, phone, appointment_date, appointment_time, department_id, doctor_id, create_time, tenant_id, delete_flag)
VALUES
('TICKET000011', '内科', '张三', '普通', '09:00-09:50', 'booked', '50', 1, '患者A', 'PAT001', '13900139001', current_date, CURRENT_TIMESTAMP, 1, 1, CURRENT_TIMESTAMP, 1, '0'),
('TICKET000012', '内科', '张三', '普通', '09:50-10:40', 'booked', '50', 2, '患者B', 'PAT002', '13900139002', current_date, CURRENT_TIMESTAMP, 1, 1, CURRENT_TIMESTAMP, 1, '0');
-- 已取号状态的号源(用于测试逆向用例:取消已取号的预约)
INSERT INTO clinical_ticket (bus_no, department, doctor, ticket_type, time, status, fee, patient_id, patient_name, medical_card, phone, appointment_date, appointment_time, department_id, doctor_id, create_time, tenant_id, delete_flag)
VALUES
('TICKET000013', '内科', '张三', '普通', '10:40-11:30', 'checked', '50', 3, '患者C', 'PAT003', '13900139003', current_date, CURRENT_TIMESTAMP, 1, 1, CURRENT_TIMESTAMP, 1, '0');
-- 专家号源(用于测试号源类型切换功能)
INSERT INTO clinical_ticket (bus_no, department, doctor, ticket_type, time, status, fee, appointment_date, appointment_time, department_id, doctor_id, create_time, tenant_id, delete_flag)
VALUES
('TICKET000014', '内科', '张三', '专家', '08:00-08:50', 'unbooked', '150', CURRENT_DATE, CURRENT_TIMESTAMP, 1, 1, CURRENT_TIMESTAMP, 1, '0'),
('TICKET000015', '内科', '张三', '专家', '09:00-09:50', 'unbooked', '150', CURRENT_DATE, CURRENT_TIMESTAMP, 1, 1, CURRENT_TIMESTAMP, 1, '0'),
('TICKET000016', '内科', '张三', '专家', '10:00-10:50', 'unbooked', '150', CURRENT_DATE, CURRENT_TIMESTAMP, 1, 1, CURRENT_TIMESTAMP, 1, '0'),
('TICKET000017', '内科', '张三', '专家', '11:00-11:50', 'unbooked', '150', CURRENT_DATE, CURRENT_TIMESTAMP, 1, 1, CURRENT_TIMESTAMP, 1, '0'),
('TICKET000018', '内科', '张三', '专家', '14:00-14:50', 'unbooked', '150', CURRENT_DATE, CURRENT_TIMESTAMP, 1, 1, CURRENT_TIMESTAMP, 1, '0');
-- 明天日期的号源(用于测试日期选择功能)
INSERT INTO clinical_ticket (bus_no, department, doctor, ticket_type, time, status, fee, appointment_date, appointment_time, department_id, doctor_id, create_time, tenant_id, delete_flag)
SELECT
'TICKET' || lpad((20 + i)::text, 6, '0'),
'外科', '王五', '普通', '08:00-' || (7 + i) || ':50',
'unbooked', '60', tomorrow_date, CURRENT_TIMESTAMP, 2, 3, CURRENT_TIMESTAMP, 1, '0'
FROM generate_series(1, 10) i
WHERE NOT EXISTS (SELECT 1 FROM clinical_ticket WHERE bus_no = 'TICKET' || lpad((20 + i)::text, 6, '0'));
END $$;
-- ====================================
-- 6. 预约记录数据 (cli_reservation_record)
-- 用于测试预约历史查询
-- ====================================
INSERT INTO cli_reservation_record (patient_name, patient_tel, chief_complaint, reservation_time, org_id, practitioner_id, tenant_id, create_time, create_by, update_time, update_by)
VALUES
('患者A', '13900139001', '感冒发烧', CURRENT_TIMESTAMP, 1, 1, 1, CURRENT_TIMESTAMP, 'system', CURRENT_TIMESTAMP, 'system'),
('患者B', '13900139002', '腹痛', CURRENT_TIMESTAMP, 1, 1, 1, CURRENT_TIMESTAMP, 'system', CURRENT_TIMESTAMP, 'system');
-- ====================================
-- 数据插入完成提示
-- ====================================
SELECT '门诊预约测试数据插入完成!' AS message;
SELECT '未预约号源数量:' || COUNT(*) FROM clinical_ticket WHERE status = 'unbooked';
SELECT '已预约号源数量:' || COUNT(*) FROM clinical_ticket WHERE status = 'booked';
SELECT '已取号号源数量:' || COUNT(*) FROM clinical_ticket WHERE status = 'checked';