81 Commits

Author SHA1 Message Date
sindir
9b35fec931 诊室页面新增卫生机构、操作人列展示 2026-01-13 14:26:51 +08:00
e20e2b637f 挂号补单功能的完善 2026-01-13 13:26:09 +08:00
ebd2e8aa75 迁移:将DB变更记录SQL文件移动到sql目录下 2026-01-13 10:05:32 +08:00
cb268fe26d feat(operating-room): 添加手术室类型和所属科室字段
- 新增手术室类型字段支持急诊、择期、日间、复合手术室四种类型
- 添加所属科室字段实现科室级别资源管理
- 前端列表页面新增类型和所属科室显示列
- 新增类型选择器和科室选择器组件
- 后端实体类和服务类添加对应字段处理逻辑
- 数据库添加room_type_enum字段和相关索引
- 创建手术室类型字典数据和字典项配置
- 生成手术室管理功能说明文档
2026-01-13 10:03:57 +08:00
23bd49d940 挂号单补打功能完善 2026-01-12 18:14:54 +08:00
32adb984e2 实现科室护士管理患者排队叫号队列,实现患者智能分诊、队列调整、叫号控制等功能 2026-01-12 17:36:55 +08:00
py
c1d453600b 测试:科室预约工作时间维护 2026-01-12 15:49:10 +08:00
py
02eab2d932 测试:科室预约工作时间维护1 2026-01-12 15:36:58 +08:00
py
d5c8b7a1ad 测试:科室预约工作时间维护1 2026-01-12 15:32:41 +08:00
wangjian963
4053064a22 Merge remote-tracking branch 'origin/develop' into develop 2026-01-09 17:38:19 +08:00
wangjian963
089e28f913 修复门诊工作站医生开医嘱的表单标题与内容无法对应的问题 2026-01-09 17:36:36 +08:00
cf9ab03b17 feat(operatingroom): 添加手术室类型和所属科室字段支持
- 在手术室管理界面添加类型和所属科室表格列显示
- 添加手术室类型下拉选择功能,支持急诊、择期、日间、复合四种类型
- 添加手术室详情查看页面中的类型字段展示
- 在后端服务中实现手术室类型的字典转换和文本显示
- 添加手术室实体类中的类型和所属机构名称字段
- 更新路由配置注释掉废弃的系统管理相关路径配置
2026-01-09 17:18:07 +08:00
d332650bfa feat(patient): 实现门诊挂号页面跳转到患者档案页面的精确定位功能
- 在门诊挂号页面添加患者ID和姓名查询参数传递到患者档案页面
- 在患者档案页面实现路由参数接收和按数据库ID精确查询功能
- 新增searchType字段支持按姓名和病人ID两种查询方式
- 优化患者档案页面初始化逻辑,分离字典数据加载和列表查询
- 修改后端服务实现,对精确ID查询跳过医生患者过滤条件以确保跳转查询成功
2026-01-09 16:15:36 +08:00
840983ac94 修复了中医诊断保存时主诊断标记不唯一的问题,并优化了诊断保存逻辑 2026-01-09 15:33:06 +08:00
April
86673d7be3 Merge remote-tracking branch 'origin/develop' into develop 2026-01-09 15:10:05 +08:00
April
3753a916f5 修复了门诊医生站重新调入该患者的本次就诊记录,点击中医诊断发现诊断详情是空的问题 2026-01-09 15:08:19 +08:00
chenjinyang
0556f77870 Merge branch 'develop' of https://gitea.gentronhealth.com/wangyizhe/his into develop
# Conflicts:
#	openhis-ui-vue3/src/router/index.js
2026-01-09 14:07:02 +08:00
chenjinyang
b185c156ca 新增了系统操作手册实例,后续可以加入md文件 2026-01-09 14:01:35 +08:00
ljj
a48308dcbf Merge branch 'develop' of https://gitea.gentronhealth.com/Yajentine/his into develop 2026-01-09 13:28:45 +08:00
e37f6a70f9 revert 28629ccd35
revert Merge remote-tracking branch 'origin/develop' into develop
2026-01-09 04:05:51 +00:00
April
28629ccd35 Merge remote-tracking branch 'origin/develop' into develop 2026-01-09 12:02:45 +08:00
April
fbd7f0be78 修复了在新建患者信息时就诊卡号无法录入的问题,并且修改了前端页面添加中医诊断表单宽度过大的问题。 2026-01-09 11:47:39 +08:00
ljj
763f05da84 Merge remote-tracking branch 'origin/develop' into develop 2026-01-09 11:38:35 +08:00
ljj
8c74d45332 76 门诊预约挂号 2026-01-09 11:33:03 +08:00
8d62c0461b 挂号单补打功能的实现 2026-01-09 10:03:21 +08:00
58936c957d fix(doctorstation): 解决医嘱开具和报告查询中的问题
- 修复医嘱开具时诊断验证警告显示逻辑,支持可选的警告提示
- 修复中医医嘱库存检查条件判断逻辑
- 修复中医医嘱表单验证后数据处理逻辑,添加剂量单位字典值设置
- 优化报告查询处理,独立处理检查和检验报告查询,避免相互影响
- 修复LIS和PACS报告地址配置缺失时的处理逻辑,改为警告而非异常抛出
2026-01-08 16:32:45 +08:00
062c4a92b8 chore(router): 添加患者档案管理路由配置
- 添加患者档案管理路由配置代码
- 注释掉租户用户设置路由部分代码
- 新增patientmgr路由项配置
- 配置路由组件和元信息
- 设置路由隐藏属性
- 完善路由路径和名称定义
2026-01-08 14:52:28 +08:00
fb9f85e967 chore(router): 添加患者档案管理路由配置
- 添加患者档案管理路由配置代码
- 注释掉租户用户设置路由部分代码
- 新增patientmgr路由项配置
- 配置路由组件和元信息
- 设置路由隐藏属性
- 完善路由路径和名称定义
2026-01-08 14:52:22 +08:00
38ef377cbd 手术管理->优化字典数据获取逻辑 2026-01-07 22:56:48 +08:00
240d5dc3f7 手术管理->更改部分组件数据来源,改为从字典中获取。 2026-01-07 17:28:56 +08:00
82702f16e0 Merge remote-tracking branch 'origin/develop' into develop 2026-01-07 17:00:19 +08:00
0b4b63dfbe feat(surgery): 增加手术室确认信息和次要手术功能
- 添加手术室确认时间和确认人字段显示
- 实现次要手术的添加、编辑和删除功能
- 增加急诊标志和植入高值耗材开关选项
- 添加手术费用和麻醉费用计算功能
- 实现手术和麻醉项目的远程搜索功能
- 增加第一助手和第二助手选择功能
- 优化医生列表加载逻辑,支持多接口获取
- 添加按钮图标提升界面体验
- 修复encounterId为空时的接口调用问题
2026-01-07 17:00:06 +08:00
b4422a0dca 收费工作站:合并后按钮重复问题,档案按钮跳转无反应问题。 2026-01-07 13:51:30 +08:00
d8627df2dd 检验项目->套餐设置->部分组件、布局问题 2026-01-07 11:01:49 +08:00
09ca077559 refactor(surgery): 优化手术服务中医生信息查询逻辑
- 引入 IPractitionerService 服务替代 SysUserService 查询医生信息
- 修改手术列表查询中主刀医生、麻醉医生、助手和护士的姓名填充逻辑
- 使用 Practitioner 实体的 name 字段替代 SysUser 的 nickName 字段
- 更新 SQL 查询使用 COALESCE 函数合并数据库中存储的姓名和实时查询结果
- 添加多个 LEFT JOIN 查询以支持手术相关医生和科室信息的实时获取
- 优化申请医生和申请科室名称的查询机制,支持数据回退逻辑
2026-01-06 16:40:57 +08:00
py
3091fc7337 新增科室预约工作时间维护页面 2026-01-06 16:31:08 +08:00
b0850257c8 feat(surgery): 完善手术管理功能模块
- 添加手术申请相关API接口,包括根据患者ID查询就诊列表功能
- 在医生工作站界面集成手术申请功能选项卡
- 实现手术管理页面的完整功能,包括手术申请的增删改查
- 添加手术排期、开始、完成等状态流转功能
- 优化手术管理页面表格展示,增加手术类型、等级、计划时间等字段
- 实现手术申请表单的完整编辑和查看模式
- 集成患者信息和就诊记录关联功能
- 添加手术室、医生、护士等资源选择功能
- 更新系统依赖配置,添加core-common模块
- 优化图标资源和manifest配置文件
- 调整患者档案和门诊记录相关状态枚举
2026-01-06 16:23:15 +08:00
fa2884b320 实现科室护士管理患者排队叫号队列,实现患者智能分诊、队列调整、叫号控制等功能 2026-01-06 15:34:16 +08:00
941054734f 实现科室护士管理患者排队叫号队列,实现患者智能分诊、队列调整、叫号控制等功能 2026-01-06 14:48:45 +08:00
8d69dc3c00 患者档案(现为患者列表)->修改和查看按钮调出窗口与新建患者保持一致 2026-01-05 14:22:57 +08:00
2157806ba5 系统管理->基础数据->字典管理->添加字典数据异常 2026-01-05 11:10:14 +08:00
2236cbea36 门诊挂号->下方列表获取挂号记录-SQL报错。 2026-01-04 16:31:02 +08:00
2128e717e7 门诊挂号->医生字段无数据问题。 2026-01-04 15:59:49 +08:00
1311e87e13 叫号显示屏页面开发,诊疗目录新增或修改时添加医保编码唯一性校验。 2026-01-04 14:24:33 +08:00
ddf1553846 检验项目设置-检验类型的实现 2026-01-04 13:50:05 +08:00
5d82800976 refactor(patientmanage): 为门诊记录服务添加日志功能
- 引入 lombok 的 Slf4j 注解用于日志记录
- 为 OutpatientRecordServiceImpl 添加日志支持
- 为后续调试和监控提供日志输出能力
2026-01-03 23:52:45 +08:00
0c35044231 feat(menu): 优化菜单路径唯一性校验并更新前端界面
- 在SysLoginController中添加optionMap数据返回
- 添加JSQLParser依赖支持MyBatis Plus功能
- 实现selectMenuByPathExcludeId方法用于排除当前菜单的路径唯一性校验
- 在SysMenuServiceImpl中添加日志记录并优化路径唯一性判断逻辑
- 在SysMenuMapper.xml中添加LIMIT 1限制并实现排除ID查询
- 在前端路由中注释患者管理相关路由配置
- 在用户store中添加optionMap配置项并优先从optionMap获取医院名称
- 重构检查项目设置页面的操作按钮样式为统一的圆形按钮设计
- 更新检查项目设置页面的导航栏样式和交互体验
- 优化门诊记录页面的搜索条件和表格展示功能
- 添加性别和状态筛选条件并改进数据加载逻辑
2026-01-03 23:47:09 +08:00
61f4020487 新增患者:监护人信息限制 2025-12-31 13:43:24 +08:00
aeb6b95970 Merge branch 'develop' of https://gitea.gentronhealth.com/py/his into develop 2025-12-31 13:37:29 +08:00
b5ce854eb6 fix(login): 修复登录页面invokeYb默认值设置问题
- 修正了localStorage中invokeYb的默认值从true改为false
- 添加了loginForm.value.invokeYb的默认值设置
- 更新了注释内容以反映正确的默认行为
2025-12-31 11:00:17 +08:00
4d4828ea71 feat(login): 添加租户名称获取功能并优化前端布局
- 在登录控制器中注入租户服务并获取租户名称信息
- 添加租户名称到登录响应结果中
- 更新样式变量定义侧边栏宽度和Logo高度
- 重构公告面板组件统一公告通知显示逻辑
- 简化公告类型图标和样式映射关系
- 更新侧边栏为垂直菜单布局并添加折叠功能
- 优化Logo组件显示租户名称和系统标题
- 调整导航栏布局结构和响应式样式
- 重构主应用容器样式和标签页显示逻辑
2025-12-31 10:28:52 +08:00
10e738edd9 feat(notice): 新增公告优先级和未读状态功能,优化公告展示逻辑 2025-12-30 22:49:14 +08:00
88a4e58130 feat(notice): 新增公告优先级和未读状态功能,优化公告展示逻辑 2025-12-30 22:49:03 +08:00
76c324b0df feat(organization): 添加科室管理查询过滤功能
- 修复api.js中params参数拼写错误
- 添加科室名称、类型、分类的查询表单
- 实现搜索和重置功能
- 集成分页组件并修正页码参数映射
- 在后端服务中添加查询条件过滤逻辑
- 支持按科室名称、类型、分类进行条件查询
- 实现动态排序功能并修复分页查询逻辑
2025-12-30 17:01:37 +08:00
a65e8dd2cc feat(organization): 添加科室管理查询过滤功能
- 修复api.js中params参数拼写错误
- 添加科室名称、类型、分类的查询表单
- 实现搜索和重置功能
- 集成分页组件并修正页码参数映射
- 在后端服务中添加查询条件过滤逻辑
- 支持按科室名称、类型、分类进行条件查询
- 实现动态排序功能并修复分页查询逻辑
2025-12-30 17:01:34 +08:00
1ac9b5ae0b feat(organization): 添加科室管理查询过滤功能
- 修复api.js中params参数拼写错误
- 添加科室名称、类型、分类的查询表单
- 实现搜索和重置功能
- 集成分页组件并修正页码参数映射
- 在后端服务中添加查询条件过滤逻辑
- 支持按科室名称、类型、分类进行条件查询
- 实现动态排序功能并修复分页查询逻辑
2025-12-30 16:52:01 +08:00
8f77fe8bc9 Merge remote-tracking branch 'origin/develop' into develop 2025-12-30 16:13:52 +08:00
b567747901 style(flowable): 优化流程相关代码的import顺序和代码结构 2025-12-30 16:12:16 +08:00
ed7004ae85 style(flowable): 优化流程相关代码的import顺序和代码结构 2025-12-30 15:34:43 +08:00
d1670b79a0 style(flowable): 优化流程相关代码的import顺序和代码结构 2025-12-30 15:11:32 +08:00
54cde91aac style(ui): 统一界面样式标准并优化组件布局
- 引入 ui-standard.scss 样式文件以统一界面标准
- 为查询表单添加 query-form 样式类
- 为搜索按钮组添加 search-buttons 样式类
- 为按钮组容器添加 button-group 样式类
- 为字典标签添加 dict-tag 样式类以统一样式
- 为操作按钮添加 action-button 样式类
- 为分页组件添加 pagination-container 容器
- 优化患者管理页面表格列的字典标签显示
- 调整公告页面布局结构并添加容器样式
- 优化表格单元格间距和样式显示
2025-12-30 14:57:20 +08:00
7974bdc51c feat(ui): 添加OpenHIS UI统一风格规范和样式
- 创建ui-standard.scss样式文件,定义全局组件样式规范
- 添加容器、表格、表单、按钮、对话框等统一UI样式
- 实现响应式设计和动画效果
- 编写完整的UI风格规范文档
- 定义颜色、间距、圆角、阴影等设计规范
- 提供组件使用示例和命名规范指导
2025-12-30 14:56:29 +08:00
91cb465962 医生常用语维护->名称字数限制、前端名称唯一性校验等 2025-12-30 14:26:14 +08:00
1b2c248fa2 Merge remote-tracking branch 'origin/develop' into develop 2025-12-30 14:10:01 +08:00
1c16d6ba0f feat(invoice): 完善发票管理权限控制和检验申请功能
- 超级管理员可以编辑操作员字段,普通用户不可编辑
- 修改权限判断逻辑,只有用户名等于 'admin' 的用户才是超级管理员
- 非超级管理员用户只能查询自己的发票数据
- 添加根据员工ID更新操作员名称功能
- 新增行时根据用户权限填充信息
- 严格检查权限,超级管理员可以删除所有记录,普通用户只能删除自己维护的记录
- 在 bargain 组件中验证患者选择
- 添加检验申请单相关API接口
- 在医生工作站中添加检验申请tab页
- 实现检验申请单的增删改查功能
- 添加公告通知已读记录相关功能
- 实现用户未读公告数量统计和标记已读功能
2025-12-30 14:09:37 +08:00
49b8a975a8 feat(invoice): 完善发票管理权限控制和检验申请功能
- 超级管理员可以编辑操作员字段,普通用户不可编辑
- 修改权限判断逻辑,只有用户名等于 'admin' 的用户才是超级管理员
- 非超级管理员用户只能查询自己的发票数据
- 添加根据员工ID更新操作员名称功能
- 新增行时根据用户权限填充信息
- 严格检查权限,超级管理员可以删除所有记录,普通用户只能删除自己维护的记录
- 在 bargain 组件中验证患者选择
- 添加检验申请单相关API接口
- 在医生工作站中添加检验申请tab页
- 实现检验申请单的增删改查功能
- 添加公告通知已读记录相关功能
- 实现用户未读公告数量统计和标记已读功能
2025-12-30 13:52:06 +08:00
d17a502da1 Merge remote-tracking branch 'origin/develop' into develop 2025-12-30 13:43:54 +08:00
430adc2112 feat(system): 添加公告通知已读记录功能
- 新增 SysNoticeRead 实体类用于存储公告/通知已读记录
- 实现 SysNoticeReadMapper 数据访问层接口及 XML 映射文件
- 创建 ISysNoticeReadService 服务接口及实现类
- 添加数据库表 sys_notice_read 存储用户阅读状态
- 添加发布状态字段到公告表支持公告发布控制
- 实现前端 NoticePanel 组件支持未读标记和阅读状态显示
- 提供标记已读、批量标记、未读数量统计等功能
- 优化公告列表按已读状态和时间排序显示
2025-12-30 13:43:28 +08:00
5f5c47f528 新增门诊医生开立检验申请单前端页面样式、模板初步完成 2025-12-30 13:30:06 +08:00
92d74c47ce 增加发票号码维护界面bug修复和新增门诊医生开立检验申请单前端页面样式、模板初步完成 2025-12-30 13:25:55 +08:00
5d8e7b667f 门诊划价->新增按钮报错BUG,叫号语音设置页面开发。 2025-12-30 13:19:01 +08:00
58449fc2f9 Merge remote-tracking branch 'origin/develop' into develop 2025-12-30 10:24:44 +08:00
4c541f43b9 `` refactor(doctorstation): 修正处方列表组件导入路径大小写问题并删除废弃的体温单组件`` 2025-12-30 10:24:25 +08:00
9a037ae446 Merge remote-tracking branch 'origin/develop' into develop 2025-12-30 10:11:25 +08:00
bb0eb60eae Merge branch 'merge_1.3' into develop 2025-12-30 10:10:40 +08:00
cf5dbc6133 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	openhis-ui-vue3/src/views/doctorstation/components/diagnosis/addDiagnosisDialog.vue
#	openhis-ui-vue3/src/views/doctorstation/components/tcm/tcmAdvice.vue
2025-12-30 10:04:25 +08:00
5f6fa50000 修复了作废的中医诊断也显示出来的问题, 2025-12-30 10:03:46 +08:00
08b2e76d47 修复了作废的中医诊断也显示出来的问题, 2025-12-30 09:59:58 +08:00
5ffeab8999 修复了作废的中医诊断也显示出来的问题, 2025-12-30 09:56:39 +08:00
fc6640b846 Merge branch 'merge_1.3' into develop 2025-12-30 09:38:57 +08:00
8914c8b143 ```
chore(app): 更新应用启动横幅

更新了应用启动时显示的横幅,替换了原有的ASCII艺术样式,
使用了新的样式以提升视觉效果和品牌识别度。
```
2025-12-27 14:21:00 +08:00
2869 changed files with 47506 additions and 13394 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

