Compare commits
55 Commits
fix-668-te
...
4ff58b3f2e
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ff58b3f2e | |||
| ab3431c53d | |||
| bb20d8308e | |||
| db84f4b2bd | |||
| 0a2978bc0a | |||
| 4e9c1a9716 | |||
| 2aa8b88b3a | |||
| 1a51508e78 | |||
| cd523cced0 | |||
| 4b3663c7d7 | |||
|
|
5e594e7c25 | ||
| a18143ef41 | |||
| 4f3b1dff8f | |||
| a45b6e7955 | |||
| cec2f47a1f | |||
| a350095ced | |||
| 3f52a98a32 | |||
| d60b579dcd | |||
| d413a4cd60 | |||
| 93447b0e46 | |||
| 2921d4535a | |||
| b71354d3b6 | |||
| 57a33e0baa | |||
| 759f10d9d0 | |||
| d8e2c485a4 | |||
| 69e24ba2b4 | |||
| 81f5001601 | |||
| 96087d8dac | |||
| 9331dc7525 | |||
| 6372e3c80f | |||
| 615be87c71 | |||
| c0ab80bd4d | |||
| 772119e320 | |||
| 256791348c | |||
|
|
a08808b41d | ||
|
|
f407a2a886 | ||
| babd8d0c04 | |||
| 1f738c969a | |||
| 3f67753344 | |||
| 3e650dd041 | |||
| 773a485114 | |||
| 9675106d4b | |||
|
|
f655f06871 | ||
| 2c2dbd7542 | |||
| 8b47a8ab55 | |||
| 5ebe6c6333 | |||
| 65a52e9742 | |||
|
|
d04be6062b | ||
|
|
defab36cca | ||
| 681107ca64 | |||
| f75133369a | |||
| ca812421d2 | |||
| ae12cb2135 | |||
| d2a1cd6f29 | |||
| d9d2b83c5b |
544
AGENTS.md
544
AGENTS.md
@@ -107,6 +107,143 @@
|
||||
- **违规判定**: 因修改导致原有代码编译失败或运行报错,视为违反铁律18,必须立即回滚修复
|
||||
|
||||
|
||||
**铁律19: 编译错误不区分来源(Bug #698 教训)**
|
||||
- `mvn compile`、`vite build`、`vue-tsc` 等构建命令报错 = 不过关,**不管是自己引入的还是历史遗留的**
|
||||
- 禁止说"这是预存问题""不是我改的""原有bug"——构建通不过就不能宣称完成
|
||||
- 正确做法:定位错误 → 修复 → 重新构建确认通过 → 然后才能继续
|
||||
- **违规判定**: 构建命令有 ERROR 但未修复就报告"编译通过",视为违反铁律
|
||||
|
||||
**铁律20: 数据来源必须验证(Bug #698 教训)**
|
||||
- 涉及数据查询/提取时,必须先确认数据实际存储位置,不能假设
|
||||
- 案例:从 `raw_steps_html` 提取 fileID,而不是从 `steps`(纯文本,已被 strip)
|
||||
- 修复前必须:打印/检查原始数据结构 → 确认字段存在 → 再写提取逻辑
|
||||
- 禁止:凭代码推断数据位置、假设"应该在这里"
|
||||
|
||||
**铁律21: 外部配置值必须实测验证(Bug #698 教训)**
|
||||
- 使用外部服务(API、模型、数据库)的配置值,必须实际调用验证,不能仅凭记忆或推测
|
||||
- 案例:模型名 `mino-v2.5` 应为 `mimo-v2.5`,拼写错误导致 400
|
||||
- 配置变更后必须:发起一次真实请求 → 确认返回 200 → 再宣称配置正确
|
||||
- 禁止:改完配置不测试、假设"应该能用"
|
||||
|
||||
**铁律22: 端到端验证必须有实际输出证据(Bug #698 教训)**
|
||||
- 声称功能生效前,必须有实际的端到端输出证据
|
||||
- 不能仅凭代码路径推断"应该走了 vision"——必须看到实际返回内容
|
||||
- 验证方式:运行命令 → 检查输出中包含预期关键词(如 vision 分析结果、图片识别文字)
|
||||
- 禁止:只检查代码路径可达就算"验证通
|
||||
**铁律23: 文件读写强制 UTF-8 编码(必遵守)**
|
||||
- **禁止**使用 Get-Content -Raw(不带 -Encoding UTF8)读取源文件
|
||||
- **禁止**使用 Out-File -Encoding utf8(会写 BOM)
|
||||
- **正确写法**:
|
||||
- 读取:[System.IO.File]::ReadAllText(, [System.Text.Encoding]::UTF8)
|
||||
- 写入:[System.IO.File]::WriteAllText(, # HealthLink-HIS — AI 开发规范
|
||||
|
||||
> 🤖 本文件由 Codex CLI、Claude Code 等工具自动读取。
|
||||
> 工具进入项目目录时会自动加载此文件作为开发规范上下文。
|
||||
|
||||
---
|
||||
|
||||
# HealthLink-HIS — AI 开发规范(自动加载)
|
||||
|
||||
> 🤖 **本文件供所有 AI 编码工具自动读取**。进入本项目后必须遵守以下规范。
|
||||
>
|
||||
> **模型决定上限,Harness 决定底线。**
|
||||
|
||||
---
|
||||
|
||||
## 一、项目概览
|
||||
|
||||
| 属性 | 值 |
|
||||
|------|------|
|
||||
| 项目名 | HealthLink-HIS(医院信息系统) |
|
||||
| 后端路径 | `healthlink-his-server/` |
|
||||
| 前端路径 | `healthlink-his-ui/` |
|
||||
| 文档路径 | `MD/` |
|
||||
| JDK | 25 (OpenJDK) |
|
||||
| Spring Boot | 4.0.6 |
|
||||
| MyBatis-Plus | 3.5.16 |
|
||||
| Vue | 3.x + Vite + Element Plus |
|
||||
| 数据库 | PostgreSQL 15+ |
|
||||
| 包名 | `com.healthlink.his` |
|
||||
| 后端端口 | 18082 |
|
||||
| 前端端口 | 81 |
|
||||
|
||||
---
|
||||
|
||||
## 二、铁律(必须遵守,违反即失败)
|
||||
|
||||
### 🔴 P0 铁律 — 不可违反
|
||||
|
||||
**铁律1: 修改完必须测试**
|
||||
```
|
||||
后端: mvn clean compile -DskipTests → mvn install -DskipTests → mvn test
|
||||
前端: npm run build:dev → npm run lint
|
||||
```
|
||||
- 白盒:编译通过,无 ERROR
|
||||
- 黑盒:关键接口返回 `{code:200, data:...}`,验证业务逻辑
|
||||
- 冒烟:应用正常启动,核心流程通畅
|
||||
|
||||
**铁律2: Flyway 数据库迁移**
|
||||
- 凡是新建表、新增字段,必须创建 Flyway 迁移脚本
|
||||
- 路径:`healthlink-his-domain/src/main/resources/db/migration/`
|
||||
- 命名:`V{版本号}__{描述}.sql`(双下划线)
|
||||
|
||||
**铁律3: 测试通过后才提交**
|
||||
- 编译 + 测试全部通过后才能 git commit
|
||||
- 不提交未完成的功能、调试代码、临时文件
|
||||
|
||||
**铁律4: 前后端API路径对齐**
|
||||
- 后端前缀:`/healthlink-his/api/v1/`
|
||||
- 前端 `request.js` 的 baseURL 必须与后端匹配
|
||||
|
||||
**铁律5: 状态值一致性(Bug #574 教训)**
|
||||
- 修改任何状态值前,必须先列出完整的状态流转链路
|
||||
- 检查项:枚举定义 → Service 设置 → 查询映射 → 前端 STATUS_CLASS_MAP → 前端 v-if → 统计SQL
|
||||
- 禁止:只改一端不检查其他端
|
||||
|
||||
**铁律6: 禁止删除源文件(Bug #574 教训)**
|
||||
- 绝对禁止删除项目中已有的 Java/Vue/SQL 源文件
|
||||
- 编译错误 → 修复错误;重复文件 → 重构合并
|
||||
- 唯一例外:明确由人类确认删除的文件
|
||||
|
||||
**铁律7: 禁止修改已有公开方法签名**
|
||||
- 不能删除/重命名已有的 public 方法,不能修改参数列表
|
||||
- 需要新功能 → 添加重载方法;需要改行为 → 修改内部实现
|
||||
|
||||
**铁律8: 验证后才宣称完成(Verification Before Completion)**
|
||||
- **没有跑过验证命令,就不能说"完成了""通过了""没问题"**
|
||||
- 禁止使用"应该可以""大概没问题""看起来正确"
|
||||
- 必须:运行命令 → 读取输出 → 确认结果 → 才能宣称
|
||||
- 这是诚实原则,不是效率问题
|
||||
|
||||
|
||||
**铁律9: 开发前必须审核原有代码(P0 — 铁律)**
|
||||
- **任何新功能开发前,必须先搜索项目中是否已有相关代码**
|
||||
- 搜索路径:Controller / AppService / Service / Mapper / Entity / 前端页面 / API接口
|
||||
- 如果已有部分功能 → 在原有代码基础上**升级优化完善**,禁止另起炉灶
|
||||
- 如果已有接口但前端缺失 → 只补前端,不重复建后端
|
||||
- 如果已有前端但后端缺失 → 只补后端,不重写前端
|
||||
- 搜索命令:`rg -l "关键词" healthlink-his-server/ healthlink-his-ui/src/`
|
||||
- 禁止:不看代码就新建模块、重复实现已有功能、废弃原有代码另写一套
|
||||
|
||||
|
||||
**铁律12: 设计文档确认后自主开发(铁律)**
|
||||
- 设计文档(如 `MD/architecture/GRADE3A_GAP_ANALYSIS_AND_DESIGN.md`)一旦确认,后续开发**必须按文档自主执行**
|
||||
- **禁止反复询问"是否继续""下一步做什么""是否开始"**——直接按计划推进
|
||||
- 每完成一个 Sprint,自动提交推送,然后立即开始下一个 Sprint
|
||||
- 只在遇到**无法解决的阻塞**(如技术选型冲突、需求不明确、第三方依赖不可用)时才暂停询问
|
||||
- 设计文档是"**已签合同**",不是"参考意见"。铁律执行优先级:设计文档 > 人类临时指令 > AI 自行判断
|
||||
|
||||
**铁律18: 禁止破坏原有功能(P0绝对铁律)**
|
||||
- **完善增加功能和流程时,绝对不能破坏或者让原有功能不能用**
|
||||
- 修改已有实体前必须对比原始文件(`git show HEAD~N:./file.java`),保留所有原有字段和方法
|
||||
- 新增字段只能追加,不能删除或重命名已有字段
|
||||
- SQL迁移只允许 `ALTER TABLE ADD COLUMN`,不允许 `DROP COLUMN` 或 `RENAME COLUMN`
|
||||
- Controller新端点不能修改已有端点的路径或参数
|
||||
- 前端新页面不能修改已有页面的组件结构
|
||||
- 每次修改后必须 `mvn clean compile -DskipTests` 验证
|
||||
- **违规判定**: 因修改导致原有代码编译失败或运行报错,视为违反铁律18,必须立即回滚修复
|
||||
|
||||
|
||||
**铁律19: 编译错误不区分来源(Bug #698 教训)**
|
||||
- `mvn compile`、`vite build`、`vue-tsc` 等构建命令报错 = 不过关,**不管是自己引入的还是历史遗留的**
|
||||
- 禁止说"这是预存问题""不是我改的""原有bug"——构建通不过就不能宣称完成
|
||||
@@ -533,3 +670,410 @@ git status && git add -A && git commit -m "feat(module): desc" && git push origi
|
||||
---
|
||||
|
||||
> 📅 最后同步: 2026-06-06 15:09 | 源文件: RULES.md | 重新同步: `bash scripts/sync-ai-rules.sh`
|
||||
, [System.Text.UTF8Encoding]::new(False))
|
||||
- git提取:先 Out-File -Encoding utf8 保存到临时文件,再用 [System.IO.File]::ReadAllText() 读取
|
||||
- **根因**:PowerShell 管道会丢失换行符,git show | Out-File 会将多行文件压缩为一行
|
||||
过"
|
||||
|
||||
|
||||
### 🟡 P1 铁律 — 强烈建议
|
||||
|
||||
**铁律9: 先分解再行动**
|
||||
- 修改超过3个文件、涉及多模块、数据库变更,必须先制定计划
|
||||
|
||||
**铁律10: 验证后信**
|
||||
- 每次修改后必须验证编译通过,不信记忆
|
||||
|
||||
**铁律13: 文档统一管理**
|
||||
- 所有文档存储在 `MD/` 目录
|
||||
- 文件名:大写英文+下划线(如 `BACKEND_CHECKLIST.md`)
|
||||
- 文档头部必须包含元数据块(文档类型、版本、日期)
|
||||
|
||||
---
|
||||
|
||||
|
||||
**铁律14: 设计文档必须包含UI设计和调用流程**
|
||||
- 所有新模块/页面的设计文档必须包含:UI布局描述、交互效果清单、前后端调用流程
|
||||
- 没有明确UI设计的模块,禁止直接编码
|
||||
- 详见
|
||||
- 设计文档必须写清楚:系统调用关系、方法函数调用关系、完整业务流程
|
||||
- 设计文档中每个用户操作必须对应:前端事件 → API调用 → 后端处理链路 → 返回数据 → UI渲染
|
||||
|
||||
---
|
||||
|
||||
## 三、Karpathy 编码准则
|
||||
|
||||
> 减少 LLM 常见编码错误。偏向谨慎而非速度。
|
||||
|
||||
### 3.1 先想再写
|
||||
- 明确陈述假设,不确定就问
|
||||
- 多种解读时都列出来,不要默默选一种
|
||||
- 有更简单的方案就说出来,该推回就推回
|
||||
- 不清楚的地方停下来,说清楚哪里不清楚
|
||||
|
||||
### 3.2 简洁优先
|
||||
- 不做没要求的功能,不做一次性代码的抽象
|
||||
- 不加没要求的"灵活性"和"可配置性"
|
||||
- 200 行能 50 行搞定就重写
|
||||
- 自问:"高级工程师会不会觉得这过度设计?"
|
||||
|
||||
### 3.3 精准修改
|
||||
- 只改必须改的,不"顺手改进"相邻代码
|
||||
- 匹配现有代码风格,即使你有不同的偏好
|
||||
- 每行改动都能追溯到用户的请求
|
||||
- 只清理你自己改动产生的无用代码
|
||||
|
||||
### 3.4 目标驱动
|
||||
- 把任务转化为可验证目标
|
||||
- 多步任务声明计划:`[步骤] → 验证: [检查]`
|
||||
- 强验收标准让 Agent 能独立循环,弱标准需要持续澄清
|
||||
|
||||
---
|
||||
|
||||
## 四、全链路 6 环分析
|
||||
|
||||
> ⚠️ **涉及数据库字段的 Bug / 需求,必须走完整链路。**
|
||||
|
||||
```
|
||||
前端/页面 → Controller → Service → Mapper → DB/SQL → 关联模块
|
||||
①录入 ②验证 ③业务 ④持久化 ⑤存储 ⑥联动
|
||||
```
|
||||
|
||||
| 环 | 检查内容 |
|
||||
|----|---------|
|
||||
| ① 录入 | 前端有无输入入口(弹窗、表格行编辑、表单) |
|
||||
| ② 验证 | Controller 参数校验、@Valid、权限控制 |
|
||||
| ③ 业务 | Service 业务逻辑、事务边界、多个 Service 实现类入口 |
|
||||
| ④ 持久化 | Mapper XML、DTO 字段映射、类型转换 |
|
||||
| ⑤ 存储 | 数据库表结构、索引、NOT NULL 约束 |
|
||||
| ⑥ 联动 | 上游(医嘱→护士站)、下游(打印、计费、报表)是否同步 |
|
||||
|
||||
**修复后的验证顺序**:
|
||||
1. 数据库:确认状态值已正确写入
|
||||
2. 后端接口:确认返回的状态映射正确
|
||||
3. 前端显示:确认页面显示正确状态文本
|
||||
4. 前端交互:确认按钮/操作基于正确状态启用/禁用
|
||||
5. 统计数据:确认池/报表统计包含新状态
|
||||
|
||||
---
|
||||
|
||||
## 五、Harness Engineering 方法论
|
||||
|
||||
> Harness = 约束 + 反馈 + 控制平面 + 持久执行
|
||||
|
||||
### 5.1 四层约束金字塔
|
||||
|
||||
| 层级 | 内容 | 落地方式 |
|
||||
|------|------|---------|
|
||||
| **L1 架构约束** | 接口合约、包结构、命名规范、禁止模式 | 本文件铁律 |
|
||||
| **L2 代码质量** | 圈复杂度、代码风格、类型提示 | 编译门禁 + ESLint |
|
||||
| **L3 安全约束** | 敏感信息检测、权限检查、输入验证 | 配置不可硬编码 |
|
||||
| **L4 业务规则** | 领域逻辑、数据一致性、事务边界 | 全链路 6 环验证 |
|
||||
|
||||
**约束设计原则**:
|
||||
- **可验证**:每条约束必须能被自动化检查("覆盖率>90%"✅ "质量要高"❌)
|
||||
- **无歧义**:"每函数不超过50行"✅ "函数不要太长"❌
|
||||
- **优先级**:安全(1) > 架构(2) > 业务(3) > 质量(4) > 性能(5)
|
||||
- **渐进增强**:L1编译通过 → L2+命名规范 → L3+测试覆盖 → L4+安全扫描
|
||||
|
||||
### 5.2 三层反馈系统
|
||||
|
||||
| 层级 | 速度 | 覆盖范围 | 失败处理 |
|
||||
|------|------|---------|---------|
|
||||
| **L1 编译检查** | <30秒 | 语法、类型、签名 | 立即阻断,自行修复 |
|
||||
| **L2 数据流验证** | <5分钟 | 全链路字段、Mapper XML、DTO | 修复后上报 |
|
||||
| **L3 人工审查** | 10-30分钟 | 架构、设计、业务正确性 | 驳回/指导/批准 |
|
||||
|
||||
**反馈铁律**:
|
||||
- 反馈必须可行动(文件 + 行号 + 错误类型 + 修复方向)
|
||||
- 失败后先回滚到最近检查点,再重试
|
||||
- 持续失败3次 → 上报人类
|
||||
|
||||
### 5.3 控制平面
|
||||
|
||||
```
|
||||
战略层(人类) → 设定目标、审批决策、异常升级
|
||||
战术层(Agent) → 任务分解、update_plan、依赖协调、检查点保存
|
||||
执行层(Agent) → 代码生成、测试执行、错误恢复、幂等重试
|
||||
```
|
||||
|
||||
### 5.4 持久执行
|
||||
|
||||
- 每个关键步骤保存检查点(`update_plan` 进度)
|
||||
- 失败后从最新检查点恢复,不从头开始
|
||||
- 幂等设计:同一操作重复执行结果一致
|
||||
- **三层状态管理**:系统层(工作流ID/超时/重试) → 执行层(当前活动/进度) → 业务层(已完成工作/中间产物)
|
||||
|
||||
---
|
||||
|
||||
## 六、五层质量门禁
|
||||
|
||||
| 门禁 | 时间 | 范围 | 失败处理 |
|
||||
|------|------|------|---------|
|
||||
| **L1 编译检查** | <30秒 | 语法、类型、导入 | Agent 自行修复 |
|
||||
| **L2 静态分析** | <2分钟 | 代码风格、复杂度、安全 | Agent 修复 |
|
||||
| **L3 单元测试** | <5分钟 | 功能正确性、边界条件 | 自动修复或上报 |
|
||||
| **L4 集成测试** | <15分钟 | 模块间交互、数据流 | 上报人工 |
|
||||
| **L5 生产验证** | 持续 | 监控、告警、性能 | 自动回滚 |
|
||||
|
||||
**提交铁律**:L1-L2 必须通过才能 commit,L3(如有DB变更)必须通过才能 push
|
||||
|
||||
---
|
||||
|
||||
## 七、系统化调试(Systematic Debugging)
|
||||
|
||||
> **铁律:没有根因调查,不能提出修复方案。**
|
||||
|
||||
### 四阶段流程
|
||||
|
||||
**阶段1:根因调查**(修复前必须完成)
|
||||
1. 仔细阅读错误信息(堆栈、行号、错误码)
|
||||
2. 稳定复现(能否可靠触发?步骤?每次?)
|
||||
3. 检查最近变更(git diff、新依赖、配置变更)
|
||||
4. 多组件系统:在每个组件边界加诊断日志,定位哪一层断裂
|
||||
5. 追踪数据流:坏值从哪里来?谁调用的?一直追溯到源头
|
||||
|
||||
**阶段2:模式分析**
|
||||
- 找到同代码库中类似的正常工作代码
|
||||
- 逐项对比差异
|
||||
- 理解依赖关系
|
||||
|
||||
**阶段3:假设与测试**
|
||||
- 形成单一假设:"我认为X是根因,因为Y"
|
||||
- 做最小改动测试
|
||||
- 有效 → 阶段4;无效 → 新假设
|
||||
|
||||
**阶段4:实施**
|
||||
- 创建失败测试用例
|
||||
- 修复根因(不是症状)
|
||||
- 验证修复
|
||||
|
||||
---
|
||||
|
||||
## 八、后端开发规范
|
||||
|
||||
### 分层架构
|
||||
```
|
||||
Controller → AppService → Service → Mapper → Entity
|
||||
```
|
||||
|
||||
### 命名规范
|
||||
| 类型 | 规则 | 示例 |
|
||||
|------|------|------|
|
||||
| Controller | `XxxController` | `RegistrationController` |
|
||||
| AppService | `IXxxAppService` / `XxxAppServiceImpl` | `IRegistrationAppService` |
|
||||
| Service | `IXxxService` / `XxxServiceImpl` | `IRegistrationService` |
|
||||
| Mapper | `XxxMapper` | `RegistrationMapper` |
|
||||
| Entity | `Xxx` | `Registration` |
|
||||
| DTO | `XxxDto` / `XxxQueryDto` | `RegistrationDto` |
|
||||
|
||||
### 包结构
|
||||
```
|
||||
com.healthlink.his.web.{module}.controller
|
||||
com.healthlink.his.web.{module}.appservice
|
||||
com.healthlink.his.web.{module}.service
|
||||
com.healthlink.his.web.{module}.mapper
|
||||
com.healthlink.his.web.{module}.dto
|
||||
com.healthlink.his.domain.{module}
|
||||
com.healthlink.his.common.enums
|
||||
```
|
||||
|
||||
### 关键约束
|
||||
- 所有查询使用 `LambdaQueryWrapper`,禁止字符串拼接 SQL
|
||||
- `@Transactional(rollbackFor = Exception.class)` 管理事务
|
||||
- 所有接口标注 `@PreAuthorize` 权限控制
|
||||
- 患者敏感信息在日志中脱敏
|
||||
- **扩展功能不修改原有函数签名**
|
||||
|
||||
---
|
||||
|
||||
## 九、前端开发规范
|
||||
|
||||
### 技术栈
|
||||
- Vue 3 + Vite + Element Plus + Pinia + Axios(基于 RuoYi-Vue3)
|
||||
|
||||
### 目录结构
|
||||
```
|
||||
src/api/{module}/ # API接口
|
||||
src/views/{module}/ # 页面组件
|
||||
src/store/modules/ # Pinia状态管理
|
||||
src/components/ # 公共组件
|
||||
```
|
||||
|
||||
### 关键约束
|
||||
- API前缀:`/healthlink-his/api/v1/`
|
||||
- 路由懒加载:`() => import('@/views/xxx/index.vue')`
|
||||
- 页面使用 `<script setup>` 语法
|
||||
- 按钮权限使用 `v-hasPermi` 指令
|
||||
- `onMounted` 中注册的事件在 `onUnmounted` 中移除
|
||||
|
||||
---
|
||||
|
||||
## 十、Agent 体系
|
||||
|
||||
### 角色与路由
|
||||
|
||||
| 代号 | 名称 | 角色 | 路由关键词 |
|
||||
|------|------|------|-----------|
|
||||
| liubei | 刘备 | 项目经理 | 协调、分派、异常升级 |
|
||||
| zhugeliang | 诸葛亮 | 架构师 | 分析、路由、设计 |
|
||||
| guanyu | 关羽 | 后端开发 | java, api, spring, service, controller |
|
||||
| zhaoyun | 赵云 | 前端开发 | vue, 界面, 显示, 弹窗, 按钮 |
|
||||
| xunyu | 荀彧 | DBA | 数据库, sql, 迁移, mapper xml |
|
||||
| zhangfei | 张飞 | 测试 | 测试, QA, 回归 |
|
||||
| huatuo | 华佗 | 验收 | 需求验收、质量确认 |
|
||||
| chenlin | 陈琳 | 文档 | 文档、归档、Git提交 |
|
||||
|
||||
### 协作流水线
|
||||
|
||||
```
|
||||
刘备(协调) → 诸葛亮(分析路由) → {关羽|赵云}(修复) → 荀彧(DB审查) → 张飞(测试) → 华佗(验收) → 陈琳(归档)
|
||||
```
|
||||
|
||||
### Bug 修复完整管线(BDT 方法论)
|
||||
|
||||
```
|
||||
获取Bug → 设计测试用例 → 基线测试(应失败) → 全链路修复 → 回归测试(应通过) → 扩展测试(无回归) → 提交
|
||||
```
|
||||
|
||||
### Bug 状态管理铁律
|
||||
- 人类提的 Bug:只加备注,不改状态,不改分配
|
||||
- 智能体提的 Bug:可以改分配和加备注
|
||||
- 已关闭/已解决的 Bug 不再处理
|
||||
|
||||
---
|
||||
|
||||
## 十一、审查与审计
|
||||
|
||||
### 三层审查体系
|
||||
|
||||
| 层级 | 内容 | 时机 |
|
||||
|------|------|------|
|
||||
| **L1 自审** | Agent 对照约束逐条检查 | 每次提交前 |
|
||||
| **L2 配对审查** | Agent 生成变更摘要,人类终审 | PR/提交时 |
|
||||
| **L3 合规审查** | 审计追踪,记录所有 AI 操作 | 持续 |
|
||||
|
||||
### L1 自审清单
|
||||
```yaml
|
||||
self_review:
|
||||
- "所有修改能通过编译?"
|
||||
- "遵守命名规范?"
|
||||
- "测试覆盖达标?"
|
||||
- "没有遗漏的 TODO / DEBUG?"
|
||||
- "变更范围没超出任务边界?"
|
||||
```
|
||||
|
||||
### 评审评分维度
|
||||
| 维度 | 问题 |
|
||||
|------|------|
|
||||
| 正确性 | 行为是否符合目标功能? |
|
||||
| 验证 | 检查是否真的跑过并留下证据? |
|
||||
| 范围纪律 | 是否保持在选定功能范围内? |
|
||||
| 可靠性 | 结果能否在重启后继续工作? |
|
||||
| 可维护性 | 代码和文档是否清楚到可交接? |
|
||||
|
||||
---
|
||||
|
||||
## 十二、标准工作循环
|
||||
|
||||
```
|
||||
开始会话
|
||||
├→ 1. Init — 读 AGENTS.md + PROGRESS.md + git log
|
||||
├→ 2. Select — 只选一个未完成功能
|
||||
├→ 3. Implement — 一次只做一个,不扩大范围
|
||||
├→ 4. Verify — 运行验证命令,有证据才标记完成
|
||||
└→ 5. Cleanup — 更新进度 + clean-state-checklist + git commit
|
||||
```
|
||||
|
||||
### 会话结束前必须运行 Clean State Checklist
|
||||
```
|
||||
□ 标准启动路径仍然可用
|
||||
□ 标准验证路径仍然可运行
|
||||
□ 当前进度已记录到进度日志
|
||||
□ 无半成品步骤处于未记录状态
|
||||
□ 下一轮会话无需人工修复即可继续
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十三、开发流程
|
||||
|
||||
```
|
||||
收到任务
|
||||
├→ ① 分析需求 → 读相关文档(MD/)、读全链路6环
|
||||
├→ ② 制定计划 → update_plan (3-6个阶段)
|
||||
├→ ③ 后端开发 → Controller → AppService → Service → Mapper → Entity → Flyway
|
||||
├→ ④ 后端测试 → mvn test → 接口测试(业务逻辑验证)
|
||||
├→ ⑤ 前端开发 → API接口 → 页面组件 → 路由配置
|
||||
├→ ⑥ 前端测试 → npm run build:dev → 功能验证
|
||||
├→ ⑦ 质量门禁 → L1编译 → L2测试 → L3DB审查 → L4验收 → L5归档
|
||||
└→ ⑧ 提交代码 → git commit(规范格式) → git push → 文档更新
|
||||
```
|
||||
|
||||
### Git Commit 格式
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
type: feat|fix|docs|refactor|test|chore
|
||||
scope: 模块名(如 registration, billing, pharmacy)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十四、快速参考命令
|
||||
|
||||
```bash
|
||||
# === 后端 ===
|
||||
export JAVA_HOME=/opt/jdk-25
|
||||
mvn clean compile -DskipTests # 编译
|
||||
mvn install -DskipTests # 构建
|
||||
mvn test -pl healthlink-his-application -Dtest="XxxTest" -Dsurefire.failIfNoSpecifiedTests=false
|
||||
|
||||
# === 前端 ===
|
||||
cd healthlink-his-ui
|
||||
npm run dev && npm run build:dev && npm run lint && npm run test:run
|
||||
|
||||
# === Git ===
|
||||
git status && git add -A && git commit -m "feat(module): desc" && git push origin develop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十五、详细规范文档索引
|
||||
|
||||
| 文档 | 路径 | 用途 |
|
||||
|------|------|------|
|
||||
| 执行铁律 | `MD/specs/IRON_RULES.md` | 铁律完整版 |
|
||||
| 后端规范 | `MD/specs/BACKEND_DEVELOPMENT_STANDARD.md` | 后端编码标准 |
|
||||
| 前端规范 | `MD/specs/FRONTEND_DEVELOPMENT_STANDARD.md` | 前端编码标准 |
|
||||
| Harness方法论 | `MD/specs/HARNESS_ENGINEERING.md` | 完整Harness+Agent方法论 |
|
||||
| 文档规范 | `MD/DOCUMENTATION_STANDARD.md` | 文档管理标准 |
|
||||
| 后端清单 | `MD/specs/BACKEND_CHECKLIST.md` | 发布前检查 |
|
||||
| 前端清单 | `MD/specs/FRONTEND_CHECKLIST.md` | 发布前检查 |
|
||||
| 三甲标准 | `MD/standards/GRADE3A_HIS_STANDARD.md` | 三甲医院达标标准 |
|
||||
| Flyway指南 | `MD/guides/FLYWAY_USAGE_GUIDE.md` | 数据库迁移指南 |
|
||||
|
||||
---
|
||||
|
||||
## 十六、过往教训
|
||||
|
||||
| 教训 | 内容 |
|
||||
|------|------|
|
||||
| 状态链路断裂 | Bug#574: 签到设 BOOKED(1) 而非 CHECKED_IN(3),前端映射缺失 → 必须走完整状态链路 |
|
||||
| 盲删源文件 | AI 看到编译错误直接删文件,没检查 baseline → 必须先确认文件来源 |
|
||||
| 修复方向偏差 | 多次 fallback 改的是错误的 Service → 必须用 rg 搜索所有相关代码路径 |
|
||||
| bug_reports 缺列 | INSERT 静默失败 → 必须检查表结构 |
|
||||
| 禅道 comment API | API 不存在,用 resolve+activate workaround |
|
||||
| SQLite WAL 并发 | 多进程并发写需要 checkpoint |
|
||||
| UTF-8 切片 | 多字节字符不能用 byte index 切片 |
|
||||
| 上下文焦虑 | Agent 感觉上下文快满时会匆忙结束,跳过验证 → 注意 context 40% 阈值 |
|
||||
| 过早宣告胜利 | 自评≠验证,分开"干活"和"检查" |
|
||||
| 覆盖率幻觉 | 覆盖率达标但逻辑没测 → 引入变异测试 |
|
||||
|
||||
---
|
||||
|
||||
> ⚠️ 本文件是 AI 开发规范的唯一信源。各工具配置文件由 `bash scripts/sync-ai-rules.sh` 同步。
|
||||
|
||||
---
|
||||
|
||||
> 📅 最后同步: 2026-06-06 15:09 | 源文件: RULES.md | 重新同步: `bash scripts/sync-ai-rules.sh`
|
||||
|
||||
424
MD/MODULE_INDEX.md
Normal file
424
MD/MODULE_INDEX.md
Normal file
@@ -0,0 +1,424 @@
|
||||
# HealthLink-HIS 代码模块索引
|
||||
|
||||
> 供 LLM 快速定位代码。每个模块列出 Controller → Service → Mapper 关键文件。
|
||||
|
||||
## 关键词 → 模块速查
|
||||
|
||||
| 关键词 | 后端模块 | 前端目录 |
|
||||
|---|---|---|
|
||||
| 门诊医生站/门诊医嘱/门诊处方/诊断/检查申请 | `doctorstation` | `doctorstation` |
|
||||
| 住院医生站/住院医嘱/临床医嘱/签发/停嘱 | `regdoctorstation` | `inpatientDoctor` |
|
||||
| 住院护士站/医嘱校对/医嘱执行/护理/换床 | `inhospitalnursestation` | `inpatientNurse` |
|
||||
| 挂号/门诊收费/门诊结算 | `chargemanage` | `charge` |
|
||||
| 住院收费/住院结算/预交金 | `inhospitalcharge` | `inHospitalManagement` |
|
||||
| 收费管理/计费/退费 | `paymentmanage` | `outpatientFinance` |
|
||||
| 药品/药房/药库/发药/取药 | `pharmacymanage` | `pharmacymanagement` |
|
||||
| 药房发药/门诊发药 | `pharmacyDispensarymanage` | `drug` |
|
||||
| 药库管理/库存 | `pharmacyWarehousemanage` | `medicineStorage` |
|
||||
| 库存管理/盘点/出入库 | `inventorymanage` | `medicineStorage` |
|
||||
| 物资管理/耗材 | `materialmanage` | `` |
|
||||
| 字典/数据字典/诊疗目录/基础数据 | `datadictionary` | `datadictionary` |
|
||||
| 部门/科室管理 | `departmentmanage` | `system` |
|
||||
| 卡管理/就诊卡 | `cardmanagement` | `cardmanagement` |
|
||||
| 检验/化验/标本 | `lab` | `inspection` |
|
||||
| 检查/影像/放射 | `Inspection` | `inspection` |
|
||||
| 手术/手术安排/手术申请 | `surgicalschedule` | `surgerymanage` |
|
||||
| 病历/电子病历/EMR | `emr` | `emr` |
|
||||
| 护理记录/护理评估 | `nursing` | `nursing` |
|
||||
| 分诊/排队/叫号 | `triageandqueuemanage` | `triageandqueuemanage` |
|
||||
| 医保/医保对码/医保目录 | `ybmanage` | `ybmanagement` |
|
||||
| 会诊/会诊申请 | `consultation` | `consultationmanagement` |
|
||||
| 院感/感染上报 | `infection` | `infection` |
|
||||
| 合理用药/处方审核 | `rationaldrug` | `rationaldrug` |
|
||||
| 中医/中医处方 | `tcm` | `tcm` |
|
||||
| 患者管理/患者信息 | `patientmanage` | `patientmanagement` |
|
||||
| 预约/挂号预约 | `appointmentmanage` | `appoinmentmanage` |
|
||||
| 报告/报告管理 | `reportmanage` | `` |
|
||||
| 质控/质量 | `quality` | `quality` |
|
||||
| 系统管理/用户/角色/权限 | `basicmanage` | `system` |
|
||||
| 门诊管理/门诊工作站 | `outpatientmanage` | `doctorstation` |
|
||||
| 前置手术/术前管理 | `preopmanage` | `preopmanage` |
|
||||
| 危急值 | `criticalvalue` | `criticalvalue` |
|
||||
| 抗菌药 | `antibiotic` | `antibiotic` |
|
||||
| 随访 | `followup` | `followup` |
|
||||
| request.js/请求拦截/响应拦截 | `common` | `crossmodule` |
|
||||
|
||||
## 后端模块详情
|
||||
|
||||
### `Inspection` (40 files)
|
||||
- **Controller**: `Inspection/controller/SampleCollectController.java` `Inspection/controller/ObservationDefController.java` `Inspection/controller/LabReferenceRangeController.java`
|
||||
- **AppService**: `Inspection/appservice/ISampleCollectAppManageAppService.java` `Inspection/appservice/ILisConfigManageAppService.java` `Inspection/appservice/IInstrumentManageAppService.java`
|
||||
- **ServiceImpl**: `Inspection/appservice/impl/LisConfigManageAppServiceImpl.java` `Inspection/appservice/impl/ObservationManageAppServiceImpl.java` `Inspection/appservice/impl/SpecimenManageAppServiceImpl.java`
|
||||
- **Mapper**: `Inspection/mapper/SampleCollectMapper.java` `Inspection/mapper/LisReportMapper.java` `Inspection/mapper/GroupRecMapper.java`
|
||||
- **DTO**: `Inspection/dto/SampleCollectManageDto.java` `Inspection/dto/SpecimenDefManageDto.java` `Inspection/dto/InstrumentManageDto.java` `Inspection/dto/LisConfigManageDto.java` `Inspection/dto/InstrumentSelParam.java`
|
||||
|
||||
### `adjustprice` (10 files)
|
||||
- **Controller**: `adjustprice/controller/ChangePriceController.java` `adjustprice/controller/ChangePriceDataListPageController.java`
|
||||
- **ServiceImpl**: `adjustprice/appservice/impl/AdjustPriceServiceImpl.java`
|
||||
- **Mapper**: `adjustprice/mapper/AdjustPriceMapper.java`
|
||||
- **DTO**: `adjustprice/dto/ChangePriceDataDto.java` `adjustprice/dto/AdjustPriceManagerSearchParam.java` `adjustprice/dto/ChangePricePageDto.java`
|
||||
|
||||
### `anesthesia` (4 files)
|
||||
- **Controller**: `anesthesia/controller/AnesthesiaController.java` `anesthesia/controller/AnesthesiaEnhancedController.java`
|
||||
- **AppService**: `anesthesia/appservice/IAnesthesiaAppService.java`
|
||||
- **ServiceImpl**: `anesthesia/appservice/impl/AnesthesiaAppServiceImpl.java`
|
||||
|
||||
### `antibiotic` (3 files)
|
||||
- **Controller**: `antibiotic/controller/AntibioticController.java`
|
||||
- **AppService**: `antibiotic/appservice/IAntibioticAppService.java`
|
||||
- **ServiceImpl**: `antibiotic/appservice/impl/AntibioticAppServiceImpl.java`
|
||||
|
||||
### `appointmentmanage` (29 files)
|
||||
- **Controller**: `appointmentmanage/controller/ScheduleSlotController.java` `appointmentmanage/controller/DeptAppthoursController.java` `appointmentmanage/controller/SchedulePoolController.java`
|
||||
- **AppService**: `appointmentmanage/appservice/IDeptAppService.java` `appointmentmanage/appservice/IDoctorScheduleAppService.java` `appointmentmanage/appservice/IClinicRoomAppService.java`
|
||||
- **ServiceImpl**: `appointmentmanage/appservice/impl/DoctorScheduleAppServiceImpl.java` `appointmentmanage/appservice/impl/DeptAppointmentHoursAppServiceImpl.java` `appointmentmanage/appservice/impl/TicketAppServiceImpl.java`
|
||||
- **Mapper**: `appointmentmanage/mapper/DoctorScheduleAppMapper.java` `appointmentmanage/mapper/SchedulePoolAppMapper.java` `appointmentmanage/mapper/DeptAppMapper.java`
|
||||
- **DTO**: `appointmentmanage/dto/TicketDto.java` `appointmentmanage/dto/SchedulePoolDto.java`
|
||||
|
||||
### `basedatamanage` (44 files)
|
||||
- **Controller**: `basedatamanage/controller/OrganizationLocationController.java` `basedatamanage/controller/BodyStructureController.java` `basedatamanage/controller/OperatingRoomController.java`
|
||||
- **AppService**: `basedatamanage/appservice/IOrganizationAppService.java` `basedatamanage/appservice/IBodyStructureAppService.java` `basedatamanage/appservice/ILocationAppService.java`
|
||||
- **ServiceImpl**: `basedatamanage/appservice/impl/PractitionerAppServiceImpl.java` `basedatamanage/appservice/impl/BodyStructureAppServiceImpl.java` `basedatamanage/appservice/impl/OrganizationAppServiceImpl.java`
|
||||
- **Mapper**: `basedatamanage/mapper/PractitionerAppAppMapper.java`
|
||||
- **DTO**: `basedatamanage/dto/SelectableOrgDto.java` `basedatamanage/dto/PractitionerOrgAndLocationDto.java` `basedatamanage/dto/OrganizationInitDto.java` `basedatamanage/dto/OperatingRoomDto.java` `basedatamanage/dto/LocationInitDto.java`
|
||||
|
||||
### `basicmanage` (5 files)
|
||||
- **Controller**: `basicmanage/controller/BedController.java` `basicmanage/controller/InvoiceController.java` `basicmanage/controller/InvoiceSegmentController.java`
|
||||
|
||||
### `basicservice` (7 files)
|
||||
- **Controller**: `basicservice/controller/HealthcareServiceController.java`
|
||||
- **Mapper**: `basicservice/mapper/HealthcareServiceBizMapper.java`
|
||||
- **DTO**: `basicservice/dto/HealthcareServiceAddOrUpdateParam.java` `basicservice/dto/HealthcareServiceDto.java` `basicservice/dto/HealthcareServiceInitDto.java`
|
||||
|
||||
### `ca` (3 files)
|
||||
- **Controller**: `ca/controller/CaSignatureController.java`
|
||||
- **AppService**: `ca/appservice/ICaSignatureAppService.java`
|
||||
- **ServiceImpl**: `ca/appservice/impl/CaSignatureAppServiceImpl.java`
|
||||
|
||||
### `cardmanagement` (17 files)
|
||||
- **Controller**: `cardmanagement/controller/CardManageController.java`
|
||||
- **AppService**: `cardmanagement/appservice/ICardManageAppService.java`
|
||||
- **ServiceImpl**: `cardmanagement/appservice/impl/CardManageAppServiceImpl.java`
|
||||
- **Mapper**: `cardmanagement/mapper/InfectiousAuditMapper.java` `cardmanagement/mapper/InfectiousCardMapper.java`
|
||||
- **DTO**: `cardmanagement/dto/InfectiousCardDto.java` `cardmanagement/dto/DoctorCardQueryDto.java` `cardmanagement/dto/DoctorCardListDto.java` `cardmanagement/dto/SingleReturnDto.java` `cardmanagement/dto/CardStatisticsDto.java`
|
||||
|
||||
### `catalogmanage` (4 files)
|
||||
- **Controller**: `catalogmanage/controller/CatalogController.java`
|
||||
- **ServiceImpl**: `catalogmanage/appservice/impl/CatalogServiceImpl.java`
|
||||
- **Mapper**: `catalogmanage/mapper/CatalogMapper.java`
|
||||
|
||||
### `charge` (4 files)
|
||||
- **Controller**: `charge/patientcardrenewal/PatientCardRenewalController.java`
|
||||
- **ServiceImpl**: `charge/patientcardrenewal/PatientCardRenewalServiceImpl.java`
|
||||
|
||||
### `chargemanage` (46 files)
|
||||
- **Controller**: `chargemanage/controller/OutpatientRegistrationController.java` `chargemanage/controller/OutpatientPricingController.java` `chargemanage/controller/InpatientChargeController.java`
|
||||
- **AppService**: `chargemanage/appservice/IInpatientChargeAppService.java` `chargemanage/appservice/IOutpatientRegistrationAppService.java` `chargemanage/appservice/IOutpatientRefundAppService.java`
|
||||
- **ServiceImpl**: `chargemanage/appservice/impl/OutpatientChargeAppServiceImpl.java` `chargemanage/appservice/impl/InpatientChargeAppServiceImpl.java` `chargemanage/appservice/impl/OutpatientRefundAppServiceImpl.java`
|
||||
- **Mapper**: `chargemanage/mapper/OutpatientRefundAppMapper.java` `chargemanage/mapper/OutpatientRegistrationAppMapper.java` `chargemanage/mapper/OutpatientChargeAppMapper.java`
|
||||
- **DTO**: `chargemanage/dto/ReprintRegistrationDto.java` `chargemanage/dto/EncounterPatientRefundDto.java` `chargemanage/dto/OutpatientPricingPriceDto.java` `chargemanage/dto/OutpatientPricingInventoryDto.java` `chargemanage/dto/RefundItemParam.java`
|
||||
|
||||
### `check` (27 files)
|
||||
- **Controller**: `check/controller/CheckMethodController.java` `check/controller/SpecimenBarcodeController.java` `check/controller/RadiologyEnhancedController.java`
|
||||
- **AppService**: `check/appservice/ILisGroupInfoAppService.java` `check/appservice/ICheckPartAppService.java` `check/appservice/ICheckMethodAppService.java`
|
||||
- **ServiceImpl**: `check/appservice/impl/CheckMethodAppServiceImpl.java` `check/appservice/impl/CheckPartAppServiceImpl.java` `check/appservice/impl/CheckPackageAppServiceImpl.java`
|
||||
- **Mapper**: `check/mapper/LisGroupInfoAppMapper.java` `check/mapper/CheckMethodAppMapper.java` `check/mapper/CheckPartAppMapper.java`
|
||||
- **DTO**: `check/dto/CheckPackageDetailDto.java` `check/dto/ExamApplyDto.java` `check/dto/ExamApplyItemDto.java` `check/dto/CheckPackageDto.java` `check/dto/CheckMethodDto.java`
|
||||
|
||||
### `clinical` (2 files)
|
||||
- **Controller**: `clinical/controller/KnowledgeBaseController.java` `clinical/controller/ClinicalPathwayController.java`
|
||||
|
||||
### `clinicalmanage` (11 files)
|
||||
- **Controller**: `clinicalmanage/controller/SurgicalScheduleController.java` `clinicalmanage/controller/SurgeryController.java`
|
||||
- **AppService**: `clinicalmanage/appservice/ISurgicalScheduleAppService.java` `clinicalmanage/appservice/ISurgeryAppService.java`
|
||||
- **ServiceImpl**: `clinicalmanage/appservice/impl/SurgicalScheduleAppServiceImpl.java` `clinicalmanage/appservice/impl/SurgeryAppServiceImpl.java`
|
||||
- **Mapper**: `clinicalmanage/mapper/SurgicalScheduleAppMapper.java` `clinicalmanage/mapper/SurgeryAppMapper.java`
|
||||
- **DTO**: `clinicalmanage/dto/SurgeryDto.java` `clinicalmanage/dto/OpScheduleDto.java` `clinicalmanage/dto/OpCreateScheduleDto.java`
|
||||
|
||||
### `common` (17 files)
|
||||
- **Controller**: `common/controller/CommonAppController.java`
|
||||
- **ServiceImpl**: `common/appservice/impl/CommonServiceImpl.java`
|
||||
- **Mapper**: `common/mapper/CommonAppMapper.java`
|
||||
- **DTO**: `common/dto/ActivityDefinitionDto.java` `common/dto/PerformInfoDto.java` `common/dto/PractitionerInfoDto.java` `common/dto/LocationInventoryDto.java` `common/dto/PerformRecordDto.java`
|
||||
|
||||
### `consultation` (19 files)
|
||||
- **Controller**: `consultation/controller/ConsultationController.java`
|
||||
- **AppService**: `consultation/appservice/IConsultationAppService.java`
|
||||
- **ServiceImpl**: `consultation/appservice/impl/ConsultationAppServiceImpl.java`
|
||||
- **Mapper**: `consultation/mapper/ConsultationInvitedMapper.java` `consultation/mapper/ConsultationConfirmationMapper.java` `consultation/mapper/ConsultationRequestMapper.java`
|
||||
- **DTO**: `consultation/dto/PhysicianNodeDto.java` `consultation/dto/InvitedObjectDto.java` `consultation/dto/ConsultationActivityDto.java` `consultation/dto/DepartmentTreeDto.java` `consultation/dto/ConsultationRequestDto.java`
|
||||
|
||||
### `controller` (2 files)
|
||||
- **Controller**: `controller/WorkflowController.java` `controller/HomeStatisticsController.java`
|
||||
|
||||
### `criticalvalue` (3 files)
|
||||
- **Controller**: `criticalvalue/controller/CriticalValueController.java`
|
||||
- **AppService**: `criticalvalue/appservice/ICriticalValueAppService.java`
|
||||
- **ServiceImpl**: `criticalvalue/appservice/impl/CriticalValueAppServiceImpl.java`
|
||||
|
||||
### `crossmodule` (3 files)
|
||||
- **Controller**: `crossmodule/controller/CrossModuleController.java` `crossmodule/controller/EnhancementController.java` `crossmodule/controller/IntegrationController.java`
|
||||
|
||||
### `datadictionary` (65 files)
|
||||
- **Controller**: `datadictionary/controller/DiagnosisTreatmentController.java` `datadictionary/controller/MedicationManageController.java` `datadictionary/controller/DiseaseManageController.java`
|
||||
- **AppService**: `datadictionary/appservice/IDeviceManageAppService.java` `datadictionary/appservice/IDiagTreatMAppService.java` `datadictionary/appservice/ItemDefinitionAppService.java`
|
||||
- **ServiceImpl**: `datadictionary/appservice/impl/DiagTreatMAppServiceImpl.java` `datadictionary/appservice/impl/SupplierManagementAppServiceImpl.java` `datadictionary/appservice/impl/ItemDefinitionAppServiceImpl.java`
|
||||
- **Mapper**: `datadictionary/mapper/MedicationManageSearchMapper.java` `datadictionary/mapper/ICDCodeMapper.java` `datadictionary/mapper/ActivityDefinitionManageMapper.java`
|
||||
- **DTO**: `datadictionary/dto/DeviceManageUpDto.java` `datadictionary/dto/ChargeItemOptionDto.java` `datadictionary/dto/SupplierDto.java` `datadictionary/dto/DiagnosisTreatmentInitDto.java` `datadictionary/dto/DiagnosisTreatmentSelParam.java`
|
||||
|
||||
### `departmentmanage` (42 files)
|
||||
- **Controller**: `departmentmanage/controller/DepartmentTransferOutOrderController.java` `departmentmanage/controller/DepartmentReturnToWarehouseOrderController.java` `departmentmanage/controller/DepartmentStocktakingOrderController.java`
|
||||
- **ServiceImpl**: `departmentmanage/appservice/impl/DepartmentReceiptApprovalServiceImpl.java` `departmentmanage/appservice/impl/DepartmentStockInOrderServiceImpl.java` `departmentmanage/appservice/impl/DepartmentCommonServiceImpl.java`
|
||||
- **Mapper**: `departmentmanage/mapper/DepartmentTransferInOrderMapper.java` `departmentmanage/mapper/DepartmentStocktakingOrderMapper.java` `departmentmanage/mapper/DepartmentTransferOutOrderMapper.java`
|
||||
- **DTO**: `departmentmanage/dto/DepartmentDeviceInfoDto.java` `departmentmanage/dto/DepartmentDetailDto.java` `departmentmanage/dto/DepartmentInitDto.java` `departmentmanage/dto/DepartmentSearchParam.java` `departmentmanage/dto/DepartmentDto.java`
|
||||
|
||||
### `doctorstation` (91 files)
|
||||
- **Controller**: `doctorstation/controller/DoctorStationDiagnosisController.java` `doctorstation/controller/DoctorStationInspectionLabApplyController.java` `doctorstation/controller/DoctorStationChineseMedicalController.java`
|
||||
- **AppService**: `doctorstation/appservice/IDoctorPhraseAppService.java` `doctorstation/appservice/IDoctorStationEmrAppService.java` `doctorstation/appservice/IDoctorStationMainAppService.java`
|
||||
- **ServiceImpl**: `doctorstation/appservice/impl/DoctorStationPtDetailsAppServiceImpl.java` `doctorstation/appservice/impl/DoctorStationElepPrescriptionServiceImpl.java` `doctorstation/appservice/impl/DoctorPhraseAppServiceImpl.java`
|
||||
- **Mapper**: `doctorstation/mapper/DoctorStationAdviceAppMapper.java` `doctorstation/mapper/DoctorStationEmrAppMapper.java` `doctorstation/mapper/DoctorStationDiagnosisAppMapper.java`
|
||||
- **DTO**: `doctorstation/dto/EncounterContractDto.java` `doctorstation/dto/AdviceInventoryDto.java` `doctorstation/dto/ActivityChildrenJsonParams.java` `doctorstation/dto/DoctorStationLabApplyItemDto.java` `doctorstation/dto/DoctorStationInitDto.java`
|
||||
|
||||
### `document` (47 files)
|
||||
- **Controller**: `document/controller/DocRecordController.java` `document/controller/DocDefinitionController.java` `document/controller/InformedConsentController.java`
|
||||
- **AppService**: `document/appservice/IDocStatisticsAppService.java` `document/appservice/IDocRecordAppService.java` `document/appservice/IDocTemplateAppService.java`
|
||||
- **ServiceImpl**: `document/appservice/impl/DocStatisticsDefinitionAppServiceImpl.java` `document/appservice/impl/DocRecordAppServiceImpl.java` `document/appservice/impl/DocStatisticsAppServiceImpl.java`
|
||||
- **Mapper**: `document/mapper/DocRecordAppMapper.java` `document/mapper/DocStatisticsDefinitionAppMapper.java` `document/mapper/DocDefinitionAppMapper.java`
|
||||
- **DTO**: `document/dto/DocStatisticsDefinitionDto.java` `document/dto/DocRecordPatientQueryParam.java` `document/dto/DocDefinitionOrganizationDto.java` `document/dto/DocRecordDto.java` `document/dto/DocTemplateDto.java`
|
||||
|
||||
### `empi` (5 files)
|
||||
- **Controller**: `empi/controller/EmpiController.java` `empi/controller/EmpiIdVerificationController.java` `empi/controller/EmpiEnhancedController.java`
|
||||
- **AppService**: `empi/appservice/IEmpiAppService.java`
|
||||
- **ServiceImpl**: `empi/appservice/impl/EmpiAppServiceImpl.java`
|
||||
|
||||
### `emr` (6 files)
|
||||
- **Controller**: `emr/controller/EmrArchiveController.java` `emr/controller/StructuredEmrController.java` `emr/controller/EmrRevisionController.java`
|
||||
- **AppService**: `emr/appservice/IStructuredEmrAppService.java`
|
||||
- **ServiceImpl**: `emr/appservice/impl/StructuredEmrAppServiceImpl.java`
|
||||
|
||||
### `epidemic` (3 files)
|
||||
- **Controller**: `epidemic/controller/EpidemicController.java`
|
||||
- **AppService**: `epidemic/appservice/IEpidemicAppService.java`
|
||||
- **ServiceImpl**: `epidemic/appservice/impl/EpidemicAppServiceImpl.java`
|
||||
|
||||
### `esbmanage` (4 files)
|
||||
- **Controller**: `esbmanage/controller/EsbReliabilityController.java` `esbmanage/controller/EsbMessageController.java` `esbmanage/controller/EsbServiceRegistryController.java`
|
||||
|
||||
### `externalintegration` (18 files)
|
||||
- **Controller**: `externalintegration/controller/FoodborneAcquisitionAppController.java`
|
||||
- **AppService**: `externalintegration/appservice/IBankPosCloudAppService.java` `externalintegration/appservice/IFoodborneAcquisitionAppService.java`
|
||||
- **ServiceImpl**: `externalintegration/appservice/impl/FoodborneAcquisitionAppServiceImpl.java` `externalintegration/appservice/impl/BankPosCloudAppServiceImpl.java`
|
||||
- **Mapper**: `externalintegration/mapper/FoodborneAcquisitionAppMapper.java`
|
||||
- **DTO**: `externalintegration/dto/BpcTransactionResponseDto.java` `externalintegration/dto/BpcPaymentScanNotifyDto.java` `externalintegration/dto/FaSimplediseaseAddNopwParam.java` `externalintegration/dto/BpcTransactionRequestDto.java` `externalintegration/dto/BpcDataElementDto.java`
|
||||
|
||||
### `infection` (4 files)
|
||||
- **Controller**: `infection/controller/InfectionEnhancedController.java` `infection/controller/InfectionController.java`
|
||||
- **AppService**: `infection/appservice/IInfectionAppService.java`
|
||||
- **ServiceImpl**: `infection/appservice/impl/InfectionAppServiceImpl.java`
|
||||
|
||||
### `inhospitalcharge` (17 files)
|
||||
- **Controller**: `inhospitalcharge/controller/AdvancePaymentManageController.java` `inhospitalcharge/controller/InHospitalRegisterController.java`
|
||||
- **AppService**: `inhospitalcharge/appservice/IInHospitalRegisterAppService.java` `inhospitalcharge/appservice/IAdvancePaymentManageAppService.java`
|
||||
- **ServiceImpl**: `inhospitalcharge/appservice/impl/AdvancePaymentManageAppServiceImpl.java` `inhospitalcharge/appservice/impl/InHospitalRegisterAppServiceImpl.java`
|
||||
- **Mapper**: `inhospitalcharge/mapper/InHospitalRegisterAppMapper.java` `inhospitalcharge/mapper/AdvancePaymentManageAppMapper.java`
|
||||
- **DTO**: `inhospitalcharge/dto/AdvancePaymentInAndOutDto.java` `inhospitalcharge/dto/PatientUpdateDto.java` `inhospitalcharge/dto/NoFilesRegisterDto.java` `inhospitalcharge/dto/InHospitalPatientInfoDto.java` `inhospitalcharge/dto/InHospitalRegisterQueryDto.java`
|
||||
|
||||
### `inhospitalnursestation` (52 files)
|
||||
- **Controller**: `inhospitalnursestation/controller/AdviceProcessController.java` `inhospitalnursestation/controller/NurseBillingController.java` `inhospitalnursestation/controller/EncounterAutoRollAppController.java`
|
||||
- **AppService**: `inhospitalnursestation/appservice/IOrgDeviceStockTakeAppService.java` `inhospitalnursestation/appservice/IAdviceProcessAppService.java` `inhospitalnursestation/appservice/INurseBillingAppService.java`
|
||||
- **ServiceImpl**: `inhospitalnursestation/appservice/impl/OrgDeviceStockTakeAppServiceImpl.java` `inhospitalnursestation/appservice/impl/ATDManageAppServiceImpl.java` `inhospitalnursestation/appservice/impl/EncounterAutoRollAppServiceImpl.java`
|
||||
- **Mapper**: `inhospitalnursestation/mapper/ATDManageAppMapper.java` `inhospitalnursestation/mapper/EncounterAutoRollAppMapper.java` `inhospitalnursestation/mapper/MedicineSummaryAppMapper.java`
|
||||
- **DTO**: `inhospitalnursestation/dto/AdmissionBedPageDto.java` `inhospitalnursestation/dto/AdviceExecuteParam.java` `inhospitalnursestation/dto/InpatientAdviceParam.java` `inhospitalnursestation/dto/DispenseFormSearchParam.java` `inhospitalnursestation/dto/AutoRollNursingDto.java`
|
||||
|
||||
### `inpatientmanage` (40 files)
|
||||
- **Controller**: `inpatientmanage/controller/NursingVitalSignsChartController.java` `inpatientmanage/controller/VitalSignsController.java` `inpatientmanage/controller/PatientHomeController.java`
|
||||
- **AppService**: `inpatientmanage/appservice/IPatientHomeAppService.java` `inpatientmanage/appservice/IDepositAppService.java` `inpatientmanage/appservice/INursingRecordAppService.java`
|
||||
- **ServiceImpl**: `inpatientmanage/appservice/impl/DepositAppServiceImpl.java` `inpatientmanage/appservice/impl/NursingRecordAppServiceImpl.java` `inpatientmanage/appservice/impl/PatientHomeAppServiceImpl.java`
|
||||
- **Mapper**: `inpatientmanage/mapper/VitalSignsAppMapper.java` `inpatientmanage/mapper/DepositMapper.java` `inpatientmanage/mapper/NursingRecordAppMapper.java`
|
||||
- **DTO**: `inpatientmanage/dto/DepositDetailDto.java` `inpatientmanage/dto/VitalSignsChartSmallDto.java` `inpatientmanage/dto/VitalSignsSaveDto.java` `inpatientmanage/dto/PatientHomeSearchParam.java` `inpatientmanage/dto/PatientHomeEmptyBedDto.java`
|
||||
|
||||
### `inventorymanage` (107 files)
|
||||
- **Controller**: `inventorymanage/controller/PurchaseReturnController.java` `inventorymanage/controller/InventorySettlementController.java` `inventorymanage/controller/ReturnIssueController.java`
|
||||
- **AppService**: `inventorymanage/appservice/IProductStocktakingAppService.java` `inventorymanage/appservice/IInventoryDetailsAppService.java` `inventorymanage/appservice/IReturnIssueAppService.java`
|
||||
- **ServiceImpl**: `inventorymanage/appservice/impl/InventoryDetailsAppServiceImpl.java` `inventorymanage/appservice/impl/ProductTransferAppServiceImpl.java` `inventorymanage/appservice/impl/ReceiptApprovalAppServiceImpl.java`
|
||||
- **Mapper**: `inventorymanage/mapper/ProductDetailAppMapper.java` `inventorymanage/mapper/RequisitionIssueMapper.java` `inventorymanage/mapper/PurchaseReturnMapper.java`
|
||||
- **DTO**: `inventorymanage/dto/ProductTransferPageDto.java` `inventorymanage/dto/PurchaseInventoryDto.java` `inventorymanage/dto/ReceiptDetailDto.java` `inventorymanage/dto/RequisitionOutDetailDto.java` `inventorymanage/dto/InventoryReceiptDetailDto.java`
|
||||
|
||||
### `jlau` (5 files)
|
||||
- **Controller**: `jlau/controller/ReviewPrescriptionRecordsController.java`
|
||||
- **AppService**: `jlau/appservice/IReviewPrescriptionRecordsAppService.java`
|
||||
- **ServiceImpl**: `jlau/appservice/impl/ReviewPrescriptionRecordsAppServiceImpl.java`
|
||||
- **Mapper**: `jlau/mapper/ReviewPrescriptionRecordsAppMapper.java`
|
||||
- **DTO**: `jlau/dto/ReviewPrescriptionRecordsDto.java`
|
||||
|
||||
### `lab` (7 files)
|
||||
- **Controller**: `lab/controller/LabActivityDefinitionController.java` `lab/controller/LabHistoryController.java` `lab/controller/LabEnhancedController.java`
|
||||
- **AppService**: `lab/appservice/ILabActivityDefinitionAppService.java`
|
||||
- **ServiceImpl**: `lab/appservice/impl/LabActivityDefinitionAppServiceImpl.java`
|
||||
|
||||
### `materialmanage` (46 files)
|
||||
- **Controller**: `materialmanage/controller/MaterialReturnOrderController.java` `materialmanage/controller/MaterialTransferInOrderController.java` `materialmanage/controller/MaterialTransferOutOrderController.java`
|
||||
- **ServiceImpl**: `materialmanage/appservice/impl/MaterialPurchaseOrderServiceImpl.java` `materialmanage/appservice/impl/MaterialTransferOutOrderServiceImpl.java` `materialmanage/appservice/impl/MaterialReturnToWarehouseOrderServiceImpl.java`
|
||||
- **Mapper**: `materialmanage/mapper/MaterialCommonMapper.java` `materialmanage/mapper/MaterialProfitLossOrderMapper.java` `materialmanage/mapper/MaterialTransferOutOrderMapper.java`
|
||||
- **DTO**: `materialmanage/dto/MaterialInitDto.java` `materialmanage/dto/MaterialSearchParam.java` `materialmanage/dto/MaterialDto.java` `materialmanage/dto/MaterialDetailDto.java` `materialmanage/dto/MaterialDeviceInfoDto.java`
|
||||
|
||||
### `mrhomepage` (6 files)
|
||||
- **Controller**: `mrhomepage/controller/DrgAnalysisController.java` `mrhomepage/controller/MrManagementController.java` `mrhomepage/controller/MrHomepageController.java`
|
||||
- **AppService**: `mrhomepage/appservice/IMrHomepageAppService.java`
|
||||
- **ServiceImpl**: `mrhomepage/appservice/impl/MrHomepageAppServiceImpl.java`
|
||||
|
||||
### `nenu` (22 files)
|
||||
- **Controller**: `nenu/controller/GfRatioApplicationRecordController.java` `nenu/controller/GfStudentListController.java` `nenu/controller/GfRatioManageController.java`
|
||||
- **AppService**: `nenu/appservice/IGfRatioManageAppService.java` `nenu/appservice/IGfRatioApplicationRecordAppService.java` `nenu/appservice/IGfStudentListAppService.java`
|
||||
- **ServiceImpl**: `nenu/appservice/impl/GfRatioApplicationRecordAppServiceImpl.java` `nenu/appservice/impl/GfRatioManageAppServiceImpl.java` `nenu/appservice/impl/GfStudentListAppServiceImpl.java`
|
||||
- **Mapper**: `nenu/mapper/GfStudentListAppMapper.java` `nenu/mapper/GfRatioManageAppMapper.java` `nenu/mapper/GfRatioApplicationRecordAppMapper.java`
|
||||
- **DTO**: `nenu/dto/GfIndividualRatioDto.java` `nenu/dto/GfRatioApplicationRecordDto.java` `nenu/dto/GfStudentListImportDto.java` `nenu/dto/GfRatioApplicationProcessDto.java` `nenu/dto/GfStudentPeisDto.java`
|
||||
|
||||
### `nursing` (8 files)
|
||||
- **Controller**: `nursing/controller/NursingExecutionController.java` `nursing/controller/NursingAssessmentEnhancedController.java` `nursing/controller/NursingEnhancedController.java`
|
||||
- **AppService**: `nursing/appservice/INursingAppService.java`
|
||||
- **ServiceImpl**: `nursing/appservice/impl/NursingAppServiceImpl.java`
|
||||
|
||||
### `orderclosedloop` (3 files)
|
||||
- **Controller**: `orderclosedloop/controller/OrderClosedLoopController.java`
|
||||
- **AppService**: `orderclosedloop/appservice/IOrderClosedLoopAppService.java`
|
||||
- **ServiceImpl**: `orderclosedloop/appservice/impl/OrderClosedLoopAppServiceImpl.java`
|
||||
|
||||
### `outpatientmanage` (22 files)
|
||||
- **Controller**: `outpatientmanage/controller/OutpatientTreatmentController.java` `outpatientmanage/controller/OutpatientSkinTestAppController.java` `outpatientmanage/controller/OutpatientInfusionController.java`
|
||||
- **AppService**: `outpatientmanage/appservice/IOutpatientTreatmentAppService.java` `outpatientmanage/appservice/IOutpatientInfusionAppService.java` `outpatientmanage/appservice/IOutpatientSkinTestAppService.java`
|
||||
- **ServiceImpl**: `outpatientmanage/appservice/impl/OutpatientTreatmentAppServiceImpl.java` `outpatientmanage/appservice/impl/OutpatientSkinTestAppServiceImpl.java` `outpatientmanage/appservice/impl/OutpatientInfusionAppServiceImpl.java`
|
||||
- **Mapper**: `outpatientmanage/mapper/OutpatientTreatmentAppMapper.java` `outpatientmanage/mapper/OutpatientInfusionAppMapper.java` `outpatientmanage/mapper/OutpatientSkinTestAppMapper.java`
|
||||
- **DTO**: `outpatientmanage/dto/SkinTestMedLotNumberDto.java` `outpatientmanage/dto/OutpatientInfusionRecordDto.java` `outpatientmanage/dto/SkinTestSaveDto.java` `outpatientmanage/dto/OutpatientTreatmentInfoDto.java` `outpatientmanage/dto/OutpatientStationInitDto.java`
|
||||
|
||||
### `patientmanage` (13 files)
|
||||
- **Controller**: `patientmanage/controller/PatientInformationController.java` `patientmanage/controller/OutpatientRecordController.java`
|
||||
- **ServiceImpl**: `patientmanage/appservice/impl/OutpatientRecordServiceImpl.java` `patientmanage/appservice/impl/PatientInformationServiceImpl.java`
|
||||
- **Mapper**: `patientmanage/mapper/PatientManageMapper.java`
|
||||
- **DTO**: `patientmanage/dto/PatientInfoInitDto.java` `patientmanage/dto/PatientIdInfoDto.java` `patientmanage/dto/OutpatientRecordSearchParam.java` `patientmanage/dto/PatientBaseInfoDto.java` `patientmanage/dto/OutpatientRecordDto.java`
|
||||
|
||||
### `paymentmanage` (57 files)
|
||||
- **Controller**: `paymentmanage/controller/EleInvoiceController.java` `paymentmanage/controller/ChargeBillController.java` `paymentmanage/controller/PaymentContractController.java`
|
||||
- **ServiceImpl**: `paymentmanage/appservice/impl/PaymentRecServiceImpl.java` `paymentmanage/appservice/impl/IChargeBillServiceImpl.java` `paymentmanage/appservice/impl/EleInvoiceServiceImpl.java`
|
||||
- **Mapper**: `paymentmanage/mapper/EleInvoiceMapper.java` `paymentmanage/mapper/ThreePartPayMapper.java` `paymentmanage/mapper/ChangePriceMapper.java`
|
||||
- **DTO**: `paymentmanage/dto/NenuBpcPayDto.java` `paymentmanage/dto/EleInvoiceResultDto.java` `paymentmanage/dto/ChargeSummaryDto.java` `paymentmanage/dto/EleInvoicePaymentInfoDto.java` `paymentmanage/dto/Clinic2207OrderResultInfoDto.java`
|
||||
|
||||
### `personalization` (22 files)
|
||||
- **Controller**: `personalization/controller/ActivityDeviceController.java` `personalization/controller/OrdersGroupPackageController.java` `personalization/controller/OrderGroupController.java`
|
||||
- **AppService**: `personalization/appservice/IOrderGroupAppService.java` `personalization/appservice/IOrdersGroupPackageAppService.java` `personalization/appservice/IActivityDeviceAppService.java`
|
||||
- **ServiceImpl**: `personalization/appservice/impl/OrdersGroupPackageAppServiceImpl.java` `personalization/appservice/impl/ActivityDeviceAppServiceImpl.java` `personalization/appservice/impl/IOrderGroupAppServiceImpl.java`
|
||||
- **Mapper**: `personalization/mapper/OrdersGroupPackageAppMapper.java` `personalization/mapper/OrderGroupAppMapper.java` `personalization/mapper/ActivityDeviceAppMapper.java`
|
||||
- **DTO**: `personalization/dto/OrdersGroupPackageDetailSaveDto.java` `personalization/dto/OrderGroupDto.java` `personalization/dto/OrdersGroupPackageDto.java` `personalization/dto/OrderGroupInitDto.java` `personalization/dto/OrdersGroupPackageDetailQueryDto.java`
|
||||
|
||||
### `pharmacyDispensarymanage` (42 files)
|
||||
- **Controller**: `pharmacyDispensarymanage/controller/PharmacyDispensaryTransferOutOrderController.java` `pharmacyDispensarymanage/controller/PharmacyDispensaryDispensingOrderController.java` `pharmacyDispensarymanage/controller/PharmacyDispensaryStocktakingOrderController.java`
|
||||
- **ServiceImpl**: `pharmacyDispensarymanage/appservice/impl/PharmacyDispensaryStocktakingOrderServiceImpl.java` `pharmacyDispensarymanage/appservice/impl/PharmacyDispensaryTransferInOrderServiceImpl.java` `pharmacyDispensarymanage/appservice/impl/PharmacyDispensaryStockInOrderServiceImpl.java`
|
||||
- **Mapper**: `pharmacyDispensarymanage/mapper/PharmacyDispensaryReturnToWarehouseOrderMapper.java` `pharmacyDispensarymanage/mapper/PharmacyDispensaryTransferOutOrderMapper.java` `pharmacyDispensarymanage/mapper/PharmacyDispensaryRequisitionOrderMapper.java`
|
||||
- **DTO**: `pharmacyDispensarymanage/dto/PharmacyDispensaryDto.java` `pharmacyDispensarymanage/dto/PharmacyDispensaryDetailDto.java` `pharmacyDispensarymanage/dto/PharmacyDispensarySearchParam.java` `pharmacyDispensarymanage/dto/PharmacyDispensaryMedicationInfoDto.java` `pharmacyDispensarymanage/dto/PharmacyDispensaryInitDto.java`
|
||||
|
||||
### `pharmacyWarehousemanage` (42 files)
|
||||
- **Controller**: `pharmacyWarehousemanage/controller/PharmacyWarehouseProfitLossOrderController.java` `pharmacyWarehousemanage/controller/PharmacyWarehouseReturnToWarehouseOrderController.java` `pharmacyWarehousemanage/controller/PharmacyWarehouseStockOutOrderController.java`
|
||||
- **ServiceImpl**: `pharmacyWarehousemanage/appservice/impl/PharmacyWarehousePurchaseOrderServiceImpl.java` `pharmacyWarehousemanage/appservice/impl/PharmacyWarehouseDocumentManagementServiceImpl.java` `pharmacyWarehousemanage/appservice/impl/PharmacyWarehouseProfitLossOrderServiceImpl.java`
|
||||
- **Mapper**: `pharmacyWarehousemanage/mapper/PharmacyWarehousePurchaseOrderMapper.java` `pharmacyWarehousemanage/mapper/PharmacyWarehouseDocumentManagementMapper.java` `pharmacyWarehousemanage/mapper/PharmacyWarehouseStockInOrderMapper.java`
|
||||
- **DTO**: `pharmacyWarehousemanage/dto/PharmacyWarehouseDto.java` `pharmacyWarehousemanage/dto/PharmacyWarehouseDetailDto.java` `pharmacyWarehousemanage/dto/PharmacyWarehouseMedicationInfoDto.java` `pharmacyWarehousemanage/dto/PharmacyWarehouseInitDto.java` `pharmacyWarehousemanage/dto/PharmacyWarehouseSearchParam.java`
|
||||
|
||||
### `pharmacymanage` (53 files)
|
||||
- **Controller**: `pharmacymanage/controller/InHospitalReturnMedicineController.java` `pharmacymanage/controller/PharmacyStockAlertController.java` `pharmacymanage/controller/MedicationDetailsController.java`
|
||||
- **AppService**: `pharmacymanage/appservice/ISummaryDispenseMedicineAppService.java` `pharmacymanage/appservice/IPendingMedicationDetailsAppService.java` `pharmacymanage/appservice/IInHospitalReturnMedicineAppService.java`
|
||||
- **ServiceImpl**: `pharmacymanage/appservice/impl/ReturnMedicineAppServiceImpl.java` `pharmacymanage/appservice/impl/MedicationDetailsAppServiceImpl.java` `pharmacymanage/appservice/impl/WesternMedicineDispenseAppServiceImpl.java`
|
||||
- **Mapper**: `pharmacymanage/mapper/PendingMedicationDetailsMapper.java` `pharmacymanage/mapper/MedicalDeviceDispenseMapper.java` `pharmacymanage/mapper/SummaryDispenseMedicineMapper.java`
|
||||
- **DTO**: `pharmacymanage/dto/MedDetailsInitDto.java` `pharmacymanage/dto/EncounterInfoSearchParam.java` `pharmacymanage/dto/ItemDispenseOrderDto.java` `pharmacymanage/dto/MedicineSummaryDto.java` `pharmacymanage/dto/MedicineSummarySearchParam.java`
|
||||
|
||||
### `quality` (5 files)
|
||||
- **Controller**: `quality/controller/BusinessAnalyticsController.java` `quality/controller/QualityEnhancedController.java` `quality/controller/EmrQualityController.java`
|
||||
- **AppService**: `quality/appservice/IEmrQualityAppService.java`
|
||||
- **ServiceImpl**: `quality/appservice/impl/EmrQualityAppServiceImpl.java`
|
||||
|
||||
### `rationaldrug` (3 files)
|
||||
- **Controller**: `rationaldrug/controller/RationalDrugController.java`
|
||||
- **AppService**: `rationaldrug/appservice/IRationalDrugAppService.java`
|
||||
- **ServiceImpl**: `rationaldrug/appservice/impl/RationalDrugAppServiceImpl.java`
|
||||
|
||||
### `regdoctorstation` (38 files)
|
||||
- **Controller**: `regdoctorstation/controller/NurseManageController.java` `regdoctorstation/controller/AdviceManageController.java` `regdoctorstation/controller/SpecialAdviceController.java`
|
||||
- **AppService**: `regdoctorstation/appservice/IAdviceManageAppService.java` `regdoctorstation/appservice/IRequestFormManageAppService.java` `regdoctorstation/appservice/ISpecialAdviceAppService.java`
|
||||
- **ServiceImpl**: `regdoctorstation/appservice/impl/SpecialAdviceAppServiceImpl.java` `regdoctorstation/appservice/impl/RequestFormManageAppServiceImpl.java` `regdoctorstation/appservice/impl/NurseManageServiceImpl.java`
|
||||
- **Mapper**: `regdoctorstation/mapper/RequestFormManageAppMapper.java` `regdoctorstation/mapper/AdviceManageAppMapper.java` `regdoctorstation/mapper/SpecialAdviceAppMapper.java`
|
||||
- **DTO**: `regdoctorstation/dto/RegPatientMainInfoDto.java` `regdoctorstation/dto/NursingOrdersDetailDto.java` `regdoctorstation/dto/LeaveHospitalParam.java` `regdoctorstation/dto/NursingOrdersSaveDto.java` `regdoctorstation/dto/NursingOrdersEncounterDto.java`
|
||||
|
||||
### `reportManagement` (11 files)
|
||||
- **Controller**: `reportManagement/controller/reportManagementController.java`
|
||||
- **AppService**: `reportManagement/appservice/IInfectiousCardAppService.java`
|
||||
- **ServiceImpl**: `reportManagement/appservice/impl/InfectiousCardAppServiceImpl.java`
|
||||
- **Mapper**: `reportManagement/mapper/ReportManageCardMapper.java`
|
||||
- **DTO**: `reportManagement/dto/InfectiousCardDto.java` `reportManagement/dto/InfectiousCardParam.java`
|
||||
|
||||
### `reportmanage` (164 files)
|
||||
- **Controller**: `reportmanage/controller/AmbAdviceStatisticsAppController.java` `reportmanage/controller/MonthlySettlementController.java` `reportmanage/controller/PurchaseReturnReportController.java`
|
||||
- **AppService**: `reportmanage/appservice/PurchaseReturnReportAppService.java` `reportmanage/appservice/IDrugDosageSettlementAppService.java` `reportmanage/appservice/IDepartmentRevenueStatisticsAppService.java`
|
||||
- **ServiceImpl**: `reportmanage/appservice/impl/InboundReportAppServiceImpl.java` `reportmanage/appservice/impl/MedicationInboundReportAppServiceImpl.java` `reportmanage/appservice/impl/ReportStatisticsAppServiceImpl.java`
|
||||
- **Mapper**: `reportmanage/mapper/PrintReportMapper.java` `reportmanage/mapper/ReportStatisticsMapper.java` `reportmanage/mapper/LossReportMapper.java`
|
||||
- **DTO**: `reportmanage/dto/ReportDiseaseDetailsDto.java` `reportmanage/dto/InboundReportSearchParam.java` `reportmanage/dto/InpatientMedicalRecordHomePageCollectionDto.java` `reportmanage/dto/ZyCostDetailParam.java` `reportmanage/dto/BottleLabelDto.java`
|
||||
|
||||
### `review` (3 files)
|
||||
- **Controller**: `review/controller/ReviewController.java`
|
||||
- **AppService**: `review/appservice/IReviewAppService.java`
|
||||
- **ServiceImpl**: `review/appservice/impl/ReviewAppServiceImpl.java`
|
||||
|
||||
### `service` (2 files)
|
||||
- **ServiceImpl**: `service/impl/HomeStatisticsServiceImpl.java`
|
||||
|
||||
### `system` (5 files)
|
||||
- **Controller**: `system/controller/ApiAuthController.java` `system/controller/DashboardController.java` `system/controller/SysAuditLogController.java`
|
||||
|
||||
### `tcm` (3 files)
|
||||
- **Controller**: `tcm/controller/TcmController.java`
|
||||
- **AppService**: `tcm/appservice/ITcmAppService.java`
|
||||
- **ServiceImpl**: `tcm/appservice/impl/TcmAppServiceImpl.java`
|
||||
|
||||
### `tencentJH` (13 files)
|
||||
- **Controller**: `tencentJH/controller/TencentController.java`
|
||||
- **AppService**: `tencentJH/appservice/ITencentAppService.java`
|
||||
- **ServiceImpl**: `tencentJH/appservice/impl/TencentAppServiceImpl.java`
|
||||
- **Mapper**: `tencentJH/mapper/TencentAppMapper.java`
|
||||
- **DTO**: `tencentJH/dto/PatientInfoTencentDto.java` `tencentJH/dto/CurrentDayEncounterTencentDto.java`
|
||||
|
||||
### `triageandqueuemanage` (13 files)
|
||||
- **Controller**: `triageandqueuemanage/controller/CallNumberVoiceConfigController.java` `triageandqueuemanage/controller/TriageQueueController.java`
|
||||
- **AppService**: `triageandqueuemanage/appservice/CallNumberVoiceConfigAppService.java` `triageandqueuemanage/appservice/TriageQueueAppService.java`
|
||||
- **ServiceImpl**: `triageandqueuemanage/appservice/impl/CallNumberVoiceConfigAppServiceImpl.java` `triageandqueuemanage/appservice/impl/TriageQueueAppServiceImpl.java`
|
||||
- **Mapper**: `triageandqueuemanage/mapper/CallNumberVoiceConfigAppMapper.java`
|
||||
|
||||
### `ybmanage` (55 files)
|
||||
- **Controller**: `ybmanage/controller/YbInpatientController.java` `ybmanage/controller/YbElepController.java` `ybmanage/controller/YbController.java`
|
||||
- **ServiceImpl**: `ybmanage/service/impl/YbEleHttpServiceImpl.java` `ybmanage/service/impl/YbServiceImpl.java` `ybmanage/service/impl/YbElepBaseServiceImpl.java`
|
||||
- **Mapper**: `ybmanage/mapper/YbElepMapper.java` `ybmanage/mapper/YbMapper.java`
|
||||
- **DTO**: `ybmanage/dto/FinancialHand3203AWebParam.java` `ybmanage/dto/FinancialHand3201WebParam.java` `ybmanage/dto/Financial13203WebParam.java` `ybmanage/dto/VeriPrescriptionInfoDto.java` `ybmanage/dto/YbInHospitalRegisterQueryDto.java`
|
||||
|
||||
## 前端关键文件
|
||||
|
||||
| 目录 | 说明 |
|
||||
|---|---|
|
||||
| `src/utils/request.js` | Axios 请求/响应拦截器 |
|
||||
| `src/api/` | API 接口定义 |
|
||||
| `src/components/` | 公共组件 |
|
||||
| `src/views/doctorstation/` | 门诊医生站 |
|
||||
| `src/views/inpatientDoctor/` | 住院医生站 |
|
||||
| `src/views/inpatientNurse/` | 住院护士站 |
|
||||
| `src/views/charge/` | 收费工作站 |
|
||||
| `src/views/datadictionary/` | 数据字典 |
|
||||
| `src/views/system/` | 系统管理 |
|
||||
|
||||
## 公共/通用文件
|
||||
|
||||
- `com.core.common.core.domain.R` — 统一响应封装
|
||||
- `com.core.common.core.domain.entity.SysDictData` — 字典数据实体
|
||||
- `com.core.common.utils.SecurityUtils` — 安全工具(获取当前用户)
|
||||
- `com.core.common.enums.*` — 枚举定义
|
||||
- `com.healthlink.his.common.constant.CommonConstants` — 公共常量
|
||||
- `com.healthlink.his.common.utils.HisQueryUtils` — 查询工具
|
||||
- `com.healthlink.his.common.utils.HisPageUtils` — 分页工具
|
||||
- `com.healthlink.his.web.doctorstation.utils.AdviceUtils` — 医嘱工具类
|
||||
|
||||
|
||||
=== 已生成 421 行索引 ===
|
||||
25
MD/bugs/BUG_698_ANALYSIS.md
Normal file
25
MD/bugs/BUG_698_ANALYSIS.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Bug #698 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-11 16:51:39
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 698
|
||||
- **标题**: [收费工作站-住院登记-已登记入院] 检索维度单一,且关键归档信息缺失(需增设检索条件与补充列表字段展示)
|
||||
- **模块**: 住院登记管理
|
||||
- **提出人**: 陈显精
|
||||
|
||||
---
|
||||
|
||||
(LLM 失败,关键词分析)
|
||||
Bug: [收费工作站-住院登记-已登记入院] 检索维度单一,且关键归档信息缺失(需增设检索条件与补充列表字段展示)
|
||||
模块: 住院登记管理
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **修复 Agent**: zhaoyun
|
||||
- **原因**: 关键词: 前端
|
||||
84
MD/bugs/BUG_741_ANALYSIS.md
Normal file
84
MD/bugs/BUG_741_ANALYSIS.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Bug #741 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 11:46:17
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 741
|
||||
- **标题**: 【住院医生工作站】打开门诊医生工作站会有代码列表报错
|
||||
- **模块**: 住院医生工作站
|
||||
- **提出人**: 王栩坤
|
||||
|
||||
---
|
||||
|
||||
### 一、Bug 理解
|
||||
|
||||
用户登录 doctor1 账号进入**住院医生工作站**页面时,页面加载过程中某个 API 请求返回了 500 错误,错误信息为 `class java.util.ArrayList cannot be cast to class com.fasterxml.jackson.databind.JsonNode`。这个 Java ClassCastException 被前端 `request.js` 拦截器捕获并弹出错误提示,导致页面功能异常。期望是页面能正常加载,患者列表和各 Tab 页正常工作。
|
||||
|
||||
### 二、根因分析
|
||||
|
||||
**直接原因**:后端某个 API 接口在处理请求或序列化响应时,抛出了 `ClassCastException: ArrayList cannot be cast to JsonNode`。异常被 `GlobalExceptionHandler` 捕获后返回 `{code:500, msg:"class java.util.ArrayList cannot be cast to class com.fasterxml.jackson.databind.JsonNode"}`。
|
||||
|
||||
**根因定位**:commit `68cfa4882` 修改了 `ApplicationConfig`,将 `Jackson2ObjectMapperBuilderCustomizer` 替换为直接创建 `new ObjectMapper()` 的 `@Bean`。
|
||||
|
||||
```java
|
||||
// 旧代码 — 定制 Spring Boot 自动配置的 ObjectMapper
|
||||
@Bean
|
||||
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
|
||||
return builder -> { ... };
|
||||
}
|
||||
|
||||
// 新代码 — 直接覆盖 Spring Boot 自动配置的 ObjectMapper
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**问题机制**:
|
||||
1. 定义 `@Bean ObjectMapper` 会**替换** Spring Boot 自动配置的 `ObjectMapper`,丢失大量自动配置(模块注册、类型解析器、序列化注解处理等)
|
||||
2. Spring Boot 4.x 的 `MappingJackson2HttpMessageConverter` 使用此 Bean 做响应序列化
|
||||
3. 当 `DictAspect`(拦截所有 `@GetMapping`/`@PostMapping`)处理 `R<IPage<RegPatientMainInfoDto>>` 响应时,`IPage` 的泛型信息在新 `ObjectMapper` 下无法正确解析
|
||||
4. Jackson 内部在序列化过程中尝试将 `ArrayList`(`IPage` 内部的 records 列表)强转为 `JsonNode`,导致 `ClassCastException`
|
||||
|
||||
**涉及文件**:
|
||||
- `core-framework/.../ApplicationConfig.java` — **根因所在**,ObjectMapper Bean 配置不当
|
||||
- `healthlink-his-common/.../DictAspect.java` — 拦截所有 Controller 方法,触发序列化链路
|
||||
- `regdoctorstation/.../AdviceManageController.java` — `/reg-patient-zk` 端点
|
||||
- `regdoctorstation/.../AdviceManageAppServiceImpl.java` — `getRegPatientMainInfo()` 返回 `IPage`
|
||||
- `utils/request.js` — 前端拦截器,line 186 抛出 Promise reject
|
||||
|
||||
### 三、修复方案
|
||||
|
||||
**修改文件**:`core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java`
|
||||
|
||||
**修复方式**:将 `@Bean ObjectMapper` 回退为 `Jackson2ObjectMapperBuilderCustomizer`,这样 Spring Boot 自动配置的 ObjectMapper 保持不变,只在其基础上追加自定义配置:
|
||||
|
||||
```java
|
||||
@Bean
|
||||
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
|
||||
return builder -> {
|
||||
builder.timeZone(TimeZone.getDefault());
|
||||
builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, LOCAL_DATE_TIME_DESERIALIZER);
|
||||
javaTimeModule.addSerializer(LocalDateTime.class,
|
||||
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
builder.modules(javaTimeModule);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**验证步骤**:
|
||||
1. `mvn clean compile -DskipTests` — 编译通过
|
||||
2. 启动应用 → 以 doctor1 登录 → 进入住院医生工作站 → 确认无报错
|
||||
3. 验证患者列表正常加载、诊断/医嘱 Tab 页正常切换
|
||||
|
||||
### 四、路由决策
|
||||
|
||||
- **修复 Agent**: guanyu(后端开发)
|
||||
- **原因**: 修复点在 `ApplicationConfig.java` 的 ObjectMapper Bean 配置,属于后端 Spring 配置问题,需要回退 Jackson 序列化配置并验证编译通过。
|
||||
26
MD/bugs/BUG_752_ANALYSIS.md
Normal file
26
MD/bugs/BUG_752_ANALYSIS.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Bug #752 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 22:09:00
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 752
|
||||
- **标题**: 【门诊医生站-检查项目】保存检查申请单时报JSON解析错误
|
||||
- **模块**: 门诊医生工作站
|
||||
- **提出人**: 陈显精
|
||||
|
||||
---
|
||||
|
||||
分析完成。根因明确、修复方案清晰,建议直接分派给赵云执行修复。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **FIXER_ID**: guanyu
|
||||
- **修复 Agent**: guanyu(后端)
|
||||
- **原因**: LLM 分析决策
|
||||
|
||||
> ⚠️ 修复人员请先验证以上分析是否正确,再执行修复。
|
||||
174
MD/bugs/BUG_753_ANALYSIS.md
Normal file
174
MD/bugs/BUG_753_ANALYSIS.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# Bug #753 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 22:03:57
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 753
|
||||
- **标题**: 【收费工作站-住院登记】“待登记入院”与“已登记入院”页签下页面容器高度异常,系统自动无限下扩产生大量空白区
|
||||
- **模块**: 住院登记管理
|
||||
- **提出人**: 陈显精
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 一、Bug 理解
|
||||
|
||||
**原文引用:**
|
||||
|
||||
> **标题**:【收费工作站-住院登记】"待登记入院"与"已登记入院"页签下页面容器高度异常,系统自动无限下扩产生大量空白区
|
||||
>
|
||||
> **重现步骤**:
|
||||
> 1. 登录医院信息管理系统账号。
|
||||
> 2. 在左侧主菜单栏中依次点击:「收费工作站」->「住院登记」。
|
||||
> 3. 在右侧主工作区中,首先查看默认的「待登记入院」页签,往下滑动页面或观察底部数据区域。
|
||||
> 4. 接着点击切换至「已登记入院」页签,同样向下滑动并观察底部。
|
||||
>
|
||||
> **结果**:表格与尾底部分页控件(写有"共 59 条 10条/页..."的控制条)之间存在异常巨大的空白间隔。页面右侧纵向滚动条极长,系统自动无限往下拉高窗口,给用户视觉呈瞬间呈现"断层/留白过长"现象。(注:在"待登记入院"和"已登记入院"两个子标签下均存在该问题。)
|
||||
>
|
||||
> **期望**:页面各组件高度计算正确,自适应浏览器窗口大小。分页控件行应紧跟紧随在表格区域底部显示,或者将表格区固定在一定高度内(超出出现局部滚动条),不能出现无限制的页面底部空白区和全局滚动条。
|
||||
|
||||
**附图分析**:
|
||||
- 已登记入院页签:10行数据的表格下方出现大面积空白区域,分页控件被推到页面最底部,右侧纵向滚动条极长。
|
||||
- 待登记入院页签:表格显示"暂无数据",但页面同样存在高度异常。
|
||||
- 两个标签页下均有"系统自动无限往下扩高窗口"的标注。
|
||||
|
||||
**综合总结**:用户在住院登记页面的两个页签(待登记/已登记)中,表格下方出现大面积异常空白,分页控件与表格严重脱节,页面滚动条极长。这是一个纯前端布局问题,由 CSS 高度约束缺失导致 vxe-table 在无约束父容器中无限撑开高度。
|
||||
|
||||
---
|
||||
|
||||
## 二、根因分析
|
||||
|
||||
**直接原因**:两个组件(`accomplishList.vue` 和 `awaitList.vue`)的 `.table-container` 缺少高度约束和溢出控制。
|
||||
|
||||
**详细技术分析**:
|
||||
|
||||
布局链条高度约束断裂:
|
||||
|
||||
```
|
||||
.app-container (min-height: calc(100vh - 84px), 无固定height, 无overflow)
|
||||
└─ el-tabs (auto height)
|
||||
└─ .awaitList-container (height: 100%, 但父元素无固定高度 → 实际等于 auto)
|
||||
├─ .operate (height: 40px, 固定)
|
||||
└─ .table-container (无高度约束, 仅 padding)
|
||||
├─ vxe-table (height="100%", 父容器auto → 无法正确约束)
|
||||
└─ pagination
|
||||
```
|
||||
|
||||
关键问题:
|
||||
1. **`.awaitList-container` 使用 `height: 100%`,但其父元素(el-tabs content)无固定高度**——导致 `100%` 实际解析为 auto,无法约束子元素。
|
||||
2. **`.table-container` 无 `overflow` 约束、无 flex 布局**——内容可以无限撑高。
|
||||
3. **`vxe-table` 的 `height="100%"` 属性**——在无固定高度的父容器中无法创建滚动区域,表格内容直接撑开容器。
|
||||
|
||||
**涉及文件**(均为纯前端 CSS 问题):
|
||||
- `healthlink-his-ui/src/views/inHospitalManagement/charge/register/components/accomplishList.vue` — 已登记入院列表
|
||||
- `healthlink-his-ui/src/views/inHospitalManagement/charge/register/components/awaitList.vue` — 待登记入院列表
|
||||
|
||||
---
|
||||
|
||||
## 三、修复方案
|
||||
|
||||
**核心思路**:用 flexbox 将 `.awaitList-container` 改为纵向弹性布局,让 `.table-container` 自动填充剩余空间并限制溢出,使 vxe-table 的 `height="100%"` 能正确约束在固定高度内。
|
||||
|
||||
### 修改文件 1:`accomplishList.vue`
|
||||
|
||||
```scss
|
||||
<!-- 修改前 -->
|
||||
<style lang="scss" scoped>
|
||||
.awaitList-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.operate {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
align-items: center;
|
||||
}
|
||||
.table-container {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 修改后 -->
|
||||
<style lang="scss" scoped>
|
||||
.awaitList-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.operate {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
align-items: center;
|
||||
}
|
||||
.table-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### 修改文件 2:`awaitList.vue`
|
||||
|
||||
同样的 CSS 修改(两个文件的 style 完全一致):
|
||||
|
||||
```scss
|
||||
<!-- 修改后 -->
|
||||
<style lang="scss" scoped>
|
||||
.awaitList-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.operate {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
align-items: center;
|
||||
}
|
||||
.table-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
**修改要点**(3处 CSS 变更,两文件相同):
|
||||
1. `.awaitList-container` 加 `display: flex; flex-direction: column;` — 建立纵向弹性布局
|
||||
2. `.operate` 加 `flex-shrink: 0;` — 防止搜索栏被压缩
|
||||
3. `.table-container` 加 `flex: 1; min-height: 0; overflow: hidden;` — **关键修复**:自动填充剩余空间 + 禁止溢出撑高 + `min-height: 0` 确保 flex 子元素可缩小
|
||||
|
||||
这样 vxe-table 的 `height="100%"` 就能在固定的 `.table-container` 内正确创建滚动区域,表格内容超出时在表格内部滚动,分页控件紧随表格下方,不再撑开整个页面。
|
||||
|
||||
---
|
||||
|
||||
## 四、路由决策
|
||||
|
||||
**FIXER**: zhaoyun
|
||||
**REASON**: 这是纯前端 CSS 布局问题,只涉及两个 Vue 组件的 `<style>` 块修改,无后端变更、无数据库变更,由前端开发修复最合适。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **FIXER_ID**: guanyu
|
||||
- **修复 Agent**: guanyu(后端)
|
||||
- **原因**: LLM 分析决策
|
||||
|
||||
> ⚠️ 修复人员请先验证以上分析是否正确,再执行修复。
|
||||
164
MD/bugs/BUG_757_ANALYSIS.md
Normal file
164
MD/bugs/BUG_757_ANALYSIS.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Bug #757 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 14:58:17
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 757
|
||||
- **标题**: 【门诊医生工作站】中医诊断中没有相同的诊断和症候,但就无法新增中医诊断
|
||||
- **模块**: 门诊医生工作站
|
||||
- **提出人**: 王栩坤
|
||||
|
||||
---
|
||||
|
||||
I have enough information now to produce the analysis. Let me compile the findings.
|
||||
|
||||
---
|
||||
|
||||
## 一、Bug 理解
|
||||
|
||||
用户在门诊医生站打开"中医诊断"对话框,选择中医诊断和中医证候后点击确定,**无法成功保存**。同时,已经保存过的中医诊断也无法正确回显诊断详情。期望:能正常选择诊断和证候、保存成功,并回显诊断详情数据。
|
||||
|
||||
## 二、根因分析
|
||||
|
||||
核心问题在 `tcmdiagnosisDialog.vue` 的 `submit()` 函数,它向后端发送的字段名与后端 DTO 完全不匹配:
|
||||
|
||||
**前端发送的数据结构:**
|
||||
```js
|
||||
const diagnosisChildList = [{
|
||||
conditionCode: condition.value, // ← 字段名错误
|
||||
syndromeCode: syndrome.value, // ← 字段名错误
|
||||
}];
|
||||
```
|
||||
|
||||
**后端 `SaveDiagnosisChildParam` 期望的数据结构:**
|
||||
- `definitionId` (Long) — 诊断定义 ID
|
||||
- `ybNo` (String) — 医保编码
|
||||
- `syndromeGroupNo` (String) — 中医证候组号(用于关联病和证)
|
||||
- `maindiseFlag` (Integer) — 主诊断标记
|
||||
- `diagSrtNo` (Integer) — 排序号
|
||||
|
||||
**具体根因列表:**
|
||||
|
||||
| # | 问题 | 位置 | 影响 |
|
||||
|---|------|------|------|
|
||||
| 1 | `submit()` 发送 `conditionCode`/`syndromeCode`,后端接收不到 `definitionId` 和 `ybNo` | `tcmdiagnosisDialog.vue:submit()` | 保存时 Condition 记录的 `definitionId` 为 null,保存失败或数据损坏 |
|
||||
| 2 | 没有传 `syndromeGroupNo` 来关联"病"和"证" | `tcmdiagnosisDialog.vue:submit()` | 后端无法将诊断和证候配对成一组 |
|
||||
| 3 | `openDialog()` 获取下拉选项时用 `item.ybNo` 作为 value,但没有保存 `definitionId` | `tcmdiagnosisDialog.vue:openDialog()` | 丢失了关键的 `definitionId` |
|
||||
| 4 | 没有传 `patientInfo` prop(dialog 用 `defineProps` 声明了但父组件可能未传) | `tcmdiagnosisDialog.vue` | `saveTcmDiagnosis` 请求中 `patientId`/`encounterId` 为 null |
|
||||
|
||||
**可能涉及的文件:**
|
||||
- `healthlink-his-ui/src/views/doctorstation/components/tcm/tcmdiagnosisDialog.vue` — 主要 Bug 所在
|
||||
- `healthlink-his-ui/src/views/doctorstation/components/api.js` — API 定义(无误)
|
||||
- `healthlink-his-server/.../appservice/impl/DoctorStationChineseMedicalAppServiceImpl.java` — 后端 `saveTcmDiagnosis` 方法
|
||||
|
||||
## 三、修复方案
|
||||
|
||||
### 修复 1:`tcmdiagnosisDialog.vue` — `openDialog()` 方法
|
||||
|
||||
将下拉选项的 value 从 `ybNo` 改为同时保存 `definitionId`:
|
||||
|
||||
```js
|
||||
// 修改前
|
||||
conditionOptions.value = res.data.records.map((item) => ({
|
||||
value: item.ybNo,
|
||||
label: item.name,
|
||||
}));
|
||||
|
||||
// 修改后
|
||||
conditionOptions.value = res.data.records.map((item) => ({
|
||||
value: item.id, // 用 definition ID 作为 value
|
||||
ybNo: item.ybNo, // 保留医保编码
|
||||
label: item.name,
|
||||
}));
|
||||
syndromeOptions.value = res.data.records.map((item) => ({
|
||||
value: item.id,
|
||||
ybNo: item.ybNo,
|
||||
label: item.name,
|
||||
}));
|
||||
```
|
||||
|
||||
### 修复 2:`tcmdiagnosisDialog.vue` — `submit()` 方法
|
||||
|
||||
重写提交逻辑,匹配后端 `SaveDiagnosisChildParam` 的字段名:
|
||||
|
||||
```js
|
||||
function submit() {
|
||||
if (!condition.value || !syndrome.value) {
|
||||
proxy.$modal.msgWarning('请选择诊断和证候');
|
||||
return;
|
||||
}
|
||||
|
||||
// 找到选中的诊断和证候的完整信息
|
||||
const selectedCondition = conditionOptions.value.find(item => item.value === condition.value);
|
||||
const selectedSyndrome = syndromeOptions.value.find(item => item.value === syndrome.value);
|
||||
|
||||
// 生成证候组号(时间戳)
|
||||
const syndromeGroupNo = 'TCM' + Date.now();
|
||||
|
||||
const diagnosisChildList = [
|
||||
{
|
||||
definitionId: condition.value, // 中医诊断 definition ID
|
||||
ybNo: selectedCondition?.ybNo, // 中医诊断医保编码
|
||||
syndromeGroupNo: syndromeGroupNo,
|
||||
maindiseFlag: 1, // 主诊断标记
|
||||
diagSrtNo: 1, // 排序号(病)
|
||||
},
|
||||
{
|
||||
definitionId: syndrome.value, // 中医证候 definition ID
|
||||
ybNo: selectedSyndrome?.ybNo, // 中医证候医保编码
|
||||
syndromeGroupNo: syndromeGroupNo, // 同一组号
|
||||
maindiseFlag: 0,
|
||||
diagSrtNo: 2, // 排序号(证)
|
||||
},
|
||||
];
|
||||
|
||||
saveTcmDiagnosis({
|
||||
patientId: props.patientInfo.patientId,
|
||||
encounterId: props.patientInfo.encounterId,
|
||||
diagnosisChildList: diagnosisChildList,
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('保存成功');
|
||||
emit('flush');
|
||||
close();
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '保存失败');
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('保存中医诊断失败:', error);
|
||||
proxy.$modal.msgError('保存失败,请重试');
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 修复 3:确保父组件传递 `patientInfo` prop
|
||||
|
||||
检查父组件(`tcmAdvice.vue` 或使用 `tcmdiagnosisDialog` 的页面)是否正确传递了 `patientInfo` prop。`tcmAdvice.vue` 已有 `patientInfo` prop 定义,所以如果是从 `tcmAdvice` 中使用该 dialog,需要通过 `:patientInfo="patientInfo"` 传递。
|
||||
|
||||
### 修复 4(可选):后端防御性处理
|
||||
|
||||
后端 `saveTcmDiagnosis` 方法可以增加对 `definitionId` 为空的校验,避免写入脏数据:
|
||||
|
||||
```java
|
||||
// 在 saveTcmDiagnosis 方法开头增加
|
||||
for (SaveDiagnosisChildParam param : diagnosisChildList) {
|
||||
if (param.getDefinitionId() == null) {
|
||||
return R.fail("诊断定义ID不能为空,请重新选择诊断");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 四、路由决策
|
||||
|
||||
**FIXER: zhaoyun**(前端开发)
|
||||
**REASON:** Bug 根因在前端对话框组件 `tcmdiagnosisDialog.vue` 的字段映射错误和提交逻辑缺陷,需要修改 Vue 前端代码(openDialog 数据映射 + submit 参数构建 + 父组件 prop 传递),属于前端界面和 API 调用层修复。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **修复 Agent**: zhaoyun
|
||||
- **原因**: ** Bug 根因在前端对话框组件 `tcmdiagnosisDialog.vue` 的字段映射错误和提交逻辑缺陷,需要修改 Vue 前端代码(openDialog 数据映射 + submit 参数构建 + 父组件 prop 传递),属于前端界面和 API 调用层修复。
|
||||
122
MD/bugs/BUG_758_ANALYSIS.md
Normal file
122
MD/bugs/BUG_758_ANALYSIS.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Bug #758 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 15:34:50
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 758
|
||||
- **标题**: 【门诊医生工作站-检验】检验信息下的新增的字段按钮点击会出现报错Cannot deserialize value of type `com.core.common.core.domain.entity.SysDictData` from Array value
|
||||
- **模块**: 门诊医生工作站
|
||||
- **提出人**: 王栩坤
|
||||
|
||||
---
|
||||
|
||||
I now have a complete understanding of the root cause. Let me provide the analysis.
|
||||
|
||||
---
|
||||
|
||||
### 一、Bug 理解
|
||||
|
||||
用户在门诊医生工作站的"检验信息"区域点击"新增"按钮时,页面弹出 Jackson 反序列化错误 `Cannot deserialize value of type SysDictData from Array value`,无法正常新增检验信息。期望点击新增后能正常清空表单、准备录入新检验申请单。
|
||||
|
||||
### 二、根因分析
|
||||
|
||||
**错误触发链路:**
|
||||
|
||||
```
|
||||
用户点击"新增"
|
||||
→ handleNewApplication()
|
||||
→ resetForm()
|
||||
→ getEncounterDiagnosis(encounterId) [API 调用]
|
||||
→ 后端返回 R<List<DiagnosisQueryDto>>
|
||||
→ DictAspect 拦截 @GetMapping 响应
|
||||
→ processDict() 发现 DiagnosisQueryDto.medTypeCode 有 @Dict(dictCode = "med_type")
|
||||
→ DictUtils.getDictLabel("med_type", value)
|
||||
→ DictUtils.getDictCache("med_type")
|
||||
→ mapper.convertValue(cached, TypeReference<List<SysDictData>>) ← 💥 这里抛异常
|
||||
→ DictAspect 无 try-catch,异常直接传播到前端
|
||||
```
|
||||
|
||||
**根因:`DictUtils.getDictCache()` 缺少异常处理**
|
||||
|
||||
- 文件:`core-common/src/main/java/com/core/common/utils/DictUtils.java:38-62`
|
||||
- Redis 中的字典缓存数据结构异常(可能是旧 Fastjson 序列化格式遗留、嵌套数组等)
|
||||
- Jackson `ObjectMapper.convertValue()` 无法将异常结构转为 `List<SysDictData>`,抛出 `JsonMappingException`
|
||||
- 该异常未被 `DictUtils` 或 `DictAspect` 捕获,直接传播为 HTTP 500 错误
|
||||
|
||||
**涉及的关键文件:**
|
||||
1. `core-common/src/main/java/com/core/common/utils/DictUtils.java` — `getDictCache()` 方法(核心问题)
|
||||
2. `healthlink-his-common/src/main/java/com/healthlink/his/common/aspectj/DictAspect.java` — `processDict()` 方法(缺少异常保护)
|
||||
3. `healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/controller/DoctorStationDiagnosisController.java` — 被 DictAspect 拦截的控制器
|
||||
4. `healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/dto/DiagnosisQueryDto.java` — 含 `@Dict` 注解的 DTO
|
||||
|
||||
**与历史 Bug 的关联:** 此前 commit `babd8d0c0` 修复了类似问题(Jackson 配置从 ObjectMapper bean 改回 Jackson2ObjectMapperBuilderCustomizer),但 `DictUtils.getDictCache()` 的 `convertValue` 仍缺少防御性异常处理。
|
||||
|
||||
### 三、修复方案
|
||||
|
||||
**修改 1:`DictUtils.getDictCache()` 添加 try-catch 防御**(核心修复)
|
||||
|
||||
文件:`core-common/src/main/java/com/core/common/utils/DictUtils.java`
|
||||
|
||||
在 `getDictCache()` 方法中,为 `mapper.convertValue()` 添加 try-catch,失败时清理损坏缓存并返回 null:
|
||||
|
||||
```java
|
||||
public static List<SysDictData> getDictCache(String key) {
|
||||
Object cached = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
|
||||
if (StringUtils.isNull(cached)) {
|
||||
return null;
|
||||
}
|
||||
if (cached instanceof List && ((List<?>) cached).stream().allMatch(e -> e instanceof SysDictData)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SysDictData> result = (List<SysDictData>) cached;
|
||||
return result;
|
||||
}
|
||||
try {
|
||||
com.fasterxml.jackson.core.type.TypeReference<List<SysDictData>> typeRef =
|
||||
new com.fasterxml.jackson.core.type.TypeReference<List<SysDictData>>() {};
|
||||
ObjectMapper mapper = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
if (cached instanceof JsonNode jsonNode) {
|
||||
return mapper.convertValue(jsonNode, typeRef);
|
||||
}
|
||||
return mapper.convertValue(cached, typeRef);
|
||||
} catch (Exception e) {
|
||||
// 缓存数据格式异常,清理损坏缓存,下次重新加载
|
||||
org.slf4j.LoggerFactory.getLogger(DictUtils.class)
|
||||
.warn("字典缓存转换失败(key={}),已清理: {}", key, e.getMessage());
|
||||
removeDictCache(key);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**修改 2:`DictAspect.processDict()` 添加 try-catch 防御**(防止字典翻译失败影响 API 响应)
|
||||
|
||||
文件:`healthlink-his-common/src/main/java/com/healthlink/his/common/aspectj/DictAspect.java`
|
||||
|
||||
在 `processDict()` 方法中,对字典查询部分包裹 try-catch:
|
||||
|
||||
```java
|
||||
} else if (field.isAnnotationPresent(Dict.class)) {
|
||||
try {
|
||||
// ... 原有字典翻译逻辑 ...
|
||||
} catch (Exception e) {
|
||||
log.debug("字段 {} 字典翻译失败,跳过: {}", field.getName(), e.getMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 四、路由决策
|
||||
|
||||
**FIXER: guanyu(后端开发)**
|
||||
|
||||
**REASON:** 修复涉及 `DictUtils`(core-common 模块)和 `DictAspect`(healthlink-his-common 模块)两个后端 Java 文件的异常处理逻辑,纯后端修复,无需前端改动。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **修复 Agent**: guanyu
|
||||
- **原因**: ** 修复涉及 `DictUtils`(core-common 模块)和 `DictAspect`(healthlink-his-common 模块)两个后端 Java 文件的异常处理逻辑,纯后端修复,无需前端改动。
|
||||
104
MD/bugs/BUG_759_ANALYSIS.md
Normal file
104
MD/bugs/BUG_759_ANALYSIS.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Bug #759 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 21:51:43
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 759
|
||||
- **标题**: 【门诊医生工作站】电子处方点击新增处方,报卡在100%卡出屏幕且诊断等下拉框无数据回显
|
||||
- **模块**: 门诊医生工作站
|
||||
- **提出人**: 王栩坤
|
||||
|
||||
---
|
||||
|
||||
I now have a complete understanding. Here is my analysis:
|
||||
|
||||
---
|
||||
|
||||
## 一、Bug 理解
|
||||
|
||||
**禅道 Bug #759 原文:**
|
||||
|
||||
- **标题**:【门诊医生工作站】电子处方点击新增处方,报卡在100%卡出屏幕且诊断等下拉框无数据回显
|
||||
- **重现步骤**:登录内科医生1账号(doctor1/123456)→ 进入门诊医生工作站 → 选择一名患者 → 点击处方 → 点击新增处方
|
||||
- **实际结果**:报卡界面边距超出,下拉框无数据回显
|
||||
- **期望结果**:能够在100%视图下,新增处方的报卡能够完全展示出来;诊断和慢性诊断的下拉框有数据回显
|
||||
|
||||
**附图关键信息**:
|
||||
- 截图显示"新增处方"弹窗右侧被截断,"服药时间(结束)"和"途径"列不可见,右下角"删除"按钮被裁剪
|
||||
- "诊断"下拉框显示占位文本"诊断","慢病诊断"下拉框点击后显示"无数据"
|
||||
|
||||
**综合总结**:用户在门诊医生工作站新增处方时,弹窗宽度固定为 `1840px`,在 100% 缩放下超出屏幕可视区域导致内容溢出;同时"诊断"和"慢病诊断"两个下拉框均无数据可选,无法正常使用处方开具功能。
|
||||
|
||||
---
|
||||
|
||||
## 二、根因分析
|
||||
|
||||
**涉及唯一文件**:`healthlink-his-ui/src/views/doctorstation/components/eprescriptiondialog.vue`
|
||||
|
||||
### 问题1:弹窗宽度溢出
|
||||
|
||||
- **根因**:`el-dialog` 第5行设置 `width="1840px"` 为固定像素值。标准 1920px 屏幕减去浏览器 UI 和滚动条后可用宽度不足 1840px,导致右侧内容被截断。此外第240行药品搜索弹出框 `popover` 设置 `width="1200"`,也加剧了溢出。
|
||||
- **证据**:第5行 `width="1840px"`,第240行 `:width="1200"`
|
||||
|
||||
### 问题2:诊断下拉框无数据
|
||||
|
||||
- **根因**:"诊断"下拉框使用 `remote-method="getInit"`(第134行),这是一个远程搜索模式 —— **只有用户输入关键字时才会触发 `getInit` 函数调用 API**。弹窗打开时 `open()` 函数调用了 `getDiagnosisInfo()`,其中 `getEncounterDiagnosis()` 获取的诊断数据存入了 `diagnosisList`(第764行),但 **没有同步填充 `diagnosisListOption`**(下拉框 v-for 绑定的数据源,第138行)。因此弹窗打开时下拉框列表为空。
|
||||
- **代码证据**:`getDiagnosisInfo()` 第764行 `diagnosisList.value = res.data`,但 `diagnosisListOption` 仅在 `getInit` 的 remote 回调中被赋值(第716行)
|
||||
|
||||
### 问题3:慢病诊断下拉框无数据
|
||||
|
||||
- **根因**:`getChronicDisease` 调用医保接口 `/yb-request/getConditionDefinition`(第779行)。如果医保服务未连接或返回空数据,catch 中静默处理并设为空数组(第786行)。这可能是医保环境未配置的预期行为,但即使医保已连接,如果 `encounterId` 传参有问题也会导致返回空。
|
||||
|
||||
---
|
||||
|
||||
## 三、修复方案
|
||||
|
||||
### 修复1:弹窗宽度改为响应式
|
||||
|
||||
**文件**:`eprescriptiondialog.vue` 第5行
|
||||
|
||||
```diff
|
||||
- width="1840px"
|
||||
+ width="90vw"
|
||||
+ style="max-width: 1840px"
|
||||
```
|
||||
|
||||
同时将第240行药品搜索 popover 宽度从 `1200` 缩减为 `min(1200px, 80vw)` 或直接改为 `"100%"`。
|
||||
|
||||
### 修复2:诊断下拉框打开时加载初始数据
|
||||
|
||||
**文件**:`eprescriptiondialog.vue` `getDiagnosisInfo()` 函数(约第763行)
|
||||
|
||||
在 `getEncounterDiagnosis` 成功回调中,增加 `diagnosisListOption` 的赋值:
|
||||
|
||||
```diff
|
||||
getEncounterDiagnosis(props.patient.encounterId).then((res) => {
|
||||
diagnosisList.value = res.data;
|
||||
+ diagnosisListOption.value = res.data || [];
|
||||
let diagnosisInfo = diagnosisList.value.filter((item) => {
|
||||
```
|
||||
|
||||
### 修复3:慢病诊断 — 确认 API 返回
|
||||
|
||||
此问题取决于医保服务是否可用。如果医保服务已连接但仍无数据,需检查后端 `/yb-request/getConditionDefinition` 接口的返回值。如果是医保未配置环境,则属于已知限制,可考虑在下拉框上增加"医保未连接"的提示文案。
|
||||
|
||||
---
|
||||
|
||||
## 四、路由决策
|
||||
|
||||
**FIXER: zhaoyun(赵云 — 前端开发)**
|
||||
|
||||
**REASON**:两个问题的根因全部在前端 `eprescriptiondialog.vue` 的样式和数据初始化逻辑中,不需要后端改动,属于纯前端修复任务。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **FIXER_ID**: zhaoyun
|
||||
- **修复 Agent**: zhaoyun(前端)
|
||||
- **原因**: LLM 分析决策
|
||||
|
||||
> ⚠️ 修复人员请先验证以上分析是否正确,再执行修复。
|
||||
114
MD/bugs/BUG_760_ANALYSIS.md
Normal file
114
MD/bugs/BUG_760_ANALYSIS.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Bug #760 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 21:48:44
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 760
|
||||
- **标题**: [住院护士站-护理记录] 选中患者时系统报错
|
||||
- **模块**: 病区护士工作站
|
||||
- **提出人**: 陈显精
|
||||
|
||||
---
|
||||
|
||||
Analysis complete. Here's the full report.
|
||||
|
||||
---
|
||||
|
||||
### 一、Bug 理解
|
||||
|
||||
**禅道 Bug #760:[住院护士站-护理记录] 选中患者时系统报错**
|
||||
|
||||
**重现步骤:**
|
||||
1. 登录账号:wx,密码:123456,登录系统,进入【住院护士站】
|
||||
2. 点击【护理记录】页签
|
||||
3. 在左侧列表中选中任意已入院的患者
|
||||
|
||||
**期望结果:**
|
||||
1. 选中患者后,系统正常响应,无报错弹窗
|
||||
2. 成功加载并展示该患者对应的护理记录表单及历史数据
|
||||
|
||||
**实际结果:**
|
||||
- 页面弹出红色报错:`No static resource nursing/statistics/summary/list for request '/healthlink-his/nursing/statistics/summary/list'`
|
||||
- 右侧护理记录列表显示"暂无数据"
|
||||
|
||||
**截图确认:** 顶部红色报错横幅清晰可见,错误 URL 为 `/healthlink-his/nursing/statistics/summary/list`,选中患者"陈显精"后右侧表格为空。
|
||||
|
||||
**总结:** 用户在住院护士站的「护理记录」页签选中患者时,前端发起了一个后端不存在的 API 请求 `/nursing/statistics/summary/list`,导致 404 报错,护理记录数据无法加载。
|
||||
|
||||
---
|
||||
|
||||
### 二、根因分析
|
||||
|
||||
**根因:Tab 组件映射错误 — "护理记录"页签加载了错误的组件。**
|
||||
|
||||
在 `healthlink-his-ui/src/views/inpatientNurse/inpatientNurseStation/index.vue` 中:
|
||||
|
||||
```html
|
||||
<!-- 第72-76行 -->
|
||||
<el-tab-pane label="护理记录" name="NursingRecord">
|
||||
<Criticalrecord v-if="activeTabName === 'NursingRecord'" />
|
||||
</el-tab-pane>
|
||||
```
|
||||
|
||||
这里渲染的是 `Criticalrecord` 组件,而不是 `NursingRecord` 组件。
|
||||
|
||||
**关键文件对比:**
|
||||
|
||||
| 文件 | API 路径 | 后端是否有对应 Controller |
|
||||
|------|----------|--------------------------|
|
||||
| `nursingstatistics/criticalrecord.vue` → `nursingstatistics/api.js` | `/nursing/statistics/summary/list` | ❌ **不存在** |
|
||||
| `nursingRecord/index.vue` → `nursingRecord/components/api.js` | `/nursing-record/nursing-patient-page` 等 | ✅ `NursingRecordController` |
|
||||
|
||||
- `inpatientNurseStation/index.js` 中已经同时导入了两个组件:
|
||||
- `NursingRecord` from `@/views/inpatientNurse/nursingRecord/index.vue`
|
||||
- `Criticalrecord` from `@/views/inpatientNurse/nursingstatistics/criticalrecord.vue`
|
||||
- 但 `index.vue` 的模板中**用错了组件名**:`<Criticalrecord>` 应该是 `<NursingRecord>`
|
||||
- 后端 `NursingRecordController` 映射在 `/nursing-record`,有完整的 patient-page、nursing-patient-page、save-nursing、delete-nursing 等端点
|
||||
|
||||
**涉及的文件:**
|
||||
- `healthlink-his-ui/src/views/inpatientNurse/inpatientNurseStation/index.vue` — 模板中 Tab 渲染了错误组件
|
||||
|
||||
---
|
||||
|
||||
### 三、修复方案
|
||||
|
||||
**修改 1 个文件,1 处改动:**
|
||||
|
||||
**文件:** `healthlink-his-ui/src/views/inpatientNurse/inpatientNurseStation/index.vue`
|
||||
|
||||
**改动:** 将第 74 行的 `<Criticalrecord>` 替换为 `<NursingRecord>`
|
||||
|
||||
```diff
|
||||
- <el-tab-pane label="护理记录" name="NursingRecord">
|
||||
- <Criticalrecord v-if="activeTabName === 'NursingRecord'" />
|
||||
+ <el-tab-pane label="护理记录" name="NursingRecord">
|
||||
+ <NursingRecord v-if="activeTabName === 'NursingRecord'" />
|
||||
</el-tab-pane>
|
||||
```
|
||||
|
||||
`NursingRecord` 已在同文件的 `index.js` 中导入(第3行),无需额外添加 import。
|
||||
|
||||
**验证:**
|
||||
1. `cd healthlink-his-ui && npm run build:dev` — 编译通过
|
||||
2. 登录后进入住院护士站 → 护理记录 → 选中患者 → 应加载出患者护理记录列表,无红色报错
|
||||
|
||||
---
|
||||
|
||||
### 四、路由决策
|
||||
|
||||
**FIXER: zhaoyun**
|
||||
|
||||
**REASON:** 纯前端组件映射修复,仅改动 1 个 Vue 文件中的 1 行模板标签(`Criticalrecord` → `NursingRecord`),属于前端界面/组件层级的问题。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **FIXER_ID**: zhaoyun
|
||||
- **修复 Agent**: zhaoyun(前端)
|
||||
- **原因**: ** 纯前端组件映射修复,仅改动 1 个 Vue 文件中的 1 行模板标签(`Criticalrecord` → `NursingRecord`),属于前端界面/组件层级的问题。
|
||||
|
||||
> ⚠️ 修复人员请先验证以上分析是否正确,再执行修复。
|
||||
132
MD/bugs/BUG_761_ANALYSIS.md
Normal file
132
MD/bugs/BUG_761_ANALYSIS.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# Bug #761 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 16:31:36
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 761
|
||||
- **标题**: [住院护士站-汇总领药]领药明细列表,“领药时间”显示逻辑异常且早于医嘱开立时间
|
||||
- **模块**: 病区护士工作站
|
||||
- **提出人**: 陈显精
|
||||
|
||||
---
|
||||
|
||||
I now have a complete understanding of the bug. Let me produce the analysis.
|
||||
|
||||
---
|
||||
|
||||
### 一、Bug 理解
|
||||
|
||||
用户在"住院护士站 → 汇总领药"页面看到"领药时间"列显示了错误的日期("06-09"),早于医嘱开立时间(06-10 05:18:44)和实际执行时间(06-10 05:18)。期望:①列名改为"执行时间";②时间值应与医嘱执行界面的执行时间一致;③未发药/未汇总前不应展示"领药时间"。
|
||||
|
||||
### 二、根因分析
|
||||
|
||||
**全链路追踪:**
|
||||
|
||||
```
|
||||
前端 prescriptionList.vue → API: getPrescriptionList
|
||||
→ 后端 MedicineSummaryController.getMedicineDispenseFormPage
|
||||
→ MedicineSummaryAppServiceImpl.getMedicineDispenseFormPage
|
||||
→ MedicineSummaryAppMapper.selectMedicineDispenseFormPage (SQL)
|
||||
→ 返回 MedicineDispenseFormDto.medicineSummaryParamList
|
||||
→ 其中 dispenseTime 来自 med_medication_dispense.planned_dispense_time
|
||||
```
|
||||
|
||||
**核心问题(2处):**
|
||||
|
||||
**问题1:数据源错误 — `planned_dispense_time` ≠ 执行时间**
|
||||
|
||||
- Mapper XML 第39行:`<result property="dispenseTime" column="planned_dispense_time"/>`,将 `med_medication_dispense.planned_dispense_time` 映射为 `dispenseTime`
|
||||
- `planned_dispense_time` 是在 `AdviceProcessAppServiceImpl` 调用 `generateMedicationDispense()` 时通过 `parseExecuteTime(executeTime)` 设置的,来自前端传入的执行时间字符串
|
||||
- 实际执行时间存储在 `cli_procedure.occurrence_time`(`Procedure.java` 第54行),这才是医嘱执行界面显示的"执行时间"
|
||||
- `planned_dispense_time` 的值可能因时区转换、字符串解析精度等原因与 `occurrence_time` 不一致(差一天即 "06-09" vs "06-10")
|
||||
|
||||
**问题2:列名语义错误 — "领药时间"应为"执行时间"**
|
||||
|
||||
- `prescriptionList.vue` 第164行:`title="领药时间"`,此列展示的是每条医嘱的执行时间点,不是"领药时间"
|
||||
- 在未生成汇总领药单之前,不存在"领药"动作,显示"领药时间"不符合业务逻辑
|
||||
|
||||
**涉及文件:**
|
||||
|
||||
| 文件 | 行号 | 问题 |
|
||||
|------|------|------|
|
||||
| `prescriptionList.vue` | 164 | 列名 "领药时间" 应改为 "执行时间" |
|
||||
| `MedicineSummaryAppMapper.xml` | 39, 78, ~125, 203 | `planned_dispense_time` 应改为 `cli_procedure.occurrence_time` |
|
||||
| `MedicineSummaryParam.java` | 22 | 字段名 `dispenseTime` 可保持不变(仅改数据源) |
|
||||
|
||||
### 三、修复方案
|
||||
|
||||
#### 修改1:后端 Mapper XML — 改用执行时间
|
||||
|
||||
**文件**: `healthlink-his-server/healthlink-his-application/src/main/resources/mapper/inhospitalnursestation/MedicineSummaryAppMapper.xml`
|
||||
|
||||
**改动A**:resultMap collection 映射,将 `dispenseTime` 的数据源从 `planned_dispense_time` 改为 `occurrence_time`(来自 `cli_procedure` 表):
|
||||
|
||||
```xml
|
||||
<!-- 第36-41行,collection 改为: -->
|
||||
<collection property="medicineSummaryParamList" ofType="com.healthlink.his.web.inhospitalnursestation.dto.MedicineSummaryParam">
|
||||
<result property="procedureId" column="procedure_id"/>
|
||||
<result property="dispenseId" column="dispense_id"/>
|
||||
<result property="dispenseTime" column="execution_time"/>
|
||||
<result property="dispenseStatus" column="dispense_status"/>
|
||||
</collection>
|
||||
```
|
||||
|
||||
**改动B**:内层 SQL 增加 `cli_procedure` JOIN 并选取 `occurrence_time`:
|
||||
|
||||
在内层 SELECT(约第98行 `mmd.status_enum AS dispense_status` 之后)添加:
|
||||
```sql
|
||||
cp.occurrence_time AS execution_time,
|
||||
```
|
||||
|
||||
在 FROM 子句中 `med_medication_dispense AS mmd` 之后添加 LEFT JOIN:
|
||||
```sql
|
||||
LEFT JOIN cli_procedure cp
|
||||
ON cp.id = mmd.procedure_id
|
||||
AND cp.delete_flag = '0'
|
||||
```
|
||||
|
||||
**改动C**:外层 SELECT(第78行)将 `ii.planned_dispense_time` 改为 `ii.execution_time`:
|
||||
|
||||
```sql
|
||||
ii.execution_time,
|
||||
```
|
||||
|
||||
**改动D**:ORDER BY(第203行)将 `mmd.planned_dispense_time` 改为 `cp.occurrence_time`:
|
||||
|
||||
```sql
|
||||
ORDER BY cp.occurrence_time )) AS ii
|
||||
```
|
||||
|
||||
#### 修改2:前端列名 — "领药时间" → "执行时间"
|
||||
|
||||
**文件**: `healthlink-his-ui/src/views/inpatientNurse/drugDistribution/components/prescriptionList.vue`
|
||||
|
||||
第164行:
|
||||
```vue
|
||||
<!-- 修改前 -->
|
||||
<vxe-column title="领药时间" field="times">
|
||||
|
||||
<!-- 修改后 -->
|
||||
<vxe-column title="执行时间" field="times">
|
||||
```
|
||||
|
||||
#### 无需修改的部分
|
||||
- `MedicineSummaryParam.java` — 字段名 `dispenseTime` 保持不变,仅后端数据源改变
|
||||
- `MedicineSummaryAppServiceImpl.java` — 无变化(`medicineSummary()` 方法提交时自行用 `new Date()` 覆盖)
|
||||
- 前端时间格式化逻辑 — `item.dispenseTime?.substring(5, 10)` 从 "yyyy-MM-dd HH:mm:ss" 截取 "MM-dd",格式兼容
|
||||
|
||||
### 四、路由决策
|
||||
|
||||
**FIXER: guanyu(后端开发)**
|
||||
|
||||
**REASON**: 修复核心在 Mapper XML SQL 改写(JOIN `cli_procedure` 取 `occurrence_time` 替代 `planned_dispense_time`),属于后端数据查询层改动。前端仅改一个列名文本,可一并处理但主要工作量在后端 SQL。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **修复 Agent**: guanyu
|
||||
- **原因**: LLM 分析决策
|
||||
102
MD/bugs/BUG_762_ANALYSIS.md
Normal file
102
MD/bugs/BUG_762_ANALYSIS.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Bug #762 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 16:21:51
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 762
|
||||
- **标题**: [住院护士站-领药汇总]“汇总领药”界面的筛选标签文字丢失且功能失效
|
||||
- **模块**: 病区护士工作站
|
||||
- **提出人**: 陈显精
|
||||
|
||||
---
|
||||
|
||||
Now I have the complete picture. Here's my analysis:
|
||||
|
||||
---
|
||||
|
||||
### 一、Bug 理解
|
||||
|
||||
住院护士站的「汇总领药」页面(`drugDistribution/index.vue`)顶部筛选栏有 4 个按钮——"西药""中药""明细""汇总",但目前这 4 个按钮**仅显示空白**,用户看不到文字标签,也无法通过点击"西药/中药"进行药物类别筛选。期望按钮正常显示中文标签,且点击可触发对应的数据筛选。
|
||||
|
||||
### 二、根因分析
|
||||
|
||||
**根因完全在前端**,`healthlink-his-ui/src/views/inpatientNurse/drugDistribution/index.vue` 存在两个问题:
|
||||
|
||||
**问题1:`el-radio-button` 缺少标签文字**
|
||||
|
||||
```html
|
||||
<!-- 第50~64行,当前代码 -->
|
||||
<el-radio-group v-model="drugType" class="ml10">
|
||||
<el-radio-button value="1" /> <!-- ← 缺少标签文字 -->
|
||||
<el-radio-button value="2" /> <!-- ← 缺少标签文字 -->
|
||||
</el-radio-group>
|
||||
<el-radio-group v-model="isDetails" class="ml20" @change="handleRadioChange">
|
||||
<el-radio-button value="1" /> <!-- ← 缺少标签文字 -->
|
||||
<el-radio-button value="2" /> <!-- ← 缺少标签文字 -->
|
||||
</el-radio-group>
|
||||
```
|
||||
|
||||
Element Plus 的 `el-radio-button` 需要通过默认 slot 或 `label` 属性显示文字,仅设 `value` 不会渲染任何可见文本。
|
||||
|
||||
**问题2:`drugType` 未传递给子组件,筛选功能失效**
|
||||
|
||||
- `drugType` 在 `index.vue:178` 定义(`ref('1')`),但 `PrescriptionList` 和 `SummaryMedicineList` 组件在模板中**未接收此 prop**:
|
||||
```html
|
||||
<PrescriptionList v-if="isDetails == 1" ref="prescriptionRefs"
|
||||
:exe-status="exeStatus" :request-status="requestStatus"
|
||||
:deadline="deadline" :therapy-enum="therapyEnum" />
|
||||
<!-- 缺少 :drug-type="drugType" -->
|
||||
```
|
||||
- `prescriptionList.vue` 和 `summaryMedicineList.vue` 的 `defineProps` 中均未定义 `drugType`
|
||||
- API 调用时也未传 `tcmFlag` 参数
|
||||
|
||||
**后端已具备筛选能力**:`DispenseFormSearchParam` 已有 `tcmFlag` 字段(第34行),`HisQueryUtils.buildQueryWrapper` 会自动生成 WHERE 条件,Mapper XML 使用 `${ew.customSqlSegment}` 动态拼接。前端只需传参即可。
|
||||
|
||||
### 三、修复方案
|
||||
|
||||
纯前端修复,涉及 3 个文件,后端无需改动。
|
||||
|
||||
**文件1:`healthlink-his-ui/src/views/inpatientNurse/drugDistribution/index.vue`**
|
||||
|
||||
| 修改点 | 内容 |
|
||||
|--------|------|
|
||||
| 第53行 `el-radio-button value="1"` | 改为 `<el-radio-button value="1">西药</el-radio-button>` |
|
||||
| 第56行 `el-radio-button value="2"` | 改为 `<el-radio-button value="2">中药</el-radio-button>` |
|
||||
| 第62行 `el-radio-button value="1"` | 改为 `<el-radio-button value="1">明细</el-radio-button>` |
|
||||
| 第65行 `el-radio-button value="2"` | 改为 `<el-radio-button value="2">汇总</el-radio-button>` |
|
||||
| `PrescriptionList` 组件标签 | 添加 `:drug-type="drugType"` prop |
|
||||
| `SummaryMedicineList` 组件标签 | 添加 `:drug-type="drugType"` prop |
|
||||
|
||||
**文件2:`healthlink-his-ui/src/views/inpatientNurse/drugDistribution/components/prescriptionList.vue`**
|
||||
|
||||
| 修改点 | 内容 |
|
||||
|--------|------|
|
||||
| `defineProps` 块(第259行) | 新增 `drugType: { type: String, default: '1' }` |
|
||||
| `handleGetPrescription` 函数(第281行) | API 调用参数中添加 `tcmFlag: props.drugType === '1' ? 0 : 1`(西药=0,中药=1) |
|
||||
|
||||
**文件3:`healthlink-his-ui/src/views/inpatientNurse/drugDistribution/components/summaryMedicineList.vue`**
|
||||
|
||||
| 修改点 | 内容 |
|
||||
|--------|------|
|
||||
| `defineProps` 块(第168行) | 新增 `drugType: { type: String, default: '1' }` |
|
||||
| `handleGetPrescription` 函数(第186行) | API 调用参数中添加 `tcmFlag: props.drugType === '1' ? 0 : 1` |
|
||||
|
||||
**验证方式:**
|
||||
1. `cd healthlink-his-ui && npm run build:dev` 编译通过
|
||||
2. `npm run lint` 无报错
|
||||
3. 手动验证:进入汇总领药页,4 个按钮显示"西药""中药""明细""汇总"文字;点击切换正常
|
||||
|
||||
### 四、路由决策
|
||||
|
||||
**FIXER: zhaoyun**(前端开发)
|
||||
**REASON:** 此 Bug 纯前端问题,涉及 3 个 Vue 组件的模板和 props 修复,属于前端 UI/交互层面,无需后端改动,最适合由赵云处理。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **修复 Agent**: zhaoyun
|
||||
- **原因**: ** 此 Bug 纯前端问题,涉及 3 个 Vue 组件的模板和 props 修复,属于前端 UI/交互层面,无需后端改动,最适合由赵云处理。
|
||||
90
MD/bugs/BUG_763_ANALYSIS.md
Normal file
90
MD/bugs/BUG_763_ANALYSIS.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Bug #763 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 16:18:00
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 763
|
||||
- **标题**: [住院护士站-医嘱校对]医生工作站已签发的临时医嘱在护士站“医嘱校对”中无法查询
|
||||
- **模块**: 病区护士工作站
|
||||
- **提出人**: 陈显精
|
||||
|
||||
---
|
||||
|
||||
This confirms `effectiveDoseEnd` is only set in outpatient prescriptions, NOT in inpatient order signing. Now I have the complete root cause.
|
||||
|
||||
---
|
||||
|
||||
### 一、Bug 理解
|
||||
|
||||
护士在"医嘱校对"界面选择患者并点击查询时,医生已签发的临时医嘱(药品类,如注射用头孢)无法显示,列表为空。期望行为:已签发的临时医嘱应出现在"未校对"列表中,供护士校对。
|
||||
|
||||
### 二、根因分析
|
||||
|
||||
**根因:Bug #665 修复引入的 `deadline` 过滤条件未处理 `NULL` 值。**
|
||||
|
||||
完整数据链路追踪:
|
||||
|
||||
1. **医生签发临时医嘱** → `AdviceManageAppServiceImpl.handMedication()` → `tempMedicationRequest.setStatusEnum(ACTIVE=2)` → 调用 `saveOrUpdate()` → 写入 `med_medication_request` 表
|
||||
2. **关键遗漏**:`effectiveDoseEnd`(服药结束时间)在整个签发流程中**从未被设置**(`setEffectiveDoseEnd` 仅在门诊处方 `DoctorStationElepPrescriptionServiceImpl` 中调用,住院医嘱路径不涉及),因此数据库中 `effective_dose_end = NULL`
|
||||
3. **护士站查询** → 前端 `prescriptionList.vue` 默认发送 `deadline = "2026-06-12 23:59:59"` → 后端 `AdviceProcessAppServiceImpl.getInpatientAdvicePage()` 拼接条件:
|
||||
```java
|
||||
queryWrapper.le("end_time", deadlineTime); // Bug #665 引入
|
||||
```
|
||||
生成 SQL:`end_time <= '2026-06-12 23:59:59'`
|
||||
4. **NULL 比较失败**:PostgreSQL 中 `NULL <= anything` 结果为 `NULL`(等价于 `FALSE`),WHERE 子句排除该行 → 查询结果为空
|
||||
|
||||
**涉及文件:**
|
||||
- `AdviceProcessAppServiceImpl.java`(第 235-243 行)— deadline 条件拼接
|
||||
- `AdviceProcessAppMapper.xml` — UNION 查询,`T1.effective_dose_end AS end_time`
|
||||
|
||||
### 三、修复方案
|
||||
|
||||
**修改文件**:`healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/AdviceProcessAppServiceImpl.java`
|
||||
|
||||
**修改内容**:将 deadline 过滤条件从 `le` 改为 NULL 安全的写法:
|
||||
|
||||
```java
|
||||
// 修改前(Bug #665 代码):
|
||||
if (deadline != null && !deadline.isEmpty()) {
|
||||
try {
|
||||
LocalDateTime deadlineTime = LocalDateTime.parse(deadline,
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
queryWrapper.le("end_time", deadlineTime);
|
||||
} catch (DateTimeParseException e) {
|
||||
// 忽略无效的日期格式
|
||||
}
|
||||
}
|
||||
|
||||
// 修改后:
|
||||
if (deadline != null && !deadline.isEmpty()) {
|
||||
try {
|
||||
LocalDateTime deadlineTime = LocalDateTime.parse(deadline,
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
queryWrapper.and(w -> w.le("end_time", deadlineTime).or().isNull("end_time"));
|
||||
} catch (DateTimeParseException e) {
|
||||
// 忽略无效的日期格式
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**原理**:`end_time <= deadline OR end_time IS NULL`,确保 `effective_dose_end` 为 NULL 的新签发医嘱也能被查询到。
|
||||
|
||||
**验证**:
|
||||
1. `mvn clean compile -DskipTests` → 编译通过
|
||||
2. 签发临时医嘱 → 护士站查询 → 应能查到
|
||||
3. 长期医嘱、有截止时间的医嘱 → deadline 筛选仍然生效
|
||||
|
||||
### 四、路由决策
|
||||
|
||||
**FIXER**: guanyu
|
||||
**REASON**: 修复范围仅涉及后端 Java Service 层一处 SQL 条件修改,属于纯后端逻辑修复,由后端开发关羽执行。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **修复 Agent**: guanyu
|
||||
- **原因**: LLM 分析决策
|
||||
121
MD/bugs/BUG_764_ANALYSIS.md
Normal file
121
MD/bugs/BUG_764_ANALYSIS.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Bug #764 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 16:05:34
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 764
|
||||
- **标题**: [门诊医生工作站-检验] 已维护诊疗项目的检验项目“肾功能5项”在开立保存时报错“未找到对应的诊疗定义”
|
||||
- **模块**: 门诊医生工作站
|
||||
- **提出人**: 陈显精
|
||||
|
||||
---
|
||||
|
||||
Now I have a complete understanding of the bug. Let me output the analysis.
|
||||
|
||||
---
|
||||
|
||||
### 一、Bug 理解
|
||||
|
||||
用户在门诊医生工作站选择检验项目"肾功能5项"并点击保存时,系统报错"未找到检验项目 '肾功能5项' 对应的诊疗定义"。该检验项目已在后台正确维护,期望保存成功并自动联动生成门诊医嘱记录。
|
||||
|
||||
### 二、根因分析
|
||||
|
||||
**根因**:`DoctorStationLabApplyServiceImpl.java` 的检验申请保存方法包含两个阶段:
|
||||
|
||||
1. **阶段一(保存申请单明细)**:正确使用 `labActivityDefinitionService.getById(activityId)` 从 `lab_activity_definition` 表查询检验项目定义(第182行)。
|
||||
2. **阶段二(自动创建门诊医嘱)**:**错误地**调用 `activityDefinitionService.getAppointActivityDefinitionId(itemName)`,该方法按名称精确匹配 `wor_activity_definition` 表(SQL: `WHERE NAME = #{activityName}`)。
|
||||
|
||||
**关键问题**:检验项目(如"肾功能5项")存储在独立的 `lab_activity_definition` 表中,**不存在于** `wor_activity_definition` 表中。按名称查询 `wor_activity_definition` 必然返回 null,触发 RuntimeException。
|
||||
|
||||
**对比参考**:`ExamApplyController`(检查申请)已正确处理类似情况——使用 `activityId = 0L` 占位,不依赖 `wor_activity_definition`(第224行注释:"检查申请不走诊疗定义,设置为0占位")。
|
||||
|
||||
**涉及文件**:
|
||||
- `DoctorStationLabApplyServiceImpl.java:254-259` — 查询逻辑错误(根因)
|
||||
- `ActivityDefinitionMapper.xml:7-13` — SQL 查询 `wor_activity_definition` by name
|
||||
- `ActivityDefinitionServiceImpl.java:76-77` — `getAppointActivityDefinitionId` 方法
|
||||
|
||||
### 三、修复方案
|
||||
|
||||
修改 `DoctorStationLabApplyServiceImpl.java` 中阶段二(门诊医嘱创建循环),将从 `wor_activity_definition` 按名称查询改为使用 `lab_activity_definition` 的 `activityId` 直接查询:
|
||||
|
||||
**修改文件**:`healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/appservice/impl/DoctorStationLabApplyServiceImpl.java`
|
||||
|
||||
**具体改动**:
|
||||
|
||||
将第254-370行附近的代码从:
|
||||
|
||||
```java
|
||||
// 1. 根据检验项目名称查询诊疗定义(检验项目)
|
||||
String itemName = labApplyItemDto.getItemName();
|
||||
Long activityDefinitionId = activityDefinitionService.getAppointActivityDefinitionId(itemName);
|
||||
if (activityDefinitionId == null) {
|
||||
throw new RuntimeException("未找到检验项目 '" + itemName + "' 对应的诊疗定义");
|
||||
}
|
||||
// 2. 获取诊疗定义详情
|
||||
ActivityDefinition activityDefinition = activityDefinitionService.getById(activityDefinitionId);
|
||||
if (activityDefinition == null) {
|
||||
throw new RuntimeException("诊疗定义不存在");
|
||||
}
|
||||
...
|
||||
adviceSaveDto.setAdviceDefinitionId(activityDefinitionId);
|
||||
adviceSaveDto.setDefinitionId(activityDefinitionId);
|
||||
adviceSaveDto.setCategoryCode(activityDefinition.getCategoryCode());
|
||||
adviceSaveDto.setActivityId(activityDefinitionId);
|
||||
adviceSaveDto.setAdviceTableName("wor_activity_definition");
|
||||
...
|
||||
adviceSaveDto.setUnitCode(activityDefinition.getPermittedUnitCode());
|
||||
Long feePackageId = activityDefinition.getFeePackageId();
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```java
|
||||
// 1. 获取检验项目定义(从 lab_activity_definition 表,不走 wor_activity_definition)
|
||||
String itemName = labApplyItemDto.getItemName();
|
||||
Long labActivityId = labApplyItemDto.getActivityId();
|
||||
if (labActivityId == null) {
|
||||
throw new RuntimeException("检验项目 '" + itemName + "' 未传入 activityId,请重新选择检验项目");
|
||||
}
|
||||
LabActivityDefinition labActivityDef = labActivityDefinitionService.getById(labActivityId);
|
||||
if (labActivityDef == null) {
|
||||
throw new RuntimeException("检验项目定义不存在,activityId=" + labActivityId);
|
||||
}
|
||||
...
|
||||
// 医嘱定义 ID:使用 lab_activity_definition 的 ID
|
||||
adviceSaveDto.setAdviceDefinitionId(labActivityId);
|
||||
// 费用定价 ID:检验项目不走 wor_activity_definition 定价体系,用 0 占位(与 ExamApplyController 一致)
|
||||
adviceSaveDto.setDefinitionId(0L);
|
||||
adviceSaveDto.setCategoryCode(labActivityDef.getCategoryCode());
|
||||
adviceSaveDto.setActivityId(labActivityId);
|
||||
adviceSaveDto.setAdviceTableName("lab_activity_definition");
|
||||
...
|
||||
adviceSaveDto.setUnitCode(labActivityDef.getPermittedUnitCode());
|
||||
Long feePackageId = labActivityDef.getFeePackageId();
|
||||
```
|
||||
|
||||
**核心改动点**:
|
||||
| 字段 | 原值 | 新值 | 原因 |
|
||||
|------|------|------|------|
|
||||
| 数据源 | `wor_activity_definition` (by name) | `lab_activity_definition` (by ID) | 检验项目独立存储 |
|
||||
| `adviceDefinitionId` | `wor_activity_definition.id` | `lab_activity_definition.id` | 正确引用产品表 |
|
||||
| `definitionId` | `wor_activity_definition.id` | `0L` 占位 | 检验项目不走诊疗定价体系 |
|
||||
| `adviceTableName` | `"wor_activity_definition"` | `"lab_activity_definition"` | 正确指向产品来源表 |
|
||||
| `categoryCode` | `ActivityDefinition.categoryCode` | `LabActivityDefinition.categoryCode` | 同名字段,来源切换 |
|
||||
| `unitCode` | `ActivityDefinition.permittedUnitCode` | `LabActivityDefinition.permittedUnitCode` | 同名字段,来源切换 |
|
||||
| `feePackageId` | `ActivityDefinition.feePackageId` | `LabActivityDefinition.feePackageId` | 同名字段,来源切换 |
|
||||
|
||||
### 四、路由决策
|
||||
|
||||
**FIXER**: `guanyu`(后端开发)
|
||||
|
||||
**REASON**: 此 Bug 仅涉及后端 Java 代码修改(`DoctorStationLabApplyServiceImpl.java` 一个文件),核心是修正检验项目的数据源查询逻辑——从 `wor_activity_definition` 切换到 `lab_activity_definition`。不需要前端改动(前端已正确传递 `activityId`),不需要数据库变更。属于后端 Service 层逻辑修复,由关羽执行最合适。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **修复 Agent**: guanyu
|
||||
- **原因**: LLM 分析决策
|
||||
74
MD/bugs/BUG_765_ANALYSIS.md
Normal file
74
MD/bugs/BUG_765_ANALYSIS.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Bug #765 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-12 15:58:59
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **Bug #**: 765
|
||||
- **标题**: [收费工作站-门诊挂号] 在“患者身份信息”框中选中检索的患者后,患者列表浮窗未自动隐藏
|
||||
- **模块**: 建档挂号管理
|
||||
- **提出人**: 陈显精
|
||||
|
||||
---
|
||||
|
||||
Now I have the complete picture. Here's my analysis:
|
||||
|
||||
---
|
||||
|
||||
### 一、Bug 理解
|
||||
|
||||
在门诊挂号页面的"患者身份信息"输入框中,用户输入关键字后弹出患者列表浮窗(`el-popover`),点击选中某行患者后,表单信息已正确回填,但浮窗没有自动关闭,继续遮挡下方录入项。用户期望选中后浮窗自动收起。
|
||||
|
||||
### 二、根因分析
|
||||
|
||||
问题出在 `outpatientregistration/index.vue` 中的 **popover 可见性控制逻辑**。
|
||||
|
||||
**关键代码链路:**
|
||||
|
||||
| 位置 | 代码 | 作用 |
|
||||
|------|------|------|
|
||||
| `index.vue:118` | `:visible="showPopover"` | popover 显隐由 `showPopover` 控制 |
|
||||
| `index.vue:125` | `@mousedown.prevent` | 阻止 mousedown 冒泡到 input,防止点击列表时 input 失焦 |
|
||||
| `index.vue:1990` | `handleFocus() → showPopover = true` | input 获焦显示浮窗 |
|
||||
| `index.vue:1996` | `handleBlur() → showPopover = false` | input 失焦隐藏浮窗 |
|
||||
| `index.vue:2228` | `selsectPatient(row)` — **只回填表单,未关闭浮窗** | ⬅️ 根因 |
|
||||
|
||||
**根因**:`selsectPatient` 函数只做了 `Object.assign(form.value, row)` 等字段回填,**没有设置 `showPopover.value = false`**。由于 `@mousedown.prevent` 阻止了点击列表时 input 的 blur 事件,`handleBlur` 不会被触发,浮窗就一直保持显示。
|
||||
|
||||
### 三、修复方案
|
||||
|
||||
**修改文件**:`healthlink-his-ui/src/views/charge/outpatientregistration/index.vue`
|
||||
|
||||
**修改内容**:在 `selsectPatient` 函数末尾添加一行,关闭 popover:
|
||||
|
||||
```javascript
|
||||
function selsectPatient(row) {
|
||||
Object.assign(form.value, row);
|
||||
form.value.patientId = row.id;
|
||||
form.value.searchKey = row.name;
|
||||
form.value.name = row.name;
|
||||
form.value.idCard = row.idCard;
|
||||
form.value.genderEnum_enumText = row.genderEnum_enumText;
|
||||
form.value.phone = row.phone;
|
||||
form.value.firstEnum_enumText = row.firstEnum_enumText;
|
||||
form.value.age = row.age;
|
||||
form.value.identifierNo = row.identifierNo;
|
||||
showPopover.value = false; // ← 新增:选中患者后关闭浮窗
|
||||
}
|
||||
```
|
||||
|
||||
**影响范围**:仅一处,不涉及后端、不涉及数据库、不涉及其他组件。
|
||||
|
||||
### 四、路由决策
|
||||
|
||||
**FIXER**: `zhaoyun`(前端开发)
|
||||
**REASON**: 纯前端 Vue 组件修复,只需在 `index.vue` 的 `selsectPatient` 函数中增加一行 `showPopover.value = false;`,属于 Element Plus popover 交互问题,赵云负责前端界面修复。
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **修复 Agent**: guanyu
|
||||
- **原因**: LLM 分析决策
|
||||
43
MD/bugs/BUG_诊疗目录_SYSDESDICT_ANALYSIS.md
Normal file
43
MD/bugs/BUG_诊疗目录_SYSDESDICT_ANALYSIS.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# 诊疗目录 SysDictData 反序列化错误 诸葛亮分析报告
|
||||
|
||||
> **文档类型**: Bug分析
|
||||
> **分析时间**: 2026-06-11
|
||||
> **分析模型**: mimo-v2.5 (LLM深度分析)
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
- **标题**: 诊疗目录 /system/catalog/diagnosistreatment 进入报错
|
||||
- **错误**: Cannot deserialize value of type SysDictData from Array value
|
||||
- **模块**: 诊疗目录管理
|
||||
|
||||
---
|
||||
|
||||
## 根因分析
|
||||
|
||||
**根本原因**: commit `68cfa4882` 将 Jackson 配置从 `Jackson2ObjectMapperBuilderCustomizer` 改为直接定义 `ObjectMapper` bean。
|
||||
|
||||
直接定义 `ObjectMapper` bean 会导致 Spring Boot 的 Jackson 自动配置完全失效:
|
||||
1. Spring Boot 默认的 Jackson 模块(如 `jackson-datatype-jdk8`、`jackson-datatype-jsr310`)不会自动注册
|
||||
2. 默认的序列化/反序列化特性设置丢失
|
||||
3. `ObjectMapper` 的默认可见性设置可能不同
|
||||
|
||||
当 `DictAspect` 处理 `@Dict` 注解的 DTO 时,Jackson 在序列化/反序列化过程中遇到 `SysDictData` 类型,由于缺少正确的模块配置,无法正确处理嵌套的数组/对象结构。
|
||||
|
||||
## 修复方案
|
||||
|
||||
将 `ApplicationConfig.java` 中的 `ObjectMapper` bean 改回 `Jackson2ObjectMapperBuilderCustomizer`,让 Spring Boot 自动配置保持生效。
|
||||
|
||||
### 修改文件
|
||||
- `core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java`
|
||||
|
||||
### 修改内容
|
||||
- `public ObjectMapper objectMapper()` → `public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()`
|
||||
- 移除手动创建的 `ObjectMapper` 实例
|
||||
- 使用 builder 模式配置,保留 Spring Boot 默认设置
|
||||
|
||||
---
|
||||
|
||||
## 路由决策
|
||||
- **修复 Agent**: guanyu (后端)
|
||||
- **原因**: Jackson 配置问题,纯后端修改
|
||||
@@ -115,10 +115,6 @@
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tools.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 阿里JSONè§£æžÂÂ器 -->
|
||||
<!-- io常çâ€Â¨å·¥å…·ç±» -->
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.core.common.core.redis;
|
||||
|
||||
import com.core.common.exception.UtilException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.BoundSetOperations;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
@@ -18,6 +19,7 @@ import java.util.concurrent.TimeUnit;
|
||||
**/
|
||||
@SuppressWarnings(value = {"unchecked", "rawtypes"})
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RedisCache {
|
||||
@Autowired
|
||||
public RedisTemplate redisTemplate;
|
||||
@@ -94,8 +96,20 @@ public class RedisCache {
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public <T> T getCacheObject(final String key) {
|
||||
ValueOperations<String, T> operation = redisTemplate.opsForValue();
|
||||
return operation.get(key);
|
||||
try {
|
||||
ValueOperations<String, T> operation = redisTemplate.opsForValue();
|
||||
return operation.get(key);
|
||||
} catch (Exception e) {
|
||||
log.error("Redis获取对象异常, key: {}, 错误信息: {}", key, e.getMessage());
|
||||
// 如果发生序列化等异常,可能是旧版本数据或损坏数据,直接删除以防止程序崩溃
|
||||
try {
|
||||
redisTemplate.delete(key);
|
||||
log.info("已自动清理损坏的缓存Key: {}", key);
|
||||
} catch (Exception ex) {
|
||||
log.error("自动清理损坏缓存失败, key: {}", key, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.core.common.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.core.common.constant.CacheConstants;
|
||||
@@ -38,11 +39,24 @@ public class DictUtils {
|
||||
* @return dictDatas 字典数据列表
|
||||
*/
|
||||
public static List<SysDictData> getDictCache(String key) {
|
||||
JsonNode arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
|
||||
if (StringUtils.isNotNull(arrayCache)) {
|
||||
return new ObjectMapper().convertValue(arrayCache, new com.fasterxml.jackson.core.type.TypeReference<List<SysDictData>>() {});
|
||||
Object cached = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
|
||||
if (StringUtils.isNull(cached)) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
// 如果已经是目标类型,直接返回
|
||||
if (cached instanceof List && ((List<?>) cached).stream().allMatch(e -> e instanceof SysDictData)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<SysDictData> result = (List<SysDictData>) cached;
|
||||
return result;
|
||||
}
|
||||
com.fasterxml.jackson.core.type.TypeReference<List<SysDictData>> typeRef =
|
||||
new com.fasterxml.jackson.core.type.TypeReference<List<SysDictData>>() {};
|
||||
ObjectMapper mapper = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
if (cached instanceof JsonNode jsonNode) {
|
||||
return mapper.convertValue(jsonNode, typeRef);
|
||||
}
|
||||
return mapper.convertValue(cached, typeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,17 +3,15 @@ package com.core.framework.config;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.jackson2.autoconfigure.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.TimeZone;
|
||||
@@ -40,19 +38,14 @@ public class ApplicationConfig {
|
||||
};
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
|
||||
mapper.setDateFormat(sdf);
|
||||
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, LOCAL_DATE_TIME_DESERIALIZER);
|
||||
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
mapper.registerModule(javaTimeModule);
|
||||
|
||||
return mapper;
|
||||
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
|
||||
return builder -> {
|
||||
builder.timeZone(TimeZone.getDefault());
|
||||
builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, LOCAL_DATE_TIME_DESERIALIZER);
|
||||
builder.modules(javaTimeModule);
|
||||
builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.core.framework.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.SerializationException;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Jackson Redis序列化器 - 兼容fastjson旧格式
|
||||
*
|
||||
* 新数据: 纯JSON (无类型包装),调用方用 convertValue 转换
|
||||
* 旧fastjson: 去除L后缀后按JSON解析
|
||||
* 旧Jackson activateDefaultTyping: 解包 ["className",{data}] 后取data部分
|
||||
*/
|
||||
public class FastjsonCompatibleRedisSerializer implements RedisSerializer<Object> {
|
||||
private static final Logger log = LoggerFactory.getLogger(FastjsonCompatibleRedisSerializer.class);
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/** 全局ObjectMapper,供外部调用方做 convertValue 转换 */
|
||||
private static final ObjectMapper sharedMapper = createMapper();
|
||||
|
||||
public FastjsonCompatibleRedisSerializer() { log.info("[INIT] FastjsonCompatibleRedisSerializer loaded - plain JSON mode");
|
||||
this.objectMapper = createMapper();
|
||||
}
|
||||
|
||||
private static ObjectMapper createMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
/** 获取共享ObjectMapper,供 DictUtils / TokenService 等做 convertValue */
|
||||
public static ObjectMapper getSharedMapper() {
|
||||
return sharedMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize(Object object) throws SerializationException {
|
||||
if (object == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
try {
|
||||
return objectMapper.writeValueAsBytes(object);
|
||||
} catch (Exception e) {
|
||||
throw new SerializationException("Redis序列化失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object deserialize(byte[] bytes) throws SerializationException {
|
||||
if (bytes == null || bytes.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
String json = new String(bytes, StandardCharsets.UTF_8);
|
||||
// 移除fastjson特有的Long L后缀: 123L -> 123
|
||||
String cleaned = json.replaceAll("(\\d+)L", "$1");
|
||||
try {
|
||||
// 处理旧Jackson activateDefaultTyping格式: ["className", {data}]
|
||||
if (cleaned.startsWith("[\"") && cleaned.length() > 10) {
|
||||
com.fasterxml.jackson.databind.JsonNode node = objectMapper.readTree(cleaned);
|
||||
if (node.isArray() && node.size() >= 2 && node.get(0).isTextual()) {
|
||||
// 取data部分(第2个元素),忽略className
|
||||
return objectMapper.treeToValue(node.get(1), Object.class);
|
||||
}
|
||||
}
|
||||
return objectMapper.readValue(cleaned, Object.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("Redis数据反序列化失败(已忽略): {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,51 +4,24 @@ import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.data.redis.serializer.GenericJacksonJsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import tools.jackson.databind.DeserializationFeature;
|
||||
import tools.jackson.databind.DatabindContext;
|
||||
import tools.jackson.databind.JavaType;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import tools.jackson.databind.jsontype.PolymorphicTypeValidator;
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public class RedisConfig extends CachingConfigurerSupport {
|
||||
|
||||
private static final PolymorphicTypeValidator ALLOW_ALL = new PolymorphicTypeValidator() {
|
||||
@Override
|
||||
public Validity validateBaseType(DatabindContext ctxt, JavaType baseType) {
|
||||
return Validity.ALLOWED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Validity validateSubClassName(DatabindContext ctxt, JavaType baseType, String subClassName) {
|
||||
return Validity.ALLOWED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Validity validateSubType(DatabindContext ctxt, JavaType baseType, JavaType subType) {
|
||||
return Validity.ALLOWED;
|
||||
}
|
||||
};
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@SuppressWarnings(value = {"unchecked", "rawtypes"})
|
||||
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<Object, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
|
||||
tools.jackson.databind.ObjectMapper objectMapper = JsonMapper.builder()
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.activateDefaultTyping(ALLOW_ALL, tools.jackson.databind.DefaultTyping.NON_FINAL)
|
||||
.build();
|
||||
|
||||
GenericJacksonJsonRedisSerializer serializer = new GenericJacksonJsonRedisSerializer(objectMapper);
|
||||
FastjsonCompatibleRedisSerializer serializer = new FastjsonCompatibleRedisSerializer();
|
||||
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setValueSerializer(serializer);
|
||||
@@ -68,16 +41,18 @@ public class RedisConfig extends CachingConfigurerSupport {
|
||||
return redisScript;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ValueOperations<Object, Object> valueOperations(RedisTemplate<Object, Object> redisTemplate) {
|
||||
return redisTemplate.opsForValue();
|
||||
}
|
||||
|
||||
private String limitScriptText() {
|
||||
return "local key = KEYS[1]\n" + "local count = tonumber(ARGV[1])\n" + "local time = tonumber(ARGV[2])\n"
|
||||
+ "local current = redis.call('get', key);\n" + "if current and tonumber(current) > count then\n"
|
||||
+ " return tonumber(current);\n" + "end\n" + "current = redis.call('incr', key)\n"
|
||||
+ "if tonumber(current) == 1 then\n" + " redis.call('expire', key, time)\n" + "end\n"
|
||||
+ "return tonumber(current);";
|
||||
return "local key = KEYS[1]\n" +
|
||||
"local count = tonumber(ARGV[1])\n" +
|
||||
"local ttl = tonumber(ARGV[2])\n" +
|
||||
"local current = redis.call('get', KEYS[1]);\n" +
|
||||
"if current and tonumber(current) > count then\n" +
|
||||
" return tonumber(current);\n" +
|
||||
"end\n" +
|
||||
"current = redis.call('incr', KEYS[1]);\n" +
|
||||
"if tonumber(current) == 1 then\n" +
|
||||
" redis.call('expire', KEYS[1], ttl);\n" +
|
||||
"end\n" +
|
||||
"return tonumber(current);\n";
|
||||
}
|
||||
}
|
||||
@@ -63,8 +63,28 @@ public class TokenService {
|
||||
// 解析对应的权限以及用户信息
|
||||
String uuid = (String)claims.get(Constants.LOGIN_USER_KEY);
|
||||
String userKey = getTokenKey(uuid);
|
||||
LoginUser user = redisCache.getCacheObject(userKey);
|
||||
return user;
|
||||
Object cached = redisCache.getCacheObject(userKey);
|
||||
if (cached instanceof LoginUser) {
|
||||
return (LoginUser) cached;
|
||||
}
|
||||
// 兼容旧Jackson activateDefaultTyping格式: ["className",{data}]
|
||||
if (cached instanceof java.util.List<?> list && list.size() >= 2 && list.get(0) instanceof String) {
|
||||
Object data = list.get(1);
|
||||
if (data instanceof java.util.Map) {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper =
|
||||
new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
return mapper.convertValue(data, LoginUser.class);
|
||||
}
|
||||
}
|
||||
// 兼容纯JSON格式: LinkedHashMap -> LoginUser
|
||||
if (cached instanceof java.util.Map) {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper =
|
||||
new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
return mapper.convertValue(cached, LoginUser.class);
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户信息异常'{}'", e.getMessage());
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class AntibioticAppServiceImpl implements IAntibioticAppService {
|
||||
}
|
||||
@Override
|
||||
public AntibioticApproval requestApproval(AntibioticApproval a) {
|
||||
a.setStatus("PENDING"); a.setDelFlag("0"); approvalService.save(a); return a;
|
||||
a.setStatus("PENDING"); a.setDeleteFlag("0"); approvalService.save(a); return a;
|
||||
}
|
||||
@Override
|
||||
public void approve(Long id, Long approverId, String approverName, String result) {
|
||||
|
||||
@@ -153,7 +153,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
dto.setIdCard(raw.getIdCard());
|
||||
dto.setDoctorId(raw.getDoctorId());
|
||||
dto.setDepartmentId(raw.getDepartmentId());
|
||||
dto.setRealPatientId(raw.getPatientId());
|
||||
dto.setRealPatientId(raw.getRealPatientId() != null ? raw.getRealPatientId() : raw.getPatientId());
|
||||
dto.setOrderId(raw.getOrderId());
|
||||
dto.setOrderNo(raw.getOrderNo());
|
||||
|
||||
|
||||
@@ -515,29 +515,28 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
// 构建查询条件
|
||||
QueryWrapper<CurrentDayEncounterDto> queryWrapper = HisQueryUtils.buildQueryWrapper(null, searchKey,
|
||||
new HashSet<>(Arrays.asList("patient_name", "organization_name", "practitioner_name", "healthcare_name", "identifier_no")),
|
||||
request);
|
||||
null); // registerTimeSTime/ETime 已下推到 SQL 内层 WHERE,跳过 buildQueryWrapper 的自动 *STime/*ETime 处理
|
||||
|
||||
// 手动处理 statusEnum 参数(用于过滤退号记录)
|
||||
// 提取statusEnum参数,下推到内层WHERE(避免外层重复过滤)
|
||||
Integer statusFilter = null;
|
||||
String statusEnumParam = request.getParameter("statusEnum");
|
||||
if (statusEnumParam != null && !statusEnumParam.isEmpty()) {
|
||||
try {
|
||||
Integer statusEnum = Integer.parseInt(statusEnumParam);
|
||||
if (statusEnum == -1) {
|
||||
// -1 表示排除退号记录(正常挂号)
|
||||
queryWrapper.ne("status_enum", 6);
|
||||
} else {
|
||||
// 其他值表示精确匹配
|
||||
queryWrapper.eq("status_enum", statusEnum);
|
||||
}
|
||||
statusFilter = Integer.parseInt(statusEnumParam);
|
||||
} catch (NumberFormatException e) {
|
||||
// 忽略无效的参数值
|
||||
}
|
||||
}
|
||||
|
||||
// 提取日期范围参数,下推到内层WHERE以优化性能(避免全表JOIN后再过滤)
|
||||
String registerTimeSTime = request.getParameter("registerTimeSTime");
|
||||
String registerTimeETime = request.getParameter("registerTimeETime");
|
||||
|
||||
IPage<CurrentDayEncounterDto> currentDayEncounter = outpatientRegistrationAppMapper.getCurrentDayEncounter(
|
||||
new Page<>(pageNo, pageSize), EncounterClass.AMB.getValue(), EncounterStatus.IN_PROGRESS.getValue(),
|
||||
ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode(), queryWrapper,
|
||||
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue());
|
||||
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue(),
|
||||
registerTimeSTime, registerTimeETime, statusFilter);
|
||||
|
||||
// 过滤候选池排除列表
|
||||
// 仅当调用方显式传 excludeFromCandidatePool=true 时才过滤,避免非分诊场景(挂号/收费)
|
||||
|
||||
@@ -54,7 +54,10 @@ public interface OutpatientRegistrationAppMapper {
|
||||
@Param("classEnum") Integer classEnum, @Param("statusEnum") Integer statusEnum,
|
||||
@Param("participantType1") String participantType1, @Param("participantType2") String participantType2,
|
||||
@Param(Constants.WRAPPER) QueryWrapper<CurrentDayEncounterDto> queryWrapper,
|
||||
@Param("register") Integer register, @Param("paymentStatus") Integer paymentStatus);
|
||||
@Param("register") Integer register, @Param("paymentStatus") Integer paymentStatus,
|
||||
@Param("registerTimeSTime") String registerTimeSTime,
|
||||
@Param("registerTimeETime") String registerTimeETime,
|
||||
@Param("statusFilter") Integer statusFilter);
|
||||
|
||||
/**
|
||||
* 查询item绑定的信息(耗材或诊疗)
|
||||
|
||||
@@ -30,13 +30,17 @@ public class ClinicalPathwayController {
|
||||
}
|
||||
@PutMapping("/complete/{id}") @Transactional(rollbackFor=Exception.class)
|
||||
public R<?> completePathway(@PathVariable Long id) {
|
||||
ClinicalPathwayExecution e = executionService.getById(id); if (e == null) return R.fail("执行记录不存在");
|
||||
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
|
||||
qw.eq(ClinicalPathwayExecution::getPathwayId, id).eq(ClinicalPathwayExecution::getStatus, "IN_PATH").orderByDesc(ClinicalPathwayExecution::getCreateTime).last("LIMIT 1");
|
||||
ClinicalPathwayExecution e = executionService.getOne(qw); if (e == null) return R.fail("执行记录不存在");
|
||||
e.setStatus("COMPLETED"); e.setCompleteDate(java.time.LocalDate.now());
|
||||
executionService.updateById(e); return R.ok();
|
||||
}
|
||||
@PutMapping("/vary/{id}") @Transactional(rollbackFor=Exception.class)
|
||||
public R<?> varyPathway(@PathVariable Long id, @RequestParam("reason") String reason) {
|
||||
ClinicalPathwayExecution e = executionService.getById(id); if (e == null) return R.fail("执行记录不存在");
|
||||
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
|
||||
qw.eq(ClinicalPathwayExecution::getPathwayId, id).eq(ClinicalPathwayExecution::getStatus, "IN_PATH").orderByDesc(ClinicalPathwayExecution::getCreateTime).last("LIMIT 1");
|
||||
ClinicalPathwayExecution e = executionService.getOne(qw); if (e == null) return R.fail("执行记录不存在");
|
||||
e.setStatus("VARIATION"); e.setVariationReason(reason); executionService.updateById(e); return R.ok();
|
||||
}
|
||||
@GetMapping("/stats")
|
||||
@@ -44,11 +48,12 @@ public class ClinicalPathwayController {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("totalPathways", pathwayService.count());
|
||||
stats.put("totalExecutions", executionService.count());
|
||||
LambdaQueryWrapper<ClinicalPathwayExecution> cw = new LambdaQueryWrapper<>();
|
||||
cw.eq(ClinicalPathwayExecution::getStatus, "COMPLETED");
|
||||
stats.put("completedExecutions", executionService.count(cw));
|
||||
cw.eq(ClinicalPathwayExecution::getStatus, "VARIATION");
|
||||
stats.put("variedExecutions", executionService.count(cw));
|
||||
LambdaQueryWrapper<ClinicalPathwayExecution> ccw = new LambdaQueryWrapper<>();
|
||||
ccw.eq(ClinicalPathwayExecution::getStatus, "COMPLETED");
|
||||
stats.put("completedExecutions", executionService.count(ccw));
|
||||
LambdaQueryWrapper<ClinicalPathwayExecution> vcw = new LambdaQueryWrapper<>();
|
||||
vcw.eq(ClinicalPathwayExecution::getStatus, "VARIATION");
|
||||
stats.put("variedExecutions", executionService.count(vcw));
|
||||
long total = executionService.count();
|
||||
long completed = stats.containsKey("completedExecutions") ? (long) stats.get("completedExecutions") : 0;
|
||||
stats.put("completionRate", total > 0 ? Math.round(completed * 100.0 / total) : 0);
|
||||
|
||||
@@ -666,7 +666,7 @@ public class CommonServiceImpl implements ICommonService {
|
||||
.eq(DocInventoryItemStatic::getDeleteFlag, DelFlag.NO.getCode())
|
||||
.eq(DocInventoryItemStatic::getTenantId, SecurityUtils.getLoginUser().getTenantId()));
|
||||
if (docInventoryItemStaticList.isEmpty()) {
|
||||
return null;
|
||||
return R.ok(Collections.emptyList());
|
||||
}
|
||||
// 直接去重并按BusNo倒序排序
|
||||
List<String> busNoList = docInventoryItemStaticList.stream().map(DocInventoryItemStatic::getBusNo).distinct() // 去重
|
||||
|
||||
@@ -451,10 +451,12 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
|
||||
// 删除费用项
|
||||
iChargeItemService.deleteByServiceTableAndId(CommonConstants.TableName.MED_MEDICATION_REQUEST,
|
||||
adviceSaveDto.getRequestId());
|
||||
// 删除代煎费
|
||||
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
|
||||
.eq(ChargeItem::getPrescriptionNo, adviceSaveDto.getPrescriptionNo())
|
||||
.eq(ChargeItem::getProductId, sufferingDefinitionId));
|
||||
// 删除代煎费(按处方号精确清理)
|
||||
if (adviceSaveDto.getPrescriptionNo() != null) {
|
||||
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
|
||||
.eq(ChargeItem::getPrescriptionNo, adviceSaveDto.getPrescriptionNo())
|
||||
.eq(ChargeItem::getProductId, sufferingDefinitionId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -614,7 +616,7 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
|
||||
Long encounterDiagnosisId = medicineList.get(0).getEncounterDiagnosisId();
|
||||
// 中药付数
|
||||
BigDecimal chineseHerbsDoseQuantity = medicineList.get(0).getChineseHerbsDoseQuantity();
|
||||
// 🔧 Bug Fix #668: 收集所有处方号(不同分组可能有不同处方号)
|
||||
// 收集所有处方号(不同分组可能有不同处方号)
|
||||
List<String> prescriptionNos = insertOrUpdateList.stream()
|
||||
.map(AdviceSaveDto::getPrescriptionNo)
|
||||
.filter(Objects::nonNull)
|
||||
@@ -628,12 +630,10 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
|
||||
AdviceBaseDto adviceBaseDto = new AdviceBaseDto();
|
||||
adviceBaseDto.setAdviceDefinitionId(sufferingDefinitionId); // 医嘱定义id
|
||||
|
||||
// 🔧 Bug Fix #668: 先删除所有处方号关联的中药代煎账单
|
||||
if (!prescriptionNos.isEmpty()) {
|
||||
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
|
||||
.in(ChargeItem::getPrescriptionNo, prescriptionNos)
|
||||
.eq(ChargeItem::getProductId, sufferingDefinitionId));
|
||||
}
|
||||
// 先删除该就诊关联的所有中药代煎账单
|
||||
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
|
||||
.eq(ChargeItem::getEncounterId, encounterId)
|
||||
.eq(ChargeItem::getProductId, sufferingDefinitionId));
|
||||
|
||||
// 对应的诊疗医嘱信息
|
||||
AdviceBaseDto activityAdviceBaseDto = iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, null,
|
||||
@@ -642,7 +642,7 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
|
||||
// 费用定价
|
||||
AdvicePriceDto advicePriceDto = activityAdviceBaseDto.getPriceList().get(0);
|
||||
if (advicePriceDto != null) {
|
||||
// 🔧 Bug Fix #668: 为每个处方号分别生成代煎账单
|
||||
// 为每个处方号分别生成代煎账单
|
||||
for (String prescriptionNo : prescriptionNos) {
|
||||
chargeItem = new ChargeItem();
|
||||
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
|
||||
@@ -674,12 +674,10 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
|
||||
}
|
||||
}
|
||||
} else if (Whether.NO.getValue().equals(sufferingFlag)) {
|
||||
// 🔧 Bug Fix #668: 删除所有处方号关联的中药代煎账单
|
||||
if (!prescriptionNos.isEmpty()) {
|
||||
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
|
||||
.in(ChargeItem::getPrescriptionNo, prescriptionNos)
|
||||
.eq(ChargeItem::getProductId, sufferingDefinitionId));
|
||||
}
|
||||
// 删除该就诊关联的所有中药代煎账单
|
||||
iChargeItemService.remove(new LambdaQueryWrapper<ChargeItem>()
|
||||
.eq(ChargeItem::getEncounterId, encounterId)
|
||||
.eq(ChargeItem::getProductId, sufferingDefinitionId));
|
||||
}
|
||||
|
||||
// 签发时,把草稿状态的账单更新为待收费[中医]
|
||||
|
||||
@@ -251,18 +251,16 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
|
||||
// 遍历检验申请单明细,为每个检验项目创建对应的门诊医嘱记录
|
||||
for (DoctorStationLabApplyItemDto labApplyItemDto : doctorStationLabApplyDto.getLabApplyItemList()) {
|
||||
// 1. 根据检验项目名称查询诊疗定义(检验项目)
|
||||
// 1. Bug #764: 使用 lab_activity_definition 表的 activityId 查询检验项目定义
|
||||
// 检验项目存储在 lab_activity_definition 独立表中,不走 wor_activity_definition
|
||||
String itemName = labApplyItemDto.getItemName();
|
||||
Long activityDefinitionId = activityDefinitionService.getAppointActivityDefinitionId(itemName);
|
||||
|
||||
if (activityDefinitionId == null) {
|
||||
throw new RuntimeException("未找到检验项目 '" + itemName + "' 对应的诊疗定义");
|
||||
Long labActivityId = labApplyItemDto.getActivityId();
|
||||
if (labActivityId == null) {
|
||||
throw new RuntimeException("检验项目 '" + itemName + "' 未传入 activityId,请重新选择检验项目");
|
||||
}
|
||||
|
||||
// 2. 获取诊疗定义详情
|
||||
ActivityDefinition activityDefinition = activityDefinitionService.getById(activityDefinitionId);
|
||||
if (activityDefinition == null) {
|
||||
throw new RuntimeException("诊疗定义不存在");
|
||||
LabActivityDefinition labActivityDef = labActivityDefinitionService.getById(labActivityId);
|
||||
if (labActivityDef == null) {
|
||||
throw new RuntimeException("检验项目 '" + itemName + "' 对应的检验项目定义不存在(ID: " + labActivityId + ")");
|
||||
}
|
||||
|
||||
// 3. 根据执行科室代码获取科室 ID(positionId)
|
||||
@@ -297,17 +295,17 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
|
||||
// 设置检验项目相关信息
|
||||
// 医嘱定义 ID(诊疗定义 ID)
|
||||
adviceSaveDto.setAdviceDefinitionId(activityDefinitionId);
|
||||
adviceSaveDto.setAdviceDefinitionId(labActivityId);
|
||||
// 费用定价主表 ID(对应 adm_charge_item 表的 definition_id 字段)
|
||||
adviceSaveDto.setDefinitionId(activityDefinitionId);
|
||||
adviceSaveDto.setDefinitionId(labActivityId);
|
||||
// 医嘱名称
|
||||
adviceSaveDto.setAdviceName(itemName);
|
||||
// 医嘱详细分类
|
||||
adviceSaveDto.setCategoryCode(activityDefinition.getCategoryCode());
|
||||
adviceSaveDto.setCategoryCode(labActivityDef.getCategoryCode());
|
||||
// 活动 ID(诊疗定义 ID)
|
||||
adviceSaveDto.setActivityId(activityDefinitionId);
|
||||
adviceSaveDto.setActivityId(labActivityId);
|
||||
// 医嘱定义对应表名
|
||||
adviceSaveDto.setAdviceTableName("wor_activity_definition");
|
||||
adviceSaveDto.setAdviceTableName("lab_activity_definition");
|
||||
// 执行科室 ID
|
||||
adviceSaveDto.setPositionId(positionId);
|
||||
|
||||
@@ -346,13 +344,13 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
||||
// 请求数量
|
||||
adviceSaveDto.setQuantity(labApplyItemDto.getItemQty() != null ? labApplyItemDto.getItemQty() : java.math.BigDecimal.ONE);
|
||||
// 请求单位编码(使用诊疗定义的使用单位)
|
||||
adviceSaveDto.setUnitCode(activityDefinition.getPermittedUnitCode());
|
||||
adviceSaveDto.setUnitCode(labActivityDef.getPermittedUnitCode());
|
||||
|
||||
// 单价处理(BugFix#CodeReview: 根据套餐ID从正确的数据源获取价格)
|
||||
// 套餐项目:从 inspection_basic_information 表获取 package_amount
|
||||
// 普通项目:使用前端传入的 itemPrice(已从诊疗项目获取)
|
||||
java.math.BigDecimal unitPrice;
|
||||
Long feePackageId = activityDefinition.getFeePackageId();
|
||||
Long feePackageId = labActivityDef.getFeePackageId();
|
||||
|
||||
if (feePackageId != null) {
|
||||
// 套餐项目:查询套餐价格
|
||||
|
||||
@@ -304,6 +304,9 @@ public class EmergencyController {
|
||||
@PostMapping("/green-channel/activate")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> activateGreenChannel(@RequestBody EmergencyGreenChannel gc) {
|
||||
if (gc.getPatientId() == null) {
|
||||
return R.fail("患者ID不能为空,请选择患者后再激活绿色通道");
|
||||
}
|
||||
gc.setActivateTime(new Date());
|
||||
gc.setCreateTime(new Date());
|
||||
greenChannelService.save(gc);
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.healthlink.his.web.inhospitalcharge.dto.*;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -30,11 +31,15 @@ public interface IInHospitalRegisterAppService {
|
||||
* @param registeredFlag 已登记标识,已登记传 1 ,待登记传 0
|
||||
* @param pageNo 当前页
|
||||
* @param pageSize 每页多少条
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param organizationId 入院科室ID
|
||||
* @param request 请求
|
||||
* @return 住院登记信息
|
||||
*/
|
||||
IPage<InHospitalRegisterQueryDto> getRegisterInfo(InHospitalRegisterQueryDto inHospitalRegisterQueryDto,
|
||||
String searchKey, String registeredFlag, Integer pageNo, Integer pageSize, HttpServletRequest request);
|
||||
String searchKey, String registeredFlag, Integer pageNo, Integer pageSize,
|
||||
Date startTime, Date endTime, Long organizationId, HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 查询患者基本信息
|
||||
|
||||
@@ -173,7 +173,8 @@ public class InHospitalRegisterAppServiceImpl implements IInHospitalRegisterAppS
|
||||
*/
|
||||
@Override
|
||||
public IPage<InHospitalRegisterQueryDto> getRegisterInfo(InHospitalRegisterQueryDto inHospitalRegisterQueryDto,
|
||||
String searchKey, String registeredFlag, Integer pageNo, Integer pageSize, HttpServletRequest request) {
|
||||
String searchKey, String registeredFlag, Integer pageNo, Integer pageSize,
|
||||
Date startTime, Date endTime, Long organizationId, HttpServletRequest request) {
|
||||
Integer encounterStatus = EncounterZyStatus.TO_BE_REGISTERED.getValue(); // 待登记
|
||||
// 构建查询条件
|
||||
QueryWrapper<InHospitalRegisterQueryDto> queryWrapper
|
||||
@@ -182,7 +183,8 @@ public class InHospitalRegisterAppServiceImpl implements IInHospitalRegisterAppS
|
||||
|
||||
IPage<InHospitalRegisterQueryDto> inHospitalRegisterInfo = inHospitalRegisterAppMapper
|
||||
.getInHospitalRegisterInfo(new Page<>(pageNo, pageSize), EncounterClass.IMP.getValue(), encounterStatus,
|
||||
registeredFlag, LocationForm.WARD.getValue(), queryWrapper);
|
||||
registeredFlag, LocationForm.WARD.getValue(), startTime, endTime, organizationId,
|
||||
queryWrapper);
|
||||
inHospitalRegisterInfo.getRecords().forEach(e -> {
|
||||
// 性别
|
||||
e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
|
||||
|
||||
@@ -10,6 +10,8 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
@@ -53,6 +55,9 @@ public class InHospitalRegisterController {
|
||||
* @param registeredFlag 已登记标识,已登记传 1 ,待登记传 0
|
||||
* @param pageNo 当前页
|
||||
* @param pageSize 每页多少条
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param organizationId 入院科室ID
|
||||
* @param request 请求
|
||||
* @return 住院登记信息
|
||||
*/
|
||||
@@ -61,9 +66,13 @@ public class InHospitalRegisterController {
|
||||
@RequestParam(value = "searchKey", defaultValue = "") String searchKey,
|
||||
@RequestParam(value = "registeredFlag") String registeredFlag,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest request) {
|
||||
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(value = "startTime", required = false) Date startTime,
|
||||
@RequestParam(value = "endTime", required = false) Date endTime,
|
||||
@RequestParam(value = "organizationId", required = false) Long organizationId,
|
||||
HttpServletRequest request) {
|
||||
return R.ok(iInHospitalRegisterAppService.getRegisterInfo(inHospitalRegisterQueryDto, searchKey, registeredFlag,
|
||||
pageNo, pageSize, request));
|
||||
pageNo, pageSize, startTime, endTime, organizationId, request));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,5 +100,24 @@ public class InHospitalRegisterQueryDto {
|
||||
*/
|
||||
private Integer statusEnum;
|
||||
|
||||
}
|
||||
// PLACEHOLDER_FOR_NEW_FIELDS
|
||||
|
||||
/**
|
||||
* 开始时间(查询条件)
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 结束时间(查询条件)
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* 组织ID(查询条件)
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long organizationId;
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import com.healthlink.his.web.inhospitalcharge.dto.InHospitalRegisterQueryDto;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -27,12 +28,17 @@ public interface InHospitalRegisterAppMapper {
|
||||
* @param encounterStatus 登记状态
|
||||
* @param registeredFlag 已登记标识,已登记传 1 ,待登记传 0
|
||||
* @param formEnum 物理位置
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param organizationId 入院科室ID
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 住院登记信息
|
||||
*/
|
||||
IPage<InHospitalRegisterQueryDto> getInHospitalRegisterInfo(@Param("page") Page<InHospitalRegisterQueryDto> page,
|
||||
@Param("encounterClass") Integer encounterClass, @Param("encounterStatus") Integer encounterStatus,
|
||||
@Param("registeredFlag") String registeredFlag, @Param("formEnum") Integer formEnum,
|
||||
@Param("startTime") Date startTime, @Param("endTime") Date endTime,
|
||||
@Param("organizationId") Long organizationId,
|
||||
@Param(Constants.WRAPPER) QueryWrapper<InHospitalRegisterQueryDto> queryWrapper);
|
||||
|
||||
/**
|
||||
|
||||
@@ -192,6 +192,9 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
// 提取requestStatus手动处理,支持COMPLETED(3)和CHECK_VERIFIED(10)同时查询
|
||||
Integer requestStatus = inpatientAdviceParam.getRequestStatus();
|
||||
inpatientAdviceParam.setRequestStatus(null);
|
||||
// Bug #714: 提取deadline手动处理,UNION子查询列名为end_time
|
||||
String deadline = inpatientAdviceParam.getDeadline();
|
||||
inpatientAdviceParam.setDeadline(null);
|
||||
// 构建查询条件
|
||||
QueryWrapper<InpatientAdviceParam> queryWrapper
|
||||
= HisQueryUtils.buildQueryWrapper(inpatientAdviceParam, null, null, null);
|
||||
@@ -223,6 +226,16 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
if (therapyEnum != null) {
|
||||
queryWrapper.and(w -> w.eq("therapy_enum", therapyEnum).or().isNull("therapy_enum"));
|
||||
}
|
||||
// Bug #714: 手动拼接deadline条件,按医嘱截止时间筛选
|
||||
if (deadline != null && !deadline.isEmpty()) {
|
||||
try {
|
||||
LocalDateTime deadlineTime = LocalDateTime.parse(deadline,
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
queryWrapper.le("end_time", deadlineTime);
|
||||
} catch (DateTimeParseException e) {
|
||||
// 忽略无效的日期格式
|
||||
}
|
||||
}
|
||||
// 患者医嘱分页列表
|
||||
Page<InpatientAdviceDto> inpatientAdvicePage
|
||||
= adviceProcessAppMapper.selectInpatientAdvicePage(new Page<>(pageNo, pageSize), queryWrapper,
|
||||
@@ -642,6 +655,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
* @return 操作结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> adviceCancel(AdviceExecuteParam adviceExecuteParam) {
|
||||
// 获取当前执行时间
|
||||
Date exeDate = DateUtils.getNowDate();
|
||||
@@ -688,6 +702,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
longMedDispensedList.add(medicationDispense);
|
||||
} else if (DispenseStatus.EXECUTED.getValue().equals(medicationDispense.getStatusEnum())
|
||||
|| DispenseStatus.SUBMITTED.getValue().equals(medicationDispense.getStatusEnum())) {
|
||||
longMedDispensedList.add(medicationDispense);
|
||||
longMedUndispenseList.add(medicationDispense);
|
||||
}
|
||||
}
|
||||
@@ -716,6 +731,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
tempMedDispensedList.add(medicationDispense);
|
||||
} else if (DispenseStatus.EXECUTED.getValue().equals(medicationDispense.getStatusEnum())
|
||||
|| DispenseStatus.SUBMITTED.getValue().equals(medicationDispense.getStatusEnum())) {
|
||||
tempMedDispensedList.add(medicationDispense);
|
||||
tempMedUndispenseList.add(medicationDispense);
|
||||
}
|
||||
}
|
||||
@@ -863,7 +879,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
if (!longMedUndispenseList.isEmpty()) {
|
||||
// 排除已汇总的药品
|
||||
List<MedicationDispense> medicationDispenseList
|
||||
= tempMedUndispenseList.stream().filter(x -> x.getSummaryNo() == null).toList();
|
||||
= longMedUndispenseList.stream().filter(x -> x.getSummaryNo() == null).toList();
|
||||
medicationDispenseService
|
||||
.removeByIds(medicationDispenseList.stream().map(MedicationDispense::getId).toList());
|
||||
}
|
||||
|
||||
@@ -150,10 +150,21 @@ public class MedicineSummaryAppServiceImpl implements IMedicineSummaryAppService
|
||||
@Override
|
||||
public R<?> getMedicineSummaryFormPage(DispenseFormSearchParam dispenseFormSearchParam, Integer pageNo,
|
||||
Integer pageSize, String searchKey, HttpServletRequest request) {
|
||||
// 就诊ID集合
|
||||
String encounterIds = dispenseFormSearchParam.getEncounterIds();
|
||||
dispenseFormSearchParam.setEncounterIds(null);
|
||||
|
||||
// 构建查询条件
|
||||
QueryWrapper<DispenseFormSearchParam> queryWrapper = HisQueryUtils.buildQueryWrapper(dispenseFormSearchParam,
|
||||
searchKey, new HashSet<>(List.of(CommonConstants.FieldName.BusNo)), request);
|
||||
|
||||
// 如果传了就诊ID,过滤关联的汇总单
|
||||
if (StringUtils.isNotEmpty(encounterIds)) {
|
||||
queryWrapper.inSql(CommonConstants.FieldName.BusNo,
|
||||
"SELECT DISTINCT summary_no FROM med_medication_dispense " +
|
||||
"WHERE encounter_id IN (" + encounterIds + ") AND summary_no IS NOT NULL");
|
||||
}
|
||||
|
||||
// 汇总单分页列表
|
||||
Page<MedicineSummaryFormDto> medicineSummaryFormPage = medicineSummaryAppMapper.selectMedicineSummaryFormPage(
|
||||
new Page<>(pageNo, pageSize), queryWrapper, DispenseStatus.PREPARATION.getValue(),
|
||||
|
||||
@@ -11,5 +11,6 @@ public interface IOrderClosedLoopAppService {
|
||||
void executeOrder(OrderExecuteRecord record);
|
||||
void completeOrder(OrderExecuteRecord record);
|
||||
void cancelOrder(OrderExecuteRecord record);
|
||||
Map<String, Object> getStatistics();
|
||||
Map<String, Object> getStatistics(String type, String groupBy, Integer pageNum, Integer pageSize);
|
||||
void remindOrder(Map<String, Object> params);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.*;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springframework.jdbc.support.JdbcUtils;
|
||||
@Service
|
||||
public class OrderClosedLoopAppServiceImpl implements IOrderClosedLoopAppService {
|
||||
@Autowired private IOrderExecuteRecordService recordService;
|
||||
@@ -90,20 +91,128 @@ public class OrderClosedLoopAppServiceImpl implements IOrderClosedLoopAppService
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private com.healthlink.his.orderclosedloop.mapper.OrderExecuteRecordMapper recordMapper;
|
||||
|
||||
private long getLong(java.util.Map<String, Object> map, String key) {
|
||||
Object val = map.get(key);
|
||||
if (val == null) {
|
||||
// Try lowercase key (PostgreSQL returns lowercase column names)
|
||||
val = map.get(key.toLowerCase());
|
||||
}
|
||||
return val instanceof Number ? ((Number) val).longValue() : 0L;
|
||||
}
|
||||
|
||||
private double getDouble(java.util.Map<String, Object> map, String key) {
|
||||
Object val = map.get(key);
|
||||
if (val == null) {
|
||||
val = map.get(key.toLowerCase());
|
||||
}
|
||||
return val instanceof Number ? ((Number) val).doubleValue() : 0.0;
|
||||
}
|
||||
|
||||
private String getString(java.util.Map<String, Object> map, String key) {
|
||||
Object val = map.get(key);
|
||||
if (val == null) {
|
||||
val = map.get(key.toLowerCase());
|
||||
}
|
||||
return val != null ? val.toString() : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStatistics() {
|
||||
public Map<String, Object> getStatistics(String type, String groupBy, Integer pageNum, Integer pageSize) {
|
||||
if ("unclosedWarnings".equals(type)) {
|
||||
return getUnclosedWarnings(pageNum, pageSize);
|
||||
}
|
||||
if (groupBy != null && !groupBy.isEmpty()) {
|
||||
return getGroupStats(groupBy);
|
||||
}
|
||||
return getOverviewStats();
|
||||
}
|
||||
|
||||
private Map<String, Object> getOverviewStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
long total = recordService.count();
|
||||
long pending = recordService.count(new LambdaQueryWrapper<OrderExecuteRecord>().eq(OrderExecuteRecord::getExecuteStatus, "pending"));
|
||||
long executing = recordService.count(new LambdaQueryWrapper<OrderExecuteRecord>().eq(OrderExecuteRecord::getExecuteStatus, "executing"));
|
||||
long completed = recordService.count(new LambdaQueryWrapper<OrderExecuteRecord>().eq(OrderExecuteRecord::getExecuteStatus, "completed"));
|
||||
long cancelled = recordService.count(new LambdaQueryWrapper<OrderExecuteRecord>().eq(OrderExecuteRecord::getExecuteStatus, "cancelled"));
|
||||
List<Map<String, Object>> rows = recordMapper.selectOverviewByType();
|
||||
String[] rateKeys = {"drugClosedRate", "labClosedRate", "examClosedRate", "treatmentClosedRate"};
|
||||
String[] types = {"drug", "lab", "exam", "treatment"};
|
||||
long total = 0;
|
||||
for (Map<String, Object> row : rows) {
|
||||
String orderType = getString(row, "orderType");
|
||||
long totalOrders = getLong(row, "totalOrders");
|
||||
long closedCount = getLong(row, "closedCount");
|
||||
total += totalOrders;
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i].equals(orderType)) {
|
||||
double rate = totalOrders > 0 ? Math.round(closedCount * 1000.0 / totalOrders) / 10.0 : 0;
|
||||
stats.put(rateKeys[i], rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
stats.put("total", total);
|
||||
stats.put("pending", pending);
|
||||
stats.put("executing", executing);
|
||||
stats.put("completed", completed);
|
||||
stats.put("cancelled", cancelled);
|
||||
stats.put("completionRate", total > 0 ? Math.round(completed * 100.0 / total) : 0);
|
||||
return stats;
|
||||
}
|
||||
|
||||
private Map<String, Object> getGroupStats(String groupBy) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
List<Map<String, Object>> dbRows;
|
||||
if ("doctor".equals(groupBy)) {
|
||||
dbRows = recordMapper.selectGroupByDoctor();
|
||||
} else {
|
||||
dbRows = recordMapper.selectGroupByDepartment();
|
||||
}
|
||||
List<Map<String, Object>> records = new ArrayList<>();
|
||||
for (Map<String, Object> row : dbRows) {
|
||||
Map<String, Object> item = new LinkedHashMap<>(row);
|
||||
long totalOrders = getLong(row, "totalOrders");
|
||||
long closedCount = getLong(row, "closedCount");
|
||||
item.put("unclosedCount", totalOrders - closedCount);
|
||||
item.put("closedRate", totalOrders > 0 ? Math.round(closedCount * 1000.0 / totalOrders) / 10.0 : 0);
|
||||
records.add(item);
|
||||
}
|
||||
result.put("records", records);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, Object> getUnclosedWarnings(Integer pageNum, Integer pageSize) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
List<Map<String, Object>> rows = recordMapper.selectUnclosedWarnings();
|
||||
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
List<Map<String, Object>> warnings = new ArrayList<>();
|
||||
for (Map<String, Object> row : rows) {
|
||||
Map<String, Object> warning = new LinkedHashMap<>(row);
|
||||
Object orderTimeObj = row.get("orderTime");
|
||||
if (orderTimeObj instanceof java.sql.Timestamp) {
|
||||
long hours = (System.currentTimeMillis() - ((java.sql.Timestamp) orderTimeObj).getTime()) / (1000 * 60 * 60);
|
||||
if (hours > 24) {
|
||||
warning.put("overdueDuration", (hours / 24) + "天" + (hours % 24) + "小时");
|
||||
} else {
|
||||
warning.put("overdueDuration", hours + "小时");
|
||||
}
|
||||
warning.put("orderTime", sdf.format((java.sql.Timestamp) orderTimeObj));
|
||||
} else {
|
||||
warning.put("overdueDuration", "未知");
|
||||
warning.put("orderTime", "");
|
||||
}
|
||||
warnings.add(warning);
|
||||
}
|
||||
result.put("records", warnings);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remindOrder(Map<String, Object> params) {
|
||||
String orderNo = params.get("orderNo") != null ? params.get("orderNo").toString() : null;
|
||||
if (orderNo == null || orderNo.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LambdaQueryWrapper<OrderExecuteRecord> w = new LambdaQueryWrapper<>();
|
||||
w.eq(OrderExecuteRecord::getOrderNo, orderNo);
|
||||
OrderExecuteRecord record = recordService.getOne(w);
|
||||
if (record != null) {
|
||||
record.setUpdateBy("system");
|
||||
record.setUpdateTime(new Date());
|
||||
recordService.updateById(record);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,17 @@ public class OrderClosedLoopController {
|
||||
|
||||
@Operation(summary = "统计")
|
||||
@GetMapping("/statistics")
|
||||
public AjaxResult statistics() {
|
||||
return AjaxResult.success(appService.getStatistics());
|
||||
public AjaxResult statistics(@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) String groupBy,
|
||||
@RequestParam(required = false) Integer pageNum,
|
||||
@RequestParam(required = false) Integer pageSize) {
|
||||
return AjaxResult.success(appService.getStatistics(type, groupBy, pageNum, pageSize));
|
||||
}
|
||||
|
||||
@Operation(summary = "催办提醒")
|
||||
@PostMapping("/remind")
|
||||
public AjaxResult remind(@RequestBody Map<String, Object> params) {
|
||||
appService.remindOrder(params);
|
||||
return AjaxResult.success("催办提醒已发送");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
@@ -227,6 +228,7 @@ public class InHospitalReturnMedicineAppServiceImpl implements IInHospitalReturn
|
||||
* @return 处理结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> medicineReturn(List<ReturnMedicineDto> medicineReturnList) {
|
||||
if (medicineReturnList == null || medicineReturnList.isEmpty()) {
|
||||
return R.ok();
|
||||
@@ -249,6 +251,10 @@ public class InHospitalReturnMedicineAppServiceImpl implements IInHospitalReturn
|
||||
// 进销存参数
|
||||
List<SupplyItemDetailDto> supplyItemDetailList = new ArrayList<>();
|
||||
|
||||
// 记录未发药(无库存变动)的发药单ID
|
||||
Set<Long> noInventoryUpdateMedIds = new HashSet<>();
|
||||
Set<Long> noInventoryUpdateDevIds = new HashSet<>();
|
||||
|
||||
// 处理退药
|
||||
// 获取药品退药id列表
|
||||
List<Long> medReturnIdList = new ArrayList<>();
|
||||
@@ -278,6 +284,14 @@ public class InHospitalReturnMedicineAppServiceImpl implements IInHospitalReturn
|
||||
throw new ServiceException("药品已退药,请勿重复退药");
|
||||
}
|
||||
|
||||
// 在更新退药单状态前,统计出实际没有发药(已发药数量为0/空)的退药单,避免后续加回库存和报错
|
||||
for (MedicationDispense medicationDispense : refundMedList) {
|
||||
if (medicationDispense.getDispenseQuantity() == null
|
||||
|| medicationDispense.getDispenseQuantity().compareTo(BigDecimal.ZERO) == 0) {
|
||||
noInventoryUpdateMedIds.add(medicationDispense.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 更新退药单
|
||||
for (MedicationDispense medicationDispense : refundMedList) {
|
||||
// 退药状态
|
||||
@@ -291,20 +305,22 @@ public class InHospitalReturnMedicineAppServiceImpl implements IInHospitalReturn
|
||||
// 退药人
|
||||
medicationDispense.setPractitionerId(SecurityUtils.getLoginUser().getPractitionerId());
|
||||
|
||||
// 设置库存变更参数
|
||||
SupplyItemDetailDto supplyItemDetailDto = new SupplyItemDetailDto();
|
||||
for (MedicationRequest medicationRequest : refundMedRequestList) {
|
||||
// 根据退药id查询退药请求id(用于医保关联)
|
||||
if (medicationDispense.getMedReqId().equals(medicationRequest.getId())) {
|
||||
supplyItemDetailDto.setRequestId(medicationRequest.getRefundMedicineId());
|
||||
// 设置库存变更参数(仅针对实际发过药的药品)
|
||||
if (!noInventoryUpdateMedIds.contains(medicationDispense.getId())) {
|
||||
SupplyItemDetailDto supplyItemDetailDto = new SupplyItemDetailDto();
|
||||
for (MedicationRequest medicationRequest : refundMedRequestList) {
|
||||
// 根据退药id查询退药请求id(用于医保关联)
|
||||
if (medicationDispense.getMedReqId().equals(medicationRequest.getId())) {
|
||||
supplyItemDetailDto.setRequestId(medicationRequest.getRefundMedicineId());
|
||||
}
|
||||
}
|
||||
supplyItemDetailDto.setItemTable(CommonConstants.TableName.MED_MEDICATION_DEFINITION)
|
||||
.setItemId(medicationDispense.getMedicationId()).setLotNumber(medicationDispense.getLotNumber());
|
||||
supplyItemDetailList.add(supplyItemDetailDto);
|
||||
}
|
||||
supplyItemDetailDto.setItemTable(CommonConstants.TableName.MED_MEDICATION_DEFINITION)
|
||||
.setItemId(medicationDispense.getMedicationId()).setLotNumber(medicationDispense.getLotNumber());
|
||||
supplyItemDetailList.add(supplyItemDetailDto);
|
||||
|
||||
// 追溯码表相关处理
|
||||
if (medicationDispense.getTraceNo() != null) {
|
||||
// 追溯码表相关处理(仅针对实际发过药的药品)
|
||||
if (!noInventoryUpdateMedIds.contains(medicationDispense.getId()) && medicationDispense.getTraceNo() != null) {
|
||||
// 使用逗号分割追溯码并转换为List
|
||||
String[] traceNoList = medicationDispense.getTraceNo().split(CommonConstants.Common.COMMA);
|
||||
for (String item : traceNoList) {
|
||||
@@ -345,7 +361,7 @@ public class InHospitalReturnMedicineAppServiceImpl implements IInHospitalReturn
|
||||
devReturnIdList =
|
||||
returnDeviceList.stream().map(ReturnMedicineDto::getDispenseId).collect(Collectors.toList());
|
||||
// 获取退耗材请求id列表
|
||||
List<Long> devRequestIdList = returnDeviceList.stream().map(ReturnMedicineDto::getRequestId).toList();
|
||||
List<Long> devRequestIdList = returnDeviceList.stream().map(ReturnMedicineDto::getRequestId).collect(Collectors.toList());
|
||||
if (devReturnIdList.isEmpty()) {
|
||||
throw new ServiceException("请选择要退的耗材");
|
||||
}
|
||||
@@ -362,6 +378,15 @@ public class InHospitalReturnMedicineAppServiceImpl implements IInHospitalReturn
|
||||
.anyMatch(x -> x.equals(DispenseStatus.REFUNDED.getValue()))) {
|
||||
throw new ServiceException("耗材已退,请勿重复操作");
|
||||
}
|
||||
|
||||
// 在更新退耗材单状态前,统计出实际没有发放(已发放数量为0/空)的退耗材单
|
||||
for (DeviceDispense deviceDispense : refundDevList) {
|
||||
if (deviceDispense.getDispenseQuantity() == null
|
||||
|| deviceDispense.getDispenseQuantity().compareTo(BigDecimal.ZERO) == 0) {
|
||||
noInventoryUpdateDevIds.add(deviceDispense.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 更新退耗材单状态
|
||||
for (DeviceDispense deviceDispense : refundDevList) {
|
||||
// 退药时间
|
||||
@@ -370,40 +395,20 @@ public class InHospitalReturnMedicineAppServiceImpl implements IInHospitalReturn
|
||||
deviceDispense.setDispenseQuantity(deviceDispense.getQuantity());
|
||||
// 退药状态
|
||||
deviceDispense.setStatusEnum(DispenseStatus.REFUNDED.getValue());
|
||||
// 设置库存变更参数
|
||||
supplyItemDetailList
|
||||
.add(new SupplyItemDetailDto().setItemTable(CommonConstants.TableName.ADM_DEVICE_DEFINITION)
|
||||
.setItemId(deviceDispense.getDeviceDefId()).setLotNumber(deviceDispense.getLotNumber()));
|
||||
// // 使用逗号分割追溯码并转换为List
|
||||
// String[] traceNoList = deviceDispense.getTraceNo().split(CommonConstants.Common.COMMA);
|
||||
// for (String item : traceNoList) {
|
||||
// traceNoManage = new TraceNoManage();
|
||||
// // 追溯码处理
|
||||
// traceNoManage.setItemTable(CommonConstants.TableName.ADM_DEVICE_DEFINITION)
|
||||
// // 项目id
|
||||
// .setItemId(deviceDispense.getDeviceDefId())
|
||||
// // 仓库类型
|
||||
// .setLocationTypeEnum(LocationForm.PHARMACY.getValue())
|
||||
// // 仓库
|
||||
// .setLocationId(deviceDispense.getLocationId())
|
||||
// // 产品批号
|
||||
// .setLotNumber(deviceDispense.getLotNumber())
|
||||
// // 追溯码
|
||||
// .setTraceNo(item)
|
||||
// // 追溯码状态
|
||||
// .setStatusEnum(TraceNoStatus.IN.getValue())
|
||||
// // 追溯码单位
|
||||
// .setUnitCode(deviceDispense.getUnitCode())
|
||||
// // 操作类型
|
||||
// .setOperationType(SupplyType.RETURN_MEDICATION.getValue());
|
||||
// traceNoManageList.add(traceNoManage);
|
||||
// }
|
||||
// 设置库存变更参数(仅针对实际发放过的耗材)
|
||||
if (!noInventoryUpdateDevIds.contains(deviceDispense.getId())) {
|
||||
supplyItemDetailList
|
||||
.add(new SupplyItemDetailDto().setItemTable(CommonConstants.TableName.ADM_DEVICE_DEFINITION)
|
||||
.setItemId(deviceDispense.getDeviceDefId()).setLotNumber(deviceDispense.getLotNumber()));
|
||||
}
|
||||
}
|
||||
deviceDispenseService.updateBatchById(refundDevList);
|
||||
}
|
||||
|
||||
// 追溯码管理表数据追加
|
||||
traceNoManageService.saveBatch(traceNoManageList);
|
||||
if (!traceNoManageList.isEmpty()) {
|
||||
traceNoManageService.saveBatch(traceNoManageList);
|
||||
}
|
||||
|
||||
// 处理退库存
|
||||
// 获取库存信息
|
||||
@@ -424,6 +429,11 @@ public class InHospitalReturnMedicineAppServiceImpl implements IInHospitalReturn
|
||||
BigDecimal minQuantity = BigDecimal.ZERO;
|
||||
|
||||
for (UnDispenseInventoryDto unDispenseInventoryDto : inventoryList) {
|
||||
// 过滤未实际发药/发耗材的项目,其库存不加回
|
||||
if (noInventoryUpdateMedIds.contains(unDispenseInventoryDto.getDispenseId())
|
||||
|| noInventoryUpdateDevIds.contains(unDispenseInventoryDto.getDispenseId())) {
|
||||
continue;
|
||||
}
|
||||
BigDecimal quantity = unDispenseInventoryDto.getQuantity();
|
||||
if (!unDispenseInventoryDto.getDispenseUnit()
|
||||
.equals(unDispenseInventoryDto.getInventoryUnitCode())) {
|
||||
@@ -433,14 +443,19 @@ public class InHospitalReturnMedicineAppServiceImpl implements IInHospitalReturn
|
||||
}
|
||||
minQuantity = minQuantity.add(quantity);
|
||||
}
|
||||
// 理论上不出bug的情况下以项目id,批号,仓库进行分组处理库存一定唯一所以get(0)
|
||||
// 设置待更新的库存信息
|
||||
inventoryItemList.add(new InventoryItem().setId(inventoryList.get(0).getInventoryId())
|
||||
.setQuantity(inventoryList.get(0).getInventoryQuantity().add(minQuantity)));
|
||||
// 只有当有需要恢复库存的药品/器材时才加回库存
|
||||
if (minQuantity.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 理论上不出bug的情况下以项目id,批号,仓库进行分组处理库存一定唯一所以get(0)
|
||||
// 设置待更新的库存信息
|
||||
inventoryItemList.add(new InventoryItem().setId(inventoryList.get(0).getInventoryId())
|
||||
.setQuantity(inventoryList.get(0).getInventoryQuantity().add(minQuantity)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// 库存更新
|
||||
iInventoryItemService.updateBatchById(inventoryItemList);
|
||||
if (!inventoryItemList.isEmpty()) {
|
||||
iInventoryItemService.updateBatchById(inventoryItemList);
|
||||
}
|
||||
} else {
|
||||
throw new ServiceException("请检查库存信息");
|
||||
}
|
||||
@@ -497,13 +512,26 @@ public class InHospitalReturnMedicineAppServiceImpl implements IInHospitalReturn
|
||||
}
|
||||
}
|
||||
}
|
||||
uploadFailedNoList = this.ybReturnIntegrated(medReturnIdList, null);
|
||||
uploadFailedNoList = receiptApprovalAppService.ybInventoryIntegrated(supplyItemDetailList,
|
||||
YbInvChgType.OTHER_OUT, DateUtils.getNowDate(), true);
|
||||
if (uploadFailedNoList != null) {
|
||||
returnMsg = "3506商品销售退货上传错误,错误项目编码" + uploadFailedNoList;
|
||||
} else {
|
||||
returnMsg = "3506商品销售退货上传成功";
|
||||
|
||||
// 仅对实际发过药(生成了收费记录且计费的)调用医保退货接口
|
||||
List<Long> medReturnIdsForYb = medReturnIdList.stream()
|
||||
.filter(id -> !noInventoryUpdateMedIds.contains(id))
|
||||
.collect(Collectors.toList());
|
||||
if (!medReturnIdsForYb.isEmpty()) {
|
||||
uploadFailedNoList = this.ybReturnIntegrated(medReturnIdsForYb, null);
|
||||
if (uploadFailedNoList != null) {
|
||||
returnMsg = "3506商品销售退货上传错误,错误项目编码" + uploadFailedNoList;
|
||||
} else {
|
||||
returnMsg = "3506商品销售退货上传成功";
|
||||
}
|
||||
}
|
||||
|
||||
if (!supplyItemDetailList.isEmpty()) {
|
||||
uploadFailedNoList = receiptApprovalAppService.ybInventoryIntegrated(supplyItemDetailList,
|
||||
YbInvChgType.OTHER_OUT, DateUtils.getNowDate(), true);
|
||||
if (uploadFailedNoList != null) {
|
||||
returnMsg = (returnMsg != null ? returnMsg + ";" : "") + "医保进销存集成上传错误: " + uploadFailedNoList;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 返回退药成功信息
|
||||
|
||||
@@ -61,7 +61,7 @@ public class RationalDrugController {
|
||||
|
||||
@GetMapping("/trend")
|
||||
@Operation(summary = "审核趋势")
|
||||
public AjaxResult getAuditTrend(@RequestParam String startDate) {
|
||||
public AjaxResult getAuditTrend(@RequestParam(required = false) String startDate) {
|
||||
List<Map<String, Object>> trend = rationalDrugAppService.getAuditTrend(startDate);
|
||||
return AjaxResult.success(trend);
|
||||
}
|
||||
|
||||
@@ -712,6 +712,7 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
tempServiceRequest.setUnitCode(regAdviceSaveDto.getUnitCode()); // 请求单位编码
|
||||
tempServiceRequest.setCategoryEnum(regAdviceSaveDto.getCategoryEnum()); // 请求类型
|
||||
tempServiceRequest.setTherapyEnum(regAdviceSaveDto.getTherapyEnum()); // 治疗类型,临时(需要前端传)
|
||||
tempServiceRequest.setRateCode(regAdviceSaveDto.getRateCode()); // 用药频次
|
||||
// 文字医嘱(type=8)不走定价体系,activityId设置为0L占位
|
||||
if (ItemType.TEXT.getValue().equals(regAdviceSaveDto.getAdviceType())) {
|
||||
tempServiceRequest.setActivityId(0L);
|
||||
|
||||
@@ -192,7 +192,7 @@ public class RequestFormManageController {
|
||||
* @return 申请单
|
||||
*/
|
||||
@RequestMapping(value = "/page")
|
||||
public R<IPage<RequestFormPageDto>> getRequestFormPage(@RequestBody RequestFormDto requestFormDto) {
|
||||
public R<IPage<RequestFormPageDto>> getRequestFormPage(@ModelAttribute RequestFormDto requestFormDto) {
|
||||
return R.ok(iRequestFormManageAppService.getRequestFormPage(requestFormDto));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Fix: prescription_intercept_log missing HisBaseEntity columns
|
||||
ALTER TABLE prescription_intercept_log ADD COLUMN IF NOT EXISTS delete_flag VARCHAR(1) DEFAULT '0';
|
||||
ALTER TABLE prescription_intercept_log ADD COLUMN IF NOT EXISTS create_by VARCHAR(64) DEFAULT '';
|
||||
ALTER TABLE prescription_intercept_log ADD COLUMN IF NOT EXISTS create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
|
||||
ALTER TABLE prescription_intercept_log ADD COLUMN IF NOT EXISTS update_by VARCHAR(64) DEFAULT '';
|
||||
ALTER TABLE prescription_intercept_log ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
|
||||
ALTER TABLE prescription_intercept_log ADD COLUMN IF NOT EXISTS tenant_id BIGINT DEFAULT 1;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Bug #735: 新医嘱签发后"停嘱医生"字段错误生成数据
|
||||
-- 原因:stopper_name 映射到 update_by 字段,签发时 MyBatis-Plus 自动填充导致错误赋值
|
||||
-- 修复:添加专用 stopper_id 字段,仅在停嘱操作时设置
|
||||
|
||||
-- 药品请求表添加停嘱医生ID字段
|
||||
ALTER TABLE med_medication_request ADD COLUMN IF NOT EXISTS stopper_id BIGINT;
|
||||
|
||||
-- 服务请求表添加停嘱医生ID字段
|
||||
ALTER TABLE wor_service_request ADD COLUMN IF NOT EXISTS stopper_id BIGINT;
|
||||
@@ -0,0 +1,10 @@
|
||||
-- 为 sys_audit_log 和 antibiotic_approval 添加 delete_flag 列以匹配 HisBaseEntity 默认映射
|
||||
-- HisBaseEntity.deleteFlag 映射到 delete_flag (MyBatis-Plus camelCase默认)
|
||||
ALTER TABLE sys_audit_log ADD COLUMN IF NOT EXISTS delete_flag CHAR(1) DEFAULT '0';
|
||||
COMMENT ON COLUMN sys_audit_log.delete_flag IS '删除标识(0=正常,1=删除)';
|
||||
UPDATE sys_audit_log SET delete_flag = '0' WHERE delete_flag IS NULL;
|
||||
|
||||
-- antibiotic_approval 表原有 del_flag 列,新增 delete_flag 列供 HisBaseEntity 使用
|
||||
ALTER TABLE antibiotic_approval ADD COLUMN IF NOT EXISTS delete_flag CHAR(1) DEFAULT '0';
|
||||
COMMENT ON COLUMN antibiotic_approval.delete_flag IS '删除标识(0=正常,1=删除)';
|
||||
UPDATE antibiotic_approval SET delete_flag = '0' WHERE delete_flag IS NULL;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- 为 clinical_pathway_execution 添加 HisBaseEntity 所需的基础字段
|
||||
ALTER TABLE clinical_pathway_execution ADD COLUMN IF NOT EXISTS create_by VARCHAR(64);
|
||||
ALTER TABLE clinical_pathway_execution ADD COLUMN IF NOT EXISTS create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
|
||||
ALTER TABLE clinical_pathway_execution ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
|
||||
ALTER TABLE clinical_pathway_execution ADD COLUMN IF NOT EXISTS update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- 为 prescription_intercept_log 添加 delete_flag 列以匹配 HisBaseEntity 默认映射
|
||||
ALTER TABLE prescription_intercept_log ADD COLUMN IF NOT EXISTS delete_flag CHAR(1) DEFAULT '0';
|
||||
COMMENT ON COLUMN prescription_intercept_log.delete_flag IS '删除标识(0=正常,1=删除)';
|
||||
UPDATE prescription_intercept_log SET delete_flag = '0' WHERE delete_flag IS NULL;
|
||||
@@ -52,4 +52,5 @@ apl.payment.M00007=\u672A\u67E5\u8BE2\u5230\u6536\u8D39\u9879\u76EE
|
||||
apl.payment.M00008=\u672A\u67E5\u8BE2\u5230{0}\u8D26\u6237\u4FE1\u606F
|
||||
apl.payment.M00009=\u672A\u67E5\u8BE2\u5230\u6536\u8D39\u9879\u76EE\uFF0C\u4E0D\u9700\u8981\u8F6C\u6362\u8D26\u6237
|
||||
apl.adjustPrice.M00001=\u6267\u884C\u5931\u8D25\uFF0C\u672A\u52A0\u8F7D\u5230\u4EFB\u4F55\u6570\u636E\uFF01
|
||||
apl.adjustPrice.M00002=\u6267\u884C\u5931\u8D25\uFF0C\u6539\u4EF7\u5355\u4E2D\u6709\u6B63\u5728\u5BA1\u6838\u4E2D\u7684\u8D27\u54C1\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u65B0\u63D0\u4EA4\uFF01
|
||||
apl.adjustPrice.M00002=\u6267\u884C\u5931\u8D25\uFF0C\u6539\u4EF7\u5355\u4E2D\u6709\u6B63\u5728\u5BA1\u6838\u4E2D\u7684\u8D27\u54C1\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u65B0\u63D0\u4EA4\uFF01
|
||||
apl.yb.M00001={0} catalog does not exist, please contact administrator
|
||||
|
||||
@@ -52,4 +52,5 @@ apl.payment.M00007=\u672A\u67E5\u8BE2\u5230\u6536\u8D39\u9879\u76EE
|
||||
apl.payment.M00008=\u672A\u67E5\u8BE2\u5230{0}\u8D26\u6237\u4FE1\u606F
|
||||
apl.payment.M00009=\u672A\u67E5\u8BE2\u5230\u6536\u8D39\u9879\u76EE\uFF0C\u4E0D\u9700\u8981\u8F6C\u6362\u8D26\u6237
|
||||
apl.adjustPrice.M00001=\u6267\u884C\u5931\u8D25\uFF0C\u672A\u52A0\u8F7D\u5230\u4EFB\u4F55\u6570\u636E\uFF01
|
||||
apl.adjustPrice.M00002=\u6267\u884C\u5931\u8D25\uFF0C\u6539\u4EF7\u5355\u4E2D\u6709\u6B63\u5728\u5BA1\u6838\u4E2D\u7684\u8D27\u54C1\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u65B0\u63D0\u4EA4\uFF01
|
||||
apl.adjustPrice.M00002=\u6267\u884C\u5931\u8D25\uFF0C\u6539\u4EF7\u5355\u4E2D\u6709\u6B63\u5728\u5BA1\u6838\u4E2D\u7684\u8D27\u54C1\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u65B0\u63D0\u4EA4\uFF01
|
||||
apl.yb.M00001={0}\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
|
||||
|
||||
@@ -217,6 +217,20 @@
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.class_enum = #{classEnum}
|
||||
AND T10.context_enum = #{register}
|
||||
<if test='registerTimeSTime != null'>
|
||||
AND T1.create_time >= CAST(#{registerTimeSTime} AS TIMESTAMP)
|
||||
</if>
|
||||
<if test='registerTimeETime != null'>
|
||||
AND T1.create_time <= CAST(#{registerTimeETime} AS TIMESTAMP)
|
||||
</if>
|
||||
<if test='statusFilter != null'>
|
||||
<if test='statusFilter >= 0'>
|
||||
AND T1.status_enum = #{statusFilter}
|
||||
</if>
|
||||
<if test='statusFilter == -1'>
|
||||
AND T1.status_enum != 6
|
||||
</if>
|
||||
</if>
|
||||
) AS T9
|
||||
${ew.customSqlSegment}
|
||||
ORDER BY T9.register_time DESC
|
||||
|
||||
@@ -512,7 +512,7 @@
|
||||
personal_account.balance_amount,
|
||||
personal_account.id AS account_id,
|
||||
T2.category_code,
|
||||
ao.name AS org_name
|
||||
COALESCE(ao.name, al1."name") AS org_name
|
||||
FROM med_medication_request AS T1
|
||||
LEFT JOIN med_medication_definition AS T2
|
||||
ON T2.id = T1.medication_id
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<collection property="medicineSummaryParamList" ofType="com.healthlink.his.web.inhospitalnursestation.dto.MedicineSummaryParam">
|
||||
<result property="procedureId" column="procedure_id"/>
|
||||
<result property="dispenseId" column="dispense_id"/>
|
||||
<result property="dispenseTime" column="planned_dispense_time"/>
|
||||
<result property="dispenseTime" column="execution_time"/>
|
||||
<result property="dispenseStatus" column="dispense_status"/>
|
||||
</collection>
|
||||
</resultMap>
|
||||
@@ -75,7 +75,7 @@
|
||||
ii.admitting_doctor_name,
|
||||
ii.balance_amount,
|
||||
ii.dispense_id,
|
||||
ii.planned_dispense_time,
|
||||
ii.execution_time,
|
||||
ii.procedure_id,
|
||||
ii.dispense_status
|
||||
FROM (( SELECT T1.encounter_id,
|
||||
@@ -113,7 +113,7 @@
|
||||
pra."name" AS admitting_doctor_name,
|
||||
personal_account.balance_amount,
|
||||
mmd.id AS dispense_id,
|
||||
mmd.planned_dispense_time,
|
||||
cp.occurrence_time AS execution_time,
|
||||
mmd.procedure_id,
|
||||
mmd.status_enum AS dispense_status
|
||||
FROM med_medication_request AS T1
|
||||
@@ -121,6 +121,9 @@
|
||||
ON T1.id = mmd.med_req_id
|
||||
AND mmd.delete_flag = '0'
|
||||
AND mmd.status_enum != #{summarized}
|
||||
LEFT JOIN cli_procedure cp
|
||||
ON cp.id = mmd.procedure_id
|
||||
AND cp.delete_flag = '0'
|
||||
LEFT JOIN med_medication_definition AS T2
|
||||
ON T2.id = T1.medication_id
|
||||
AND T2.delete_flag = '0'
|
||||
@@ -200,7 +203,7 @@
|
||||
AND T1.status_enum = #{completed}
|
||||
AND T1.refund_medicine_id IS NULL
|
||||
AND mmd.procedure_id IS NOT NULL
|
||||
ORDER BY mmd.planned_dispense_time )) AS ii
|
||||
ORDER BY cp.occurrence_time )) AS ii
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
<select id="selectMedicineSummaryFormPage"
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.healthlink.his.web.pharmacymanage.mapper.InHospitalReturnMedicineAppMapper">
|
||||
<select id="selectEncounterInfoListPage" resultType="com.healthlink.his.web.pharmacymanage.dto.EncounterInfoDto">
|
||||
SELECT ii.reception_time,
|
||||
ii.start_time,
|
||||
SELECT MAX(ii.reception_time) AS reception_time,
|
||||
MAX(ii.start_time) AS start_time,
|
||||
ii.encounter_id,
|
||||
ii.encounter_no,
|
||||
ii.refund_enum,
|
||||
MAX(ii.refund_enum) AS refund_enum,
|
||||
ii.patient_name,
|
||||
ii.patient_wb_str,
|
||||
ii.patient_py_str,
|
||||
@@ -15,11 +15,11 @@
|
||||
ii.birth_date,
|
||||
ii.department_name
|
||||
FROM (
|
||||
SELECT ae.reception_time,
|
||||
SELECT mmd.create_time AS reception_time,
|
||||
ae.id AS encounter_id,
|
||||
ae.bus_no AS encounter_no,
|
||||
ae.tenant_id,
|
||||
ae.start_time,
|
||||
mmd.create_time AS start_time,
|
||||
ae.class_enum,
|
||||
mmd.status_enum AS refund_enum,
|
||||
ap."name" AS patient_name,
|
||||
@@ -52,11 +52,11 @@
|
||||
AND mmd.status_enum = #{refundStatus}
|
||||
</if>
|
||||
UNION
|
||||
SELECT ae.reception_time,
|
||||
SELECT wdd.create_time AS reception_time,
|
||||
ae.id AS encounter_id,
|
||||
ae.bus_no AS encounter_no,
|
||||
ae.tenant_id,
|
||||
ae.start_time,
|
||||
wdd.create_time AS start_time,
|
||||
ae.class_enum,
|
||||
wdd.status_enum AS refund_enum,
|
||||
ap."name" AS patient_name,
|
||||
@@ -90,7 +90,16 @@
|
||||
</if>
|
||||
) AS ii
|
||||
${ew.customSqlSegment}
|
||||
ORDER BY ii.reception_time DESC
|
||||
GROUP BY ii.encounter_id,
|
||||
ii.encounter_no,
|
||||
ii.patient_name,
|
||||
ii.patient_wb_str,
|
||||
ii.patient_py_str,
|
||||
ii.id_card,
|
||||
ii.gender_enum,
|
||||
ii.birth_date,
|
||||
ii.department_name
|
||||
ORDER BY reception_time DESC
|
||||
</select>
|
||||
<select id="selectReturnMedicineInfo"
|
||||
resultType="com.healthlink.his.web.pharmacymanage.dto.ReturnMedicineInfoDto">
|
||||
|
||||
@@ -108,8 +108,6 @@
|
||||
<if test="statusEnum == 18">
|
||||
T4.status_enum = #{submitted}
|
||||
</if>
|
||||
AND T4.summary_no IS NOT NULL
|
||||
AND T4.summary_no != ''
|
||||
) AS ii
|
||||
${ew.customSqlSegment}
|
||||
GROUP BY ii.encounter_id,
|
||||
@@ -268,8 +266,6 @@
|
||||
AND T15.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
-- 因发药配药合并,前台只能看到待发药,已发药状态,但是后台配药发药状态都查
|
||||
AND T1.summary_no IS NOT NULL
|
||||
AND T1.summary_no != ''
|
||||
AND
|
||||
<if test="dispenseStatus == null">
|
||||
T1.status_enum IN (#{inProgress},#{completed},#{preparation},#{prepared},#{summarized})
|
||||
|
||||
@@ -222,8 +222,8 @@
|
||||
T1.based_on_id AS based_on_id,
|
||||
T1.medication_id AS advice_definition_id,
|
||||
T1.content_json::jsonb ->> 'remark' AS remark,
|
||||
T1.effective_dose_end AS stop_time,
|
||||
T1.update_by AS stop_user_name
|
||||
CASE WHEN T1.status_enum = 6 THEN T1.effective_dose_end ELSE NULL END AS stop_time,
|
||||
CASE WHEN T1.status_enum = 6 THEN T1.update_by ELSE NULL END AS stop_user_name
|
||||
FROM med_medication_request AS T1
|
||||
LEFT JOIN adm_practitioner AS ap ON ap.id = T1.practitioner_id AND ap.delete_flag = '0'
|
||||
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
||||
@@ -339,8 +339,8 @@
|
||||
T1.based_on_id AS based_on_id,
|
||||
T1.activity_id AS advice_definition_id,
|
||||
T1.remark AS remark,
|
||||
T1.occurrence_end_time AS stop_time,
|
||||
T1.update_by AS stop_user_name
|
||||
CASE WHEN T1.status_enum = 6 THEN T1.occurrence_end_time ELSE NULL END AS stop_time,
|
||||
CASE WHEN T1.status_enum = 6 THEN T1.update_by ELSE NULL END AS stop_user_name
|
||||
FROM wor_service_request AS T1
|
||||
LEFT JOIN adm_practitioner AS ap ON ap.id = T1.requester_id AND ap.delete_flag = '0'
|
||||
LEFT JOIN wor_activity_definition AS T2
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
package com.healthlink.his.antibiotic.domain;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
@Data @TableName("antibiotic_approval") @Accessors(chain = true) @EqualsAndHashCode(callSuper = false)
|
||||
|
||||
@Data
|
||||
@TableName("antibiotic_approval")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class AntibioticApproval extends HisBaseEntity {
|
||||
@TableId(type = IdType.ASSIGN_ID) private Long id;
|
||||
private Long encounterId; private Long patientId;
|
||||
private String drugCode; private String drugName; private String antibioticClass;
|
||||
private Long requesterId; private String requesterName;
|
||||
private Long approverId; private String approverName; private Date approvalTime;
|
||||
private String approvalResult; private String reason; private String status; private String delFlag;
|
||||
}
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String drugCode;
|
||||
private String drugName;
|
||||
private String antibioticClass;
|
||||
private Long requesterId;
|
||||
private String requesterName;
|
||||
private Long approverId;
|
||||
private String approverName;
|
||||
private Date approvalTime;
|
||||
private String approvalResult;
|
||||
private String reason;
|
||||
private String status;
|
||||
}
|
||||
@@ -21,6 +21,7 @@ public class TicketSlotDTO {
|
||||
private String patientName;
|
||||
private String medicalCard;
|
||||
private Long patientId;
|
||||
private Long realPatientId;
|
||||
private String phone;
|
||||
private Integer orderStatus;
|
||||
private Long orderId;
|
||||
|
||||
@@ -1,5 +1,54 @@
|
||||
package com.healthlink.his.orderclosedloop.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.orderclosedloop.domain.OrderExecuteRecord;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface OrderExecuteRecordMapper extends BaseMapper<OrderExecuteRecord> {}
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
public interface OrderExecuteRecordMapper extends BaseMapper<OrderExecuteRecord> {
|
||||
|
||||
@Select("SELECT m.department_name FROM order_main m WHERE m.order_no = #{orderNo} AND m.delete_flag = '0' LIMIT 1")
|
||||
String findDepartmentByOrderNo(@Param("orderNo") String orderNo);
|
||||
|
||||
@Select("SELECT e.order_type AS orderType, " +
|
||||
"COUNT(*) AS totalOrders, " +
|
||||
"COUNT(CASE WHEN e.execute_status = 'completed' THEN 1 END) AS closedCount " +
|
||||
"FROM order_execute_record e " +
|
||||
"WHERE e.delete_flag = '0' AND e.execute_status != 'cancelled' " +
|
||||
"GROUP BY e.order_type")
|
||||
List<Map<String, Object>> selectOverviewByType();
|
||||
|
||||
@Select("SELECT COALESCE(m.department_name, '未知') AS department, " +
|
||||
"COUNT(*) AS totalOrders, " +
|
||||
"COUNT(CASE WHEN e.execute_status = 'completed' THEN 1 END) AS closedCount " +
|
||||
"FROM order_execute_record e " +
|
||||
"LEFT JOIN order_main m ON e.order_no = m.order_no AND m.delete_flag = '0' " +
|
||||
"WHERE e.delete_flag = '0' AND e.execute_status != 'cancelled' " +
|
||||
"GROUP BY m.department_name ORDER BY totalOrders DESC")
|
||||
List<Map<String, Object>> selectGroupByDepartment();
|
||||
|
||||
@Select("SELECT COALESCE(m.doctor_name, '未知') AS doctorName, " +
|
||||
"COUNT(*) AS totalOrders, " +
|
||||
"COUNT(CASE WHEN e.execute_status = 'completed' THEN 1 END) AS closedCount " +
|
||||
"FROM order_execute_record e " +
|
||||
"LEFT JOIN order_main m ON e.order_no = m.order_no AND m.delete_flag = '0' " +
|
||||
"WHERE e.delete_flag = '0' AND e.execute_status != 'cancelled' " +
|
||||
"GROUP BY m.doctor_name ORDER BY totalOrders DESC")
|
||||
List<Map<String, Object>> selectGroupByDoctor();
|
||||
|
||||
@Select("SELECT e.order_no AS orderNo, e.patient_name AS patientName, e.order_type AS orderType, " +
|
||||
"COALESCE(m.department_name, '未知') AS department, " +
|
||||
"COALESCE(m.doctor_name, '未知') AS doctorName, " +
|
||||
"e.current_step AS currentStep, e.create_time AS orderTime " +
|
||||
"FROM order_execute_record e " +
|
||||
"LEFT JOIN order_main m ON e.order_no = m.order_no AND m.delete_flag = '0' " +
|
||||
"WHERE e.delete_flag = '0' " +
|
||||
"AND e.execute_status IN ('pending', 'in_progress', 'overdue', 'executing') " +
|
||||
"ORDER BY e.create_time DESC")
|
||||
List<Map<String, Object>> selectUnclosedWarnings();
|
||||
}
|
||||
@@ -25,4 +25,4 @@ public class SysAuditLog extends HisBaseEntity {
|
||||
private String result;
|
||||
private String errorMsg;
|
||||
private Integer durationMs;
|
||||
}
|
||||
}
|
||||
@@ -1317,6 +1317,9 @@ public class YbDao {
|
||||
public List<Settlement3201DetailDto> reconcileGeneralLedgerDetail(Settlement3201WebParam settlement3201WebParam) {
|
||||
// 获取条件
|
||||
String clrType = settlement3201WebParam.getClrType();// 住院 or 门诊
|
||||
if (StringUtils.isEmpty(clrType)) {
|
||||
throw new ServiceException("请选择医疗类型:住院/门诊");
|
||||
}
|
||||
Integer kindEnum;
|
||||
if (clrType.equals(YbClrType.OUTPATIENT_CLINIC.getValue())) {
|
||||
kindEnum = PaymentKind.OUTPATIENT_CLINIC.getValue();
|
||||
|
||||
@@ -111,6 +111,7 @@
|
||||
o.order_no AS orderNo,
|
||||
COALESCE(CAST(o.gender AS VARCHAR), CAST(pinfo.gender_enum AS VARCHAR)) AS patientGender,
|
||||
pinfo.gender_enum AS genderEnum,
|
||||
pinfo.id AS realPatientId,
|
||||
pinfo.id_card AS idCard,
|
||||
o.appointment_time AS appointmentTime,
|
||||
<include refid="orderStatusNormExpr" /> AS orderStatus,
|
||||
@@ -230,6 +231,7 @@
|
||||
o.order_no AS orderNo,
|
||||
COALESCE(CAST(o.gender AS VARCHAR), CAST(pinfo.gender_enum AS VARCHAR)) AS patientGender,
|
||||
pinfo.gender_enum AS genderEnum,
|
||||
pinfo.id AS realPatientId,
|
||||
pinfo.id_card AS idCard,
|
||||
o.appointment_time AS appointmentTime,
|
||||
<include refid="orderStatusNormExpr" /> AS orderStatus,
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
AND account.contract_no = #{contractNo}
|
||||
</if>
|
||||
GROUP BY payment.ID,
|
||||
account.ID
|
||||
account.ID,
|
||||
account.contract_no
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -1,9 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
export function createRecord(data) { return request({ url: '/healthlink-his/api/v1/anesthesia/record', method: 'post', data }) }
|
||||
export function updateRecord(data) { return request({ url: '/healthlink-his/api/v1/anesthesia/record', method: 'put', data }) }
|
||||
export function getRecordDetail(id) { return request({ url: '/healthlink-his/api/v1/anesthesia/record/' + id, method: 'get' }) }
|
||||
export function getByEncounter(encounterId) { return request({ url: '/healthlink-his/api/v1/anesthesia/record/encounter/' + encounterId, method: 'get' }) }
|
||||
export function getVitalSigns(recordId) { return request({ url: '/healthlink-his/api/v1/anesthesia/vital-sign/' + recordId, method: 'get' }) }
|
||||
export function getMedications(recordId) { return request({ url: '/healthlink-his/api/v1/anesthesia/medication/' + recordId, method: 'get' }) }
|
||||
export function getIoSummary(recordId) { return request({ url: '/healthlink-his/api/v1/anesthesia/io-summary/' + recordId, method: 'get' }) }
|
||||
export function completeRecord(id) { return request({ url: '/healthlink-his/api/v1/anesthesia/complete/' + id, method: 'put' }) }
|
||||
export function createRecord(data) { return request({ url: '/api/v1/anesthesia/record', method: 'post', data }) }
|
||||
export function updateRecord(data) { return request({ url: '/api/v1/anesthesia/record', method: 'put', data }) }
|
||||
export function getRecordDetail(id) { return request({ url: '/api/v1/anesthesia/record/' + id, method: 'get' }) }
|
||||
export function getByEncounter(encounterId) { return request({ url: '/api/v1/anesthesia/record/encounter/' + encounterId, method: 'get' }) }
|
||||
export function getVitalSigns(recordId) { return request({ url: '/api/v1/anesthesia/vital-sign/' + recordId, method: 'get' }) }
|
||||
export function getMedications(recordId) { return request({ url: '/api/v1/anesthesia/medication/' + recordId, method: 'get' }) }
|
||||
export function getIoSummary(recordId) { return request({ url: '/api/v1/anesthesia/io-summary/' + recordId, method: 'get' }) }
|
||||
export function completeRecord(id) { return request({ url: '/api/v1/anesthesia/complete/' + id, method: 'put' }) }
|
||||
|
||||
@@ -2,23 +2,23 @@ import request from '@/utils/request'
|
||||
|
||||
// ==================== 抗菌药物管控 ====================
|
||||
export function getRules(drugCode) {
|
||||
return request({ url: `/healthlink-his/api/v1/antibiotic/rules/${drugCode}`, method: 'get' })
|
||||
return request({ url: `/api/v1/antibiotic/rules/${drugCode}`, method: 'get' })
|
||||
}
|
||||
|
||||
export function checkRestriction(drugCode, doctorLevel) {
|
||||
return request({ url: '/healthlink-his/api/v1/antibiotic/check-restriction', method: 'get', params: { drugCode, doctorLevel } })
|
||||
return request({ url: '/api/v1/antibiotic/check-restriction', method: 'get', params: { drugCode, doctorLevel } })
|
||||
}
|
||||
|
||||
export function requestApproval(data) {
|
||||
return request({ url: '/healthlink-his/api/v1/antibiotic/approval', method: 'post', data })
|
||||
return request({ url: '/api/v1/antibiotic/approval', method: 'post', data })
|
||||
}
|
||||
|
||||
export function approve(id, approverId, approverName, result) {
|
||||
return request({ url: `/healthlink-his/api/v1/antibiotic/approval/${id}`, method: 'put', params: { approverId, approverName, result } })
|
||||
return request({ url: `/api/v1/antibiotic/approval/${id}`, method: 'put', params: { approverId, approverName, result } })
|
||||
}
|
||||
|
||||
export function getStatistics(startDate, endDate) {
|
||||
return request({ url: '/healthlink-his/api/v1/antibiotic/statistics', method: 'get', params: { startDate, endDate } })
|
||||
return request({ url: '/api/v1/antibiotic/statistics', method: 'get', params: { startDate, endDate } })
|
||||
}
|
||||
|
||||
// 新增抗菌药物规则
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import request from '@/utils/request'
|
||||
export function getRules(drugCode) { return request({ url: '/healthlink-his/api/v1/antibiotic/rules/' + drugCode, method: 'get' }) }
|
||||
export function checkRestriction(drugCode, doctorLevel) { return request({ url: '/healthlink-his/api/v1/antibiotic/check-restriction', method: 'get', params: { drugCode, doctorLevel } }) }
|
||||
export function requestApproval(data) { return request({ url: '/healthlink-his/api/v1/antibiotic/approval', method: 'post', data }) }
|
||||
export function approve(id, params) { return request({ url: '/healthlink-his/api/v1/antibiotic/approval/' + id, method: 'put', params }) }
|
||||
export function getStatistics() { return request({ url: '/healthlink-his/api/v1/antibiotic/statistics', method: 'get' }) }
|
||||
export function getRules(drugCode) { return request({ url: '/api/v1/antibiotic/rules/' + drugCode, method: 'get' }) }
|
||||
export function checkRestriction(drugCode, doctorLevel) { return request({ url: '/api/v1/antibiotic/check-restriction', method: 'get', params: { drugCode, doctorLevel } }) }
|
||||
export function requestApproval(data) { return request({ url: '/api/v1/antibiotic/approval', method: 'post', data }) }
|
||||
export function approve(id, params) { return request({ url: '/api/v1/antibiotic/approval/' + id, method: 'put', params }) }
|
||||
export function getStatistics() { return request({ url: '/api/v1/antibiotic/statistics', method: 'get' }) }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import request from '@/utils/request'
|
||||
export function verifySignature(documentType, documentId) { return request({ url: '/healthlink-his/api/v1/ca-signature/verify/' + documentType + '/' + documentId, method: 'get' }) }
|
||||
export function getSignatureHistory(documentType, documentId) { return request({ url: '/healthlink-his/api/v1/ca-signature/history/' + documentType + '/' + documentId, method: 'get' }) }
|
||||
export function revokeSignature(id) { return request({ url: '/healthlink-his/api/v1/ca-signature/revoke/' + id, method: 'put' }) }
|
||||
export function getSignatureStatistics() { return request({ url: '/healthlink-his/api/v1/ca-signature/statistics', method: 'get' }) }
|
||||
export function verifySignature(documentType, documentId) { return request({ url: '/api/v1/ca-signature/verify/' + documentType + '/' + documentId, method: 'get' }) }
|
||||
export function getSignatureHistory(documentType, documentId) { return request({ url: '/api/v1/ca-signature/history/' + documentType + '/' + documentId, method: 'get' }) }
|
||||
export function revokeSignature(id) { return request({ url: '/api/v1/ca-signature/revoke/' + id, method: 'put' }) }
|
||||
export function getSignatureStatistics() { return request({ url: '/api/v1/ca-signature/statistics', method: 'get' }) }
|
||||
|
||||
@@ -3,7 +3,7 @@ import request from '@/utils/request'
|
||||
// 查询会诊申请列表
|
||||
export function listRequest(query) {
|
||||
return request({
|
||||
url: '/consultation/request/list',
|
||||
url: '/consultation/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
@@ -12,7 +12,7 @@ export function listRequest(query) {
|
||||
// 查询会诊申请详细
|
||||
export function getRequest(id) {
|
||||
return request({
|
||||
url: '/consultation/request/' + id,
|
||||
url: '/consultation/detail/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export function getRequest(id) {
|
||||
// 新增会诊申请
|
||||
export function addRequest(data) {
|
||||
return request({
|
||||
url: '/consultation/request',
|
||||
url: '/consultation/save',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
@@ -29,8 +29,8 @@ export function addRequest(data) {
|
||||
// 修改会诊申请
|
||||
export function updateRequest(data) {
|
||||
return request({
|
||||
url: '/consultation/request',
|
||||
method: 'put',
|
||||
url: '/consultation/save',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -38,48 +38,48 @@ export function updateRequest(data) {
|
||||
// 删除会诊申请
|
||||
export function delRequest(id) {
|
||||
return request({
|
||||
url: '/consultation/request/' + id,
|
||||
method: 'delete'
|
||||
url: '/consultation/cancel/' + id,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 提交会诊申请
|
||||
export function submitRequest(id) {
|
||||
return request({
|
||||
url: '/consultation/request/submit/' + id,
|
||||
method: 'put'
|
||||
url: '/consultation/submit/' + id,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 取消提交会诊申请
|
||||
export function cancelSubmitRequest(id) {
|
||||
return request({
|
||||
url: '/consultation/request/cancelSubmit/' + id,
|
||||
method: 'put'
|
||||
url: '/consultation/cancel/' + id,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 结束会诊申请
|
||||
export function endRequest(id) {
|
||||
return request({
|
||||
url: '/consultation/request/end/' + id,
|
||||
method: 'put'
|
||||
url: '/consultation/complete/' + id,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 作废会诊申请
|
||||
export function cancelRequest(id) {
|
||||
return request({
|
||||
url: '/consultation/request/cancel/' + id,
|
||||
method: 'put'
|
||||
url: '/consultation/cancel/' + id,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 确认会诊
|
||||
export function confirmRequest(data) {
|
||||
return request({
|
||||
url: '/consultation/request/confirm',
|
||||
method: 'put',
|
||||
url: '/consultation/confirmation/confirm',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -87,8 +87,8 @@ export function confirmRequest(data) {
|
||||
// 签名会诊
|
||||
export function signRequest(data) {
|
||||
return request({
|
||||
url: '/consultation/request/sign',
|
||||
method: 'put',
|
||||
data: data
|
||||
url: '/consultation/confirmation/sign',
|
||||
method: 'post',
|
||||
params: { consultationId: data.consultationId }
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import request from '@/utils/request'
|
||||
export function getPendingList() { return request({ url: '/healthlink-his/api/v1/critical-value/pending', method: 'get' }) }
|
||||
export function confirmValue(id, params) { return request({ url: '/healthlink-his/api/v1/critical-value/confirm/' + id, method: 'put', params }) }
|
||||
export function closeValue(id) { return request({ url: '/healthlink-his/api/v1/critical-value/close/' + id, method: 'put' }) }
|
||||
export function getStatistics() { return request({ url: '/healthlink-his/api/v1/critical-value/statistics', method: 'get' }) }
|
||||
export function getOverdueList() { return request({ url: '/healthlink-his/api/v1/critical-value/overdue', method: 'get' }) }
|
||||
export function getPendingList() { return request({ url: '/api/v1/critical-value/pending', method: 'get' }) }
|
||||
export function confirmValue(id, params) { return request({ url: '/api/v1/critical-value/confirm/' + id, method: 'put', params }) }
|
||||
export function closeValue(id) { return request({ url: '/api/v1/critical-value/close/' + id, method: 'put' }) }
|
||||
export function getStatistics() { return request({ url: '/api/v1/critical-value/statistics', method: 'get' }) }
|
||||
export function getOverdueList() { return request({ url: '/api/v1/critical-value/overdue', method: 'get' }) }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
export function createRevision(data) { return request({ url: '/healthlink-his/api/v1/emr/revision', method: 'post', data }) }
|
||||
export function getRevisionHistory(emrId) { return request({ url: '/healthlink-his/api/v1/emr/revision/' + emrId, method: 'get' }) }
|
||||
export function executeCompletenessCheck(emrId) { return request({ url: '/healthlink-his/api/v1/emr/completeness-check/' + emrId, method: 'post' }) }
|
||||
export function getCompletenessCheck(emrId) { return request({ url: '/healthlink-his/api/v1/emr/completeness-check/' + emrId, method: 'get' }) }
|
||||
export function getTimelinessByEncounter(encounterId) { return request({ url: '/healthlink-his/api/v1/emr/timeliness/encounter/' + encounterId, method: 'get' }) }
|
||||
export function getOverdueList() { return request({ url: '/healthlink-his/api/v1/emr/timeliness/overdue', method: 'get' }) }
|
||||
export function getTimelinessStatistics(params) { return request({ url: '/healthlink-his/api/v1/emr/timeliness/statistics', method: 'get', params }) }
|
||||
export function checkTimeliness(data) { return request({ url: '/healthlink-his/api/v1/emr/timeliness/check', method: 'post', data }) }
|
||||
export function createRevision(data) { return request({ url: '/api/v1/emr/revision', method: 'post', data }) }
|
||||
export function getRevisionHistory(emrId) { return request({ url: '/api/v1/emr/revision/' + emrId, method: 'get' }) }
|
||||
export function executeCompletenessCheck(emrId) { return request({ url: '/api/v1/emr/completeness-check/' + emrId, method: 'post' }) }
|
||||
export function getCompletenessCheck(emrId) { return request({ url: '/api/v1/emr/completeness-check/' + emrId, method: 'get' }) }
|
||||
export function getTimelinessByEncounter(encounterId) { return request({ url: '/api/v1/emr/timeliness/encounter/' + encounterId, method: 'get' }) }
|
||||
export function getOverdueList() { return request({ url: '/api/v1/emr/timeliness/overdue', method: 'get' }) }
|
||||
export function getTimelinessStatistics(params) { return request({ url: '/api/v1/emr/timeliness/statistics', method: 'get', params }) }
|
||||
export function checkTimeliness(data) { return request({ url: '/api/v1/emr/timeliness/check', method: 'post', data }) }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import request from '@/utils/request'
|
||||
export function getCaseList(params) { return request({ url: '/healthlink-his/api/v1/infection/case', method: 'get', params }) }
|
||||
export function getStatistics() { return request({ url: '/healthlink-his/api/v1/infection/statistics', method: 'get' }) }
|
||||
export function getExposureList() { return request({ url: '/healthlink-his/api/v1/infection/exposure', method: 'get' }) }
|
||||
export function getCaseList(params) { return request({ url: '/api/v1/infection/case', method: 'get', params }) }
|
||||
export function getStatistics() { return request({ url: '/api/v1/infection/statistics', method: 'get' }) }
|
||||
export function getExposureList() { return request({ url: '/api/v1/infection/exposure', method: 'get' }) }
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import request from '@/utils/request'
|
||||
export function generateHomepage(data) { return request({ url: '/healthlink-his/api/v1/mr-homepage/generate', method: 'post', data }) }
|
||||
export function updateHomepage(data) { return request({ url: '/healthlink-his/api/v1/mr-homepage', method: 'put', data }) }
|
||||
export function getHomepageDetail(id) { return request({ url: '/healthlink-his/api/v1/mr-homepage/' + id, method: 'get' }) }
|
||||
export function executeQualityCheck(id) { return request({ url: '/healthlink-his/api/v1/mr-homepage/quality-check/' + id, method: 'post' }) }
|
||||
export function getQualityCheck(homepageId) { return request({ url: '/healthlink-his/api/v1/mr-homepage/quality-check/' + homepageId, method: 'get' }) }
|
||||
export function getStatistics(params) { return request({ url: '/healthlink-his/api/v1/mr-homepage/statistics', method: 'get', params }) }
|
||||
export function submitHomepage(id) { return request({ url: '/healthlink-his/api/v1/mr-homepage/submit/' + id, method: 'put' }) }
|
||||
export function generateHomepage(data) { return request({ url: '/api/v1/mr-homepage/generate', method: 'post', data }) }
|
||||
export function updateHomepage(data) { return request({ url: '/api/v1/mr-homepage', method: 'put', data }) }
|
||||
export function getHomepageDetail(id) { return request({ url: '/api/v1/mr-homepage/' + id, method: 'get' }) }
|
||||
export function executeQualityCheck(id) { return request({ url: '/api/v1/mr-homepage/quality-check/' + id, method: 'post' }) }
|
||||
export function getQualityCheck(homepageId) { return request({ url: '/api/v1/mr-homepage/quality-check/' + homepageId, method: 'get' }) }
|
||||
export function getStatistics(params) { return request({ url: '/api/v1/mr-homepage/statistics', method: 'get', params }) }
|
||||
export function submitHomepage(id) { return request({ url: '/api/v1/mr-homepage/submit/' + id, method: 'put' }) }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import request from '@/utils/request'
|
||||
export function createAssessment(data) { return request({ url: '/healthlink-his/api/v1/nursing/assessment', method: 'post', data }) }
|
||||
export function getAssessmentsByEncounter(encounterId) { return request({ url: '/healthlink-his/api/v1/nursing/assessment/encounter/' + encounterId, method: 'get' }) }
|
||||
export function createCarePlan(data) { return request({ url: '/healthlink-his/api/v1/nursing/care-plan', method: 'post', data }) }
|
||||
export function getCarePlansByEncounter(encounterId) { return request({ url: '/healthlink-his/api/v1/nursing/care-plan/encounter/' + encounterId, method: 'get' }) }
|
||||
export function createHandoff(data) { return request({ url: '/healthlink-his/api/v1/nursing/handoff', method: 'post', data }) }
|
||||
export function getHandoffList(params) { return request({ url: '/healthlink-his/api/v1/nursing/handoff', method: 'get', params }) }
|
||||
export function createAssessment(data) { return request({ url: '/api/v1/nursing/assessment', method: 'post', data }) }
|
||||
export function getAssessmentsByEncounter(encounterId) { return request({ url: '/api/v1/nursing/assessment/encounter/' + encounterId, method: 'get' }) }
|
||||
export function createCarePlan(data) { return request({ url: '/api/v1/nursing/care-plan', method: 'post', data }) }
|
||||
export function getCarePlansByEncounter(encounterId) { return request({ url: '/api/v1/nursing/care-plan/encounter/' + encounterId, method: 'get' }) }
|
||||
export function createHandoff(data) { return request({ url: '/api/v1/nursing/handoff', method: 'post', data }) }
|
||||
export function getHandoffList(params) { return request({ url: '/api/v1/nursing/handoff', method: 'get', params }) }
|
||||
|
||||
@@ -28,3 +28,7 @@ export function cancelOrder(data) {
|
||||
export function getClosedLoopStatistics(params) {
|
||||
return request({ url: '/api/v1/order-closed-loop/statistics', method: 'get', params })
|
||||
}
|
||||
|
||||
export function remindOrder(data) {
|
||||
return request({ url: '/api/v1/order-closed-loop/remind', method: 'post', data })
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import request from '@/utils/request'
|
||||
// 医嘱执行记录列表
|
||||
export function listOrderExecuteRecord(params) {
|
||||
return request({
|
||||
url: '/healthlink-his/api/v1/order-closed-loop/list',
|
||||
url: '/api/v1/order-closed-loop/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
@@ -12,7 +12,7 @@ export function listOrderExecuteRecord(params) {
|
||||
// 医嘱闭环状态查询
|
||||
export function getOrderClosedLoopStatus(orderId) {
|
||||
return request({
|
||||
url: '/healthlink-his/api/v1/order-closed-loop/status/' + orderId,
|
||||
url: '/api/v1/order-closed-loop/status/' + orderId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export function getOrderClosedLoopStatus(orderId) {
|
||||
// 执行医嘱步骤
|
||||
export function executeOrderStep(data) {
|
||||
return request({
|
||||
url: '/healthlink-his/api/v1/order-closed-loop/execute',
|
||||
url: '/api/v1/order-closed-loop/execute',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
@@ -29,7 +29,7 @@ export function executeOrderStep(data) {
|
||||
// 闭环统计
|
||||
export function getClosedLoopStatistics(params) {
|
||||
return request({
|
||||
url: '/healthlink-his/api/v1/order-closed-loop/statistics',
|
||||
url: '/api/v1/order-closed-loop/statistics',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import request from '@/utils/request'
|
||||
export function createPlan(data) { return request({ url: '/healthlink-his/api/v1/review/plan', method: 'post', data }) }
|
||||
export function getRecords(planId) { return request({ url: '/healthlink-his/api/v1/review/records/' + planId, method: 'get' }) }
|
||||
export function getStatistics() { return request({ url: '/healthlink-his/api/v1/review/statistics', method: 'get' }) }
|
||||
export function createPlan(data) { return request({ url: '/api/v1/review/plan', method: 'post', data }) }
|
||||
export function getRecords(planId) { return request({ url: '/api/v1/review/records/' + planId, method: 'get' }) }
|
||||
export function getStatistics() { return request({ url: '/api/v1/review/statistics', method: 'get' }) }
|
||||
|
||||
@@ -10,16 +10,16 @@ export function executeExamOrder(data) { return request({ url: "/tech-station/ex
|
||||
export function executeLabOrder(data) { return request({ url: "/tech-station/execute/lab", method: "post", data }) }
|
||||
|
||||
// 查询退费审批列表
|
||||
export function listRefundApproveOrders(params) { return request({ url: "/tech-station/refund/approve/list", method: "get", params }) }
|
||||
export function listRefundApproveOrders(params) { return request({ url: "/tech-station/refund-approve/list", method: "get", params }) }
|
||||
|
||||
// 审批通过检查退费
|
||||
export function approveExamRefund(data) { return request({ url: "/tech-station/refund/approve/exam", method: "post", data }) }
|
||||
export function approveExamRefund(data) { return request({ url: "/tech-station/refund-approve/approve/exam/" + data.applyNo, method: "put", data }) }
|
||||
|
||||
// 审批驳回检查退费
|
||||
export function rejectExamRefund(data) { return request({ url: "/tech-station/refund/reject/exam", method: "post", data }) }
|
||||
export function rejectExamRefund(data) { return request({ url: "/tech-station/refund-approve/reject/exam/" + data.applyNo, method: "put", data }) }
|
||||
|
||||
// 审批通过检验退费
|
||||
export function approveLabRefund(data) { return request({ url: "/tech-station/refund/approve/lab", method: "post", data }) }
|
||||
export function approveLabRefund(data) { return request({ url: "/tech-station/refund-approve/approve/lab/" + data.applyNo, method: "put", data }) }
|
||||
|
||||
// 审批驳回检验退费
|
||||
export function rejectLabRefund(data) { return request({ url: "/tech-station/refund/reject/lab", method: "post", data }) }
|
||||
export function rejectLabRefund(data) { return request({ url: "/tech-station/refund-approve/reject/lab/" + data.applyNo, method: "put", data }) }
|
||||
|
||||
@@ -3,33 +3,32 @@
|
||||
{
|
||||
"index": 0,
|
||||
"name": 1,
|
||||
"paperType": "自定义",
|
||||
"height": 80,
|
||||
"width": 279,
|
||||
"paperType": "A5",
|
||||
"height": 148,
|
||||
"width": 210,
|
||||
"paperList": {
|
||||
"type": "自定义",
|
||||
"width": 279,
|
||||
"height": 80
|
||||
"type": "A5",
|
||||
"width": 210,
|
||||
"height": 148
|
||||
},
|
||||
"panelPageRule": "none",
|
||||
"paperHeader": 0,
|
||||
"paperFooter": 422.3622047244095,
|
||||
"paperFooter": 419.53,
|
||||
"paperNumberDisabled": true,
|
||||
"paperNumberContinue": true,
|
||||
"panelAngle": 0,
|
||||
"overPrintOptions": {
|
||||
"content": "",
|
||||
"opacity": 0.7,
|
||||
"opacity": 0.01,
|
||||
"type": 1
|
||||
},
|
||||
"watermarkOptions": {
|
||||
"content": "",
|
||||
"fillStyle": "rgba(87, 13, 248, 0.5)",
|
||||
"fontSize": "36px",
|
||||
"rotate": 25,
|
||||
"width": 413,
|
||||
"height": 310,
|
||||
"timestamp": true,
|
||||
"fontSize": "10px",
|
||||
"rotate": 0,
|
||||
"width": 100,
|
||||
"height": 100,
|
||||
"timestamp": false,
|
||||
"format": "YYYY-MM-DD HH:mm"
|
||||
},
|
||||
"panelLayoutOptions": {
|
||||
@@ -40,238 +39,30 @@
|
||||
"printElements": [
|
||||
{
|
||||
"options": {
|
||||
"left": 0,
|
||||
"top": 15,
|
||||
"height": 16.5,
|
||||
"width": 792,
|
||||
"title": "{{HOSPITAL_NAME}}预交金收据",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"fontWeight": "bold",
|
||||
"letterSpacing": 0.75,
|
||||
"textAlign": "center",
|
||||
"qrCodeLevel": 0,
|
||||
"fontSize": 15,
|
||||
"fontFamily": "Microsoft YaHei"
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 111,
|
||||
"top": 46.5,
|
||||
"height": 14,
|
||||
"width": 151.5,
|
||||
"title": "姓名",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"qrCodeLevel": 0,
|
||||
"field": "patientName",
|
||||
"fontFamily": "Microsoft YaHei"
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 295.5,
|
||||
"top": 48,
|
||||
"height": 14,
|
||||
"width": 148.5,
|
||||
"title": "住院号",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"qrCodeLevel": 0,
|
||||
"field": "encounterNosd",
|
||||
"fontFamily": "Microsoft YaHei"
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 480,
|
||||
"top": 48,
|
||||
"height": 14,
|
||||
"width": 162,
|
||||
"title": "科室",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"qrCodeLevel": 0,
|
||||
"field": "inHospitalOrgName",
|
||||
"fontFamily": "Microsoft YaHei"
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 111,
|
||||
"top": 73.5,
|
||||
"height": 14,
|
||||
"width": 153,
|
||||
"title": "ID号",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"qrCodeLevel": 0,
|
||||
"field": "patientId",
|
||||
"fontFamily": "Microsoft YaHei"
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 295.5,
|
||||
"top": 73.5,
|
||||
"height": 14,
|
||||
"width": 147,
|
||||
"title": "医保类别",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"qrCodeLevel": 0,
|
||||
"field": "contractName",
|
||||
"fontFamily": "Microsoft YaHei"
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 480,
|
||||
"top": 73.5,
|
||||
"height": 14,
|
||||
"width": 163.5,
|
||||
"title": "时间",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"qrCodeLevel": 0,
|
||||
"field": "currentTime",
|
||||
"fontFamily": "Microsoft YaHei"
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 111,
|
||||
"top": 105,
|
||||
"height": 25,
|
||||
"width": 120,
|
||||
"title": "金额",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"textAlign": "center",
|
||||
"textContentVerticalAlign": "middle",
|
||||
"borderLeft": "solid",
|
||||
"borderTop": "solid",
|
||||
"borderRight": "solid",
|
||||
"borderBottom": "solid",
|
||||
"qrCodeLevel": 0
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 231,
|
||||
"top": 105,
|
||||
"height": 25,
|
||||
"width": 393,
|
||||
"title": "金额",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"textAlign": "center",
|
||||
"textContentVerticalAlign": "middle",
|
||||
"borderTop": "solid",
|
||||
"borderRight": "solid",
|
||||
"borderBottom": "solid",
|
||||
"qrCodeLevel": 0,
|
||||
"field": "balanceAmount",
|
||||
"hideTitle": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 111,
|
||||
"top": 129,
|
||||
"height": 30,
|
||||
"width": 120,
|
||||
"title": "人民币(大写)",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"textAlign": "center",
|
||||
"textContentVerticalAlign": "middle",
|
||||
"borderLeft": "solid",
|
||||
"borderTop": "solid",
|
||||
"borderRight": "solid",
|
||||
"qrCodeLevel": 0
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 231,
|
||||
"top": 129,
|
||||
"height": 30,
|
||||
"width": 393,
|
||||
"title": "金额",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"textAlign": "center",
|
||||
"textContentVerticalAlign": "middle",
|
||||
"borderTop": "solid",
|
||||
"borderRight": "solid",
|
||||
"qrCodeLevel": 0,
|
||||
"field": "amountInWords",
|
||||
"hideTitle": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 111,
|
||||
"top": 159,
|
||||
"height": 30,
|
||||
"width": 513,
|
||||
"left": 505.5,
|
||||
"top": 20,
|
||||
"height": 20,
|
||||
"width": 60,
|
||||
"title": " ",
|
||||
"field": "reprintTag",
|
||||
"hideTitle": true,
|
||||
"customClass": "reprint-tag",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"textAlign": "center",
|
||||
"textContentVerticalAlign": "middle",
|
||||
"fontSize": 9,
|
||||
"color": "#ff0000",
|
||||
"fontWeight": "bold",
|
||||
"borderLeft": "solid",
|
||||
"borderRight": "solid",
|
||||
"borderTop": "solid",
|
||||
"borderBottom": "solid",
|
||||
"borderColor": "#ff0000",
|
||||
"borderWidth": 1.5,
|
||||
"qrCodeLevel": 0,
|
||||
"field": "paymentDetails",
|
||||
"hideTitle": true
|
||||
"fixed": true,
|
||||
"styler": "function(value, options, target, templateData, paperNo) { if (!value || value.trim() === '') { return { display: 'none' }; } return {}; }"
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
@@ -280,16 +71,20 @@
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 111,
|
||||
"top": 198,
|
||||
"height": 14,
|
||||
"width": 120,
|
||||
"title": "签章",
|
||||
"left": 30,
|
||||
"top": 20,
|
||||
"height": 30,
|
||||
"width": 535.5,
|
||||
"title": "{{hospitalName}}",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"qrCodeLevel": 0,
|
||||
"field": "patientNamesfs",
|
||||
"fontFamily": "Microsoft YaHei"
|
||||
"textAlign": "center",
|
||||
"fontWeight": "bold",
|
||||
"fontSize": 19.5,
|
||||
"fontFamily": "SimSun",
|
||||
"letterSpacing": 5,
|
||||
"color": "#000000",
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
@@ -298,16 +93,20 @@
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 297,
|
||||
"top": 198,
|
||||
"height": 14,
|
||||
"width": 132,
|
||||
"title": "交款人",
|
||||
"left": 30,
|
||||
"top": 52,
|
||||
"height": 20,
|
||||
"width": 535.5,
|
||||
"title": "住院预缴金收据(收执联)",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"qrCodeLevel": 0,
|
||||
"field": "patientNameada",
|
||||
"fontFamily": "Microsoft YaHei"
|
||||
"textAlign": "center",
|
||||
"fontSize": 12,
|
||||
"fontFamily": "SimSun",
|
||||
"fontWeight": "normal",
|
||||
"letterSpacing": 2,
|
||||
"color": "#000000",
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
@@ -316,16 +115,143 @@
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 481.5,
|
||||
"top": 198,
|
||||
"height": 14,
|
||||
"width": 124.5,
|
||||
"title": "收款人",
|
||||
"coordinateSync": false,
|
||||
"widthHeightSync": false,
|
||||
"qrCodeLevel": 0,
|
||||
"field": "cashier",
|
||||
"fontFamily": "Microsoft YaHei"
|
||||
"left": 30,
|
||||
"top": 78,
|
||||
"height": 15,
|
||||
"width": 250,
|
||||
"title": "收据号:{{receiptNo}}",
|
||||
"fontSize": 9,
|
||||
"color": "#000000",
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 288.35,
|
||||
"top": 78,
|
||||
"height": 15,
|
||||
"width": 277.15,
|
||||
"title": "收款日期:{{currentTime}}",
|
||||
"textAlign": "right",
|
||||
"fontSize": 9,
|
||||
"color": "#000000",
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 14.17,
|
||||
"top": 96.38,
|
||||
"width": 566.92,
|
||||
"height": 185.0,
|
||||
"title": "<table style='width:100%; border-collapse:collapse; border:1px solid #000000; table-layout:fixed; font-family:SimSun, serif; font-size:11px; color:#000000;'><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:65px; font-weight:bold; color:#000000;'>住院号</td><td style='border:1px solid #000; padding:0 4px; width:130px; white-space:normal; word-break:break-all; color:#000000;'>{{encounterNosd}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:50px; font-weight:bold; color:#000000;'>姓名</td><td style='border:1px solid #000; padding:0 4px; width:110px; white-space:normal; word-break:break-all; color:#000000;'>{{patientName}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:50px; font-weight:bold; color:#000000;'>性别</td><td style='border:1px solid #000; text-align:center; width:50px; white-space:normal; word-break:break-all; color:#000000;'>{{gender}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; width:60px; font-weight:bold; color:#000000;'>年龄</td><td style='border:1px solid #000; text-align:center; width:51.92px; white-space:normal; word-break:break-all; color:#000000;'>{{age}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>病区/科室</td><td style='border:1px solid #000; padding:0 4px; white-space:normal; word-break:break-all; color:#000000;' colspan='3'>{{inHospitalOrgName}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>床号</td><td style='border:1px solid #000; text-align:center; white-space:normal; word-break:break-all; color:#000000;'>{{bedName}}</td><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>医保类型</td><td style='border:1px solid #000; text-align:center; white-space:normal; word-break:break-all; color:#000000;'>{{contractName}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>收费项目</td><td style='border:1px solid #000; padding:0 4px; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>住院预缴款</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>支付方式</td><td style='border:1px solid #000; padding:0 4px; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>{{paymentMethod}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>金额(大写)</td><td style='border:1px solid #000; padding:0 4px; font-weight:normal; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>{{amountInWords}}</td></tr><tr style='height:28px;'><td style='border:1px solid #000; background:#F5F5F5; text-align:center; font-weight:bold; color:#000000;'>金额(小写)</td><td style='border:1px solid #000; padding:0 4px; font-weight:normal; white-space:normal; word-break:break-all; color:#000000;' colspan='7'>{{balanceAmount}}</td></tr></table>",
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "HTML表格",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 25.0,
|
||||
"top": 300.0,
|
||||
"height": 55.0,
|
||||
"width": 55.0,
|
||||
"field": "receiptNo",
|
||||
"hideTitle": true,
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"type": "qrcode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 15.0,
|
||||
"top": 359.0,
|
||||
"height": 11.34,
|
||||
"width": 80.0,
|
||||
"title": "扫码查验电子票据",
|
||||
"textAlign": "center",
|
||||
"fontSize": 7,
|
||||
"color": "#000000",
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 113.39,
|
||||
"top": 300.0,
|
||||
"height": 65.0,
|
||||
"width": 467.7,
|
||||
"title": "说明/备注:\n1. 本收据为预收款凭证,非最终医疗自费/统筹消费发票。\n2. 患者出院结算时,须凭此收据联原件退回换取正式的住院发票。\n3. 请妥善保管此收据。如若遗失,请及时前往收费处办理挂失及证明审核。",
|
||||
"fontSize": 8,
|
||||
"lineHeight": 14,
|
||||
"color": "#000000",
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 14.17,
|
||||
"top": 376.0,
|
||||
"height": 14.17,
|
||||
"width": 566.92,
|
||||
"title": "根据《中华人民共和国电子签名法》规定,本电子票据由医院开具并经国家电子认证中心认证,具有法律效力。请妥善保管。",
|
||||
"textAlign": "center",
|
||||
"fontSize": 7,
|
||||
"color": "#000000",
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 14.17,
|
||||
"top": 396.0,
|
||||
"height": 17.01,
|
||||
"width": 320.0,
|
||||
"title": "收款单位:{{hospitalName}}财务结算专用章(电子印章)",
|
||||
"fontSize": 9,
|
||||
"color": "#000000",
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"options": {
|
||||
"left": 330.0,
|
||||
"top": 396.0,
|
||||
"height": 17.01,
|
||||
"width": 251.1,
|
||||
"title": "收款员:{{cashier}}",
|
||||
"textAlign": "right",
|
||||
"fontSize": 9,
|
||||
"color": "#000000",
|
||||
"fixed": true
|
||||
},
|
||||
"printElementType": {
|
||||
"title": "文本",
|
||||
@@ -335,4 +261,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -172,13 +172,13 @@ function getPatchedVxeTable() {
|
||||
// 修补 cell-click:在 dispatchEvent 前注入参数归一化
|
||||
code = code.replace(
|
||||
"dispatchEvent('cell-click', params, evnt);",
|
||||
`params.row = params; params.column = params.column; dispatchEvent('cell-click', params, evnt);`
|
||||
`var _orgRow = params.row; Object.assign(params, _orgRow); params.row = _orgRow; dispatchEvent('cell-click', params, evnt);`
|
||||
)
|
||||
|
||||
// 修补 current-change:在 dispatchEvent 前注入参数归一化
|
||||
code = code.replace(
|
||||
"dispatchEvent('current-change', Object.assign({ oldValue, newValue }, params), evnt);",
|
||||
`var _ccp = Object.assign({ oldValue, newValue }, params); _ccp.newValue = _ccp; _ccp.oldValue = oldValue; dispatchEvent('current-change', _ccp, evnt);`
|
||||
`var _ccp = Object.assign({ oldValue, newValue }, params); Object.assign(_ccp, newValue); _ccp.newValue = newValue; _ccp.oldValue = oldValue; dispatchEvent('current-change', _ccp, evnt);`
|
||||
)
|
||||
|
||||
cachedVxeTable = code
|
||||
|
||||
@@ -301,9 +301,19 @@ export function executePrint(data, template, printerName, options = {}, business
|
||||
|
||||
let processedTemplate;
|
||||
try {
|
||||
processedTemplate = JSON.parse(
|
||||
JSON.stringify(template).replace(/{{HOSPITAL_NAME}}/g, userStore.hospitalName)
|
||||
);
|
||||
let templateStr = JSON.stringify(template);
|
||||
// 统一处理医院名称占位符(支持大小写)
|
||||
const hospitalName = userStore.hospitalName || data.hospitalName || "中联医院";
|
||||
templateStr = templateStr.replace(/\{\{HOSPITAL_NAME\}\}/gi, hospitalName);
|
||||
|
||||
if (data && typeof data === 'object') {
|
||||
Object.keys(data).forEach(key => {
|
||||
// 使用更安全的替换方式
|
||||
const val = data[key] ?? '';
|
||||
templateStr = templateStr.split(`{{${key}}}`).join(val);
|
||||
});
|
||||
}
|
||||
processedTemplate = JSON.parse(templateStr);
|
||||
console.log('[4] 模板处理成功');
|
||||
} catch (parseError) {
|
||||
console.error('[4] 模板处理失败:', parseError);
|
||||
@@ -332,8 +342,8 @@ export function executePrint(data, template, printerName, options = {}, business
|
||||
|
||||
const printOptions = {
|
||||
title: '打印标题',
|
||||
height: 210,
|
||||
width: 148,
|
||||
width: 210,
|
||||
height: 297,
|
||||
...options,
|
||||
};
|
||||
console.log('[7] 打印选项:', printOptions);
|
||||
@@ -385,7 +395,13 @@ export function executePrint(data, template, printerName, options = {}, business
|
||||
hiprintTemplate.print(data, printOptions, {
|
||||
styleHandler: () => {
|
||||
console.log('[10] styleHandler被调用');
|
||||
return '<style>@media print { @page { margin: 0; } }</style>';
|
||||
// 从 printOptions 获取纸张尺寸(mm),用于 @page size
|
||||
const pageWidth = printOptions.width || 210;
|
||||
const pageHeight = printOptions.height || 297;
|
||||
const pageStyle = `<style>@media print { @page { size: ${pageWidth}mm ${pageHeight}mm; margin: 0; } }</style>`;
|
||||
// 合并外部传入的 styleHandler(包含元素定位样式)与内部 @page 样式
|
||||
const externalStyle = (options && typeof options.styleHandler === 'function') ? options.styleHandler() : '';
|
||||
return pageStyle + externalStyle;
|
||||
},
|
||||
callback: (e) => {
|
||||
console.log('[10] 打印回调被调用:', e);
|
||||
|
||||
@@ -35,7 +35,7 @@ const open = (data) => { formData.value = data || {}; visible.value = true }
|
||||
|
||||
const handleSign = async () => {
|
||||
try {
|
||||
await request({ url: '/healthlink-his/api/v1/ca-signature/sign', method: 'post', data: formData.value })
|
||||
await request({ url: '/api/v1/ca-signature/sign', method: 'post', data: formData.value })
|
||||
ElMessage.success('签名成功')
|
||||
visible.value = false
|
||||
} catch (e) { ElMessage.error('签名失败') }
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
ref="patientListRef"
|
||||
height="620"
|
||||
:data="patientList"
|
||||
:row-config="{ keyField: 'encounterId', keyField: 'id' }"
|
||||
:row-config="{ keyField: 'encounterId' }"
|
||||
@cell-click="clickRow"
|
||||
>
|
||||
<vxe-column
|
||||
@@ -447,7 +447,8 @@ function checkSelectable(row, index) {
|
||||
/**
|
||||
* 点击患者列表行 获取处方列表
|
||||
*/
|
||||
function clickRow(row) {
|
||||
function clickRow(params) {
|
||||
const row = params.row || params;
|
||||
patientInfo.value = row;
|
||||
chargeLoading.value = true;
|
||||
encounterId.value = row.encounterId;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div
|
||||
v-loading="readCardLoading"
|
||||
class="app-container"
|
||||
@@ -122,6 +122,7 @@
|
||||
<patientList
|
||||
:searchkey="patientSearchKey"
|
||||
@selsect-patient="selsectPatient"
|
||||
@mousedown.prevent
|
||||
/>
|
||||
<template #reference>
|
||||
<el-input
|
||||
@@ -2079,10 +2080,20 @@ async function confirmCheckIn() {
|
||||
// 每次开始新的签到流程先清理残留 slotId,避免历史脏值串单
|
||||
currentSlotId.value = null;
|
||||
|
||||
// 防御性校验:确保关键字段存在
|
||||
if (!patient.departmentId) {
|
||||
ElMessage.error('该号源缺少科室信息,无法完成签到,请联系管理员');
|
||||
return;
|
||||
}
|
||||
if (!patient.realPatientId) {
|
||||
ElMessage.error('该号源缺少患者信息,无法完成签到,请联系管理员');
|
||||
return;
|
||||
}
|
||||
|
||||
// 弹出确认提示
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确认为患者【${patient.patientName}】办理签到挂号?\n` +
|
||||
`确认为患者【${patient.patientName || '未知患者'}】办理签到挂号?\n` +
|
||||
`科室:${patient.department || '-'}\n` +
|
||||
`医生:${patient.doctor || '-'}\n` +
|
||||
`费用:¥${patient.fee || '0.00'}`,
|
||||
@@ -2215,7 +2226,7 @@ async function confirmCheckIn() {
|
||||
* 点击患者列表给表单赋值
|
||||
*/
|
||||
function selsectPatient(row) {
|
||||
form.value = { ...form.value, ...row };
|
||||
Object.assign(form.value, row);
|
||||
form.value.patientId = row.id;
|
||||
form.value.searchKey = row.name;
|
||||
form.value.name = row.name;
|
||||
@@ -2225,6 +2236,7 @@ function selsectPatient(row) {
|
||||
form.value.firstEnum_enumText = row.firstEnum_enumText;
|
||||
form.value.age = row.age;
|
||||
form.value.identifierNo = row.identifierNo;
|
||||
showPopover.value = false;
|
||||
}
|
||||
|
||||
// 设置新增参数
|
||||
|
||||
@@ -52,40 +52,43 @@
|
||||
搜索
|
||||
</el-button>
|
||||
</div>
|
||||
<vxe-table
|
||||
:row-config="{ isCurrent: true }" :data="encounterList"
|
||||
border
|
||||
style="width: 100%"
|
||||
height="calc(100vh - 300px)"
|
||||
@cell-click="handleGetReturnDrugList"
|
||||
>
|
||||
<vxe-column
|
||||
field="patientName"
|
||||
align="center"
|
||||
title="姓名"
|
||||
width="130"
|
||||
show-overflow
|
||||
/>
|
||||
<vxe-column
|
||||
field="genderEnum_enumText"
|
||||
align="center"
|
||||
title="性别"
|
||||
show-overflow
|
||||
/>
|
||||
<vxe-column
|
||||
align="center"
|
||||
width="140"
|
||||
title="就诊日期"
|
||||
show-overflow
|
||||
<div style="flex: 1; overflow: hidden; margin-top: 10px;">
|
||||
<vxe-table
|
||||
:row-config="{ isCurrent: true }"
|
||||
:data="encounterList"
|
||||
border
|
||||
style="width: 100%"
|
||||
height="100%"
|
||||
@cell-click="handleGetReturnDrugList"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{
|
||||
scope.row.receptionTime ? formatDateStr(scope.row.receptionTime, 'YYYY-MM-DD') : '-'
|
||||
}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<!-- <vxe-column title="状态" align="center" field="refundEnum_enumText" /> -->
|
||||
</vxe-table>
|
||||
<vxe-column
|
||||
field="patientName"
|
||||
align="center"
|
||||
title="姓名"
|
||||
width="130"
|
||||
show-overflow
|
||||
/>
|
||||
<vxe-column
|
||||
field="genderEnum_enumText"
|
||||
align="center"
|
||||
title="性别"
|
||||
show-overflow
|
||||
/>
|
||||
<vxe-column
|
||||
align="center"
|
||||
width="140"
|
||||
title="就诊日期"
|
||||
show-overflow
|
||||
>
|
||||
<template #default="scope">
|
||||
{{
|
||||
scope.row.receptionTime ? formatDateStr(scope.row.receptionTime, 'YYYY-MM-DD') : '-'
|
||||
}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<!-- <vxe-column title="状态" align="center" field="refundEnum_enumText" /> -->
|
||||
</vxe-table>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 右侧退药列表 -->
|
||||
@@ -100,130 +103,132 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!selectedMedicines.length"
|
||||
style="margin-bottom: 10px"
|
||||
@click="handleReturnDrug(undefined)"
|
||||
>
|
||||
确认退药
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
style="margin-bottom: 10px"
|
||||
@click="handleScan()"
|
||||
>
|
||||
扫码
|
||||
</el-button>
|
||||
<vxe-table
|
||||
ref="returnDrugRef"
|
||||
:data="returDrugList"
|
||||
style="width: 100%"
|
||||
height="calc(100vh - 300px)"
|
||||
border
|
||||
:span-method="handelSpanMethod"
|
||||
class="no-hover-table"
|
||||
@select="handleSelection"
|
||||
@checkbox-change="handelSelectRows"
|
||||
>
|
||||
<vxe-column
|
||||
type="checkbox"
|
||||
width="55"
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="itemName"
|
||||
title="药品名称"
|
||||
show-overflow
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="totalPrice"
|
||||
title="总价"
|
||||
width="100"
|
||||
align="right"
|
||||
header-align="center"
|
||||
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!selectedMedicines.length"
|
||||
@click="handleReturnDrug(undefined)"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.totalPrice ? scope.row.totalPrice.toFixed(2) + ' 元' : '-' }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="lotNumber"
|
||||
title="批号"
|
||||
width="180"
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="traceNo"
|
||||
title="追溯码"
|
||||
width="180"
|
||||
align="center"
|
||||
确认退药
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleScan()"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.traceNo"
|
||||
placeholder="请输入追溯码"
|
||||
/>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="reqStatus_enumText"
|
||||
title="退药状态"
|
||||
width="100"
|
||||
align="center"
|
||||
扫码
|
||||
</el-button>
|
||||
</div>
|
||||
<div style="flex: 1; overflow: hidden; margin-bottom: 10px;">
|
||||
<vxe-table
|
||||
ref="returnDrugRef"
|
||||
:data="returDrugList"
|
||||
style="width: 100%"
|
||||
height="100%"
|
||||
border
|
||||
:span-method="handelSpanMethod"
|
||||
class="no-hover-table"
|
||||
@checkbox-all="handelSelectRows"
|
||||
@checkbox-change="handelSelectRows"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.refundEnum_enumText }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="waitingQuantity"
|
||||
title="退药数量"
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span>{{
|
||||
scope.row.quantity
|
||||
? Math.abs(scope.row.quantity) + ' ' + scope.row.unitCode_dictText
|
||||
: '0' + ' ' + scope.row.unitCode_dictText
|
||||
}}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="doctorName"
|
||||
title="开单医生"
|
||||
align="center"
|
||||
width="180"
|
||||
/>
|
||||
<vxe-column
|
||||
title="操作"
|
||||
width="100"
|
||||
align="center"
|
||||
fixed="right"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-popconfirm
|
||||
width="150"
|
||||
hide-after="10"
|
||||
title="操作确认"
|
||||
placement="top-start"
|
||||
@confirm="handleReturnDrug(scope.row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
:disabled="scope.row.refundEnum != 16"
|
||||
>
|
||||
退药
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<vxe-column
|
||||
type="checkbox"
|
||||
width="55"
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="itemName"
|
||||
title="药品名称"
|
||||
show-overflow
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="totalPrice"
|
||||
title="总价"
|
||||
width="100"
|
||||
align="right"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.totalPrice ? scope.row.totalPrice.toFixed(2) + ' 元' : '-' }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="lotNumber"
|
||||
title="批号"
|
||||
width="180"
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="traceNo"
|
||||
title="追溯码"
|
||||
width="180"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.traceNo"
|
||||
placeholder="请输入追溯码"
|
||||
/>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="reqStatus_enumText"
|
||||
title="退药状态"
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.refundEnum_enumText }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="waitingQuantity"
|
||||
title="退药数量"
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span>{{
|
||||
scope.row.quantity
|
||||
? Math.abs(scope.row.quantity) + ' ' + scope.row.unitCode_dictText
|
||||
: '0' + ' ' + scope.row.unitCode_dictText
|
||||
}}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="doctorName"
|
||||
title="开单医生"
|
||||
align="center"
|
||||
width="180"
|
||||
/>
|
||||
<vxe-column
|
||||
title="操作"
|
||||
width="100"
|
||||
align="center"
|
||||
fixed="right"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-popconfirm
|
||||
width="150"
|
||||
hide-after="10"
|
||||
title="操作确认"
|
||||
placement="top-start"
|
||||
@confirm="handleReturnDrug(scope.row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
:disabled="scope.row.refundEnum != 16"
|
||||
>
|
||||
退药
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="footer">
|
||||
@@ -288,7 +293,7 @@ function initOptions() {
|
||||
});
|
||||
}
|
||||
|
||||
function handleGetReturnDrugList(row) {
|
||||
function handleGetReturnDrugList({ row }) {
|
||||
encounterId.value = row.encounterId;
|
||||
getReturnDrugList({
|
||||
encounterId: row.encounterId,
|
||||
@@ -363,7 +368,7 @@ function handleReturnDrug(row) {
|
||||
};
|
||||
});
|
||||
} else {
|
||||
saveList = proxy.$refs.returnDrugRef.getSelectionRows().map((item) => {
|
||||
saveList = proxy.$refs.returnDrugRef.getCheckboxRecords().map((item) => {
|
||||
return {
|
||||
requestId: item.requestId,
|
||||
dispenseId: item.dispenseId,
|
||||
@@ -387,31 +392,22 @@ function handleReturnDrug(row) {
|
||||
});
|
||||
}
|
||||
|
||||
// 选择框改变时的处理
|
||||
function handleSelection(selection, row) {
|
||||
const isSelected = selection.some((item) => item.dispenseId === row.dispenseId);
|
||||
returDrugList.value
|
||||
.filter((item) => {
|
||||
return item.requestId == row.requestId;
|
||||
})
|
||||
.forEach((row) => {
|
||||
proxy.$refs['returnDrugRef'].toggleCheckboxRow(row, isSelected);
|
||||
});
|
||||
function handelSelectRows({ checked, row }) {
|
||||
if (row) {
|
||||
returDrugList.value
|
||||
.filter((item) => item.requestId == row.requestId)
|
||||
.forEach((r) => {
|
||||
proxy.$refs['returnDrugRef'].setCheckboxRow(r, checked);
|
||||
});
|
||||
}
|
||||
nextTick(() => {
|
||||
selectedMedicines.value = proxy.$refs['returnDrugRef'].getSelectionRows();
|
||||
selectedMedicines.value = proxy.$refs['returnDrugRef'].getCheckboxRecords();
|
||||
totalAmount.value = selectedMedicines.value.reduce((accumulator, currentRow) => {
|
||||
return accumulator + (currentRow.totalPrice || 0);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function handelSelectRows(selection) {
|
||||
selectedMedicines.value = selection;
|
||||
totalAmount.value = selectedMedicines.value.reduce((accumulator, currentRow) => {
|
||||
return accumulator + (currentRow.totalPrice || 0);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// 根据 requestId 合并相同行的方法(只合并药品名称和总价列)
|
||||
function handelSpanMethod({ row, column, rowIndex, columnIndex }) {
|
||||
// 定义需要合并的列索引
|
||||
@@ -465,7 +461,7 @@ function handelSpanMethod({ row, column, rowIndex, columnIndex }) {
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
height: 90vh;
|
||||
height: calc(100vh - 150px);
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
background: #f0f2f5;
|
||||
@@ -474,10 +470,24 @@ function handelSpanMethod({ row, column, rowIndex, columnIndex }) {
|
||||
.patient-list {
|
||||
width: 600px;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.refund-list {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:deep(.el-card__body) {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.patient-item {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function reportCriticalValue(data) { return request({ url: '/healthlink-his/api/v1/critical-value/report', method: 'post', data }) }
|
||||
export function confirmCriticalValue(id, params) { return request({ url: '/healthlink-his/api/v1/critical-value/confirm/' + id, method: 'put', params }) }
|
||||
export function processCriticalValue(id, params) { return request({ url: '/healthlink-his/api/v1/critical-value/process/' + id, method: 'put', params }) }
|
||||
export function closeCriticalValue(id) { return request({ url: '/healthlink-his/api/v1/critical-value/close/' + id, method: 'put' }) }
|
||||
export function getPendingList() { return request({ url: '/healthlink-his/api/v1/critical-value/pending', method: 'get' }) }
|
||||
export function getOverdueList() { return request({ url: '/healthlink-his/api/v1/critical-value/overdue', method: 'get' }) }
|
||||
export function getStatistics(params) { return request({ url: '/healthlink-his/api/v1/critical-value/statistics', method: 'get', params }) }
|
||||
export function reportCriticalValue(data) { return request({ url: '/api/v1/critical-value/report', method: 'post', data }) }
|
||||
export function confirmCriticalValue(id, params) { return request({ url: '/api/v1/critical-value/confirm/' + id, method: 'put', params }) }
|
||||
export function processCriticalValue(id, params) { return request({ url: '/api/v1/critical-value/process/' + id, method: 'put', params }) }
|
||||
export function closeCriticalValue(id) { return request({ url: '/api/v1/critical-value/close/' + id, method: 'put' }) }
|
||||
export function getPendingList() { return request({ url: '/api/v1/critical-value/pending', method: 'get' }) }
|
||||
export function getOverdueList() { return request({ url: '/api/v1/critical-value/overdue', method: 'get' }) }
|
||||
export function getStatistics(params) { return request({ url: '/api/v1/critical-value/statistics', method: 'get', params }) }
|
||||
|
||||
@@ -394,7 +394,7 @@
|
||||
>
|
||||
皮试:<el-checkbox
|
||||
v-model="scope.row.skinTestFlag"
|
||||
:true-value="true"
|
||||
:true-value="1"
|
||||
:false-value="0"
|
||||
@change="handleSkinTestChange(scope.row, scope.rowIndex)"
|
||||
>
|
||||
@@ -837,7 +837,7 @@
|
||||
>
|
||||
皮试:<el-checkbox
|
||||
v-model="scope.row.skinTestFlag"
|
||||
:true-value="true"
|
||||
:true-value="1"
|
||||
:false-value="0"
|
||||
@change="handleSkinTestChange(scope.row, scope.rowIndex)"
|
||||
>
|
||||
@@ -1332,7 +1332,7 @@
|
||||
<template v-if="scope.row.isEdit">
|
||||
<el-checkbox
|
||||
v-model="scope.row.skinTestFlag"
|
||||
:true-value="true"
|
||||
:true-value="1"
|
||||
:false-value="0"
|
||||
@change="handleSkinTestChange(scope.row, scope.rowIndex)"
|
||||
>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!--
|
||||
<!--
|
||||
* @Description: 门诊手术申请
|
||||
-->
|
||||
<template>
|
||||
@@ -339,9 +339,11 @@
|
||||
<el-select
|
||||
v-model="form.surgeryName"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="请输入关键词搜索手术"
|
||||
:filter-method="filterSurgery"
|
||||
:remote-method="remoteSearchSurgery"
|
||||
:loading="surgeryLoading"
|
||||
style="width: 100%"
|
||||
@change="handleSurgeryChange"
|
||||
>
|
||||
@@ -414,9 +416,11 @@
|
||||
<el-select
|
||||
v-model="scope.row.surgeryName"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="搜索次要手术"
|
||||
:filter-method="filterSurgery"
|
||||
:remote-method="remoteSearchSurgery"
|
||||
:loading="surgeryLoading"
|
||||
style="width: 100%"
|
||||
@change="(val) => handleSecondarySurgeryChange(val, scope.row)"
|
||||
>
|
||||
@@ -1344,6 +1348,8 @@ function remoteSearchSurgery(query) {
|
||||
function doSearchSurgery(query) {
|
||||
surgeryLoading.value = true
|
||||
getDiagnosisTreatmentList({
|
||||
categoryCode: '24',
|
||||
statusEnum: 2,
|
||||
searchKey: query || '',
|
||||
pageNo: 1,
|
||||
pageSize: 100
|
||||
@@ -1438,10 +1444,13 @@ function filterAnesthesia(query) {
|
||||
|
||||
// 加载手术和麻醉全量数据供本地过滤
|
||||
function loadSurgeryAndAnesthesiaOptions() {
|
||||
// 1. 初始加载前 100 个启用状态的手术项目,供下拉菜单默认展示,避免全量加载 10,102 条数据导致卡顿
|
||||
getDiagnosisTreatmentList({
|
||||
categoryCode: '24',
|
||||
statusEnum: 2,
|
||||
searchKey: '',
|
||||
pageNo: 1,
|
||||
pageSize: 1000
|
||||
pageSize: 100
|
||||
}).then(res => {
|
||||
let data = []
|
||||
if (res.data && res.data.records) {
|
||||
@@ -1455,14 +1464,34 @@ function loadSurgeryAndAnesthesiaOptions() {
|
||||
(item.categoryCode === '24' || item.categoryCode_dictText === '手术') &&
|
||||
(item.statusEnum === 2 || item.statusEnum_enumText === '启用' || !item.statusEnum)
|
||||
)
|
||||
surgeryNameList.value = allSurgeryItems.value
|
||||
}).catch(error => {
|
||||
console.error('加载手术选项失败:', error)
|
||||
})
|
||||
|
||||
// 2. 加载全部启用状态的麻醉项目(麻醉数量较少,可以直接加载全部)
|
||||
getDiagnosisTreatmentList({
|
||||
categoryCode: '25',
|
||||
statusEnum: 2,
|
||||
searchKey: '',
|
||||
pageNo: 1,
|
||||
pageSize: 200
|
||||
}).then(res => {
|
||||
let data = []
|
||||
if (res.data && res.data.records) {
|
||||
data = res.data.records
|
||||
} else if (res.data && Array.isArray(res.data)) {
|
||||
data = res.data
|
||||
} else if (res.records && Array.isArray(res.records)) {
|
||||
data = res.records
|
||||
}
|
||||
allAnesthesiaItems.value = data.filter(item =>
|
||||
(item.categoryCode === '25' || item.categoryCode_dictText === '麻醉') &&
|
||||
(item.statusEnum === 2 || item.statusEnum_enumText === '启用' || !item.statusEnum)
|
||||
)
|
||||
surgeryNameList.value = allSurgeryItems.value
|
||||
anesthesiaNameList.value = allAnesthesiaItems.value
|
||||
}).catch(error => {
|
||||
console.error('加载手术/麻醉选项失败:', error)
|
||||
console.error('加载麻醉选项失败:', error)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1664,6 +1693,8 @@ function reset() {
|
||||
if (surgeryRef.value) {
|
||||
surgeryRef.value.resetFields()
|
||||
}
|
||||
surgeryNameList.value = allSurgeryItems.value
|
||||
anesthesiaNameList.value = allAnesthesiaItems.value
|
||||
}
|
||||
|
||||
// 获取状态标签类型
|
||||
|
||||
@@ -636,13 +636,14 @@ const inputRefs = ref({}); // 存储输入框实例
|
||||
const requiredProps = ref([]); // 存储必填项 prop 顺序
|
||||
const totalAmount = ref(0);
|
||||
const tcmDianosis = ref();
|
||||
const { method_code, unit_code, rate_code, distribution_category_code, dosage_instruction } =
|
||||
const { method_code, unit_code, rate_code, distribution_category_code, dosage_instruction, method_of_decocting_medicine } =
|
||||
proxy.useDict(
|
||||
'method_code',
|
||||
'unit_code',
|
||||
'rate_code',
|
||||
'distribution_category_code',
|
||||
'dosage_instruction'
|
||||
'dosage_instruction',
|
||||
'method_of_decocting_medicine'
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="pending-emr-page">
|
||||
<!-- 页面头部 -->
|
||||
<div class="page-header">
|
||||
@@ -139,8 +139,12 @@ import { parseTime } from '@/utils/index.js'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import { Document, Refresh, Search, Delete } from '@element-plus/icons-vue'
|
||||
import { ElDivider } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { updatePatientInfo } from '@/views/inpatientDoctor/home/store/patient'
|
||||
import { updateLocalPatientInfo } from '@/views/inpatientDoctor/home/store/localPatient'
|
||||
|
||||
// 响应式数据
|
||||
const router = useRouter()
|
||||
const loading = ref(true)
|
||||
const total = ref(0)
|
||||
const emrList = ref([])
|
||||
@@ -209,38 +213,18 @@ const handleRowClick = (row) => {
|
||||
|
||||
// 写病历
|
||||
const handleWriteEmr = (row) => {
|
||||
console.log('写病历:', row)
|
||||
// 弹出写病历弹窗
|
||||
ElMessageBox.confirm('确定要为患者 ' + row.patientName + ' 写病历吗?', '确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'info'
|
||||
}).then(() => {
|
||||
// 这里可以跳转到病历编辑页面或弹出病历编辑弹窗
|
||||
ElMessage.success('正在打开病历编辑页面...')
|
||||
// TODO: 实现写病历的具体逻辑
|
||||
// 例如:router.push({ path: '/doctorstation/emr', query: { encounterId: row.encounterId } })
|
||||
}).catch(() => {
|
||||
// 取消操作
|
||||
})
|
||||
// 存储患者信息并跳转到住院医生工作站
|
||||
updatePatientInfo(row)
|
||||
updateLocalPatientInfo(row)
|
||||
router.push({ path: '/inHospital/inpatientDoctor' })
|
||||
}
|
||||
|
||||
// 查看患者
|
||||
const handleViewPatient = (row) => {
|
||||
console.log('查看患者:', row)
|
||||
// 弹出查看患者弹窗
|
||||
ElMessageBox.confirm('确定要查看患者 ' + row.patientName + ' 的详细信息吗?', '确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'info'
|
||||
}).then(() => {
|
||||
// 这里可以跳转到患者详情页面或弹出患者详情弹窗
|
||||
ElMessage.success('正在打开患者详情页面...')
|
||||
// TODO: 实现查看患者的具体逻辑
|
||||
// 例如:router.push({ path: '/doctorstation/patient-details', query: { encounterId: row.encounterId } })
|
||||
}).catch(() => {
|
||||
// 取消操作
|
||||
})
|
||||
// 存储患者信息并跳转到住院医生工作站
|
||||
updatePatientInfo(row)
|
||||
updateLocalPatientInfo(row)
|
||||
router.push({ path: '/inHospital/inpatientDoctor' })
|
||||
}
|
||||
|
||||
// 获取性别文本
|
||||
|
||||
@@ -52,40 +52,43 @@
|
||||
搜索
|
||||
</el-button>
|
||||
</div>
|
||||
<vxe-table
|
||||
:row-config="{ isCurrent: true }" :data="encounterList"
|
||||
border
|
||||
style="width: 100%"
|
||||
height="calc(100vh - 300px)"
|
||||
@cell-click="handleGetReturnDrugList"
|
||||
>
|
||||
<vxe-column
|
||||
field="patientName"
|
||||
align="center"
|
||||
title="姓名"
|
||||
width="130"
|
||||
show-overflow
|
||||
/>
|
||||
<vxe-column
|
||||
field="genderEnum_enumText"
|
||||
align="center"
|
||||
title="性别"
|
||||
show-overflow
|
||||
/>
|
||||
<vxe-column
|
||||
align="center"
|
||||
width="140"
|
||||
title="就诊日期"
|
||||
show-overflow
|
||||
<div style="flex: 1; overflow: hidden; margin-top: 10px;">
|
||||
<vxe-table
|
||||
:row-config="{ isCurrent: true }"
|
||||
:data="encounterList"
|
||||
border
|
||||
style="width: 100%"
|
||||
height="100%"
|
||||
@cell-click="handleGetReturnDrugList"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{
|
||||
scope.row.receptionTime ? formatDateStr(scope.row.receptionTime, 'YYYY-MM-DD') : '-'
|
||||
}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<!-- <vxe-column title="状态" align="center" field="refundEnum_enumText" /> -->
|
||||
</vxe-table>
|
||||
<vxe-column
|
||||
field="patientName"
|
||||
align="center"
|
||||
title="姓名"
|
||||
width="130"
|
||||
show-overflow
|
||||
/>
|
||||
<vxe-column
|
||||
field="genderEnum_enumText"
|
||||
align="center"
|
||||
title="性别"
|
||||
show-overflow
|
||||
/>
|
||||
<vxe-column
|
||||
align="center"
|
||||
width="140"
|
||||
title="就诊日期"
|
||||
show-overflow
|
||||
>
|
||||
<template #default="scope">
|
||||
{{
|
||||
scope.row.receptionTime ? formatDateStr(scope.row.receptionTime, 'YYYY-MM-DD') : '-'
|
||||
}}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<!-- <vxe-column title="状态" align="center" field="refundEnum_enumText" /> -->
|
||||
</vxe-table>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 右侧退药列表 -->
|
||||
@@ -100,130 +103,132 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!selectedMedicines.length"
|
||||
style="margin-bottom: 10px"
|
||||
@click="handleReturnDrug(undefined)"
|
||||
>
|
||||
确认退药
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
style="margin-bottom: 10px"
|
||||
@click="handleScan()"
|
||||
>
|
||||
扫码
|
||||
</el-button>
|
||||
<vxe-table
|
||||
ref="returnDrugRef"
|
||||
:data="returDrugList"
|
||||
style="width: 100%"
|
||||
height="calc(100vh - 300px)"
|
||||
border
|
||||
:span-method="handelSpanMethod"
|
||||
class="no-hover-table"
|
||||
@select="handleSelection"
|
||||
@checkbox-change="handelSelectRows"
|
||||
>
|
||||
<vxe-column
|
||||
type="checkbox"
|
||||
width="55"
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="itemName"
|
||||
title="药品名称"
|
||||
show-overflow
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="totalPrice"
|
||||
title="总价"
|
||||
width="100"
|
||||
align="right"
|
||||
header-align="center"
|
||||
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!selectedMedicines.length"
|
||||
@click="handleReturnDrug(undefined)"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.totalPrice ? scope.row.totalPrice.toFixed(2) + ' 元' : '-' }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="lotNumber"
|
||||
title="批号"
|
||||
width="180"
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="traceNo"
|
||||
title="追溯码"
|
||||
width="180"
|
||||
align="center"
|
||||
确认退药
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleScan()"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.traceNo"
|
||||
placeholder="请输入追溯码"
|
||||
/>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="reqStatus_enumText"
|
||||
title="退药状态"
|
||||
width="100"
|
||||
align="center"
|
||||
扫码
|
||||
</el-button>
|
||||
</div>
|
||||
<div style="flex: 1; overflow: hidden; margin-bottom: 10px;">
|
||||
<vxe-table
|
||||
ref="returnDrugRef"
|
||||
:data="returDrugList"
|
||||
style="width: 100%"
|
||||
height="100%"
|
||||
border
|
||||
:span-method="handelSpanMethod"
|
||||
class="no-hover-table"
|
||||
@checkbox-all="handelSelectRows"
|
||||
@checkbox-change="handelSelectRows"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.refundEnum_enumText }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="waitingQuantity"
|
||||
title="退药数量"
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span>{{
|
||||
scope.row.quantity
|
||||
? Math.abs(scope.row.quantity) + ' ' + scope.row.unitCode_dictText
|
||||
: '0' + ' ' + scope.row.unitCode_dictText
|
||||
}}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="doctorName"
|
||||
title="开单医生"
|
||||
align="center"
|
||||
width="180"
|
||||
/>
|
||||
<vxe-column
|
||||
title="操作"
|
||||
width="100"
|
||||
align="center"
|
||||
fixed="right"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-popconfirm
|
||||
width="150"
|
||||
hide-after="10"
|
||||
title="操作确认"
|
||||
placement="top-start"
|
||||
@confirm="handleReturnDrug(scope.row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
:disabled="scope.row.refundEnum != 16"
|
||||
>
|
||||
退药
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
<vxe-column
|
||||
type="checkbox"
|
||||
width="55"
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="itemName"
|
||||
title="药品名称"
|
||||
show-overflow
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="totalPrice"
|
||||
title="总价"
|
||||
width="100"
|
||||
align="right"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.totalPrice ? scope.row.totalPrice.toFixed(2) + ' 元' : '-' }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="lotNumber"
|
||||
title="批号"
|
||||
width="180"
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="traceNo"
|
||||
title="追溯码"
|
||||
width="180"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.traceNo"
|
||||
placeholder="请输入追溯码"
|
||||
/>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="reqStatus_enumText"
|
||||
title="退药状态"
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.refundEnum_enumText }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="waitingQuantity"
|
||||
title="退药数量"
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span>{{
|
||||
scope.row.quantity
|
||||
? Math.abs(scope.row.quantity) + ' ' + scope.row.unitCode_dictText
|
||||
: '0' + ' ' + scope.row.unitCode_dictText
|
||||
}}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="doctorName"
|
||||
title="开单医生"
|
||||
align="center"
|
||||
width="180"
|
||||
/>
|
||||
<vxe-column
|
||||
title="操作"
|
||||
width="100"
|
||||
align="center"
|
||||
fixed="right"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-popconfirm
|
||||
width="150"
|
||||
hide-after="10"
|
||||
title="操作确认"
|
||||
placement="top-start"
|
||||
@confirm="handleReturnDrug(scope.row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
:disabled="scope.row.refundEnum != 16"
|
||||
>
|
||||
退药
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="footer">
|
||||
@@ -289,7 +294,7 @@ function initOptions() {
|
||||
});
|
||||
}
|
||||
|
||||
function handleGetReturnDrugList(row) {
|
||||
function handleGetReturnDrugList({ row }) {
|
||||
encounterId.value = row.encounterId;
|
||||
getReturnDrugList({
|
||||
encounterId: row.encounterId,
|
||||
@@ -364,7 +369,7 @@ function handleReturnDrug(row) {
|
||||
};
|
||||
});
|
||||
} else {
|
||||
saveList = proxy.$refs.returnDrugRef.getSelectionRows().map((item) => {
|
||||
saveList = proxy.$refs.returnDrugRef.getCheckboxRecords().map((item) => {
|
||||
return {
|
||||
requestId: item.requestId,
|
||||
dispenseId: item.dispenseId,
|
||||
@@ -388,31 +393,22 @@ function handleReturnDrug(row) {
|
||||
});
|
||||
}
|
||||
|
||||
// 选择框改变时的处理
|
||||
function handleSelection(selection, row) {
|
||||
const isSelected = selection.some((item) => item.dispenseId === row.dispenseId);
|
||||
returDrugList.value
|
||||
.filter((item) => {
|
||||
return item.requestId == row.requestId;
|
||||
})
|
||||
.forEach((row) => {
|
||||
proxy.$refs['returnDrugRef'].toggleCheckboxRow(row, isSelected);
|
||||
});
|
||||
function handelSelectRows({ checked, row }) {
|
||||
if (row) {
|
||||
returDrugList.value
|
||||
.filter((item) => item.requestId == row.requestId)
|
||||
.forEach((r) => {
|
||||
proxy.$refs['returnDrugRef'].setCheckboxRow(r, checked);
|
||||
});
|
||||
}
|
||||
nextTick(() => {
|
||||
selectedMedicines.value = proxy.$refs['returnDrugRef'].getSelectionRows();
|
||||
selectedMedicines.value = proxy.$refs['returnDrugRef'].getCheckboxRecords();
|
||||
totalAmount.value = selectedMedicines.value.reduce((accumulator, currentRow) => {
|
||||
return accumulator + (currentRow.totalPrice || 0);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function handelSelectRows(selection) {
|
||||
selectedMedicines.value = selection;
|
||||
totalAmount.value = selectedMedicines.value.reduce((accumulator, currentRow) => {
|
||||
return accumulator + (currentRow.totalPrice || 0);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// 根据 requestId 合并相同行的方法(只合并药品名称和总价列)
|
||||
function handelSpanMethod({ row, column, rowIndex, columnIndex }) {
|
||||
// 定义需要合并的列索引
|
||||
@@ -466,17 +462,31 @@ function handelSpanMethod({ row, column, rowIndex, columnIndex }) {
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
height: 80vh;
|
||||
height: calc(100vh - 150px);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.patient-list {
|
||||
width: 600px;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.refund-list {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:deep(.el-card__body) {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.patient-item {
|
||||
|
||||
@@ -247,13 +247,13 @@ export function medicineMatch(data) {
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------门诊退药接口----------------------------------------------------------------------
|
||||
// ----------------------------------------------住院退药接口----------------------------------------------------------------------
|
||||
/**
|
||||
* 获取患者列表
|
||||
*/
|
||||
export function getList(queryParams) {
|
||||
return request({
|
||||
url: '/pharmacy-manage/return-medicine/return-patient-page',
|
||||
url: '/pharmacy-manage/inHospital-return-medicine/return-patient-page',
|
||||
method: 'get',
|
||||
params: queryParams,
|
||||
});
|
||||
@@ -264,7 +264,7 @@ export function getList(queryParams) {
|
||||
*/
|
||||
export function getReturnDrugList(params) {
|
||||
return request({
|
||||
url: '/pharmacy-manage/return-medicine/medicine-return-list',
|
||||
url: '/pharmacy-manage/inHospital-return-medicine/medicine-return-list',
|
||||
method: 'get',
|
||||
params: params,
|
||||
});
|
||||
@@ -275,7 +275,7 @@ export function getReturnDrugList(params) {
|
||||
*/
|
||||
export function returnDrug(data) {
|
||||
return request({
|
||||
url: '/pharmacy-manage/return-medicine/medicine-return',
|
||||
url: '/pharmacy-manage/inHospital-return-medicine/medicine-return',
|
||||
method: 'put',
|
||||
data: data,
|
||||
});
|
||||
@@ -286,7 +286,7 @@ export function returnDrug(data) {
|
||||
*/
|
||||
export function init() {
|
||||
return request({
|
||||
url: '/pharmacy-manage/return-medicine/init',
|
||||
url: '/pharmacy-manage/inHospital-return-medicine/init',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
</el-table>
|
||||
<el-pagination style="margin-top:12px;justify-content:flex-end" v-model:current-page="q.pageNo" v-model:page-size="q.pageSize" :total="total" layout="total,prev,pager,next" @current-change="loadData"/>
|
||||
<el-dialog v-model="addVisible" title="激活绿色通道" width="500px">
|
||||
<el-form :model="addForm" label-width="120px">
|
||||
<el-form-item label="患者ID"><el-input-number v-model="addForm.patientId" :min="1"/></el-form-item>
|
||||
<el-form ref="addFormRef" :model="addForm" :rules="addFormRules" label-width="120px">
|
||||
<el-form-item label="患者ID" prop="patientId"><el-input-number v-model="addForm.patientId" :min="1" style="width:100%"/></el-form-item>
|
||||
<el-form-item label="疾病类型"><el-input v-model="addForm.diseaseType" placeholder="如: 急性心肌梗死、脑卒中"/></el-form-item>
|
||||
<el-form-item label="目标时间(min)"><el-input-number v-model="addForm.targetTime" :min="1"/></el-form-item>
|
||||
<el-form-item label="医生"><el-input v-model="addForm.doctor"/></el-form-item>
|
||||
@@ -76,13 +76,15 @@ import {getPage,activate,complete,getStats,del} from './api'
|
||||
const tableData=ref([]);const total=ref(0);const stats=ref({})
|
||||
const addVisible=ref(false);const completeVisible=ref(false)
|
||||
const addForm=ref({patientId:null,diseaseType:'',targetTime:90,doctor:''})
|
||||
const addFormRef=ref(null)
|
||||
const addFormRules={patientId:[{required:true,message:'请选择患者',trigger:'blur'}]}
|
||||
const completeForm=ref({doorToTreatmentTime:60});let currentId=null
|
||||
const q=ref({pageNo:1,pageSize:20,diseaseType:'',isAchieved:null})
|
||||
const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||
const refreshStats=async()=>{const r=await getStats({});stats.value=r.data||{}}
|
||||
const showAdd=()=>{addForm.value={patientId:null,diseaseType:'',targetTime:90,doctor:''};addVisible.value=true}
|
||||
const showComplete=(row)=>{currentId=row.id;completeForm.value={doorToTreatmentTime:60};completeVisible.value=true}
|
||||
const submitAdd=async()=>{await activate(addForm.value);ElMessage.success('绿色通道已激活');addVisible.value=false;loadData();refreshStats()}
|
||||
const submitAdd=async()=>{if(addFormRef.value){try{await addFormRef.value.validate()}catch{return}}await activate(addForm.value);ElMessage.success('绿色通道已激活');addVisible.value=false;loadData();refreshStats()}
|
||||
const doComplete=async()=>{await complete(currentId,completeForm.value);ElMessage.success('评估完成');completeVisible.value=false;loadData();refreshStats()}
|
||||
const delItem=async(id)=>{await del(id);ElMessage.success('已删除');loadData();refreshStats()}
|
||||
onMounted(()=>{loadData();refreshStats()})
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function registerPerson(data) { return request({ url: '/healthlink-his/api/v1/empi/person', method: 'post', data }) }
|
||||
export function mergePersons(primaryId, secondaryIds) { return request({ url: '/healthlink-his/api/v1/empi/merge', method: 'post', params: { primaryId, secondaryIds: secondaryIds.join(',') } }) }
|
||||
export function findByGlobalId(globalId) { return request({ url: '/healthlink-his/api/v1/empi/person/global/' + globalId, method: 'get' }) }
|
||||
export function findByIdCard(idCardNo) { return request({ url: '/healthlink-his/api/v1/empi/person/idcard/' + idCardNo, method: 'get' }) }
|
||||
export function getMappings(globalId) { return request({ url: '/healthlink-his/api/v1/empi/mappings/' + globalId, method: 'get' }) }
|
||||
export function getStatistics() { return request({ url: '/healthlink-his/api/v1/empi/statistics', method: 'get' }) }
|
||||
export function registerPerson(data) { return request({ url: '/api/v1/empi/person', method: 'post', data }) }
|
||||
export function mergePersons(primaryId, secondaryIds) { return request({ url: '/api/v1/empi/merge', method: 'post', params: { primaryId, secondaryIds: secondaryIds.join(',') } }) }
|
||||
export function findByGlobalId(globalId) { return request({ url: '/api/v1/empi/person/global/' + globalId, method: 'get' }) }
|
||||
export function findByIdCard(idCardNo) { return request({ url: '/api/v1/empi/person/idcard/' + idCardNo, method: 'get' }) }
|
||||
export function getMappings(globalId) { return request({ url: '/api/v1/empi/mappings/' + globalId, method: 'get' }) }
|
||||
export function getStatistics() { return request({ url: '/api/v1/empi/statistics', method: 'get' }) }
|
||||
|
||||
export function getPhotos(patientId) { return request({ url: '/empi-enhanced/photo/list', method: 'get', params: { patientId } }) }
|
||||
export function addPhoto(data) { return request({ url: '/empi-enhanced/photo/add', method: 'post', data }) }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="container">
|
||||
<!-- 顶部操作区域 -->
|
||||
<el-card
|
||||
@@ -252,6 +252,7 @@
|
||||
import {nextTick, ref} from 'vue';
|
||||
import {simplePrint, PRINT_TEMPLATE} from '@/utils/printUtils';
|
||||
import {getDepositInfo, getDepositInfoPage} from './components/api';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import PatientList from './components/patientList.vue';
|
||||
import ChargeDialog from './components/chargeDialog.vue';
|
||||
import RefundDialog from './components/refundDialog.vue';
|
||||
@@ -343,18 +344,44 @@ async function handlePrint(row) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const userStore = useUserStore();
|
||||
const amountValue = row.tenderedAmount || 0;
|
||||
const printData = {
|
||||
patientName: patientInfo.value.patientName || "",
|
||||
encounterNosd: patientInfo.value.busNo || "",
|
||||
inHospitalOrgName: patientInfo.value.inHospitalOrgName || "",
|
||||
patientId: patientInfo.value.patientId || "",
|
||||
contractName: patientInfo.value.contractName || "",
|
||||
hospitalName: userStore.hospitalName || "中联医院",
|
||||
receiptNo: row.paymentNo || "",
|
||||
currentTime: row.operateTime || new Date().toLocaleString(),
|
||||
balanceAmount: row.tenderedAmount ? row.tenderedAmount.toFixed(2) : "0.00",
|
||||
amountInWords: "",
|
||||
paymentDetails: "收据号: " + (row.paymentNo || ""),
|
||||
// 补打标记
|
||||
reprintTag: row.printCount && row.printCount > 0 ? `[ 补打第 ${row.printCount} 次 ]` : `[ 补打第 1 次 ]`,
|
||||
// 患者信息
|
||||
encounterNosd: patientInfo.value.busNo || "",
|
||||
patientName: patientInfo.value.patientName || "",
|
||||
gender: patientInfo.value.genderEnum_enumText || "",
|
||||
age: patientInfo.value.age ? patientInfo.value.age + "岁" : "",
|
||||
// 科室信息
|
||||
inHospitalOrgName: patientInfo.value.inHospitalOrgName || "",
|
||||
bedName: patientInfo.value.bedName || "",
|
||||
contractName: patientInfo.value.contractName || "自费",
|
||||
// 费用信息
|
||||
chargeItem: "住院预缴款",
|
||||
paymentMethod: row.paymentEnum_enumText || "现金",
|
||||
amountInWords: "人民币:" + convertToChineseNumber(amountValue),
|
||||
balanceAmount: "¥ " + amountValue.toFixed(2) + " 元",
|
||||
cashier: userStore.nickName || "",
|
||||
};
|
||||
await simplePrint(PRINT_TEMPLATE.ADVANCE_PAYMENT, printData);
|
||||
// 切换为 A5 横向尺寸 (210mm x 148mm)
|
||||
await simplePrint(PRINT_TEMPLATE.ADVANCE_PAYMENT, printData, undefined, {
|
||||
width: 210,
|
||||
height: 148,
|
||||
styleHandler: () => {
|
||||
return `
|
||||
<style>
|
||||
.hiprint-printElement { position: absolute !important; }
|
||||
.hiprint-printElement * { color: #000000 !important; }
|
||||
.hiprint-printElement[style*="color"] * { color: inherit !important; }
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
});
|
||||
proxy.$modal.msgSuccess("打印成功");
|
||||
} catch (error) {
|
||||
console.error("预交金打印失败:", error);
|
||||
@@ -362,6 +389,60 @@ async function handlePrint(row) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数字金额转换为人民币大写
|
||||
*/
|
||||
function convertToChineseNumber(amount) {
|
||||
const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
|
||||
const units = ['', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿'];
|
||||
const decimalUnits = ['角', '分'];
|
||||
|
||||
const absAmount = Math.abs(amount);
|
||||
let [integer, decimal] = absAmount.toFixed(2).split('.');
|
||||
|
||||
let result = amount < 0 ? '负' : '';
|
||||
|
||||
// 处理整数部分
|
||||
if (parseInt(integer) === 0) {
|
||||
result += '零元';
|
||||
} else {
|
||||
for (let i = 0; i < integer.length; i++) {
|
||||
const digit = parseInt(integer[i]);
|
||||
const position = integer.length - i - 1;
|
||||
|
||||
if (digit !== 0) {
|
||||
result += digits[digit] + units[position];
|
||||
} else {
|
||||
if (i > 0 && parseInt(integer[i - 1]) !== 0) {
|
||||
result += digits[digit];
|
||||
}
|
||||
if (position % 4 === 0 && position > 0) {
|
||||
result += units[position];
|
||||
}
|
||||
}
|
||||
}
|
||||
result += '元';
|
||||
}
|
||||
|
||||
// 处理小数部分
|
||||
if (parseInt(decimal) === 0) {
|
||||
result += '整';
|
||||
} else {
|
||||
if (parseInt(decimal[0]) > 0) {
|
||||
result += digits[parseInt(decimal[0])] + decimalUnits[0];
|
||||
} else if (parseInt(decimal[1]) > 0) {
|
||||
result += digits[0];
|
||||
}
|
||||
if (parseInt(decimal[1]) > 0) {
|
||||
result += digits[parseInt(decimal[1])] + decimalUnits[1];
|
||||
} else if (parseInt(decimal[0]) > 0) {
|
||||
result += '整';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 选择病人 */
|
||||
function handlePatientSelected(row) {
|
||||
// console.log(row, 'rowwwwwwwwhandlePatientSelected');
|
||||
|
||||
@@ -569,17 +569,21 @@ const printDepositReceipt = async (patientInfo, inHospitalInfo, medicalInsurance
|
||||
patientName: patientInfo.patientName || '', // 姓名
|
||||
patientId: patientInfo.idCard || patientInfo.patientCode || '', // ID号
|
||||
contractName: patientInfo.contractName || '自费', // 医保类别
|
||||
gender: patientInfo.genderEnum_enumText || '', // 性别
|
||||
age: patientInfo.age ? patientInfo.age + '岁' : '', // 年龄
|
||||
|
||||
// 住院信息
|
||||
encounterNo: inHospitalInfo.encounterNo || '', // 住院号
|
||||
encounterNosd: inHospitalInfo.encounterNo || '', // 住院号
|
||||
inHospitalOrgName: inHospitalInfo.inHospitalOrgName || '', // 机构名称
|
||||
bedName: inHospitalInfo.bedName || '', // 床号
|
||||
|
||||
// 费用信息
|
||||
balanceAmount: advance.value || '0.00', // 金额
|
||||
amountInWords: convertToChineseNumber(advance.value || '0.00'), // 人民币大写
|
||||
balanceAmount: '¥ ' + parseFloat(advance.value || 0).toFixed(2) + ' 元', // 金额
|
||||
amountInWords: '人民币:' + convertToChineseNumber(advance.value || '0.00'), // 人民币大写
|
||||
|
||||
// 支付方式详情
|
||||
paymentDetails: paymentDetails,
|
||||
paymentMethod: paymentDetails, // 支付方式绑定
|
||||
|
||||
// 时间信息
|
||||
currentTime: new Date().toLocaleString('zh-CN', {
|
||||
@@ -593,17 +597,27 @@ const printDepositReceipt = async (patientInfo, inHospitalInfo, medicalInsurance
|
||||
|
||||
// 其他信息
|
||||
cashier: userStore?.nickName || '', // 收款人(收费员)
|
||||
medicalInsuranceTitle: medicalInsuranceTitle || '', // 医保标题信息
|
||||
hospitalName: userStore?.hospitalName || '', // 医院名称
|
||||
receiptNo: '', // 挂号处打印可能还没有收据号
|
||||
reprintTag: '', // 首次打印为空
|
||||
};
|
||||
|
||||
console.log(printData, 'dayin 预交金printData');
|
||||
|
||||
// 直接导入并使用指定的预交金打印模板
|
||||
const templateModule = await import('@/components/Print/AdvancePayment.json');
|
||||
let template = templateModule.default || templateModule;
|
||||
|
||||
// 使用printUtils执行打印
|
||||
await printUtils.executePrint(printData, template);
|
||||
// 使用 simplePrint 执行打印,并指定 A5 横向尺寸
|
||||
await printUtils.simplePrint(printUtils.TEMPLATE.ADVANCE_PAYMENT, printData, undefined, {
|
||||
width: 210,
|
||||
height: 148,
|
||||
styleHandler: () => {
|
||||
return `
|
||||
<style>
|
||||
.hiprint-printElement { position: absolute !important; }
|
||||
.hiprint-printElement * { color: #000000 !important; }
|
||||
.hiprint-printElement[style*="color"] * { color: inherit !important; }
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
});
|
||||
console.log('预交金收据打印成功');
|
||||
} catch (error) {
|
||||
console.error('打印失败:', error);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="24">
|
||||
<el-col
|
||||
@@ -187,7 +187,7 @@
|
||||
style="width: 150px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in diag_type"
|
||||
v-for="item in inpatient_diag_category"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
@@ -423,7 +423,7 @@ const emits = defineEmits(['diagnosisSave']);
|
||||
const { proxy } = getCurrentInstance();
|
||||
const userStore = useUserStore();
|
||||
// 获取诊断类型字典(住院诊断类别)
|
||||
const { diag_type } = proxy.useDict('diag_type');
|
||||
const { inpatient_diag_category } = proxy.useDict('inpatient_diag_category');
|
||||
const rules = ref({
|
||||
name: [{ required: true, message: '请选择诊断', trigger: 'change' }],
|
||||
medTypeCode: [{ required: true, message: '请选择诊断类型', trigger: 'change' }],
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in diag_type"
|
||||
v-for="item in inpatient_diag_category"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
@@ -324,7 +324,7 @@ const syndromeSearchkey = ref('')
|
||||
const syndromeList = ref([])
|
||||
|
||||
// 获取诊断类型字典(住院诊断类别)
|
||||
const { diag_type } = proxy.useDict('diag_type')
|
||||
const { inpatient_diag_category } = proxy.useDict('inpatient_diag_category')
|
||||
|
||||
const filteredSyndromeList = computed(() => {
|
||||
if (!syndromeSearchkey.value) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user