View File

@@ -1,4 +1,4 @@
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>

View File

@@ -1,68 +0,0 @@
# 平台介绍
## 🏠【关于我们】
![天天开源](https://open.tntlinking.com/assets/logo-b-BzFUYaRU.png)
天天开源致⼒于打造中国应⽤管理 软件开源⽣态⾯向医疗、企业、教育三⼤⾏业信息化需求提供优质的开源软件产品与解决⽅案。平台现已发布OpenHIS、OpenCOM、OpenEDU系列开源产品并持续招募⽣态合作伙伴期待共同构建开源创新的⾏业协作模式加速⾏业的数字化进程。
天天开源的前⾝是新致开源最早于2022年6⽉发布开源医疗软件平台OpenHIS.org.cn于2023年6⽉发布开源企业软件平台OpenCOM.com.cn。2025年7⽉新致开源品牌更新为天天开源我们始终秉持开源、专业、协作的理念致⼒于为医疗、教育、中⼩企业等⾏业提供优质的开源解决⽅案。
了解我们ahttps://open.tntlinking.com/about?site=gitee
## 💾【部署包下载】
请访问官网产品中心下载部署包https://open.tntlinking.com/resource/productCenter?site=gitee
## 📚【支持文档】
技术支持资源https://open.tntlinking.com/resource/openProductDoc?site=gitee
(含演示环境、操作手册、部署手册、开发手册、常见问题等)
产品介绍https://open.tntlinking.com/resource/productPresentation?site=gitee
操作教程https://open.tntlinking.com/resource/operationTutorial?site=gitee
沙龙回顾https://open.tntlinking.com/resource/openSourceSalon#23?site=gitee
## 🤝【合作方式】
产品服务价格https://open.tntlinking.com/cost?site=gitee
加入生态伙伴https://open.tntlinking.com/ecology/becomePartner?site=gitee
## 🤗【技术社区】
请访问官网扫码加入技术社区交流https://open.tntlinking.com/ecology/joinCommunity?site=gitee
请关注公众号【天天开源软件】以便获得最新产品更新信息。
# 项目介绍
OpenHIS医院系统信创版集十大核心模块于一体涵盖目录管理、基础数据配置、个性化设置、门诊/住院全流程管理、药房药库智能管控、精细化耗材管理、财务核算体系、医保合规对接及多维报表分析等功能模块共计372项标准化功能。
系统深度适配民营及公立一二级医院业务场景,支持单体医院、集团化运营及区域医疗协同等多种部署模式,并通过国家信创认证体系,确保全栈技术自主可控。如有项目需求,可联系官方平台合作。
## 运行环境
jdk17 (必须)
node.js-v16.15 (推荐)
PostgreSQL-v16.2 (必须)
redis (常用稳定版本即可)
## 开发提示
需要修改数据库和redis的连接信息,详见:
application.yml
application-druid.yml
## 目录解释
前端: openhis-ui-vue3
后端: openhis-server
启动类: OpenHisApplication

View File

@@ -1,39 +0,0 @@
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
public class TestDeleteInspectionType {
public static void main(String[] args) {
try {
// 测试删除ID为1的检验类型
long inspectionTypeId = 1;
URL url = new URL("http://localhost:8080/system/inspection-type/" + inspectionTypeId);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("DELETE");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
// 发送请求
int responseCode = connection.getResponseCode();
System.out.println("响应代码: " + responseCode);
// 读取响应
Scanner scanner;
if (responseCode >= 200 && responseCode < 300) {
scanner = new Scanner(connection.getInputStream());
} else {
scanner = new Scanner(connection.getErrorStream());
}
String response = scanner.useDelimiter("\\A").next();
System.out.println("响应内容: " + response);
scanner.close();
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,179 @@
# 中医诊断主诊断功能实现说明
## 问题描述
中医诊断在添加时无法设置主诊断标记,导致保存后无法正确标识主诊断。
## 问题原因
`addDiagnosisDialog.vue` 中保存中医诊断时,没有传递 `maindiseFlag`(主诊断标记)字段到后端。
## 解决方案
### 1. 前端修改
#### 1.1 添加主诊断UIaddDiagnosisDialog.vue
在诊断详情区域为每个中医诊断添加了主诊断复选框:
```vue
<el-checkbox
v-model="item.isMain"
label="主诊断"
:true-label="true"
:false-label="false"
border
size="small"
style="margin-right: 10px;"
@change="(value) => handleMaindise(value, index)"
/>
```
#### 1.2 添加主诊断逻辑处理
**新增诊断时的默认行为:**
- 第一个添加的中医诊断自动设置为主诊断
- 后续添加的诊断默认不是主诊断
**主诊断唯一性校验:**
```javascript
function handleMaindise(value, index) {
if (value) {
// 检查是否已有其他主诊断
let mainCount = 0;
tcmDiagonsisList.value.forEach((item, idx) => {
if (item.isMain && idx !== index) {
mainCount++;
}
});
if (mainCount > 0) {
// 取消当前选择
tcmDiagonsisList.value[index].isMain = false;
proxy.$modal.msgWarning('只能有一条主诊断');
return;
}
// 更新保存列表中的主诊断标记
const syndromeGroupNo = tcmDiagonsisList.value[index].syndromeGroupNo;
tcmDiagonsisSaveList.value.forEach((item, idx) => {
if (item.syndromeGroupNo === syndromeGroupNo) {
// 每个证候组有两条记录(病和证),只有第一条(病)设置主诊断标记
if (idx % 2 === 0 || tcmDiagonsisSaveList.value[idx - 1]?.syndromeGroupNo !== syndromeGroupNo) {
item.maindiseFlag = 1;
}
}
});
} else {
// 取消主诊断
const syndromeGroupNo = tcmDiagonsisList.value[index].syndromeGroupNo;
tcmDiagonsisSaveList.value.forEach((item) => {
if (item.syndromeGroupNo === syndromeGroupNo) {
item.maindiseFlag = 0;
}
});
}
}
```
#### 1.3 保存时包含主诊断字段
**新增诊断时:**
```javascript
tcmDiagonsisSaveList.value.push({
definitionId: row.id,
ybNo: row.ybNo,
syndromeGroupNo: timestamp.value,
verificationStatusEnum: 4,
medTypeCode: '11',
maindiseFlag: isFirstDiagnosis ? 1 : 0, // 添加主诊断标记
});
```
**修改诊断时:**
```javascript
tcmDiagonsisSaveList.value.push({
conditionId: item.conditionId,
updateId: updateIds[0],
definitionId: item.illnessDefinitionId,
ybNo: item.ybNo,
syndromeGroupNo: item.syndromeGroupNo,
verificationStatusEnum: 4,
medTypeCode: '11',
diagSrtNo: item.diagSrtNo,
maindiseFlag: isMain ? 1 : 0, // 保留原有的主诊断标记
});
```
#### 1.4 诊断列表显示diagnosis.vue
在获取中医诊断列表时,正确读取并显示主诊断标记:
```javascript
form.value.diagnosisList.push({
name: item.name + '-' + res.data.symptom[index].name,
diagSrtNo: item.diagSrtNo,
ybNo: item.ybNo,
medTypeCode: item.medTypeCode,
syndromeGroupNo: item.syndromeGroupNo,
typeName: '中医诊断',
conditionId: item.conditionId,
symptomConditionId: res.data.symptom[index].conditionId,
updateId: item.encounterDiagnosisId + '-' + res.data.symptom[index].encounterDiagnosisId,
illnessDefinitionId: item.definitionId,
symptomDefinitionId: res.data.symptom[index].definitionId,
symptomYbNo: res.data.symptom[index].ybNo,
maindiseFlag: item.maindiseFlag || 0, // 添加主诊断标记
});
```
### 2. 后端支持
后端已经支持 `maindiseFlag` 字段的保存和读取:
**SaveDiagnosisChildParam.java**
```java
/**
* 主诊断标记 (1:是,0:否)
*/
private Integer maindiseFlag;
```
**DoctorStationChineseMedicalAppServiceImpl.java**
```java
encounterDiagnosis.setMaindiseFlag(saveDiagnosisChildParam.getMaindiseFlag());
```
## 业务规则
1. **主诊断标记位置**:主诊断标记在"病"上(每个病-证组合的第一条记录)
2. **主诊断唯一性**:中医诊断只能有一个主诊断
3. **与西医诊断的关系**:中医诊断和西医诊断可以各有一个主诊断(互不冲突)
4. **默认行为**:第一个添加的中医诊断自动设置为主诊断
## 修改文件清单
1. `openhis-ui-vue3/src/views/doctorstation/components/diagnosis/addDiagnosisDialog.vue`
- 添加主诊断复选框UI
- 添加主诊断逻辑处理函数
- 修改保存数据时包含 maindiseFlag 字段
2. `openhis-ui-vue3/src/views/doctorstation/components/diagnosis/diagnosis.vue`
- 修改获取中医诊断列表时读取 maindiseFlag 字段
- 修改传递给对话框的数据包含 maindiseFlag 字段
## 测试要点
1. ✅ 新增中医诊断时,第一个诊断自动设置为主诊断
2. ✅ 可以手动勾选/取消主诊断复选框
3. ✅ 只能有一个主诊断(尝试勾选第二个时会提示错误)
4. ✅ 保存后主诊断标记正确保存到数据库
5. ✅ 刷新页面后主诊断标记正确显示
6. ✅ 修改已有诊断时,主诊断标记正确回显
7. ✅ 中医诊断和西医诊断的主诊断互不影响
## 注意事项
1. 中医诊断是"病-证"成对出现的,主诊断标记只设置在"病"上
2. 证候记录的 maindiseFlag 始终为 0
3. 主诊断唯一性校验只在中医诊断内部进行,不影响西医诊断
## 完成时间
2026年1月9日

View File

@@ -0,0 +1,178 @@
# 公告通知弹窗功能说明
## 功能概述
用户登录后,系统会自动弹出公告通知窗口,显示未读的系统公告和通知。
## 功能特性
### 1. 自动弹出
- 登录后 1 秒自动加载公告列表
- 如果有未读公告,自动弹出弹窗显示
- 延迟加载避免影响页面初始渲染
### 2. 优先级显示
- **高优先级**1红色背景 `#fff1f0`,红色文字 `#ff4d4f`
- **中优先级**2橙色背景 `#fff7e6`,橙色文字 `#faad14`
- **低优先级**3灰色背景 `#f0f2f5`,灰色文字 `#909399`
### 3. 排序规则
- 按优先级升序排列(高 -> 中 -> 低)
- 相同优先级按创建时间降序排列
### 4. 已读状态
- 未读公告:黄色背景高亮显示
- 已读公告:默认白色背景
- 点击公告自动标记为已读
### 5. 分类显示
- **通知**1蓝色图标
- **紧急**2红色警告图标
- **信息**3绿色信息图标
- **成功**4紫色成功图标
## 文件结构
```
src/
├── components/
│ ├── NoticePopup/
│ │ └── index.vue # 登录后自动弹窗组件
│ └── NoticePanel/
│ └── index.vue # 手动打开的公告面板
├── layout/
│ └── index.vue # 主布局,引入 NoticePopup
└── views/
└── system/
└── notice/
└── index.vue # 公告管理页面
```
## API 接口
### 前端 API
```javascript
// 获取用户公告列表
getUserNotices()
// 标记为已读
markAsRead(noticeId)
// 全部标记为已读
markAllAsRead(noticeIds)
// 获取未读数量
getUnreadCount()
```
### 后端接口
```java
// GET /system/notice/public/notice
// 获取当前用户的公告列表(含已读状态)
// GET /system/notice/public/unread/count
// 获取未读公告数量
// POST /system/notice/public/read/{noticeId}
// 标记公告为已读
// POST /system/notice/public/read/all
// 批量标记为已读
```
## 数据库字段
### sys_notice 表新增字段
```sql
-- 优先级字段
priority VARCHAR(1) DEFAULT '3'
-- 发布状态字段
publish_status VARCHAR(1) DEFAULT '0'
```
## 使用说明
### 1. 数据库迁移
执行 SQL 脚本添加必要的字段:
```bash
# 添加优先级字段
ALTER TABLE sys_notice ADD COLUMN priority VARCHAR(1) DEFAULT '3';
# 添加发布状态字段
ALTER TABLE sys_notice ADD COLUMN publish_status VARCHAR(1) DEFAULT '0';
```
### 2. 后台管理
`系统管理 > 公告管理` 中:
1. 点击"新增"创建公告
2. 选择优先级:高/中/低
3. 选择公告类型:通知/紧急/信息/成功
4. 填写标题和内容
5. 点击"发布"按钮
### 3. 用户端显示
- 登录后自动弹出未读公告
- 点击顶部导航栏铃铛图标可手动打开
- 点击公告查看详情
- 支持全部标记为已读
## 样式说明
### 弹窗样式
- 宽度800px
- 高度:最大 600px
- 标题栏:渐变紫色背景 `linear-gradient(135deg, #667eea 0%, #764ba2 100%)`
- 左右分栏布局
### 列表样式
- 列表宽度380px
- 滚动区域:最大高度 500px
- 未读高亮:黄色背景 `#fffbe6`
- 激活项:蓝色左边框 `#1890ff`
### 详情样式
- 自适应宽度
- 标题字号18px
- 内容字号14px
- 行高1.8
## 注意事项
1. **权限控制**
- 只有已发布的公告才会显示
- 只有正常状态的公告才会显示
2. **性能优化**
- 使用延迟加载1秒
- 使用虚拟滚动el-scrollbar
- 按需加载详情
3. **用户体验**
- 支持点击空白处关闭
- 支持ESC键关闭
- 未读状态醒目标识
- 优先级颜色区分明显
## 故障排查
### 弹窗不显示
1. 检查后端接口 `/system/notice/public/notice` 是否正常返回
2. 检查是否有未读公告
3. 检查浏览器控制台是否有错误
4. 清除浏览器缓存重试
### 样式错乱
1. 检查 Element Plus 版本是否兼容
2. 检查样式是否被其他 CSS 覆盖
3. 使用浏览器开发者工具检查样式
### 数据不更新
1. 检查后端返回的数据格式
2. 检查 API 调用是否成功
3. 检查响应拦截器处理
## 版本信息
- 创建日期2025-12-30
- 功能版本v1.0
- 前端框架Vue 3 + Element Plus
- 后端框架Spring Boot + MyBatis Plus

332
md/前端UI规范文档.md Normal file
View File

@@ -0,0 +1,332 @@
# OpenHIS UI 风格规范文档
## 1. 整体布局
### 1.1 容器结构
```
<div class="app-container [page-name]-container">
<div class="components-container">
<!-- 查询表单 -->
<el-form class="query-form">...</el-form>
<!-- 操作按钮 -->
<el-row class="button-group">...</el-row>
<!-- 数据表格 -->
<el-table border>...</el-table>
<!-- 分页 -->
<div class="pagination-container">...</div>
</div>
</div>
```
### 1.2 样式说明
- `app-container`: 最外层容器,全屏背景
- `components-container`: 白色卡片容器,带阴影
- `query-form`: 查询表单,底部无间距
- `button-group`: 按钮组,间距 8px
- `pagination-container`: 分页容器,顶部间距 16px
## 2. 统一样式规范
### 2.1 颜色规范
```scss
// 主色调
--color-primary: #409EFF;
--color-success: #67C23A;
--color-warning: #E6A23C;
--color-danger: #F56C6C;
--color-info: #909399;
// 文字颜色
--text-regular: #606266; // 常规文字
--text-secondary: #909399; // 次要文字
--text-placeholder: #A8ABB2; // 占位符
```
### 2.2 间距规范
```scss
// 容器内边距
$spacing-xs: 4px;
$spacing-sm: 8px;
$spacing-md: 16px;
$spacing-lg: 20px;
$spacing-xl: 24px;
// 表单项间距
$form-item-margin-bottom: 18px;
```
### 2.3 圆角规范
```scss
$border-radius-sm: 4px; // 小圆角 - 按钮、输入框
$border-radius-md: 8px; // 中圆角 - 卡片、对话框
$border-radius-lg: 12px; // 大圆角 - 特殊组件
```
### 2.4 阴影规范
```scss
// 卡片阴影
$box-shadow-card: 0 2px 8px rgba(0, 0, 0, 0.08);
// 表格阴影
$box-shadow-table: 0 1px 4px rgba(0, 0, 0, 0.05);
// 浮动元素阴影
$box-shadow-float: 0 2px 12px rgba(0, 0, 0, 0.1);
```
### 2.5 字体规范
```scss
$font-size-base: 14px; // 基础字号
$font-size-sm: 12px; // 小字号
$font-size-lg: 16px; // 大字号
$font-size-xl: 18px; // 特大字号
$font-weight-regular: 400; // 常规
$font-weight-medium: 500; // 中等
$font-weight-bold: 600; // 加粗
```
## 3. 组件规范
### 3.1 表单组件
#### 输入框
```vue
<el-input
v-model="value"
placeholder="请输入xxx"
clearable
style="width: 200px"
@keyup.enter="handleQuery"
/>
```
#### 下拉框
```vue
<el-select
v-model="value"
placeholder="请选择xxx"
clearable
style="width: 200px"
>
<el-option
v-for="dict in dictList"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
```
#### 按钮规范
```vue
<!-- 主按钮 -->
<el-button type="primary" icon="IconName">按钮文字</el-button>
<!-- 次要按钮 -->
<el-button type="success">按钮文字</el-button>
<el-button type="warning">按钮文字</el-button>
<el-button type="danger">按钮文字</el-button>
<!-- 次要按钮plain -->
<el-button type="primary" plain icon="IconName">按钮文字</el-button>
<!-- 链接按钮table内 -->
<el-button link type="primary" class="action-button">文字</el-button>
```
### 3.2 表格组件
```vue
<el-table
v-loading="loading"
:data="tableData"
@selection-change="handleSelectionChange"
border
>
<!-- 复选列 -->
<el-table-column type="selection" width="55" align="center" />
<!-- 序号列 -->
<el-table-column label="序号" align="center" prop="id" width="80" />
<!-- 普通列 -->
<el-table-column
label="列名"
align="center"
prop="fieldName"
min-width="200"
:show-overflow-tooltip="true"
/>
<!-- 带标签列 -->
<el-table-column label="状态" align="center" prop="status" width="90">
<template #default="scope">
<dict-tag :options="dictType" :value="scope.row.status" />
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column label="操作" align="center" min-width="280">
<template #default="scope">
<el-button link type="primary" @click="handleEdit(scope.row)" class="action-button">编辑</el-button>
<el-button link type="danger" @click="handleDelete(scope.row)" class="action-button">删除</el-button>
</template>
</el-table-column>
</el-table>
```
### 3.3 对话框组件
```vue
<el-dialog
:title="dialogTitle"
v-model="dialogVisible"
width="800px"
destroy-on-close
>
<el-form :model="form" :rules="rules" label-width="80px">
<!-- 表单内容 -->
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleSubmit"> </el-button>
<el-button @click="handleCancel"> </el-button>
</div>
</template>
</el-dialog>
```
## 4. 响应式设计
### 4.1 断点规范
```scss
$screen-xs: 480px;
$screen-sm: 768px;
$screen-md: 992px;
$screen-lg: 1200px;
```
### 4.2 响应式规范
```vue
<!-- 栅格系统 -->
<el-row :gutter="10">
<el-col :span="6" :xs="12" :sm="8">...</el-col>
<el-col :span="18" :xs="12" :sm="16">...</el-col>
</el-row>
<!-- 移动端表单 -->
<el-form :label-width="isMobile ? '80px' : '120px'">
```
## 5. 交互规范
### 5.1 加载状态
```vue
<!-- 表格加载 -->
<el-table v-loading="loading" :data="tableData">
<!-- 按钮加载 -->
<el-button :loading="saving">保存</el-button>
<!-- 全屏加载 -->
<el-button type="primary" :loading="true">提交</el-button>
```
### 5.2 空状态
```vue
<el-empty
description="暂无数据"
:image="emptyImage"
/>
```
### 5.3 确认对话框
```vue
<el-dialog title="确认提示" v-model="confirmVisible">
<p>{{ confirmMessage }}</p>
<template #footer>
<el-button @click="confirmVisible = false"> </el-button>
<el-button type="danger" @click="handleConfirm"> </el-button>
</template>
</el-dialog>
```
## 6. 命名规范
### 6.1 CSS 类名
- 使用 kebab-case短横线命名
- 遵循 BEM 命名规范
- 示例:`notice-container`, `button-group`, `action-button`
### 6.2 变量命名
- Vue使用 camelCase
- 事件处理:以 `handle` 开头
- 数据获取:以 `get`/`load` 开头
- 示例:`handleQuery`, `loadData`, `handleSubmit`
### 6.3 组件命名
- 使用 PascalCase
- 多个单词时使用驼峰命名
- 示例:`NoticePanel`, `TableHeader`, `SearchForm`
## 7. 国际化规范
```vue
<!-- 使用字典标签 -->
<dict-tag :options="dictType" :value="row.status" />
<!-- 使用占位符 -->
<el-input :placeholder="$t('common.placeholder')"/>
```
## 8. 性能优化
### 8.1 图片优化
- 使用 WebP 格式
- 图片懒加载
- 响应式图片
### 8.2 列表优化
- 虚拟滚动(大列表)
- 分页加载
- 骨架屏
### 8.3 缓存策略
- 使用 Vuex/Pinia 缓存用户数据
- 本地存储持久化配置
- 请求去重
## 9. 无障碍规范
### 9.1 语义化标签
```vue
<nav> <!-- 导航 -->
<main> <!-- 主内容 -->
<header> <!-- 头部 -->
<footer> <!-- 底部 -->
```
### 9.2 ARIA 属性
```vue
<el-button aria-label="提交按钮">提交</el-button>
<el-input aria-label="搜索框" />
```
### 9.3 键盘导航
- 支持键盘操作
- Tab 键焦点管理
- 快捷键支持
---
**注意事项:**
1. 所有新增页面必须遵循此规范
2. 修改现有页面时尽量统一样式
3. 保持与系统整体风格一致
4. 优先使用已有的组件和样式
5. 遵循响应式设计原则

View File

@@ -0,0 +1,254 @@
# 手术人员字段不显示问题解决方案
## 问题描述
主刀医生、麻醉医生、助手1、助手2、执行科这些字段在手术查看页面中没有显示数据。
## 问题原因
这些字段在数据库中可能为 **null 或空值**,虽然保存了 ID`main_surgeon_id`),但没有保存对应的姓名(如 `main_surgeon_name`)。
## 解决步骤
### 步骤 1检查数据库中字段的实际值
执行以下 SQL 查看当前数据:
```sql
SELECT
id,
surgery_no,
main_surgeon_id,
main_surgeon_name,
anesthetist_id,
anesthetist_name,
assistant_1_id,
assistant_1_name,
assistant_2_id,
assistant_2_name,
operating_room_id,
operating_room_name,
org_id,
org_name
FROM public.cli_surgery
WHERE delete_flag = '0'
ORDER BY create_time DESC
LIMIT 5;
```
**请告诉我结果**:特别是 `main_surgeon_name``anesthetist_name``assistant_1_name``assistant_2_name``operating_room_name``org_name` 这些字段的值。
### 步骤 2检查用户表结构
执行以下 SQL 查看用户表的结构:
```sql
SELECT
column_name,
data_type
FROM information_schema.columns
WHERE table_name = 'sys_user'
AND column_name IN ('user_id', 'nick_name', 'user_name', 'practitioner_id')
ORDER BY column_name;
```
**目的**确定人员ID和姓名的对应关系。
### 步骤 3填充人员姓名字段推荐方法
使用以下 SQL 脚本填充人员姓名:
```sql
-- 填充主刀医生姓名
UPDATE public.cli_surgery s
SET main_surgeon_name = u.nick_name
FROM public.sys_user u
WHERE s.main_surgeon_id = u.user_id
AND s.main_surgeon_name IS NULL
AND s.delete_flag = '0';
-- 填充麻醉医生姓名
UPDATE public.cli_surgery s
SET anesthetist_name = u.nick_name
FROM public.sys_user u
WHERE s.anesthetist_id = u.user_id
AND s.anesthetist_name IS NULL
AND s.delete_flag = '0';
-- 填充助手1姓名
UPDATE public.cli_surgery s
SET assistant_1_name = u.nick_name
FROM public.sys_user u
WHERE s.assistant_1_id = u.user_id
AND s.assistant_1_name IS NULL
AND s.delete_flag = '0';
-- 填充助手2姓名
UPDATE public.cli_surgery s
SET assistant_2_name = u.nick_name
FROM public.sys_user u
WHERE s.assistant_2_id = u.user_id
AND s.assistant_2_name IS NULL
AND s.delete_flag = '0';
-- 填充手术室名称
UPDATE public.cli_surgery s
SET operating_room_name = r.name
FROM public.cli_operating_room r
WHERE s.operating_room_id = r.id
AND s.operating_room_name IS NULL
AND s.delete_flag = '0';
-- 填充执行科室名称
UPDATE public.cli_surgery s
SET org_name = o.name
FROM public.adm_organization o
WHERE s.org_id = o.id
AND s.org_name IS NULL
AND s.delete_flag = '0';
```
### 步骤 4验证更新结果
执行以下 SQL 验证是否更新成功:
```sql
SELECT
id,
surgery_no,
main_surgeon_id,
main_surgeon_name,
anesthetist_id,
anesthetist_name,
assistant_1_id,
assistant_1_name,
assistant_2_id,
assistant_2_name,
operating_room_id,
operating_room_name,
org_id,
org_name
FROM public.cli_surgery
WHERE delete_flag = '0'
ORDER BY create_time DESC
LIMIT 5;
```
**预期结果**:所有 `*_name` 字段都应该有值。
### 步骤 5刷新前端页面
1. 刷新手术管理页面
2. 点击某个手术记录的"查看"按钮
3. 检查详情对话框中是否显示这些字段
## 前端代码检查
### 1. 检查详情对话框显示
打开 `surgerymanage/index.vue` 文件,查看详情对话框部分:
```vue
<el-descriptions-item label="主刀医生">{{ viewData.mainSurgeonName }}</el-descriptions-item>
<el-descriptions-item label="麻醉医生">{{ viewData.anesthetistName }}</el-descriptions-item>
<el-descriptions-item label="助手1">{{ viewData.assistant1Name }}</el-descriptions-item>
<el-descriptions-item label="助手2">{{ viewData.assistant2Name }}</el-descriptions-item>
<el-descriptions-item label="手术室">{{ viewData.operatingRoomName }}</el-descriptions-item>
<el-descriptions-item label="执行科室">{{ viewData.orgName }}</el-descriptions-item>
```
**确认**:这些字段名是否正确(注意驼峰命名)。
### 2. 检查浏览器控制台
1. 打开浏览器开发者工具F12
2. 切换到 Console 标签
3. 点击"查看"按钮
4. 查看是否有 JavaScript 错误
### 3. 检查 Network 响应
1. 切换到 Network 标签
2. 点击"查看"按钮
3. 找到 `/clinical-manage/surgery/surgery-detail` 请求
4. 查看响应内容
**检查**:响应数据中是否包含这些字段,值是什么。
## 常见问题
### 问题 1UPDATE SQL 执行失败
**症状**:报错 "relation does not exist" 或 "column does not exist"
**解决**
1. 检查表名是否正确sys_user 或 adm_practitioner
2. 检查字段名是否正确user_id 或 practitioner_id
### 问题 2UPDATE 后字段仍为 null
**症状**UPDATE 执行成功,但字段仍为 null
**原因**:关联表中没有对应的记录
**解决**检查人员ID是否存在于人员表中
```sql
-- 检查主刀医生ID是否存在
SELECT s.main_surgeon_id, u.nick_name
FROM public.cli_surgery s
LEFT JOIN public.sys_user u ON s.main_surgeon_id = u.user_id
WHERE s.main_surgeon_id IS NOT NULL
AND u.user_id IS NULL
LIMIT 5;
```
### 问题 3前端仍然不显示
**症状**:数据库中有值,但前端不显示
**原因**
1. 前端字段名不匹配
2. 前端数据绑定有问题
**解决**
1. 检查 MyBatis XML 映射是否正确
2. 检查后端返回的 JSON 数据结构
3. 检查前端变量名是否正确
## 后续改进建议
### 1. 保存时自动填充姓名
在前端或后端保存手术信息时根据选择的医生ID自动查询并填充姓名字段。
### 2. 提供人员选择功能
在前端提供医生、科室等选择下拉框而不是手动输入ID。
### 3. 添加数据完整性校验
在保存前检查如果选择了人员ID必须填充对应的姓名字段。
## 相关文件
1. **检查和填充脚本**`e:/his/检查和填充手术人员字段.sql`
2. **填充脚本**`e:/his/填充手术人员字段姓名.sql`
3. **MyBatis 映射**`e:/his/openhis-server-new/openhis-application/src/main/resources/mapper/clinicalmanage/SurgeryMapper.xml`
## 验证清单
- [ ] 数据库查询显示字段为 null
- [ ] 执行了填充 SQL 脚本
- [ ] 验证更新后字段有值
- [ ] 刷新前端页面
- [ ] 详情对话框中正确显示
## 联系支持
如果以上步骤都无法解决问题,请提供:
1. **步骤 1 的查询结果**:当前数据库中这些字段的值
2. **步骤 2 的查询结果**sys_user 表的结构
3. **UPDATE SQL 执行结果**:是否有错误,更新了多少条记录
4. **步骤 4 的验证结果**:更新后的字段值
5. **浏览器控制台错误**:是否有 JavaScript 错误
6. **Network 响应数据**:后端返回的完整数据

View File

@@ -0,0 +1,120 @@
# 手术和麻醉信息Redis缓存实现说明
## 概述
为提高手术和麻醉信息的查询性能,已将手术信息缓存到Redis中。接口查询时先从Redis缓存获取,如果没有则从数据库查询并更新到Redis缓存。
## 实现细节
### 1. Redis缓存Key定义
`openhis-common/src/main/java/com/openhis/common/utils/RedisKeys.java` 中定义了以下缓存Key:
```java
// 单个手术信息缓存
public static String getSurgeryKey(Long surgeryId)
// 按患者ID查询的手术列表缓存
public static String getSurgeryListByPatientKey(Long patientId)
// 按就诊ID查询的手术列表缓存
public static String getSurgeryListByEncounterKey(Long encounterId)
```
### 2. 缓存实现
#### 2.1 SurgeryServiceImpl (Domain层)
- **getSurgeryById(Long id)**: 根据手术ID查询单个手术信息
- 先从Redis缓存获取
- 缓存未命中则从数据库查询
- 查询结果存入Redis缓存(30分钟过期)
- **getSurgeryListByPatientId(Long patientId)**: 根据患者ID查询手术列表
- 先从Redis缓存获取
- 缓存未命中则从数据库查询
- 查询结果存入Redis缓存(30分钟过期)
- **getSurgeryListByEncounterId(Long encounterId)**: 根据就诊ID查询手术列表
- 先从Redis缓存获取
- 缓存未命中则从数据库查询
- 查询结果存入Redis缓存(30分钟过期)
- **insertSurgery(Surgery surgery)**: 新增手术信息
- 插入成功后清除相关缓存
- **updateSurgery(Surgery surgery)**: 更新手术信息
- 更新成功后清除相关缓存
- **deleteSurgery(Long id)**: 删除手术信息
- 删除成功后清除相关缓存
- **updateSurgeryStatus(Long id, Integer statusEnum)**: 更新手术状态
- 更新成功后清除相关缓存
#### 2.2 SurgeryAppServiceImpl (Application层)
- **getSurgeryDetail(Long id)**: 根据ID查询手术详情
- 先从Redis缓存获取
- 缓存未命中则从数据库查询
- 查询结果存入Redis缓存(30分钟过期)
- **addSurgery(SurgeryDto surgeryDto)**: 新增手术信息
- 插入成功后清除相关缓存
- **updateSurgery(SurgeryDto surgeryDto)**: 更新手术信息
- 更新成功后清除相关缓存
- **deleteSurgery(Long id)**: 删除手术信息
- 删除成功后清除相关缓存
- **updateSurgeryStatus(Long id, Integer statusEnum)**: 更新手术状态
- 更新成功后清除相关缓存
### 3. 缓存清除策略
当手术信息发生变化时(新增、更新、删除),会清除以下相关缓存:
1. 单个手术信息缓存
2. 患者手术列表缓存
3. 就诊手术列表缓存
### 4. 缓存配置
- **缓存时间**: 30分钟
- **时间单位**: TimeUnit.MINUTES
- **序列化**: 使用RedisTemplate默认序列化方式
## 关于麻醉信息
当前项目中,麻醉信息是手术表(`cli_surgery`)中的字段,包括:
- 麻醉医生ID (anesthetistId)
- 麻醉医生姓名 (anesthetistName)
- 麻醉方式编码 (anesthesiaTypeEnum)
- 麻醉费用 (anesthesiaFee)
这些字段已经包含在手术实体的缓存中,无需单独实现麻醉信息的缓存。
## 使用示例
### 查询手术信息(自动使用缓存)
```java
// 自动从缓存获取,未命中则查询数据库
Surgery surgery = surgeryService.getSurgeryById(surgeryId);
```
### 更新手术信息(自动清除缓存)
```java
// 更新数据库,同时清除相关缓存
surgeryService.updateSurgery(surgery);
```
### 手动清除缓存(如需要)
```java
String cacheKey = RedisKeys.getSurgeryKey(surgeryId);
redisCache.deleteObject(cacheKey);
```
## 性能优化建议
1. 对于频繁访问的手术信息,缓存命中率高,可显著提升查询性能
2. 对于不常访问的手术信息,30分钟缓存时间可避免占用过多Redis内存
3. 如需调整缓存时间,可修改代码中的 `30, TimeUnit.MINUTES` 参数
4. 如需更精细的缓存控制,可考虑使用不同的缓存时间策略(如根据手术状态设置不同过期时间)
## 监控建议
建议监控以下指标:
- Redis缓存命中率
- Redis内存使用情况
- 查询响应时间对比(缓存命中 vs 缓存未命中)

View File

@@ -0,0 +1,256 @@
# 手术室管理添加类型和所属科室字段功能说明
## 概述
本次更新为手术室管理模块添加了"类型"和"所属科室"字段,优化了手术室信息的分类管理。
**数据库类型**PostgreSQL
## 功能特点
### 1. 手术室类型
支持四种手术室类型:
- **急诊手术室**:用于急诊手术的手术室
- **择期手术室**:用于择期手术的手术室(默认类型)
- **日间手术室**:用于日间手术的手术室
- **复合手术室**:用于复合手术的手术室
### 2. 所属科室
每个手术室可以关联到具体的科室,便于科室级别的资源管理。
## 修改内容
### 前端修改Vue3
#### 1. 手术室列表页面 (`operatingroom/index.vue`)
**列表表格新增列**
- 类型列:显示手术室类型(急诊手术室、择期手术室等)
- 所属科室列:显示手术室所属的科室名称
**新增/修改对话框新增字段**
- 类型选择器:下拉选择手术室类型
- 所属科室选择器:可搜索的科室下拉框
**查询表单保持原样**
- 仍支持按手术室名称和状态查询
**查看对话框新增显示**
- 类型信息
- 所属科室信息
#### 2. 表单数据结构
```javascript
const form = ref({
id: undefined,
busNo: undefined,
name: undefined,
roomTypeEnum: undefined, // 新增:手术室类型
organizationId: undefined, // 已有所属科室ID
organizationName: undefined, // 已有:所属科室名称
locationDescription: undefined,
equipmentConfig: undefined,
capacity: 1,
statusEnum: 1,
displayOrder: 0,
remark: undefined
})
```
#### 3. 类型选项配置
```javascript
const roomTypeOptions = ref([
{ value: 1, label: '急诊手术室' },
{ value: 2, label: '择期手术室' },
{ value: 3, label: '日间手术室' },
{ value: 4, label: '复合手术室' }
])
```
### 后端修改Java
#### 1. 实体类 (`OperatingRoom.java`)
**新增字段**
```java
/** 手术室类型 */
@Dict(dictCode = "operating_room_type")
private Integer roomTypeEnum;
private String roomTypeEnum_dictText;
/** 所属机构ID */
private Long organizationId;
/** 所属机构名称 */
private String organizationName;
```
#### 2. DTO类 (`OperatingRoomDto.java`)
**新增字段**
```java
/** 手术室类型 */
@Dict(dictCode = "operating_room_type")
private Integer roomTypeEnum;
private String roomTypeEnum_dictText;
/** 所属机构ID */
@JsonSerialize(using = ToStringSerializer.class)
private Long organizationId;
/** 机构名称 */
private String organizationName;
```
#### 3. Service实现类 (`OperatingRoomAppServiceImpl.java`)
**查询列表方法优化**
- 添加类型字段的枚举值转换逻辑
- 根据类型编码设置对应的中文描述
**详情查询方法优化**
- 添加类型字段的枚举值转换
- 查询所属科室的名称并回显
### 数据库修改
#### SQL脚本文件`add_operating_room_type_fields.sql`PostgreSQL版本
**1. 添加字段**
```sql
ALTER TABLE public.adm_operating_room
ADD COLUMN room_type_enum INTEGER DEFAULT 2;
COMMENT ON COLUMN public.adm_operating_room.room_type_enum IS
'手术室类型1-急诊手术室2-择期手术室3-日间手术室4-复合手术室';
```
**2. 更新现有数据**
```sql
UPDATE public.adm_operating_room
SET room_type_enum = 2
WHERE room_type_enum IS NULL;
```
**3. 添加索引**
```sql
CREATE INDEX idx_room_type ON public.adm_operating_room(room_type_enum);
CREATE INDEX idx_org_id ON public.adm_operating_room(organization_id);
```
**4. 字典数据**
- 新增字典类型:`operating_room_type`(手术室类型)
- 新增字典项:
- 急诊手术室1
- 择期手术室2
- 日间手术室3
- 复合手术室4
**PostgreSQL特定语法说明**
- 使用 `public.adm_operating_room` 替代 `` `adm_operating_room` ``
- 使用 `COMMENT ON COLUMN` 替代 `COMMENT``ALTER TABLE`
- 使用 `nextval()` 和序列来生成字典类型ID
- 使用 `information_schema.columns` 获取列信息
- 使用 `CASE WHEN` 语句进行条件判断
## 部署步骤
### 1. 数据库部署
执行SQL脚本PostgreSQL
```bash
psql -U postgres -d his_database -f add_operating_room_type_fields.sql
```
或者使用 pgAdmin 等图形化工具执行SQL脚本。
### 2. 后端部署
重启后端服务,使新的代码生效。
### 3. 前端部署
重新编译并部署前端代码:
```bash
npm run build
```
## 使用说明
### 新增手术室
1. 进入手术室管理页面
2. 点击"新增手术室"按钮
3. 填写手术室信息:
- 手术室名称(必填)
- 类型(可选,默认为择期手术室)
- 所属科室(必填)
- 位置描述
- 设备配置
- 容纳人数
- 状态(默认为启用)
- 显示顺序
- 备注
4. 点击"确定"保存
### 修改手术室
1. 在手术室列表中找到要修改的记录
2. 点击"编辑"按钮
3. 修改相关信息(包括类型和所属科室)
4. 点击"确定"保存
### 查看手术室详情
1. 在手术室列表中点击"查看"按钮
2. 查看完整的手术室信息,包括类型和所属科室
### 查询手术室
- 按手术室名称模糊查询
- 按状态筛选(启用/停用)
## 注意事项
1. **数据迁移**:现有手术室的类型默认设置为"择期手术室"2可以根据实际需要调整。
2. **科室关联**:所属科室是必填字段,需要在科室管理中先配置好科室信息。
3. **类型字典**:手术室类型字典已自动创建,可以在系统字典管理中进行维护。
4. **索引优化**:已为类型和科室字段添加索引,提升查询性能。
5. **兼容性**:此次修改保持了向后兼容性,不影响现有功能。
## 验证清单
- [ ] 数据库字段添加成功
- [ ] 字典数据创建成功
- [ ] 前端列表正确显示类型和所属科室
- [ ] 新增手术室时可选择类型和所属科室
- [ ] 修改手术室时可更新类型和所属科室
- [ ] 查看手术室详情时正确显示类型和所属科室
- [ ] 类型下拉选项显示正确
- [ ] 所属科室选择器可正常搜索和选择
- [ ] 查询功能正常工作
- [ ] 没有语法错误和运行时错误
## 回滚方案
如需撤销本次修改请执行SQL脚本中的回滚语句PostgreSQL
```sql
DROP INDEX idx_room_type ON public.adm_operating_room;
DROP INDEX idx_org_id ON public.adm_operating_room;
DELETE FROM public.sys_dict_data WHERE dict_type = 'operating_room_type';
DELETE FROM public.sys_dict_type WHERE dict_type = 'operating_room_type';
ALTER TABLE public.adm_operating_room DROP COLUMN room_type_enum;
```
## 技术支持
如有问题,请联系技术支持团队。

View File

@@ -0,0 +1,215 @@
# 手术申请医生科室字段保存问题解决方案
## 问题确认
数据库中只保存了 ID 字段(`apply_doctor_id``apply_dept_id`),但没有保存名称字段(`apply_doctor_name``apply_dept_name`)。
## 根本原因
**数据库表中缺少 `apply_doctor_name``apply_dept_name` 这两个字段!**
虽然 MyBatis 映射文件和实体类都配置了这些字段但如果数据库表中不存在这些列MyBatis 在插入时会静默忽略这些字段(不会报错),导致只有 ID 被保存。
## 解决步骤
### 步骤 1执行数据库迁移脚本必须
使用 Navicat Premium 17 执行以下 SQL
```sql
-- 方法1使用 IF NOT EXISTS 语法(推荐)
ALTER TABLE public.cli_surgery ADD COLUMN IF NOT EXISTS apply_doctor_name VARCHAR(100);
COMMENT ON COLUMN public.cli_surgery.apply_doctor_name IS '申请医生姓名';
ALTER TABLE public.cli_surgery ADD COLUMN IF NOT EXISTS apply_dept_name VARCHAR(100);
COMMENT ON COLUMN public.cli_surgery.apply_dept_name IS '申请科室名称';
-- 验证字段是否添加成功
SELECT column_name, data_type, character_maximum_length
FROM information_schema.columns
WHERE table_name = 'cli_surgery'
AND column_name IN ('apply_doctor_name', 'apply_dept_name');
```
**预期结果**
```
apply_doctor_name | character varying | 100
apply_dept_name | character varying | 100
```
### 步骤 2重启后端服务
执行数据库迁移后,必须重启后端服务以重新加载表结构。
### 步骤 3新增手术并查看日志
1. 打开后端控制台或日志文件
2. 在前端新增一条手术记录
3. 查看后端日志,应该能看到:
```
设置申请医生信息 - doctorId: 123, doctorName: 张医生, deptId: 456, deptName: 普外科
前端提交的数据 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, applyDeptName: 普外科
准备插入手术记录 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, deptName: 普外科
准备插入手术记录 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, deptName: 普外科
插入后查询结果 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, deptName: 普外科
手术记录插入成功 - surgeryId: 1234567890123456789, surgeryNo: OP202501051234
```
**关键检查点**
- `准备插入手术记录` 这行日志中,`applyDoctorName``applyDeptName` 必须有值(不能为 null
- `插入后查询结果` 这行日志中,这两个字段也必须有值
### 步骤 4验证数据库
执行以下 SQL 查询最新插入的记录:
```sql
SELECT
id,
surgery_no,
apply_doctor_id,
apply_doctor_name,
apply_dept_id,
apply_dept_name,
surgery_name,
create_time
FROM public.cli_surgery
WHERE delete_flag = '0'
ORDER BY create_time DESC
LIMIT 1;
```
**预期结果**
- `apply_doctor_id`有值例如123
- `apply_doctor_name`:有值(例如:张医生)
- `apply_dept_id`有值例如456
- `apply_dept_name`:有值(例如:普外科)
### 步骤 5测试前端显示
1. 刷新手术管理页面
2. 查看列表中是否显示申请医生和申请科室列
3. 点击"查看"或"编辑"按钮,检查详情对话框是否显示这些信息
## 常见问题和解决
### 问题 1执行 SQL 后报错 "column does not exist"
**原因**:数据库表结构可能不同,或者表名不是 `cli_surgery`
**解决**:先执行以下 SQL 检查表名:
```sql
SELECT table_name
FROM information_schema.tables
WHERE table_name LIKE '%surgery%'
AND table_schema = 'public';
```
### 问题 2执行 SQL 后字段仍然不存在
**原因**:可能是权限问题或 SQL 语法问题
**解决**:尝试使用更简单的方式:
```sql
-- 先检查表结构
\d public.cli_surgery
-- 手动添加字段(如果不存在)
-- 注意:如果字段已存在,这个语句会报错,这是正常的
ALTER TABLE public.cli_surgery ADD COLUMN apply_doctor_name VARCHAR(100);
ALTER TABLE public.cli_surgery ADD COLUMN apply_dept_name VARCHAR(100);
```
### 问题 3字段添加成功但插入时仍然为空
**原因**MyBatis 或 MyBatis-Plus 配置问题
**解决**
1. 检查实体类字段是否有 `@TableField` 注解
2. 检查字段名是否与数据库列名一致
3. 查看后端日志中的 `插入后查询结果`
### 问题 4后端日志显示字段为 null
**原因**:后端代码中 `applyDoctorName``applyDeptName` 被设置为 null
**解决**
1. 检查 `SecurityUtils.getLoginUser().getUser().getNickName()` 是否返回 null
2. 检查 `SecurityUtils.getLoginUser().getOrgId()` 是否返回 null
3. 检查 `organizationService.getById(orgId)` 是否返回 null
## 验证清单
- [ ] 数据库迁移脚本已执行
- [ ] 数据库字段已添加(步骤 1 验证 SQL 有结果)
- [ ] 后端服务已重启
- [ ] 后端日志显示 `准备插入手术记录` 且字段有值
- [ ] 后端日志显示 `插入后查询结果` 且字段有值
- [ ] 数据库查询显示字段有值(步骤 4
- [ ] 前端列表正确显示
- [ ] 前端详情正确显示
## 调试 SQL 脚本
如果需要手动测试插入功能,可以执行:
```sql
-- 测试插入(确保字段存在)
INSERT INTO public.cli_surgery (
surgery_no,
patient_id,
encounter_id,
apply_doctor_id,
apply_doctor_name,
apply_dept_id,
apply_dept_name,
surgery_name,
status_enum,
delete_flag,
create_time,
update_time
) VALUES (
'TEST202501050002',
(SELECT id FROM public.adm_patient WHERE delete_flag = '0' LIMIT 1),
(SELECT id FROM public.adm_encounter WHERE delete_flag = '0' LIMIT 1),
999,
'手动测试医生',
999,
'手动测试科室',
'手动测试手术',
0,
'0',
NOW(),
NOW()
);
-- 查询刚才插入的数据
SELECT
surgery_no,
apply_doctor_id,
apply_doctor_name,
apply_dept_id,
apply_dept_name,
surgery_name
FROM public.cli_surgery
WHERE surgery_no = 'TEST202501050002';
-- 清理测试数据
-- DELETE FROM public.cli_surgery WHERE surgery_no = 'TEST202501050002';
```
## 联系支持
如果以上步骤都无法解决问题,请提供:
1. **数据库表结构查询结果**
```sql
\d public.cli_surgery
```
2. **后端日志**:特别是 `准备插入手术记录` 和 `插入后查询结果` 这两行
3. **数据库查询结果**:执行步骤 4 中的 SQL告诉我结果
4. **错误信息**:如果有任何错误提示

View File

@@ -0,0 +1,194 @@
# 手术申请医生科室数据保存问题排查指南
## 问题现象
新增手术后,列表页面和编辑查看页面没有显示申请医生名称和科室名称。
## 排查步骤
### 步骤1检查数据库字段是否存在
执行以下 SQL 检查字段是否已添加:
```sql
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'cli_surgery'
AND column_name IN ('apply_doctor_name', 'apply_dept_name');
```
**预期结果**:应该返回两条记录
```
apply_doctor_name | character varying
apply_dept_name | character varying
```
**如果结果为空**:说明字段未添加,需要执行迁移脚本。
### 步骤2检查数据库迁移脚本是否执行
执行迁移脚本(如果未执行):
```sql
-- 检查并添加申请医生姓名字段
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'cli_surgery'
AND column_name = 'apply_doctor_name'
) THEN
ALTER TABLE public.cli_surgery ADD COLUMN apply_doctor_name VARCHAR(100);
COMMENT ON COLUMN public.cli_surgery.apply_doctor_name IS '申请医生姓名';
END IF;
END $$;
-- 检查并添加申请科室名称字段
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = 'cli_surgery'
AND column_name = 'apply_dept_name'
) THEN
ALTER TABLE public.cli_surgery ADD COLUMN apply_dept_name VARCHAR(100);
COMMENT ON COLUMN public.cli_surgery.apply_dept_name IS '申请科室名称';
END IF;
END $$;
```
### 步骤3重启后端服务
执行数据库迁移后,必须重启后端服务。
### 步骤4新增手术并查看后端日志
1. 打开后端控制台或日志文件
2. 在前端新增一条手术记录
3. 查看后端日志,应该能看到以下信息:
```
设置申请医生信息 - doctorId: 123, doctorName: 张医生, deptId: 456, deptName: 普外科
前端提交的数据 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, applyDeptName: 普外科
准备插入手术记录 - applyDoctorId: 123, applyDoctorName: 张医生, applyDeptId: 456, deptName: 普外科
手术记录插入成功 - surgeryId: 1234567890123456789, surgeryNo: OP202501051234
```
**如果看不到这些日志**:说明代码没有执行到这里,检查是否有异常抛出。
**如果看到 "前端提交的数据 - applyDoctorName: null"**:说明前端提交的数据为空,需要检查前端代码。
### 步骤5检查数据库中是否保存成功
执行以下 SQL 查询最新插入的记录:
```sql
SELECT
id,
surgery_no,
patient_id,
apply_doctor_id,
apply_doctor_name,
apply_dept_id,
apply_dept_name,
surgery_name,
status_enum,
create_time
FROM public.cli_surgery
WHERE delete_flag = '0'
ORDER BY create_time DESC
LIMIT 5;
```
**如果 apply_doctor_name 和 apply_dept_name 字段为空**:说明数据没有保存成功。
**如果字段有值**:说明保存成功,问题出在前端显示。
### 步骤6检查前端 API 响应
1. 打开浏览器开发者工具F12
2. 切换到 Network 标签
3. 新增手术
4. 找到 `/clinical-manage/surgery/surgery-page` 请求
5. 点击查看响应内容
检查响应数据中是否包含 `applyDoctorName``applyDeptName` 字段。
**如果响应中没有这些字段**:说明 MyBatis 映射有问题,检查 XML 配置。
**如果响应中有这些字段但值为 null**说明数据库中为空回到步骤5。
### 步骤7检查前端表格显示
查看前端代码中的表格列配置:
```vue
<el-table-column label="申请医生" align="center" prop="applyDoctorName" width="100" />
<el-table-column label="申请科室" align="center" prop="applyDeptName" width="120" show-overflow-tooltip />
```
确保 `prop` 属性与后端返回的字段名一致(注意大小写)。
## 常见问题和解决方案
### 问题1数据库字段未添加
**症状**:后端报错 "column apply_doctor_name does not exist"
**解决**:执行数据库迁移脚本
### 问题2后端日志显示 applyDoctorName 为 null
**症状**:日志中 "前端提交的数据 - applyDoctorName: null"
**原因**前端提交数据时disabled 字段没有被包含
**解决**:检查前端 submitForm 函数,确保手动设置了这些字段
### 问题3数据库中有值但前端不显示
**症状**:数据库查询有值,前端响应也有值,但表格不显示
**原因**
1. 前端 prop 属性名与后端字段名不一致(大小写问题)
2. 前端数据未正确绑定
**解决**
1. 检查 prop 属性名,确保与后端返回的 JSON 字段名一致
2. 检查浏览器控制台是否有 JavaScript 错误
### 问题4MyBatis 映射未生效
**症状**:后端保存成功,但查询时字段为 null
**原因**XML 映射文件未正确配置
**解决**
1. 检查 SurgeryMapper.xml 中的 resultMap 配置
2. 检查 SQL 查询中是否包含这些字段
3. 重启后端服务
## 验证清单
- [ ] 数据库迁移脚本已执行
- [ ] 数据库字段已添加步骤1
- [ ] 后端服务已重启
- [ ] 后端日志显示申请医生信息步骤4
- [ ] 数据库中已保存数据步骤5
- [ ] 前端 API 响应包含这些字段步骤6
- [ ] 前端表格正确显示步骤7
## 附加 SQL 脚本
### 查看统计信息
```sql
SELECT
COUNT(*) as total_count,
COUNT(apply_doctor_name) as has_doctor_name_count,
COUNT(apply_dept_name) as has_dept_name_count
FROM public.cli_surgery
WHERE delete_flag = '0';
```
### 手动更新测试数据
如果需要手动更新已有的测试数据:
```sql
UPDATE public.cli_surgery
SET apply_doctor_name = '测试医生',
apply_dept_name = '测试科室'
WHERE apply_doctor_name IS NULL
AND delete_flag = '0';
```
## 联系支持
如果以上步骤都无法解决问题,请提供以下信息:
1. 数据库字段查询结果步骤1
2. 后端日志截图步骤4
3. 数据库查询结果步骤5
4. 浏览器 Network 响应截图步骤6
5. 浏览器 Console 错误信息

View File

@@ -0,0 +1,351 @@
# 手术管理模块开发说明
## 模块概述
手术管理模块是一个完整的医疗手术管理系统涵盖从手术排期、执行到记录的全流程管理。本模块基于经典的Spring Boot + Vue3前后端分离架构开发。
## 功能特性
### 1. 手术信息管理
- 手术基本信息录入(手术名称、编码、类型、等级)
- 患者信息关联
- 就诊信息关联
- 手术部位描述
### 2. 手术团队管理
- 主刀医生选择
- 麻醉医生选择
- 助手1/助手2选择
- 巡回护士选择
- 麻醉方式选择
### 3. 手术状态管理
- 待排期
- 已排期
- 手术中
- 已完成
- 已取消
- 暂停
### 4. 手术时间管理
- 计划手术时间
- 实际开始时间
- 实际结束时间
### 5. 诊断信息管理
- 术前诊断
- 术后诊断
- 手术经过描述
- 术后医嘱
- 并发症描述
### 6. 手术费用管理
- 手术费用
- 麻醉费用
- 总费用自动计算
### 7. 手术切口管理
- 切口等级I级、II级、III级、IV级
- 愈合等级(甲级、乙级、丙级)
## 技术架构
### 后端技术栈
- Spring Boot 2.x
- MyBatis Plus
- PostgreSQL 12+
- JDK 1.8+
### 前端技术栈
- Vue 3.x
- Element Plus
- Axios
- Vite
## 目录结构
```
openh-is/
├── openhis-server-new/ # 后端项目
│ ├── openhis-domain/ # 领域层
│ │ └── src/main/java/com/openhis/
│ │ ├── clinical/
│ │ │ ├── domain/
│ │ │ │ └── Surgery.java # 手术实体类
│ │ │ ├── mapper/
│ │ │ │ └── SurgeryMapper.java # 手术Mapper接口
│ │ │ └── service/
│ │ │ ├── ISurgeryService.java # 手术Service接口
│ │ │ └── impl/
│ │ │ └── SurgeryServiceImpl.java # 手术Service实现
│ │ └── common/ # 公共模块
│ │ └── src/main/java/com/openhis/common/
│ │ └── enums/ # 枚举类
│ │ ├── SurgeryTypeEnum.java # 手术类型枚举
│ │ ├── SurgeryStatusEnum.java # 手术状态枚举
│ │ ├── SurgeryLevelEnum.java # 手术等级枚举
│ │ ├── AnesthesiaTypeEnum.java # 麻醉方式枚举
│ │ ├── IncisionLevelEnum.java # 切口等级枚举
│ │ └── HealingLevelEnum.java # 愈合等级枚举
│ │
│ ├── openhis-application/ # 应用层
│ │ └── src/main/java/com/openhis/web/clinicalmanage/
│ │ ├── controller/
│ │ │ └── SurgeryController.java # 手术控制器
│ │ ├── dto/
│ │ │ └── SurgeryDto.java # 手术数据传输对象
│ │ ├── appservice/
│ │ │ ├── ISurgeryAppService.java # 手术应用服务接口
│ │ │ └── impl/
│ │ │ └── SurgeryAppServiceImpl.java # 手术应用服务实现
│ │ └── mapper/
│ │ └── SurgeryAppMapper.java # 手术应用Mapper
│ │
│ └── src/main/resources/mapper/
│ ├── clinical/
│ │ └── SurgeryMapper.xml # 手术Mapper XML
│ └── clinicalmanage/
│ └── SurgeryMapper.xml # 手术应用Mapper XML
├── openhis-ui-vue3/ # 前端项目
│ └── src/
│ ├── api/
│ │ └── surgerymanage.js # 手术API接口
│ └── views/
│ └── surgerymanage/
│ └── index.vue # 手术管理页面
└── surgery_manage_init.sql # 数据库初始化脚本
```
## 数据库设计
### 主表cli_surgery手术管理表
| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | bigint | 主键ID |
| surgery_no | varchar(50) | 手术编号(唯一) |
| patient_id | bigint | 患者ID |
| patient_name | varchar(100) | 患者姓名 |
| encounter_id | bigint | 就诊ID |
| surgery_name | varchar(200) | 手术名称 |
| surgery_code | varchar(100) | 手术编码 |
| surgery_type_enum | int2 | 手术类型 |
| surgery_level | int2 | 手术等级 |
| status_enum | int2 | 手术状态 |
| planned_time | timestamp | 计划手术时间 |
| actual_start_time | timestamp | 实际开始时间 |
| actual_end_time | timestamp | 实际结束时间 |
| main_surgeon_id | bigint | 主刀医生ID |
| main_surgeon_name | varchar(100) | 主刀医生姓名 |
| anesthetist_id | bigint | 麻醉医生ID |
| anesthetist_name | varchar(100) | 麻醉医生姓名 |
| anesthesia_type_enum | int2 | 麻醉方式 |
| body_site | varchar(200) | 手术部位 |
| preoperative_diagnosis | text | 术前诊断 |
| postoperative_diagnosis | text | 术后诊断 |
| surgery_fee | numeric(10,2) | 手术费用 |
| anesthesia_fee | numeric(10,2) | 麻醉费用 |
| total_fee | numeric(10,2) | 总费用 |
### 字典表
手术管理模块包含以下字典类型:
- surgery_status手术状态
- surgery_type手术类型
- surgery_level手术等级
- anesthesia_type麻醉方式
- incision_level切口等级
- healing_level愈合等级
## 安装部署
### 1. 数据库初始化
执行SQL脚本初始化数据库表和字典数据
```bash
psql -U postgres -d his_database -f surgery_manage_init.sql
```
或者使用psql客户端执行
```sql
\i /path/to/surgery_manage_init.sql
```
### 2. 后端配置
1. 将后端代码复制到对应目录
2. 修改数据库连接配置application.yml
3. 启动Spring Boot应用
### 3. 前端配置
1. 将前端代码复制到对应目录
2. 配置API接口地址.env.development
3. 启动前端开发服务器
```bash
npm install
npm run dev
```
## API接口说明
### 1. 分页查询手术列表
**接口地址:** `GET /clinical-manage/surgery/surgery-page`
**请求参数:**
```json
{
"pageNo": 1,
"pageSize": 10,
"surgeryNo": "SS20251230001",
"surgeryName": "阑尾切除术",
"patientName": "张三",
"statusEnum": 1,
"surgeryTypeEnum": 2
}
```
### 2. 查询手术详情
**接口地址:** `GET /clinical-manage/surgery/surgery-detail`
**请求参数:**
```
id: 手术ID
```
### 3. 新增手术
**接口地址:** `POST /clinical-manage/surgery/surgery`
**请求参数:**
```json
{
"patientId": 1,
"surgeryName": "阑尾切除术",
"surgeryCode": "ICD-9-CM:47.09",
"surgeryTypeEnum": 2,
"surgeryLevel": 2,
"plannedTime": "2025-12-31 09:00:00",
"mainSurgeonId": 10,
"anesthetistId": 11,
"anesthesiaTypeEnum": 3,
"bodySite": "腹部"
}
```
### 4. 修改手术
**接口地址:** `PUT /clinical-manage/surgery/surgery`
**请求参数:** 同新增手术需包含id
### 5. 删除手术
**接口地址:** `DELETE /clinical-manage/surgery/surgery`
**请求参数:**
```
id: 手术ID
```
### 6. 更新手术状态
**接口地址:** `PUT /clinical-manage/surgery/surgery-status`
**请求参数:**
```
id: 手术ID
statusEnum: 状态值
```
## 前端页面功能
### 1. 查询功能
- 支持按手术编号、手术名称、患者姓名模糊查询
- 支持按手术状态、手术类型精确查询
- 支持按计划时间范围查询
### 2. 新增功能
- 完整的手术信息录入表单
- 患者下拉选择
- 医生/护士下拉选择
- 费用自动计算
### 3. 编辑功能
- 仅待排期和已排期状态的手术可编辑
- 手术中或已完成的手术不可编辑
### 4. 状态流转
- 已排期 → 手术中
- 手术中 → 已完成
- 待排期/已排期 → 已取消
### 5. 删除功能
- 仅待排期和已排期状态的手术可删除
- 已完成的手术不能删除
## 扩展开发建议
### 1. 手术排期管理
- 可增加手术排期日历视图
- 手术室资源冲突检测
- 手术排队优先级管理
### 2. 手术统计报表
- 手术量统计
- 手术类型分布
- 手术成功率统计
- 手术费用统计
### 3. 手术文档管理
- 手术知情同意书
- 手术安全核查表
- 手术记录单
- 麻醉记录单
### 4. 手术质控管理
- 手术质量评估
- 并发症统计
- 术后恢复跟踪
- 手术质量指标管理
## 注意事项
1. **手术编号生成**手术编号采用自动生成机制格式为SS + 10位数字
2. **权限控制**:需要配置相应的菜单权限和操作权限
3. **数据校验**:新增手术时必须选择患者和主刀医生
4. **状态流转**:手术状态的流转需要符合业务逻辑
5. **费用计算**:总费用自动计算,不允许手动修改
## 常见问题
### Q1: 手术编号重复怎么办?
A: 手术编号是系统自动生成的唯一编号不会重复。如果需要自定义编号需要修改SurgeryServiceImpl中的生成逻辑。
### Q2: 如何添加新的手术类型?
A: 在数据库sys_dict_data表中添加新的surgery_type字典数据即可。
### Q3: 手术开始后还能修改信息吗?
A: 根据业务规则,手术开始后不允许修改基本信息,但可以补充术后诊断等信息。
### Q4: 如何实现手术室资源管理?
A: 可以新增手术室管理模块,建立手术排期与手术室的关联关系,实现资源冲突检测。
## 版本历史
- v1.0.0 (2025-12-30)
- 初始版本发布
- 实现手术基本管理功能
- 实现手术状态流转
- 实现手术团队管理
## 联系方式
如有问题或建议,请联系开发团队。

View File

View File

@@ -0,0 +1,160 @@
# 门诊就诊记录SQL查询优化建议
## 当前查询分析
### 主要查询表
```sql
SELECT
enc.id as encounterId,
pt.name,
pt.id_card,
pt.bus_no as patientBusNo,
enc.bus_no as encounterBusNo,
pt.gender_enum,
pt.phone,
enc.create_time as encounterTime,
enc.status_enum as subjectStatusEnum,
org.name as organizationName,
prac.name as doctorName
FROM adm_encounter AS enc
LEFT JOIN adm_organization AS org ON enc.organization_id = org.ID AND org.delete_flag = '0'
LEFT JOIN adm_encounter_participant AS ep
ON enc.ID = ep.encounter_id AND ep.type_code = #{participantType} AND ep.delete_flag = '0'
LEFT JOIN adm_practitioner AS prac ON ep.practitioner_id = prac.ID AND prac.delete_flag = '0'
LEFT JOIN adm_patient AS pt ON enc.patient_id = pt.ID AND pt.delete_flag = '0'
```
### 常见查询条件
1. `enc.delete_flag = '0'`
2. `enc.tenant_id = ?`
3. `pt.name LIKE ?`
4. `pt.id_card LIKE ?`
5. `pt.bus_no LIKE ?`
6. `enc.bus_no LIKE ?`
7. `pt.gender_enum = ?`
8. `enc.status_enum = ?`
9. `prac.name LIKE ?`
10. `pt.phone LIKE ?`
11. `enc.create_time BETWEEN ? AND ?`
## 索引优化建议
### 1. adm_encounter 表索引
```sql
-- 复合索引:提高查询性能
CREATE INDEX idx_encounter_tenant_delete_status ON adm_encounter(tenant_id, delete_flag, status_enum);
-- 时间范围查询索引
CREATE INDEX idx_encounter_create_time ON adm_encounter(create_time);
-- 业务编号查询索引
CREATE INDEX idx_encounter_bus_no ON adm_encounter(bus_no);
-- 患者ID关联索引
CREATE INDEX idx_encounter_patient_id ON adm_encounter(patient_id);
```
### 2. adm_patient 表索引
```sql
-- 姓名模糊查询索引
CREATE INDEX idx_patient_name ON adm_patient(name);
-- 身份证号查询索引
CREATE INDEX idx_patient_id_card ON adm_patient(id_card);
-- 业务编号查询索引
CREATE INDEX idx_patient_bus_no ON adm_patient(bus_no);
-- 电话查询索引
CREATE INDEX idx_patient_phone ON adm_patient(phone);
-- 复合索引:常用查询条件
CREATE INDEX idx_patient_delete_gender ON adm_patient(delete_flag, gender_enum);
```
### 3. adm_encounter_participant 表索引
```sql
-- 复合索引:提高连接性能
CREATE INDEX idx_ep_encounter_type ON adm_encounter_participant(encounter_id, type_code, delete_flag);
-- 参与者ID索引
CREATE INDEX idx_ep_practitioner ON adm_encounter_participant(practitioner_id);
```
### 4. adm_practitioner 表索引
```sql
-- 姓名查询索引
CREATE INDEX idx_practitioner_name ON adm_practitioner(name);
-- 复合索引:常用查询条件
CREATE INDEX idx_practitioner_delete_tenant ON adm_practitioner(delete_flag, tenant_id);
```
### 5. adm_organization 表索引
```sql
-- 主键关联索引
CREATE INDEX idx_organization_id_delete ON adm_organization(id, delete_flag);
```
## 查询优化建议
### 1. 添加查询统计信息收集
```sql
-- 定期分析表统计信息
ANALYZE TABLE adm_encounter;
ANALYZE TABLE adm_patient;
ANALYZE TABLE adm_encounter_participant;
ANALYZE TABLE adm_practitioner;
ANALYZE TABLE adm_organization;
```
### 2. 考虑分区表(针对大数据量)
如果 `adm_encounter` 表数据量超过100万条考虑按时间分区
```sql
-- 按月分区
PARTITION BY RANGE (YEAR(create_time) * 100 + MONTH(create_time))
(
PARTITION p202501 VALUES LESS THAN (202501),
PARTITION p202502 VALUES LESS THAN (202502),
-- ... 更多分区
);
```
### 3. 添加覆盖索引Covering Index
对于常用查询字段,创建覆盖索引避免回表:
```sql
CREATE INDEX idx_encounter_cover ON adm_encounter(
tenant_id, delete_flag, create_time,
status_enum, bus_no, patient_id
) INCLUDE (organization_id);
```
## 执行计划检查
建议定期检查查询执行计划:
```sql
EXPLAIN ANALYZE
SELECT -- 完整查询语句
FROM adm_encounter AS enc
-- ... 连接条件
WHERE enc.delete_flag = '0'
AND enc.tenant_id = 1
-- ... 其他条件
ORDER BY enc.create_time DESC;
```
## 监控建议
1. **慢查询监控**监控执行时间超过1秒的查询
2. **索引使用监控**:定期检查未使用的索引
3. **表空间监控**:监控表增长和碎片情况
4. **连接性能监控**监控JOIN操作的性能
## 实施步骤
1. 在测试环境创建建议的索引
2. 执行查询性能测试
3. 分析执行计划,确认索引有效性
4. 在生产环境非高峰期创建索引
5. 监控生产环境性能变化
6. 定期维护和优化索引

View File

@@ -61,7 +61,25 @@ public class SysConfigController extends BaseController {
*/
@GetMapping(value = "/configKey/{configKey}")
public AjaxResult getConfigKey(@PathVariable String configKey) {
return success(configService.selectConfigByKey(configKey));
String configValue = configService.selectConfigByKey(configKey);
// 确保即使返回 null 或空字符串,也明确设置 data 字段
// 如果 configValue 是 null转换为空字符串
if (configValue == null) {
configValue = "";
}
// 直接创建 AjaxResult 并明确设置 data 字段,确保 data 字段始终存在
AjaxResult result = new AjaxResult();
result.put("code", 200);
result.put("msg", "操作成功");
result.put("data", configValue); // 明确设置 data 字段,即使值为空字符串
System.out.println("=== getConfigKey 调试信息 ===");
System.out.println("configKey: " + configKey);
System.out.println("configValue: [" + configValue + "]");
System.out.println("result.data: " + result.get("data"));
System.out.println("result.msg: " + result.get("msg"));
System.out.println("result.code: " + result.get("code"));
System.out.println("============================");
return result;
}
/**

View File

@@ -11,6 +11,7 @@ import com.core.framework.web.service.SysLoginService;
import com.core.framework.web.service.SysPermissionService;
import com.core.framework.web.service.TokenService;
import com.core.system.service.ISysMenuService;
import com.core.system.service.ISysTenantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -39,6 +40,9 @@ public class SysLoginController {
@Autowired
private TokenService tokenService;
@Autowired
private ISysTenantService tenantService;
/**已评审
* 登录方法
*
@@ -72,12 +76,22 @@ public class SysLoginController {
loginUser.setPermissions(permissions);
tokenService.refreshToken(loginUser);
}
// 获取租户名称
String tenantName = null;
if (loginUser.getTenantId() != null) {
com.core.system.domain.SysTenant tenant = tenantService.getById(loginUser.getTenantId());
if (tenant != null) {
tenantName = tenant.getTenantName();
}
}
AjaxResult ajax = AjaxResult.success();
ajax.put("optionJson", loginUser.getOptionJson());
ajax.put("optionMap", loginUser.getOptionMap());
ajax.put("practitionerId", String.valueOf(loginUser.getPractitionerId()));
ajax.put("user", user);
ajax.put("roles", roles);
ajax.put("permissions", permissions);
ajax.put("tenantName", tenantName);
return ajax;
}

View File

@@ -3,9 +3,12 @@ package com.core.web.controller.system;
import com.core.common.annotation.Log;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
import com.core.common.core.domain.entity.SysUser;
import com.core.common.core.domain.model.LoginUser;
import com.core.common.core.page.TableDataInfo;
import com.core.common.enums.BusinessType;
import com.core.system.domain.SysNotice;
import com.core.system.service.ISysNoticeReadService;
import com.core.system.service.ISysNoticeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -25,6 +28,9 @@ public class SysNoticeController extends BaseController {
@Autowired
private ISysNoticeService noticeService;
@Autowired
private ISysNoticeReadService noticeReadService;
/**
* 获取通知公告列表
*/
@@ -36,6 +42,108 @@ public class SysNoticeController extends BaseController {
return getDataTable(list);
}
/**
* 获取当前用户的公告列表(公开接口)
* 公告类型:通常 noticeType = '1' 代表通知noticeType = '2' 代表公告
*/
@GetMapping("/public/list")
public TableDataInfo getPublicList(SysNotice notice) {
// 只查询状态为正常0且已发布1的公告
notice.setStatus("0");
notice.setPublishStatus("1");
// 公告类型设置为 '2'(公告)
notice.setNoticeType("2");
// 设置分页参数
startPage();
List<SysNotice> list = noticeService.selectNoticeList(notice);
return getDataTable(list);
}
/**
* 获取当前用户的通知列表(公开接口)
* 通知类型:通常 noticeType = '1' 代表通知noticeType = '2' 代表公告
* 返回已发布且状态正常的所有公告和通知,并标注已读状态
* 按优先级排序,高优先级在前
*/
@GetMapping("/public/notice")
public AjaxResult getUserNotices() {
// 获取当前用户信息
LoginUser loginUser = getLoginUser();
SysUser currentUser = loginUser.getUser();
// 查询已发布且状态正常的所有公告和通知
SysNotice notice = new SysNotice();
notice.setStatus("0");
notice.setPublishStatus("1");
List<SysNotice> list = noticeService.selectNoticeList(notice);
// 按优先级排序1高 2中 3低相同优先级按创建时间降序
list.sort((a, b) -> {
String priorityA = a.getPriority() != null ? a.getPriority() : "3";
String priorityB = b.getPriority() != null ? b.getPriority() : "3";
int priorityCompare = priorityA.compareTo(priorityB);
if (priorityCompare != 0) {
return priorityCompare;
}
// 相同优先级,按创建时间降序
return b.getCreateTime().compareTo(a.getCreateTime());
});
// 获取用户已读的公告/通知ID列表
List<Long> readIds = noticeReadService.selectReadNoticeIdsByUserId(currentUser.getUserId());
// 为每个公告/通知添加已读状态
for (SysNotice item : list) {
boolean isRead = readIds.contains(item.getNoticeId());
item.setIsRead(isRead);
}
return success(list);
}
/**
* 获取用户未读公告/通知数量(公开接口)
*/
@GetMapping("/public/unread/count")
public AjaxResult getUnreadCount() {
LoginUser loginUser = getLoginUser();
SysUser currentUser = loginUser.getUser();
int count = noticeReadService.getUnreadCount(currentUser.getUserId());
return success(count);
}
/**
* 标记公告/通知为已读(公开接口)
*/
@PostMapping("/public/read/{noticeId}")
public AjaxResult markAsRead(@PathVariable Long noticeId) {
LoginUser loginUser = getLoginUser();
SysUser currentUser = loginUser.getUser();
return noticeReadService.markAsRead(noticeId, currentUser.getUserId());
}
/**
* 批量标记公告/通知为已读(公开接口)
*/
@PostMapping("/public/read/all")
public AjaxResult markAllAsRead(@RequestBody Long[] noticeIds) {
LoginUser loginUser = getLoginUser();
SysUser currentUser = loginUser.getUser();
return noticeReadService.markAllAsRead(noticeIds, currentUser.getUserId());
}
/**
* 获取用户已读公告/通知ID列表公开接口
*/
@GetMapping("/public/read/ids")
public AjaxResult getReadNoticeIds() {
LoginUser loginUser = getLoginUser();
SysUser currentUser = loginUser.getUser();
List<Long> readIds = noticeReadService.selectReadNoticeIdsByUserId(currentUser.getUserId());
return success(readIds);
}
/**
* 根据通知公告编号获取详细信息
*/
@@ -53,6 +161,14 @@ public class SysNoticeController extends BaseController {
@PostMapping
public AjaxResult add(@Validated @RequestBody SysNotice notice) {
notice.setCreateBy(getUsername());
// 新建的公告默认为未发布状态
if (notice.getPublishStatus() == null || notice.getPublishStatus().isEmpty()) {
notice.setPublishStatus("0");
}
// 设置默认优先级为中2
if (notice.getPriority() == null || notice.getPriority().isEmpty()) {
notice.setPriority("2");
}
return toAjax(noticeService.insertNotice(notice));
}
@@ -76,4 +192,42 @@ public class SysNoticeController extends BaseController {
public AjaxResult remove(@PathVariable Long[] noticeIds) {
return toAjax(noticeService.deleteNoticeByIds(noticeIds));
}
/**
* 发布公告/通知
*/
@PreAuthorize("@ss.hasPermi('system:notice:edit')")
@Log(title = "发布公告", businessType = BusinessType.UPDATE)
@PutMapping("/publish/{noticeId}")
public AjaxResult publish(@PathVariable Long noticeId) {
SysNotice notice = noticeService.selectNoticeById(noticeId);
if (notice == null) {
return error("公告不存在");
}
if ("1".equals(notice.getPublishStatus())) {
return error("该公告已发布");
}
notice.setPublishStatus("1");
notice.setUpdateBy(getUsername());
return toAjax(noticeService.updateNotice(notice));
}
/**
* 取消发布公告/通知
*/
@PreAuthorize("@ss.hasPermi('system:notice:edit')")
@Log(title = "取消发布", businessType = BusinessType.UPDATE)
@PutMapping("/unpublish/{noticeId}")
public AjaxResult unpublish(@PathVariable Long noticeId) {
SysNotice notice = noticeService.selectNoticeById(noticeId);
if (notice == null) {
return error("公告不存在");
}
if ("0".equals(notice.getPublishStatus())) {
return error("该公告未发布");
}
notice.setPublishStatus("0");
notice.setUpdateBy(getUsername());
return toAjax(noticeService.updateNotice(notice));
}
}

View File

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

View File

@@ -1,12 +1,10 @@
package com.core.flowable.common.expand.el;
import javax.annotation.Resource;
import com.core.system.service.ISysDeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import com.core.system.service.ISysDeptService;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
/**
* 扩展表达式

View File

@@ -1,22 +1,5 @@
package com.core.flowable.controller;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.core.common.annotation.Log;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
@@ -30,11 +13,25 @@ import com.core.system.domain.SysExpression;
import com.core.system.service.ISysExpressionService;
import com.core.system.service.ISysRoleService;
import com.core.system.service.ISysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
/**
* <p>

View File

@@ -1,21 +1,19 @@
package com.core.flowable.controller;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.core.common.annotation.Log;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
import com.core.common.enums.BusinessType;
import com.core.flowable.domain.vo.FlowTaskVo;
import com.core.flowable.service.IFlowInstanceService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* <p>

View File

@@ -1,16 +1,5 @@
package com.core.flowable.controller;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.core.common.annotation.Log;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
@@ -19,11 +8,19 @@ import com.core.flowable.domain.dto.FlowTaskDto;
import com.core.flowable.domain.vo.FlowQueryVo;
import com.core.flowable.domain.vo.FlowTaskVo;
import com.core.flowable.service.IFlowTaskService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* <p>

View File

@@ -1,13 +1,5 @@
package com.core.flowable.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.core.common.annotation.Log;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
@@ -16,6 +8,12 @@ import com.core.common.enums.BusinessType;
import com.core.common.utils.poi.ExcelUtil;
import com.core.system.domain.SysExpression;
import com.core.system.service.ISysExpressionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 流程达式Controller

View File

@@ -1,11 +1,5 @@
package com.core.flowable.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.core.common.annotation.Log;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
@@ -16,6 +10,11 @@ import com.core.flowable.service.ISysDeployFormService;
import com.core.flowable.service.ISysFormService;
import com.core.system.domain.SysDeployForm;
import com.core.system.domain.SysForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 流程表单Controller

View File

@@ -1,13 +1,5 @@
package com.core.flowable.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.core.common.annotation.Log;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
@@ -16,6 +8,12 @@ import com.core.common.enums.BusinessType;
import com.core.common.utils.poi.ExcelUtil;
import com.core.system.domain.SysListener;
import com.core.system.service.ISysListenerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 流程监听Controller

View File

@@ -1,10 +1,10 @@
package com.core.flowable.domain.dto;
import java.io.Serializable;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
/**
* @author system
* @date 2021/3/28 15:50

View File

@@ -1,9 +1,9 @@
package com.core.flowable.domain.dto;
import java.io.Serializable;
import lombok.Data;
import java.io.Serializable;
/**
* @author system
* @date 2021/3/31 23:20

View File

@@ -1,9 +1,9 @@
package com.core.flowable.domain.dto;
import java.io.Serializable;
import lombok.Data;
import java.io.Serializable;
/**
* 动态人员、组
*

View File

@@ -1,9 +1,9 @@
package com.core.flowable.domain.dto;
import java.io.Serializable;
import lombok.Data;
import java.io.Serializable;
/**
* @author system
* @date 2021/3/28 19:48

View File

@@ -1,15 +1,14 @@
package com.core.flowable.domain.dto;
import java.io.Serializable;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* 工作流任务

View File

@@ -1,9 +1,9 @@
package com.core.flowable.domain.dto;
import java.io.Serializable;
import lombok.Data;
import java.io.Serializable;
/**
* @author system
* @date 2021/4/21 20:55

View File

@@ -1,12 +1,12 @@
package com.core.flowable.domain.vo;
import java.util.List;
import java.util.Map;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* <p>
* 流程任务

View File

@@ -1,12 +1,11 @@
package com.core.flowable.factory;
import javax.annotation.Resource;
import lombok.Getter;
import org.flowable.engine.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import lombok.Getter;
import javax.annotation.Resource;
/**
* flowable 引擎注入封装

View File

@@ -1,5 +1,11 @@
package com.core.flowable.flow;
import org.flowable.bpmn.model.AssociationDirection;
import org.flowable.bpmn.model.GraphicInfo;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import org.flowable.image.util.ReflectUtil;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
@@ -13,13 +19,6 @@ import java.io.IOException;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import javax.imageio.ImageIO;
import org.flowable.bpmn.model.AssociationDirection;
import org.flowable.bpmn.model.GraphicInfo;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import org.flowable.image.util.ReflectUtil;
/**
* @author system
* @date 2021/4/4 23:58

View File

@@ -1,13 +1,13 @@
package com.core.flowable.flow;
import java.util.Iterator;
import java.util.List;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.image.impl.DefaultProcessDiagramCanvas;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import java.util.Iterator;
import java.util.List;
/**
* @author system
* @date 2021/4/5 0:31

View File

@@ -1,17 +1,16 @@
package com.core.flowable.flow;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.ProcessDefinition;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* @author system

View File

@@ -1,8 +1,6 @@
package com.core.flowable.flow;
import java.util.*;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
@@ -10,7 +8,8 @@ import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.history.HistoricTaskInstance;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author system

View File

@@ -1,12 +1,11 @@
package com.core.flowable.listener;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
/**
* 执行监听器
*

View File

@@ -1,11 +1,10 @@
package com.core.flowable.listener;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
/**
* 任务监听器
*

View File

@@ -1,13 +1,13 @@
package com.core.flowable.service;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.AjaxResult;
import com.core.system.domain.FlowProcDefDto;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* @author system
* @date 2021-04-03 14:41

View File

@@ -1,11 +1,10 @@
package com.core.flowable.service;
import java.util.Map;
import org.flowable.engine.history.HistoricProcessInstance;
import com.core.common.core.domain.AjaxResult;
import com.core.flowable.domain.vo.FlowTaskVo;
import org.flowable.engine.history.HistoricProcessInstance;
import java.util.Map;
/**
* @author system

View File

@@ -1,11 +1,11 @@
package com.core.flowable.service;
import java.io.InputStream;
import com.core.common.core.domain.AjaxResult;
import com.core.flowable.domain.vo.FlowQueryVo;
import com.core.flowable.domain.vo.FlowTaskVo;
import java.io.InputStream;
/**
* @author system
* @date 2021-04-03 14:42

View File

@@ -1,10 +1,10 @@
package com.core.flowable.service;
import java.util.List;
import com.core.system.domain.SysDeployForm;
import com.core.system.domain.SysForm;
import java.util.List;
/**
* 流程实例关联表单Service接口
*

View File

@@ -1,9 +1,9 @@
package com.core.flowable.service;
import java.util.List;
import com.core.system.domain.SysForm;
import java.util.List;
/**
* 表单
*

View File

@@ -1,25 +1,5 @@
package com.core.flowable.service.impl;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Resource;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.AjaxResult;
import com.core.common.core.domain.entity.SysUser;
@@ -37,8 +17,25 @@ import com.core.system.service.ISysDeptService;
import com.core.system.service.ISysUserService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 流程定义

View File

@@ -1,20 +1,18 @@
package com.core.flowable.service.impl;
import java.util.Map;
import java.util.Objects;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.history.HistoricProcessInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.core.common.core.domain.AjaxResult;
import com.core.common.utils.SecurityUtils;
import com.core.flowable.domain.vo.FlowTaskVo;
import com.core.flowable.factory.FlowServiceFactory;
import com.core.flowable.service.IFlowInstanceService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.history.HistoricProcessInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
import java.util.Objects;
/**
* <p>

View File

@@ -1,43 +1,5 @@
package com.core.flowable.service.impl;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.cmd.AddMultiInstanceExecutionCmd;
import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
@@ -65,8 +27,43 @@ import com.core.flowable.service.ISysFormService;
import com.core.system.domain.SysForm;
import com.core.system.service.ISysRoleService;
import com.core.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.cmd.AddMultiInstanceExecutionCmd;
import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @author system

View File

@@ -1,15 +1,14 @@
package com.core.flowable.service.impl;
import java.util.List;
import java.util.Objects;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.core.flowable.service.ISysDeployFormService;
import com.core.system.domain.SysDeployForm;
import com.core.system.domain.SysForm;
import com.core.system.mapper.SysDeployFormMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/**
* 流程实例关联表单Service业务层处理

View File

@@ -1,14 +1,13 @@
package com.core.flowable.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.core.common.utils.DateUtils;
import com.core.flowable.service.ISysFormService;
import com.core.system.domain.SysForm;
import com.core.system.mapper.SysFormMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 流程表单Service业务层处理

View File

@@ -60,6 +60,17 @@
<artifactId>core-system</artifactId>
</dependency>
<dependency>
<groupId>com.core</groupId>
<artifactId>core-common</artifactId>
</dependency>
<!-- JSQLParser - 用于MyBatis Plus -->
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,13 +1,5 @@
package com.core.framework.aspectj;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import com.core.common.annotation.DataScope;
import com.core.common.constant.UserConstants;
import com.core.common.core.domain.BaseEntity;
@@ -18,6 +10,13 @@ import com.core.common.core.text.Convert;
import com.core.common.utils.SecurityUtils;
import com.core.common.utils.StringUtils;
import com.core.framework.security.context.PermissionContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* 数据过滤处理

View File

@@ -1,7 +1,8 @@
package com.core.framework.aspectj;
import java.util.Objects;
import com.core.common.annotation.DataSource;
import com.core.common.utils.StringUtils;
import com.core.framework.datasource.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@@ -13,9 +14,7 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.core.common.annotation.DataSource;
import com.core.common.utils.StringUtils;
import com.core.framework.datasource.DynamicDataSourceContextHolder;
import java.util.Objects;
/**
* 多数据源处理

View File

@@ -1,24 +1,5 @@
package com.core.framework.aspectj;
import java.util.Collection;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson2.JSON;
import com.core.common.annotation.Log;
import com.core.common.core.domain.entity.SysUser;
@@ -33,6 +14,23 @@ import com.core.common.utils.ip.IpUtils;
import com.core.framework.manager.AsyncManager;
import com.core.framework.manager.factory.AsyncFactory;
import com.core.system.domain.SysOperLog;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
/**
* 操作日志记录处理

View File

@@ -1,9 +1,10 @@
package com.core.framework.aspectj;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import com.core.common.annotation.RateLimiter;
import com.core.common.enums.LimitType;
import com.core.common.exception.ServiceException;
import com.core.common.utils.StringUtils;
import com.core.common.utils.ip.IpUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@@ -15,11 +16,9 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import com.core.common.annotation.RateLimiter;
import com.core.common.enums.LimitType;
import com.core.common.exception.ServiceException;
import com.core.common.utils.StringUtils;
import com.core.common.utils.ip.IpUtils;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
/**
* 限流处理

View File

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

View File

@@ -1,13 +1,13 @@
package com.core.framework.config;
import java.util.TimeZone;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import java.util.TimeZone;
/**
* 程序注解配置
*

View File

@@ -1,14 +1,13 @@
package com.core.framework.config;
import static com.google.code.kaptcha.Constants.*;
import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
import static com.google.code.kaptcha.Constants.*;
/**
* 验证码配置

View File

@@ -1,19 +1,5 @@
package com.core.framework.config;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.*;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
@@ -22,6 +8,18 @@ import com.core.common.enums.DataSourceType;
import com.core.common.utils.spring.SpringUtils;
import com.core.framework.config.properties.DruidProperties;
import com.core.framework.datasource.DynamicDataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.servlet.*;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* druid 配置多数据源

View File

@@ -1,15 +1,14 @@
package com.core.framework.config;
import java.nio.charset.Charset;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.filter.Filter;
import com.core.common.constant.Constants;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.nio.charset.Charset;
/**
* Redis使用FastJson序列化

View File

@@ -1,19 +1,17 @@
package com.core.framework.config;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.DispatcherType;
import com.core.common.filter.RepeatableFilter;
import com.core.common.filter.XssFilter;
import com.core.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.core.common.filter.RepeatableFilter;
import com.core.common.filter.XssFilter;
import com.core.common.utils.StringUtils;
import javax.servlet.DispatcherType;
import java.util.HashMap;
import java.util.Map;
/**
* Filter配置

View File

@@ -1,5 +1,6 @@
package com.core.framework.config;
import com.core.common.constant.Constants;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
@@ -8,8 +9,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import com.core.common.constant.Constants;
/**
* 资源文件配置加载
*

View File

@@ -1,9 +1,9 @@
package com.core.framework.config;
import java.util.Random;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
import java.util.Random;
/**
* 验证码文本生成器
*

View File

@@ -1,17 +1,5 @@
package com.core.framework.config;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
@@ -20,9 +8,18 @@ import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInt
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.core.common.utils.SecurityUtils;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration

View File

@@ -1,7 +1,8 @@
package com.core.framework.config;
import java.util.concurrent.TimeUnit;
import com.core.common.config.CoreConfig;
import com.core.common.constant.Constants;
import com.core.framework.interceptor.RepeatSubmitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -13,9 +14,7 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.core.common.config.CoreConfig;
import com.core.common.constant.Constants;
import com.core.framework.interceptor.RepeatSubmitInterceptor;
import java.util.concurrent.TimeUnit;
/**
* 通用配置

View File

@@ -1,5 +1,9 @@
package com.core.framework.config;
import com.core.framework.config.properties.PermitAllUrlProperties;
import com.core.framework.security.filter.JwtAuthenticationTokenFilter;
import com.core.framework.security.handle.AuthenticationEntryPointImpl;
import com.core.framework.security.handle.LogoutSuccessHandlerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -17,11 +21,6 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.filter.CorsFilter;
import com.core.framework.config.properties.PermitAllUrlProperties;
import com.core.framework.security.filter.JwtAuthenticationTokenFilter;
import com.core.framework.security.handle.AuthenticationEntryPointImpl;
import com.core.framework.security.handle.LogoutSuccessHandlerImpl;
/**
* spring security配置
*

View File

@@ -1,10 +1,9 @@
package com.core.framework.config;
import javax.servlet.http.HttpServletRequest;
import com.core.common.utils.ServletUtils;
import org.springframework.stereotype.Component;
import com.core.common.utils.ServletUtils;
import javax.servlet.http.HttpServletRequest;
/**
* 服务相关配置

View File

@@ -1,15 +1,14 @@
package com.core.framework.config;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import com.core.common.utils.Threads;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import com.core.common.utils.Threads;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置

View File

@@ -1,10 +1,9 @@
package com.core.framework.config.properties;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.pool.DruidDataSource;
/**
* druid 配置属性
*

View File

@@ -1,8 +1,6 @@
package com.core.framework.config.properties;
import java.util.*;
import java.util.regex.Pattern;
import com.core.common.annotation.Anonymous;
import org.apache.commons.lang3.RegExUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
@@ -14,7 +12,8 @@ import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import com.core.common.annotation.Anonymous;
import java.util.*;
import java.util.regex.Pattern;
/**
* 设置Anonymous注解允许匿名访问的url

View File

@@ -1,10 +1,9 @@
package com.core.framework.datasource;
import java.util.Map;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.Map;
/**
* 动态数据源

View File

@@ -1,18 +1,16 @@
package com.core.framework.handler;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.core.common.core.domain.model.LoginUser;
import com.core.common.utils.SecurityUtils;
import com.core.framework.config.TenantContext;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
@Component
public class MybastisColumnsHandler implements MetaObjectHandler {

View File

@@ -1,18 +1,16 @@
package com.core.framework.interceptor;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import com.alibaba.fastjson2.JSON;
import com.core.common.annotation.RepeatSubmit;
import com.core.common.core.domain.AjaxResult;
import com.core.common.utils.ServletUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* 防止重复提交拦截器

View File

@@ -1,15 +1,5 @@
package com.core.framework.interceptor.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson2.JSON;
import com.core.common.annotation.RepeatSubmit;
import com.core.common.constant.CacheConstants;
@@ -18,6 +8,14 @@ import com.core.common.filter.RepeatedlyRequestWrapper;
import com.core.common.utils.StringUtils;
import com.core.common.utils.http.HttpHelper;
import com.core.framework.interceptor.RepeatSubmitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 判断请求url和数据是否和上一次相同 如果和上次相同,则是重复提交表单。 有效时间为10秒内。

View File

@@ -1,12 +1,12 @@
package com.core.framework.manager;
import com.core.common.utils.Threads;
import com.core.common.utils.spring.SpringUtils;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.core.common.utils.Threads;
import com.core.common.utils.spring.SpringUtils;
/**
* 异步任务管理器
*

View File

@@ -1,11 +1,11 @@
package com.core.framework.manager;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
/**
* 确保应用退出时能关闭后台线程
*

View File

@@ -1,10 +1,5 @@
package com.core.framework.manager.factory;
import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.core.common.constant.Constants;
import com.core.common.utils.LogUtils;
import com.core.common.utils.ServletUtils;
@@ -16,8 +11,11 @@ import com.core.system.domain.SysLogininfor;
import com.core.system.domain.SysOperLog;
import com.core.system.service.ISysLogininforService;
import com.core.system.service.ISysOperLogService;
import eu.bitwalker.useragentutils.UserAgent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.TimerTask;
/**
* 异步工厂(产生任务用)

View File

@@ -1,10 +1,9 @@
package com.core.framework.security.context;
import com.core.common.core.text.Convert;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import com.core.common.core.text.Convert;
/**
* 权限信息
*

View File

@@ -1,12 +1,9 @@
package com.core.framework.security.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.core.common.core.domain.model.LoginUser;
import com.core.common.utils.SecurityUtils;
import com.core.common.utils.StringUtils;
import com.core.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -14,10 +11,11 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.core.common.core.domain.model.LoginUser;
import com.core.common.utils.SecurityUtils;
import com.core.common.utils.StringUtils;
import com.core.framework.web.service.TokenService;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* token过滤器 验证token有效性

View File

@@ -1,20 +1,18 @@
package com.core.framework.security.handle;
import java.io.IOException;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson2.JSON;
import com.core.common.constant.HttpStatus;
import com.core.common.core.domain.AjaxResult;
import com.core.common.utils.ServletUtils;
import com.core.common.utils.StringUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;
/**
* 认证失败处理类 返回未授权

View File

@@ -1,16 +1,5 @@
package com.core.framework.security.handle;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import com.alibaba.fastjson2.JSON;
import com.core.common.constant.Constants;
import com.core.common.core.domain.AjaxResult;
@@ -21,6 +10,15 @@ import com.core.common.utils.StringUtils;
import com.core.framework.manager.AsyncManager;
import com.core.framework.manager.factory.AsyncFactory;
import com.core.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定义退出处理类 返回成功

View File

@@ -1,14 +1,8 @@
package com.core.framework.web.domain;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import com.core.common.utils.Arith;
import com.core.common.utils.ip.IpUtils;
import com.core.framework.web.domain.server.*;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.CentralProcessor.TickType;
@@ -19,6 +13,11 @@ import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
/**
* 服务器相关信息
*

View File

@@ -1,10 +1,10 @@
package com.core.framework.web.domain.server;
import java.lang.management.ManagementFactory;
import com.core.common.utils.Arith;
import com.core.common.utils.DateUtils;
import java.lang.management.ManagementFactory;
/**
* JVM相关信息
*

View File

@@ -1,7 +1,13 @@
package com.core.framework.web.exception;
import javax.servlet.http.HttpServletRequest;
import com.core.common.constant.HttpStatus;
import com.core.common.core.domain.AjaxResult;
import com.core.common.core.text.Convert;
import com.core.common.exception.DemoModeException;
import com.core.common.exception.NonCaptureException;
import com.core.common.exception.ServiceException;
import com.core.common.utils.StringUtils;
import com.core.common.utils.html.EscapeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
@@ -13,14 +19,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import com.core.common.constant.HttpStatus;
import com.core.common.core.domain.AjaxResult;
import com.core.common.core.text.Convert;
import com.core.common.exception.DemoModeException;
import com.core.common.exception.NonCaptureException;
import com.core.common.exception.ServiceException;
import com.core.common.utils.StringUtils;
import com.core.common.utils.html.EscapeUtil;
import javax.servlet.http.HttpServletRequest;
/**
* 全局异常处理器

View File

@@ -1,16 +1,15 @@
package com.core.framework.web.service;
import java.util.Set;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.core.common.constant.Constants;
import com.core.common.core.domain.entity.SysRole;
import com.core.common.core.domain.model.LoginUser;
import com.core.common.utils.SecurityUtils;
import com.core.common.utils.StringUtils;
import com.core.framework.security.context.PermissionContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Set;
/**
* 首创 自定义权限实现ss取自SpringSecurity首字母

View File

@@ -1,19 +1,5 @@
package com.core.framework.web.service;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Resource;
import com.core.common.enums.DelFlag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson2.JSONObject;
import com.core.common.constant.CacheConstants;
import com.core.common.constant.Constants;
@@ -24,6 +10,7 @@ import com.core.common.core.domain.entity.SysUser;
import com.core.common.core.domain.model.LoginUser;
import com.core.common.core.domain.model.LoginUserExtend;
import com.core.common.core.redis.RedisCache;
import com.core.common.enums.DelFlag;
import com.core.common.enums.TenantStatus;
import com.core.common.exception.ServiceException;
import com.core.common.exception.user.*;
@@ -39,6 +26,17 @@ import com.core.system.service.ISysConfigService;
import com.core.system.service.ISysTenantOptionService;
import com.core.system.service.ISysTenantService;
import com.core.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* 登录校验方法

View File

@@ -1,12 +1,5 @@
package com.core.framework.web.service;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.core.common.constant.CacheConstants;
import com.core.common.core.domain.entity.SysUser;
import com.core.common.core.redis.RedisCache;
@@ -14,6 +7,12 @@ import com.core.common.exception.user.UserPasswordNotMatchException;
import com.core.common.exception.user.UserPasswordRetryLimitExceedException;
import com.core.common.utils.SecurityUtils;
import com.core.framework.security.context.AuthenticationContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* 登录密码方法

View File

@@ -1,19 +1,18 @@
package com.core.framework.web.service;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import com.core.common.constant.UserConstants;
import com.core.common.core.domain.entity.SysRole;
import com.core.common.core.domain.entity.SysUser;
import com.core.common.utils.StringUtils;
import com.core.system.service.ISysMenuService;
import com.core.system.service.ISysRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 用户权限处理

View File

@@ -1,8 +1,5 @@
package com.core.framework.web.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.core.common.constant.CacheConstants;
import com.core.common.constant.Constants;
import com.core.common.constant.UserConstants;
@@ -18,6 +15,8 @@ import com.core.framework.manager.AsyncManager;
import com.core.framework.manager.factory.AsyncFactory;
import com.core.system.service.ISysConfigService;
import com.core.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 注册校验方法

View File

@@ -1,17 +1,5 @@
package com.core.framework.web.service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.core.common.constant.CacheConstants;
import com.core.common.constant.Constants;
import com.core.common.core.domain.model.LoginUser;
@@ -21,11 +9,20 @@ import com.core.common.utils.StringUtils;
import com.core.common.utils.ip.AddressUtils;
import com.core.common.utils.ip.IpUtils;
import com.core.common.utils.uuid.IdUtils;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* token验证处理

View File

@@ -1,13 +1,5 @@
package com.core.framework.web.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.core.common.core.domain.entity.SysUser;
import com.core.common.core.domain.model.LoginUser;
import com.core.common.enums.UserStatus;
@@ -15,6 +7,13 @@ import com.core.common.exception.ServiceException;
import com.core.common.utils.MessageUtils;
import com.core.common.utils.StringUtils;
import com.core.system.service.ISysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* 用户验证处理

View File

@@ -1,24 +1,5 @@
package com.core.generator.controller;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
@@ -36,6 +17,23 @@ import com.core.generator.domain.GenTable;
import com.core.generator.domain.GenTableColumn;
import com.core.generator.service.IGenTableColumnService;
import com.core.generator.service.IGenTableService;
import org.apache.commons.io.IOUtils;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 代码生成 操作处理

View File

@@ -1,15 +1,13 @@
package com.core.generator.domain;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import org.apache.commons.lang3.ArrayUtils;
import com.core.common.constant.GenConstants;
import com.core.common.core.domain.BaseEntity;
import com.core.common.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.util.List;
/**
* 业务表 gen_table

View File

@@ -1,10 +1,10 @@
package com.core.generator.domain;
import javax.validation.constraints.NotBlank;
import com.core.common.core.domain.BaseEntity;
import com.core.common.utils.StringUtils;
import javax.validation.constraints.NotBlank;
/**
* 代码生成业务字段表 gen_table_column
*

View File

@@ -1,9 +1,9 @@
package com.core.generator.mapper;
import java.util.List;
import com.core.generator.domain.GenTableColumn;
import java.util.List;
/**
* 业务字段 数据层
*

Some files were not shown because too many files have changed in this diff Show More