Compare commits
345 Commits
eaa681c9ff
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 669d669422 | |||
| 98fe9f3301 | |||
| 6f7d723c6b | |||
| 0a08088ada | |||
|
|
48309fcaa4 | ||
|
|
28160e082c | ||
|
|
29ecfd90f2 | ||
| f690b78b18 | |||
| 6f71c678bd | |||
| 1c781c1224 | |||
|
|
638f853af6 | ||
|
|
96a8f75aa1 | ||
| 15a6445e26 | |||
| 0e4b0ad6fd | |||
|
|
7da461a9cb | ||
| b0040bcd48 | |||
| fa5394cc35 | |||
| 81cc8b08a0 | |||
| 0cecf3bcad | |||
|
|
b8d7e3cdf1 | ||
|
|
df2a4c1694 | ||
|
|
a6a4e0ed58 | ||
|
|
ba31371b6f | ||
| 9bd5caaa1b | |||
| 164e4a4b75 | |||
| 4f0cc1a0c4 | |||
|
|
6dedb92b54 | ||
|
|
0f0dc70c7e | ||
|
|
acfce391dc | ||
|
|
b0f2eabf6b | ||
|
|
c5db404290 | ||
|
|
c4c3073be0 | ||
| 41494ebf7c | |||
|
|
4de4d9099e | ||
| 497af01f9b | |||
| ffc1f29b80 | |||
| 86bca03b04 | |||
| 11c2758289 | |||
| 802f845231 | |||
|
|
ea5215a1b0 | ||
| a9fb093d9c | |||
|
|
f4bf064f08 | ||
|
|
4dd824d296 | ||
| 3ab6c2d424 | |||
| 12b2bf255c | |||
|
|
c878dc19d7 | ||
|
|
c1efd84332 | ||
| 1616f66fc4 | |||
|
|
2df1ed645f | ||
|
|
4f7fc1c09a | ||
| bd873f81d2 | |||
| 1975fda73c | |||
| ffce6f81c3 | |||
| ca043de624 | |||
| 054b51c63d | |||
| 5cf2dd165c | |||
| 27b094744c | |||
|
|
55e3533600 | ||
|
|
1522183432 | ||
| 6382741b71 | |||
| 16c854d55f | |||
| 73617e1b0f | |||
| abd5bd9f2f | |||
|
|
9000d66c0c | ||
|
|
61be9ff552 | ||
|
|
9408cf6c2d | ||
|
|
66c70a2b4a | ||
| f6d9321f95 | |||
| ccff9a7246 | |||
| 2884f610f5 | |||
|
|
035738f990 | ||
|
|
d0c6f57f6b | ||
|
|
58c1e02415 | ||
|
|
1e459b8883 | ||
|
|
0d57e984a6 | ||
| 4450e3cc50 | |||
|
|
902ee0587e | ||
| 49550fcc2e | |||
|
|
1dd7ee3428 | ||
|
|
8dff5d466a | ||
|
|
19ada4ace9 | ||
| c92ff38133 | |||
| 1c07108e58 | |||
|
|
34dd969cb4 | ||
| a0b546266d | |||
|
|
fc9ce6241e | ||
|
|
5187ff1ae3 | ||
|
|
73b1d01044 | ||
|
|
b88ad89146 | ||
|
|
de8039c513 | ||
| 3464153d93 | |||
| 6b868e378f | |||
| f6403fa059 | |||
|
|
bc92b9aa62 | ||
|
|
46145ff636 | ||
|
|
3ad32fac9f | ||
| d1223aec07 | |||
| 649f7bcf5b | |||
| a3dce8de60 | |||
| f81dd54f0c | |||
| 803e4d0bb5 | |||
| deebcde41f | |||
| 095c43bbf3 | |||
| aa3beb848b | |||
|
|
ae96bbd0bb | ||
|
|
1a2c444269 | ||
|
|
9cba8fea12 | ||
|
|
f11b7380a4 | ||
| da17b2b89c | |||
| 9e4a010a8d | |||
| 7e76083c37 | |||
| de105adbdc | |||
| f3eeee7405 | |||
| 97f04d0b15 | |||
| 5667e04d12 | |||
| 59157fda56 | |||
| 2fe6d45ad4 | |||
| 982ee316f7 | |||
| 64c7db68e8 | |||
| cb6b6ced67 | |||
|
|
8fcfb481c9 | ||
|
|
be0514bc08 | ||
|
|
2b3add4808 | ||
| b33cb6f9a1 | |||
| 072e71b025 | |||
| 47394de43c | |||
| f0f1dde6b6 | |||
| 1ab1165697 | |||
| a8f1b1fdfa | |||
| 3b94d19199 | |||
| db1139a14f | |||
|
|
bea74aeac2 | ||
|
|
634a1f45f9 | ||
| 8f1ad3307c | |||
| d8080fa22d | |||
|
|
e8783d9f8f | ||
| d8c4348341 | |||
|
|
8e61490005 | ||
| f5f4e3c48e | |||
| 0f013715b8 | |||
| fb9722d328 | |||
| 6f9192d30d | |||
| f2b5b90f34 | |||
| a2cbd5e583 | |||
| d3df46858b | |||
| 47a7a945bc | |||
| 0a56c0dcf0 | |||
| 15d32134e2 | |||
| eff98ea5eb | |||
| a47306825a | |||
|
|
9b35fec931 | ||
| e20e2b637f | |||
| ebd2e8aa75 | |||
| cb268fe26d | |||
| 23bd49d940 | |||
| 32adb984e2 | |||
| c1d453600b | |||
| 02eab2d932 | |||
| d5c8b7a1ad | |||
|
|
4053064a22 | ||
|
|
089e28f913 | ||
| cf9ab03b17 | |||
| d332650bfa | |||
| 840983ac94 | |||
|
|
86673d7be3 | ||
|
|
3753a916f5 | ||
|
|
0556f77870 | ||
|
|
b185c156ca | ||
|
|
a48308dcbf | ||
| e37f6a70f9 | |||
|
|
28629ccd35 | ||
|
|
fbd7f0be78 | ||
|
|
763f05da84 | ||
|
|
8c74d45332 | ||
| 8d62c0461b | |||
| 58936c957d | |||
| 062c4a92b8 | |||
| fb9f85e967 | |||
| 38ef377cbd | |||
| 240d5dc3f7 | |||
| 82702f16e0 | |||
| 0b4b63dfbe | |||
| b4422a0dca | |||
| d8627df2dd | |||
| 09ca077559 | |||
| 3091fc7337 | |||
| b0850257c8 | |||
| fa2884b320 | |||
| 941054734f | |||
| 8d69dc3c00 | |||
| 2157806ba5 | |||
| 2236cbea36 | |||
| 2128e717e7 | |||
| 1311e87e13 | |||
| ddf1553846 | |||
| 5d82800976 | |||
| 0c35044231 | |||
| 61f4020487 | |||
| aeb6b95970 | |||
| b5ce854eb6 | |||
| 4d4828ea71 | |||
| 10e738edd9 | |||
| 88a4e58130 | |||
| 76c324b0df | |||
| a65e8dd2cc | |||
| 1ac9b5ae0b | |||
| 8f77fe8bc9 | |||
| b567747901 | |||
| ed7004ae85 | |||
| d1670b79a0 | |||
| 54cde91aac | |||
| 7974bdc51c | |||
| 91cb465962 | |||
| 1b2c248fa2 | |||
| 1c16d6ba0f | |||
| 49b8a975a8 | |||
| d17a502da1 | |||
| 430adc2112 | |||
| 5f5c47f528 | |||
| 92d74c47ce | |||
| 5d8e7b667f | |||
| 58449fc2f9 | |||
| 4c541f43b9 | |||
| 9a037ae446 | |||
| bb0eb60eae | |||
| cf5dbc6133 | |||
| 5f6fa50000 | |||
| 08b2e76d47 | |||
| 5ffeab8999 | |||
| fc6640b846 | |||
| 8e11dde7c0 | |||
| e8f24ee290 | |||
| a1e07a204b | |||
| a080b4294c | |||
| 0827ce2908 | |||
| 5f600209b8 | |||
| 79ea4ed4f7 | |||
| cbd3e7f981 | |||
| 3c497417dc | |||
| 088861f66e | |||
| 8914c8b143 | |||
| 8c607c8749 | |||
| 3115e38cc4 | |||
| ae6c486114 | |||
| a41ffa4cfc | |||
| b1f74161f8 | |||
| 3bab56a19b | |||
| abc0674531 | |||
| 85fcb7c2e2 | |||
| fc6a7437a3 | |||
| 999a0992e7 | |||
| 55b3dfc077 | |||
|
|
32d1673667 | ||
| 17c7cc70ed | |||
| 44be570ffa | |||
| c6f4996ba7 | |||
| 6c20b15339 | |||
| c297072b36 | |||
| 4c6886be6a | |||
| 8beb7f3d3d | |||
| 91968530b4 | |||
| 0833f82fb4 | |||
| cce71f324b | |||
| ee6efafde4 | |||
| 2051807ecf | |||
| 0a4de15599 | |||
| 25b3c26ec2 | |||
| 91d673de77 | |||
| 0bfa7183f9 | |||
| 79d451931d | |||
| ae5f1c795b | |||
| c1074fc4fb | |||
| e832b6d38e | |||
| 5043466e84 | |||
| fcaeedb543 | |||
| 8cf434a46f | |||
| bfbc061b0b | |||
| f92cd830e9 | |||
| a7a1a18445 | |||
| d1331cd4e1 | |||
| 37d08b8545 | |||
| e69f499ac0 | |||
| 3baf7161f1 | |||
| 85188651ca | |||
| d6fbfb1427 | |||
| 966847499a | |||
| 1e91171001 | |||
| 51f495f81e | |||
| 1e6a5972b9 | |||
| 515f03a5cd | |||
| b27542ba6d | |||
| 4790855079 | |||
| 72cf9c6f2d | |||
| 19ecb65183 | |||
| d85a8684a6 | |||
| 574a9267bf | |||
| 27a7396c78 | |||
| 633cf2c17f | |||
| 3188ca5752 | |||
| c79e4c2623 | |||
| f3c451d0a1 | |||
| a077bd57d4 | |||
| cf16c497bd | |||
| fd1ab239a9 | |||
| caf65e3113 | |||
| c18c21ff4c | |||
| ab6849c9eb | |||
| 6b555f2563 | |||
| 7645405c5b | |||
| 5bfadb9174 | |||
| e1b9d36153 | |||
| fdb8d6c934 | |||
| 22a1ac57b2 | |||
| 4d243815a6 | |||
| 882e8c9199 | |||
| a0c87f6335 | |||
| 538dde55f7 | |||
| f33e3c6f15 | |||
| 0794782505 | |||
| d37fa46b5f | |||
| 8fcc229ad8 | |||
| 39aa710fd3 | |||
| 49f95b40ea | |||
| eb2d7302b7 | |||
| eb26f9db34 | |||
| bd6f3ca587 | |||
| a58e02f2cb | |||
| 8c8ef13021 | |||
| d9a0a98f52 | |||
| 8c84b6eb46 | |||
| 3d797cc0e0 | |||
| df7281a2d4 | |||
| f83d7ae520 | |||
| d9c8525b94 | |||
| bcc5cbb2fb | |||
| 67a8351d70 | |||
| 391506e423 | |||
| b1117b4157 | |||
| b0eb9861b9 | |||
| b863c14f2b | |||
| 18f6a845e6 | |||
| e1385cb3e6 | |||
| c8ca56c3f5 | |||
| b149f7d687 | |||
| 19702c25e5 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -57,4 +57,9 @@
|
||||
# 忽略设计书
|
||||
PostgreSQL/openHis_DB设计书.xlsx
|
||||
|
||||
public.sql
|
||||
public.sql
|
||||
发版记录/2025-11-12/~$发版日志.docx
|
||||
发版记录/2025-11-12/~$S-管理系统-调价管理.docx
|
||||
发版记录/2025-11-12/发版日志.docx
|
||||
.gitignore
|
||||
openhis-server-new/openhis-application/src/main/resources/application-dev.yml
|
||||
|
||||
29
.qwen/agents/full-stack-developer.md
Normal file
29
.qwen/agents/full-stack-developer.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: full-stack-developer
|
||||
description: Use this agent when you need comprehensive full-stack development assistance including frontend, backend, database design, API integration, deployment planning, and architectural decisions. This agent excels at analyzing complex technical requirements, designing scalable solutions, implementing clean code across multiple technologies, and providing expert guidance on best practices for modern web applications.
|
||||
color: Blue
|
||||
---
|
||||
|
||||
You are an elite full-stack software engineer with extensive experience across all layers of modern web application development. You possess deep expertise in frontend technologies (React, Vue, Angular, HTML/CSS, JavaScript/TypeScript), backend systems (Node.js, Python, Java, .NET, Ruby), databases (SQL and NoSQL), cloud platforms (AWS, Azure, GCP), and DevOps practices.
|
||||
|
||||
Your primary responsibilities include:
|
||||
- Analyzing complex technical requirements and proposing optimal architectural solutions
|
||||
- Writing clean, efficient, maintainable code across frontend and backend systems
|
||||
- Designing robust APIs and data models
|
||||
- Optimizing performance and ensuring security best practices
|
||||
- Providing guidance on scalability, testing, and deployment strategies
|
||||
- Troubleshooting complex issues spanning multiple technology stacks
|
||||
|
||||
When working on projects, you will:
|
||||
1. First understand the complete scope and requirements before proposing solutions
|
||||
2. Consider scalability, maintainability, and security implications of your designs
|
||||
3. Follow industry best practices for code organization, documentation, and testing
|
||||
4. Suggest appropriate technologies based on project requirements and constraints
|
||||
5. Provide implementation details with proper error handling and edge case considerations
|
||||
6. Recommend optimization strategies for performance and resource utilization
|
||||
|
||||
For frontend development, focus on responsive design, accessibility, state management, and user experience. For backend work, emphasize proper architecture patterns, database design, authentication/authorization, and API design principles. When addressing databases, consider normalization, indexing, query optimization, and data consistency.
|
||||
|
||||
Always prioritize clean code principles, proper separation of concerns, and modular design. When uncertain about requirements, ask clarifying questions to ensure your solution meets the actual needs. Provide code examples that demonstrate best practices and include comments where necessary for understanding.
|
||||
|
||||
In your responses, balance technical depth with practical applicability. Consider trade-offs between different approaches and explain your recommendations. When reviewing existing code, identify potential improvements related to performance, security, maintainability, and adherence to best practices.
|
||||
32
.qwen/agents/his-architect-developer.md
Normal file
32
.qwen/agents/his-architect-developer.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: his-architect-developer
|
||||
description: Use this agent when designing, developing, reviewing, or troubleshooting Hospital Information System (HIS) applications. This agent specializes in full-stack development for healthcare systems including database design, backend APIs, frontend interfaces, security compliance, and integration with medical devices or third-party systems.
|
||||
color: Blue
|
||||
---
|
||||
|
||||
You are an elite Healthcare Information System (HIS) Development Architect and Full-Stack Engineer with extensive experience in designing and implementing comprehensive hospital management solutions. You possess deep expertise in healthcare software architecture, regulatory compliance (HIPAA, FDA, etc.), medical data standards (HL7, FHIR), and secure system integration.
|
||||
|
||||
Your responsibilities include:
|
||||
- Designing scalable, secure, and compliant HIS architectures
|
||||
- Developing robust backend services and APIs
|
||||
- Creating intuitive frontend interfaces for healthcare professionals
|
||||
- Ensuring patient data security and privacy compliance
|
||||
- Integrating with medical devices and external healthcare systems
|
||||
- Optimizing system performance for high-availability environments
|
||||
- Troubleshooting complex technical issues in healthcare IT infrastructure
|
||||
|
||||
When working on HIS projects, you will:
|
||||
1. Prioritize patient safety and data security above all other considerations
|
||||
2. Follow healthcare industry standards and regulations (HIPAA, HITECH, FDA guidelines)
|
||||
3. Implement proper audit trails and logging for all patient-related operations
|
||||
4. Design fail-safe mechanisms and disaster recovery procedures
|
||||
5. Ensure accessibility compliance for users with varying technical expertise
|
||||
6. Plan for high availability and minimal downtime in critical systems
|
||||
|
||||
For database design, focus on normalized schemas that support medical record integrity, implement proper indexing for fast queries, and ensure backup/recovery procedures meet healthcare requirements. When developing APIs, follow RESTful principles while incorporating OAuth 2.0 or similar authentication methods suitable for healthcare environments.
|
||||
|
||||
For frontend development, prioritize usability for healthcare workers who may be operating under stress, ensuring clear workflows and minimizing cognitive load. Implement responsive designs that work across various devices commonly used in healthcare settings.
|
||||
|
||||
Always consider scalability requirements for growing healthcare institutions and plan for future expansion. When troubleshooting, approach problems systematically considering the potential impact on patient care.
|
||||
|
||||
In your responses, provide detailed explanations of your architectural decisions, code implementations, and recommendations. Include relevant healthcare industry best practices and explain how your solutions address specific regulatory requirements.
|
||||
33
.qwen/agents/his-developer-architect.md
Normal file
33
.qwen/agents/his-developer-architect.md
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: his-developer-architect
|
||||
description: Use this agent when developing or architecting Hospital Information System (HIS) solutions using Vue3, Spring Boot, and MyBatis technologies. This agent specializes in healthcare system development, understanding medical workflows, patient management systems, and hospital operational processes. Ideal for designing secure, scalable, and compliant healthcare applications.
|
||||
color: Blue
|
||||
---
|
||||
|
||||
You are an elite Healthcare Information System (HIS) developer and architect with deep expertise in Vue3, Spring Boot, and MyBatis technologies. You specialize in building robust, secure, and scalable hospital management systems that handle critical healthcare operations including patient records, medical workflows, billing, pharmacy management, and administrative processes.
|
||||
|
||||
Your responsibilities include:
|
||||
- Designing and implementing full-stack HIS solutions using Vue3 for modern, responsive frontends and Spring Boot with MyBatis for secure, efficient backends
|
||||
- Ensuring compliance with healthcare industry standards such as HIPAA, HL7, FHIR, and local health data protection regulations
|
||||
- Creating secure authentication and authorization systems for healthcare staff with role-based access controls
|
||||
- Optimizing database designs for handling large volumes of sensitive patient data efficiently
|
||||
- Implementing audit trails and logging systems required for healthcare environments
|
||||
- Building integration capabilities between different hospital systems and external healthcare providers
|
||||
|
||||
Technical Guidelines:
|
||||
- Follow Vue3 best practices using Composition API, TypeScript, and state management with Pinia
|
||||
- Implement Spring Boot microservices architecture with proper security configurations (Spring Security)
|
||||
- Use MyBatis effectively with proper transaction management and connection pooling
|
||||
- Apply healthcare-specific design patterns and architectural principles
|
||||
- Prioritize data integrity, security, and system reliability over performance optimizations when there's a conflict
|
||||
- Implement comprehensive error handling and logging for healthcare regulatory compliance
|
||||
|
||||
When designing solutions, consider:
|
||||
- Patient privacy and data security requirements
|
||||
- High availability and disaster recovery needs for critical healthcare systems
|
||||
- Scalability to handle varying loads during peak times
|
||||
- Integration with existing hospital infrastructure and legacy systems
|
||||
- User experience for healthcare professionals who need quick, reliable access to information
|
||||
- Regulatory compliance and audit requirements specific to healthcare systems
|
||||
|
||||
You will provide detailed technical recommendations, code implementations, architectural diagrams, and best practices tailored specifically to healthcare information systems. Always prioritize patient safety and data security in your solutions.
|
||||
6
.qwen/settings.json
Normal file
6
.qwen/settings.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"tools": {
|
||||
"approvalMode": "yolo"
|
||||
},
|
||||
"$version": 2
|
||||
}
|
||||
26
.trae/documents/plan_20251231_062502.md
Normal file
26
.trae/documents/plan_20251231_062502.md
Normal 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条专家号记录。
|
||||
30
.trae/documents/plan_20251231_063300.md
Normal file
30
.trae/documents/plan_20251231_063300.md
Normal 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条专家号记录。
|
||||
32
.trae/documents/修复号源列表显示问题.md
Normal file
32
.trae/documents/修复号源列表显示问题.md
Normal 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. 检查日志,确认没有类型转换异常
|
||||
23
.trae/documents/修复门诊预约界面专家号查询问题.md
Normal file
23
.trae/documents/修复门诊预约界面专家号查询问题.md
Normal 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" 转换为 "专家"
|
||||
|
||||
## 预期效果
|
||||
修复后,前端选择"普通号"或"专家号"时,系统能正确查询到对应的号源数据,不再出现"没有更多数据了"的提示。
|
||||
23
.trae/documents/修复门诊预约界面只显示1条数据的问题.md
Normal file
23
.trae/documents/修复门诊预约界面只显示1条数据的问题.md
Normal 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条专家号数据
|
||||
|
||||
* 保持与现有代码的兼容性
|
||||
|
||||
376
CODEBUDDY.md
Normal file
376
CODEBUDDY.md
Normal file
@@ -0,0 +1,376 @@
|
||||
# CODEBUDDY.md
|
||||
|
||||
This file provides guidance to CodeBuddy Code when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a comprehensive Hospital Information System (HIS) built with a Java Spring Boot backend and Vue 3 frontend.
|
||||
|
||||
- **Backend**: Java 17, Spring Boot 2.5.15, multi-module Maven architecture
|
||||
- **Frontend**: Vue 3, Vite, Element Plus, Pinia state management
|
||||
- **Database**: PostgreSQL (recommended v16.2)
|
||||
- **Cache**: Redis
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── openhis-server-new/ # Backend multi-module Maven project
|
||||
│ ├── openhis-application/ # Main application module with startup class
|
||||
│ ├── openhis-domain/ # Business domain modules (administration, clinical, financial, etc.)
|
||||
│ ├── openhis-common/ # Shared utilities and common code
|
||||
│ ├── core-admin/ # Core administration module
|
||||
│ ├── core-framework/ # Framework configuration and security
|
||||
│ ├── core-system/ # System management module
|
||||
│ ├── core-quartz/ # Scheduled tasks
|
||||
│ ├── core-generator/ # Code generation utilities
|
||||
│ ├── core-common/ # Core utilities
|
||||
│ └── core-flowable/ # Workflow engine integration
|
||||
└── openhis-ui-vue3/ # Vue 3 frontend
|
||||
├── src/
|
||||
│ ├── api/ # API service layer
|
||||
│ ├── components/ # Reusable components
|
||||
│ ├── router/ # Vue Router configuration
|
||||
│ ├── store/ # Pinia state management
|
||||
│ ├── utils/ # Utility functions
|
||||
│ └── views/ # Page components
|
||||
└── vite/ # Vite plugins configuration
|
||||
```
|
||||
|
||||
## Build and Development Commands
|
||||
|
||||
### Backend (Java)
|
||||
|
||||
**Build the entire backend:**
|
||||
```bash
|
||||
cd openhis-server-new
|
||||
mvn clean package -DskipTests
|
||||
```
|
||||
|
||||
**Run the backend application (development):**
|
||||
```bash
|
||||
cd openhis-server-new/openhis-application
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
**Alternative: Run directly from IDE:**
|
||||
- Run the main method in `openhis-server-new/openhis-application/src/main/java/com/openhis/OpenHisApplication.java`
|
||||
|
||||
**Start scripts:**
|
||||
- Linux/Mac: `openhis-server-new/start.sh`
|
||||
- Windows: `openhis-server-new/start.bat`
|
||||
|
||||
### Frontend (Vue 3)
|
||||
|
||||
**Install dependencies:**
|
||||
```bash
|
||||
cd openhis-ui-vue3
|
||||
npm install
|
||||
```
|
||||
|
||||
**Development server (with hot reload):**
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
- Runs on port 81 by default
|
||||
- Proxies `/dev-api` requests to `http://localhost:18080/openhis`
|
||||
|
||||
**Build for production:**
|
||||
```bash
|
||||
npm run build:prod # Production build
|
||||
npm run build:stage # Staging build
|
||||
npm run build:test # Test environment build
|
||||
npm run build:dev # Development build
|
||||
npm run build:spug # Spug environment build
|
||||
```
|
||||
|
||||
**Preview production build:**
|
||||
```bash
|
||||
npm run preview
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Backend Architecture
|
||||
|
||||
The backend uses a multi-module Maven architecture with clear separation of concerns:
|
||||
|
||||
1. **openhis-application**: Entry point with `OpenHisApplication.java` (d:\his\openhis-server-new\openhis-application\src\main\java\com\openhis\OpenHisApplication.java:20)
|
||||
- Scans `com.core` and `com.openhis` packages
|
||||
- Configures async processing and YAML service configuration
|
||||
- Runs on port 18080 with context path `/openhis`
|
||||
|
||||
2. **openhis-domain**: Business domain modules organized by medical functionality:
|
||||
- `administration`: Administrative functions
|
||||
- `appointmentmanage`: Appointment management
|
||||
- `check`: Medical examination/checkup
|
||||
- `clinical`: Clinical workflows
|
||||
- `crosssystem`: Cross-system integration
|
||||
- `document`: Document management
|
||||
- `financial`: Financial/billing
|
||||
- `lab`: Laboratory operations
|
||||
- `medication`: Medication management
|
||||
- `triageandqueuemanage`: Patient triage and queue management
|
||||
- `yb`, `ybcatalog`, `ybelep`: Insurance (Yi Bao) integration
|
||||
- `workflow`: Workflow management
|
||||
- `jlau`, `nenu`: Additional domain modules
|
||||
- `template`: Template management
|
||||
|
||||
3. **Core Modules** (com.core package):
|
||||
- `core-system`: User, role, menu, and permission management
|
||||
- `core-framework`: Security, exception handling, and framework configurations
|
||||
- `core-common`: Shared utilities and base classes
|
||||
- `core-quartz`: Scheduled task management
|
||||
- `core-generator`: Code generation tools
|
||||
- `core-flowable`: Workflow engine integration
|
||||
- `core-admin`: Administrative functions
|
||||
|
||||
4. **openhis-common**: Domain-specific shared code and utilities under `com.openhis.common` package
|
||||
|
||||
**Key Technologies:**
|
||||
- MyBatis-Plus 3.5.5 for ORM with enhanced CRUD operations
|
||||
- Druid 1.2.27 connection pool with monitoring at `/druid/*`
|
||||
- Flowable 6.8.0 for workflow management
|
||||
- LiteFlow 2.12.4.1 for business rule orchestration
|
||||
- Swagger 3.0.0 for API documentation
|
||||
- JWT 0.9.1 for authentication
|
||||
- Hutool 5.3.8 utility library
|
||||
- Fastjson2 2.0.58 for JSON processing
|
||||
- Pinyin4j 2.5.1 for Chinese character to Pinyin conversion
|
||||
|
||||
### Frontend Architecture
|
||||
|
||||
The frontend uses Vue 3 with composition API and modern tooling:
|
||||
|
||||
**Key Files:**
|
||||
- Entry point: `openhis-ui-vue3/src/main.js`
|
||||
- Router configuration: `openhis-ui-vue3/src/router/index.js`
|
||||
- Store initialization: `openhis-ui-vue3/src/store/store.js`
|
||||
- Vite configuration: `openhis-ui-vue3/vite.config.js`
|
||||
|
||||
**State Management:**
|
||||
- Pinia for global state (replaces Vuex)
|
||||
- Store modules: `app`, `dict`, `permission`, `settings`, `tagsView`, `user`
|
||||
- Modules located in `openhis-ui-vue3/src/store/modules/`
|
||||
|
||||
**Routing:**
|
||||
- Vue Router 4.3.0
|
||||
- Two types of routes:
|
||||
- `constantRoutes`: Public routes (login, 404, etc.)
|
||||
- `dynamicRoutes`: Permission-based routes loaded dynamically
|
||||
- Route meta fields: `title`, `icon`, `permissions`, `noCache`, `activeMenu`
|
||||
|
||||
**API Integration:**
|
||||
- Axios 0.27.2 for HTTP requests
|
||||
- Base API URL configured via environment variables (`VITE_APP_BASE_API`)
|
||||
- Proxy configuration in vite.config.js for development
|
||||
- `/dev-api` → `http://localhost:18080/openhis`
|
||||
- `/ybplugin` → `http://localhost:5000` (insurance plugin)
|
||||
- Request/response interceptors in `openhis-ui-vue3/src/utils/request.js`
|
||||
- API service files organized by module in `openhis-ui-vue3/src/api/`
|
||||
- `administration`, `appoinmentmanage`, `monitor`, `system`, `tool`
|
||||
- Shared APIs: `home.js`, `login.js`, `menu.js`, `public.js`
|
||||
|
||||
**Component Architecture:**
|
||||
- Element Plus as the UI framework
|
||||
- Custom components in `openhis-ui-vue3/src/components/`
|
||||
- Global components registered in main.js:
|
||||
- Pagination, TreeSelect, FileUpload, ImageUpload, ImagePreview
|
||||
- RightToolbar, Editor, DictTag
|
||||
|
||||
## Configuration
|
||||
|
||||
### Backend Configuration
|
||||
|
||||
**Main configuration file:** `openhis-server-new/openhis-application/src/main/resources/application.yml`
|
||||
|
||||
**Environment-specific profiles:**
|
||||
- `application-dev.yml` - Development environment
|
||||
- `application-test.yml` - Test environment
|
||||
- `application-prd.yml` - Production environment
|
||||
|
||||
**Key configuration sections:**
|
||||
- Database: PostgreSQL connection (URL, username, password, pool settings)
|
||||
- Redis: Cache configuration (host, port, database index)
|
||||
- Server: Port (18080), context path (/openhis), thread pool
|
||||
- MyBatis-Plus: Mapper scanning (`com.core.**.domain,com.openhis.**.domain`), type aliases, logical delete
|
||||
- Logging: Debug levels for com.openhis and com.baomidou.mybatisplus
|
||||
- Swagger: API documentation at `/swagger-ui/index.html`
|
||||
- Druid: Database monitoring at `/druid/*` (credentials: openhis/123456)
|
||||
- Flowable: Workflow engine settings (schema update disabled)
|
||||
- LiteFlow: Business rule configuration at `config/flow.el.xml`
|
||||
- Token: JWT configuration (secret, expire time, header)
|
||||
- File upload: Max file size (10MB), max request size (20MB)
|
||||
|
||||
### Frontend Configuration
|
||||
|
||||
**Environment files** (in `openhis-ui-vue3/`):
|
||||
- `.env.dev` - Dev environment
|
||||
- `.env.development` - Development environment variables
|
||||
- `.env.staging` - Staging environment variables
|
||||
- `.env.production` - Production environment variables
|
||||
- `.env.test` - Test environment variables
|
||||
- `.env.spug` - Spug environment variables
|
||||
|
||||
**Key environment variables:**
|
||||
- `VITE_APP_TITLE`: Application title (e.g., "医院信息管理系统")
|
||||
- `VITE_APP_BASE_API`: Backend API base URL (e.g., `/dev-api`)
|
||||
- `VITE_APP_ENV`: Environment identifier
|
||||
|
||||
**Vite configuration:**
|
||||
- Development server: Port 81, host true, auto-open
|
||||
- Proxy: `/dev-api` → `http://localhost:18080/openhis`
|
||||
- Path aliases: `@` → `./src`, `~` → `./`
|
||||
|
||||
## Database
|
||||
|
||||
**Initialization script:** `数据库初始话脚本(请使用navicat16版本导入).sql` (located at repository root)
|
||||
- Use Navicat version 16 to import
|
||||
- Contains schema and initial demonstration data
|
||||
|
||||
**Database connection (dev environment):**
|
||||
- Type: PostgreSQL
|
||||
- URL: `jdbc:postgresql://47.116.196.11:15432/postgresql?currentSchema=hisdev`
|
||||
- Driver: `org.postgresql.Driver`
|
||||
- Schema: `hisdev`
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Running Full Stack Locally
|
||||
|
||||
**Terminal 1 - Start backend:**
|
||||
```bash
|
||||
cd openhis-server-new/openhis-application
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
**Terminal 2 - Start frontend:**
|
||||
```bash
|
||||
cd openhis-ui-vue3
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Access the application at:
|
||||
- Frontend: http://localhost:81
|
||||
- Backend API: http://localhost:18080/openhis
|
||||
- Swagger UI: http://localhost:18080/openhis/swagger-ui/index.html
|
||||
- Druid monitoring: http://localhost:18080/openhis/druid/login.html
|
||||
|
||||
### Adding a New Backend Feature
|
||||
|
||||
1. Create domain entity in appropriate module under `openhis-domain/[module]/domain/`
|
||||
2. Create mapper interface in `openhis-domain/[module]/mapper/`
|
||||
3. Create mapper XML in `openhis-domain/[module]/resources/mapper/` (if custom SQL needed)
|
||||
4. Create service interface and implementation in `openhis-domain/[module]/service/`
|
||||
5. Create controller in `openhis-application/src/main/java/com/openhis/web/[module]/`
|
||||
6. Add MyBatis-Plus annotations if using enhanced features
|
||||
7. Test endpoints via Swagger UI at `http://localhost:18080/openhis/swagger-ui/index.html`
|
||||
|
||||
**Note:** Controllers are organized under `com.openhis.web` by business module (e.g., `web.administration`, `web.clinicalmanage`, `web.patientmanage`, etc.)
|
||||
|
||||
### Adding a New Frontend Page
|
||||
|
||||
1. Create Vue component in `openhis-ui-vue3/src/views/[module]/`
|
||||
2. Add API service methods in `openhis-ui-vue3/src/api/`
|
||||
3. Add route to `openhis-ui-vue3/src/router/index.js` (constantRoutes or dynamicRoutes)
|
||||
4. Add Pinia store module if state management needed
|
||||
5. Register global components if reusable
|
||||
|
||||
### Testing
|
||||
|
||||
**Backend:**
|
||||
```bash
|
||||
cd openhis-server-new
|
||||
mvn test
|
||||
```
|
||||
|
||||
**Frontend:**
|
||||
- Run unit tests (if configured):
|
||||
```bash
|
||||
cd openhis-ui-vue3
|
||||
npm test
|
||||
```
|
||||
|
||||
## Key Patterns and Conventions
|
||||
|
||||
### Backend
|
||||
|
||||
- Package structure follows domain-driven design
|
||||
- Service layer uses `@Service` annotation
|
||||
- Controllers use `@RestController` with request mapping
|
||||
- MyBatis-Plus base mapper: `BaseMapper<T>`
|
||||
- Logical delete field: `validFlag` (1 = active, 0 = deleted)
|
||||
- Use `@EnableAsync` for async processing
|
||||
- JWT token stored in `Authorization` header
|
||||
|
||||
### Frontend
|
||||
|
||||
- Use Vue 3 Composition API (`<script setup>`)
|
||||
- Element Plus components with Chinese locale (zhCn)
|
||||
- API calls through centralized request utility in `src/utils/request.js`
|
||||
- Route-based permission control
|
||||
- Dictionary data through `useDict()` composable
|
||||
- Global properties: `$download`, `$downloadGet`, `$parseTime`, `$resetForm`, `$handleTree`, `$formatDateStr`
|
||||
- CSS in SCSS with global styles in `src/assets/styles/index.scss`
|
||||
- Registered global components: DictTag, Pagination, TreeSelect, FileUpload, ImageUpload, ImagePreview, RightToolbar, Editor
|
||||
- Hiprint plugin for printing functionality (window.hiprint)
|
||||
|
||||
## Important Files
|
||||
|
||||
### Backend
|
||||
- Startup class: `openhis-server-new/openhis-application/src/main/java/com/openhis/OpenHisApplication.java`
|
||||
- Main config: `openhis-server-new/openhis-application/src/main/resources/application.yml`
|
||||
- MyBatis config: `openhis-server-new/openhis-application/src/main/resources/mybatis/mybatis-config.xml`
|
||||
- Parent POM: `openhis-server-new/pom.xml`
|
||||
|
||||
### Frontend
|
||||
- Entry point: `openhis-ui-vue3/src/main.js`
|
||||
- Router: `openhis-ui-vue3/src/router/index.js`
|
||||
- Request utils: `openhis-ui-vue3/src/utils/request.js`
|
||||
- Vite config: `openhis-ui-vue3/vite.config.js`
|
||||
- Environment files: `openhis-ui-vue3/.env.*`
|
||||
|
||||
## External Integrations
|
||||
|
||||
- **PostgreSQL 42.2.27**: Primary database
|
||||
- **MySQL Connector 9.4.0**: MySQL database support (alternative)
|
||||
- **Redis**: Caching and session management
|
||||
- **Flowable 6.8.0**: Workflow engine
|
||||
- **LiteFlow 2.12.4.1**: Business rule engine
|
||||
- **Swagger 3.0.0**: API documentation
|
||||
- **Druid 1.2.27**: Database connection pool and monitoring
|
||||
- **Element Plus 2.12.0**: Vue 3 UI component library
|
||||
- **Pinia 2.2.0**: State management
|
||||
- **Vite 5.0.4**: Build tool and dev server
|
||||
- **Hutool 5.3.8**: Java utility library
|
||||
- **Fastjson2 2.0.58**: JSON processing
|
||||
- **Pinyin4j 2.5.1**: Chinese character to Pinyin conversion
|
||||
- **iText 5.5.12**: PDF generation
|
||||
- **Apache POI 4.1.2**: Excel file processing
|
||||
|
||||
## Additional Notes
|
||||
|
||||
### WebView Integration
|
||||
- Frontend supports WebView environment (e.g., embedded in desktop applications)
|
||||
- Chrome WebView integration with C# accessor (`chrome.webview.hostObjects.CSharpAccessor`)
|
||||
- Mounted to Vue instance as `csAccessor` global property
|
||||
|
||||
### File Upload
|
||||
- Backend upload path: Configured in `core.profile` property (default: `D:/home/uploadPath`)
|
||||
- Max file size: 10MB per file, 20MB total request
|
||||
- File upload component: `FileUpload` (global component)
|
||||
|
||||
### Authentication
|
||||
- JWT token stored in `Authorization` header
|
||||
- Token configuration: `token.secret`, `token.expireTime`, `token.header`
|
||||
- Password lockout: 5 failed attempts, 10-minute lock time
|
||||
|
||||
### Logging
|
||||
- Backend logs: Configured in `logback.xml`
|
||||
- Debug logging enabled for: `com.openhis`, `com.baomidou.mybatisplus`, `com.alibaba.druid`
|
||||
- Druid slow SQL threshold: 1000ms
|
||||
|
||||
### Code Generation
|
||||
- Backend code generator: `core-generator` module
|
||||
- Access via Swagger or `/tool/gen` route
|
||||
- Uses Velocity templates in `openhis-application/src/main/resources/vm/`
|
||||
2
LICENSE
2
LICENSE
@@ -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/>
|
||||
|
||||
223
MybastisColumnsHandler_optimization_guide.md
Normal file
223
MybastisColumnsHandler_optimization_guide.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# MyBatis-Plus 自动填充处理器优化指南
|
||||
|
||||
## 概述
|
||||
本文档说明如何优化 `MybastisColumnsHandler` 以确保所有实体的审计字段(create_by、create_time、update_by、update_time)能够正确自动填充。
|
||||
|
||||
## 问题背景
|
||||
在 OpenHIS 系统中,当保存实体时可能会遇到以下错误:
|
||||
```
|
||||
org.postgresql.util.PSQLException: ERROR: null value in column "create_by" of relation "adm_practitioner" violates not-null constraint
|
||||
```
|
||||
|
||||
这是因为数据库表中的审计字段设置了 NOT NULL 约束,但在某些情况下自动填充机制未能正确设置这些字段。
|
||||
|
||||
## 解决方案
|
||||
通过优化 `MybastisColumnsHandler` 来确保总是使用当前登录用户的用户名填充 `create_by` 字段,使用当前时间填充 `create_time` 字段。
|
||||
|
||||
## 实施步骤
|
||||
|
||||
### 1. 替换现有处理器
|
||||
将 `D:\his\openhis-server-new\core-framework\src\main\java\com\core\framework\handler\MybastisColumnsHandler.java` 文件替换为以下内容:
|
||||
|
||||
```java
|
||||
package com.core.framework.handler;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* MyBatis-Plus 自动填充处理器
|
||||
* 用于自动填充创建时间和更新时间,以及创建人和更新人
|
||||
*/
|
||||
@Component
|
||||
public class MybastisColumnsHandler implements MetaObjectHandler {
|
||||
|
||||
// 设置数据新增时的字段自动赋值规则
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
// 填充创建时间
|
||||
Date currentTime = new Date();
|
||||
this.strictInsertFill(metaObject, "createTime", Date.class, currentTime);
|
||||
this.strictInsertFill(metaObject, "create_time", Date.class, currentTime);
|
||||
|
||||
// 获取当前登录用户名
|
||||
String username = getCurrentUsername();
|
||||
|
||||
// 填充创建人
|
||||
this.strictInsertFill(metaObject, "createBy", String.class, username);
|
||||
this.strictInsertFill(metaObject, "create_by", String.class, username);
|
||||
|
||||
// 确保tenantId被设置
|
||||
Integer tenantId = getCurrentTenantId();
|
||||
if (tenantId == null) {
|
||||
throw new RuntimeException("无法获取当前租户ID,请确保用户已登录或正确设置租户上下文");
|
||||
}
|
||||
this.strictInsertFill(metaObject, "tenantId", Integer.class, tenantId);
|
||||
this.strictInsertFill(metaObject, "tenant_id", Integer.class, tenantId);
|
||||
}
|
||||
|
||||
// 设置数据修改时的字段自动赋值规则
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
// 填充更新时间
|
||||
Date currentTime = new Date();
|
||||
this.strictUpdateFill(metaObject, "updateTime", Date.class, currentTime);
|
||||
this.strictUpdateFill(metaObject, "update_time", Date.class, currentTime);
|
||||
|
||||
// 填充更新人
|
||||
String username = getCurrentUsername();
|
||||
this.strictUpdateFill(metaObject, "updateBy", String.class, username);
|
||||
this.strictUpdateFill(metaObject, "update_by", String.class, username);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户名
|
||||
* @return 当前登录用户名,如果无法获取则返回 "system"
|
||||
*/
|
||||
private String getCurrentUsername() {
|
||||
String username = "system"; // 默认值
|
||||
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null) {
|
||||
username = loginUser.getUsername();
|
||||
} else {
|
||||
// 尝试从请求中获取用户信息
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
// 可以在这里添加额外的逻辑来从请求中获取用户信息
|
||||
// 例如从请求头、session等获取用户信息
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 记录异常但不中断处理流程
|
||||
System.err.println("获取当前登录用户时发生异常: " + e.getMessage());
|
||||
// 可以考虑记录日志
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前租户 ID
|
||||
*/
|
||||
private Integer getCurrentTenantId() {
|
||||
Integer result = null;
|
||||
|
||||
// 首先尝试从线程局部变量中获取租户ID(适用于定时任务等场景)
|
||||
Integer threadLocalTenantId = TenantContext.getCurrentTenant();
|
||||
if (threadLocalTenantId != null) {
|
||||
result = threadLocalTenantId;
|
||||
} else {
|
||||
// 获取当前登录用户的租户ID(优先使用SecurityUtils中储存的LoginUser的租户ID)
|
||||
try {
|
||||
if (SecurityUtils.getAuthentication() != null) {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null) {
|
||||
result = loginUser.getTenantId();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 记录异常但不中断处理
|
||||
System.err.println("获取当前登录用户租户ID时发生异常: " + e.getMessage());
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
// 尝试从请求头中获取租户ID
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
if (request != null) {
|
||||
// 从请求头获取租户ID,假设header名称为"X-Tenant-ID" ; 登录接口前端把租户id放到请求头里
|
||||
String tenantIdHeader = request.getHeader("X-Tenant-ID");
|
||||
String requestMethodName = request.getHeader("Request-Method-Name");
|
||||
// 登录
|
||||
if ("login".equals(requestMethodName)) {
|
||||
if (tenantIdHeader != null && !tenantIdHeader.isEmpty()) {
|
||||
try {
|
||||
result = Integer.parseInt(tenantIdHeader);
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("解析请求头中的租户ID时发生异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果仍然没有获取到租户ID,返回默认值
|
||||
if (result == null) {
|
||||
System.out.println("警告: 未能获取当前租户ID,将使用默认租户ID 1");
|
||||
result = 1; // 默认租户ID
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 验证处理器是否被正确扫描
|
||||
确保在主应用类或配置类中启用了自动填充功能:
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.openhis.*.mapper") // 确保扫描到你的mapper
|
||||
@EnableTransactionManagement // 启用事务管理
|
||||
public class OpenHisApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(OpenHisApplication.class, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 测试验证
|
||||
创建一个简单的测试来验证自动填充是否正常工作:
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
public class AuditFieldTest {
|
||||
@Autowired
|
||||
private PractitionerMapper practitionerMapper;
|
||||
|
||||
@Test
|
||||
public void testAuditFieldsAutoFill() {
|
||||
Practitioner practitioner = new Practitioner();
|
||||
practitioner.setName("Test Practitioner");
|
||||
|
||||
// 保存实体
|
||||
practitionerMapper.insert(practitioner);
|
||||
|
||||
// 验证审计字段是否被正确填充
|
||||
assertThat(practitioner.getCreateBy()).isNotNull();
|
||||
assertThat(practitioner.getCreateBy()).isNotEqualTo("");
|
||||
assertThat(practitioner.getCreateTime()).isNotNull();
|
||||
|
||||
// 清理测试数据
|
||||
practitionerMapper.deleteById(practitioner.getId());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **安全上下文**:确保在调用保存方法时用户已登录,这样 `SecurityUtils.getLoginUser()` 才能返回有效的用户对象。
|
||||
|
||||
2. **异常处理**:处理器中包含了异常处理,如果无法获取当前用户,将使用 "system" 作为默认值。
|
||||
|
||||
3. **租户ID**:处理器也处理租户ID的自动填充,这对于多租户系统很重要。
|
||||
|
||||
4. **兼容性**:处理器同时支持驼峰命名(createBy)和下划线命名(create_by)的字段,以兼容不同的配置。
|
||||
|
||||
## 总结
|
||||
通过优化 `MybastisColumnsHandler`,我们可以确保所有实体在保存时都能正确填充审计字段,避免因缺少这些字段而引发的数据库约束错误,同时保持数据完整性和审计跟踪功能。
|
||||
184
QWEN.md
Normal file
184
QWEN.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# Qwen Code Context for HIS (Hospital Information System)
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a comprehensive Hospital Information System (HIS) called OpenHIS, built with a Java Spring Boot backend and Vue 3 frontend. The system is designed to manage hospital operations including patient management, appointments, clinical workflows, billing, and administrative tasks.
|
||||
|
||||
### Technology Stack
|
||||
|
||||
**Backend:**
|
||||
- Java 17
|
||||
- Spring Boot 2.5.15
|
||||
- PostgreSQL (recommended v16.2)
|
||||
- Redis
|
||||
- MyBatis-Plus 3.5.5 for ORM
|
||||
- Druid 1.2.27 for database connection pooling
|
||||
- Flowable 6.8.0 for workflow management
|
||||
- LiteFlow 2.12.4.1 for business rule orchestration
|
||||
- Swagger 3.0.0 for API documentation
|
||||
- JWT 0.9.1 for authentication
|
||||
|
||||
**Frontend:**
|
||||
- Vue 3 with Composition API
|
||||
- Vite 5.0.4 as build tool
|
||||
- Element Plus 2.12.0 as UI component library
|
||||
- Pinia 2.2.0 for state management
|
||||
- Axios 0.27.2 for HTTP requests
|
||||
- Sass for styling
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── openhis-server-new/ # Backend multi-module Maven project
|
||||
│ ├── openhis-application/ # Main application module with startup class
|
||||
│ ├── openhis-domain/ # Business domain modules (administration, clinical, financial, etc.)
|
||||
│ ├── openhis-common/ # Shared utilities and common code
|
||||
│ ├── core-admin/ # Core administration module
|
||||
│ ├── core-framework/ # Framework configuration and security
|
||||
│ ├── core-system/ # System management module
|
||||
│ ├── core-quartz/ # Scheduled tasks
|
||||
│ ├── core-generator/ # Code generation utilities
|
||||
│ ├── core-common/ # Core utilities
|
||||
│ └── core-flowable/ # Workflow engine integration
|
||||
├── openhis-ui-vue3/ # Vue 3 frontend
|
||||
│ ├── src/
|
||||
│ │ ├── api/ # API service layer
|
||||
│ │ ├── components/ # Reusable components
|
||||
│ │ ├── router/ # Vue Router configuration
|
||||
│ │ ├── store/ # Pinia state management
|
||||
│ │ ├── utils/ # Utility functions
|
||||
│ │ └── views/ # Page components
|
||||
│ └── vite/ # Vite plugins configuration
|
||||
├── sql/ # Database scripts
|
||||
├── 发版记录/ # Release records
|
||||
└── 迁移记录-DB变更记录/ # Database migration records
|
||||
```
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Backend Setup
|
||||
|
||||
1. **Prerequisites:**
|
||||
- JDK 17 (required)
|
||||
- PostgreSQL v16.2 (required)
|
||||
- Redis (stable version)
|
||||
|
||||
2. **Database Setup:**
|
||||
- Import the database initialization script using Navicat 16 or later
|
||||
- Script location: `sql/20251224init脚本(使用Navicat Premium 17导入).sql`
|
||||
- Configure database connection in `application.yml` or `application-dev.yml`
|
||||
|
||||
3. **Build and Run:**
|
||||
```bash
|
||||
cd openhis-server-new
|
||||
mvn clean package -DskipTests
|
||||
cd openhis-application
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
Or run directly from IDE by executing `OpenHisApplication.java`
|
||||
|
||||
### Frontend Setup
|
||||
|
||||
1. **Prerequisites:**
|
||||
- Node.js v16.15 (recommended)
|
||||
|
||||
2. **Installation and Run:**
|
||||
```bash
|
||||
cd openhis-ui-vue3
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
3. **Access the application:**
|
||||
- Frontend: http://localhost:81
|
||||
- Backend API: http://localhost:18080/openhis
|
||||
- Swagger UI: http://localhost:18080/openhis/swagger-ui/index.html
|
||||
|
||||
## Development Conventions
|
||||
|
||||
### Backend Architecture
|
||||
|
||||
The backend follows a multi-module Maven architecture with clear separation of concerns:
|
||||
|
||||
1. **openhis-application**: Entry point with `OpenHisApplication.java`
|
||||
- Scans `com.core` and `com.openhis` packages
|
||||
- Configured to run on port 18080 with context path `/openhis`
|
||||
|
||||
2. **openhis-domain**: Business domain modules organized by medical functionality:
|
||||
- `administration`: Administrative functions
|
||||
- `appointmentmanage`: Appointment management
|
||||
- `check`: Medical examination/checkup
|
||||
- `clinical`: Clinical workflows
|
||||
- `crosssystem`: Cross-system integration
|
||||
- `document`: Document management
|
||||
- `financial`: Financial/billing
|
||||
- `lab`: Laboratory operations
|
||||
- `medication`: Medication management
|
||||
- `triageandqueuemanage`: Patient triage and queue management
|
||||
- `yb`, `ybcatalog`, `ybelep`: Insurance (Yi Bao) integration
|
||||
- `workflow`: Workflow management
|
||||
|
||||
3. **Core Modules** (com.core package):
|
||||
- `core-system`: User, role, menu, and permission management
|
||||
- `core-framework`: Security, exception handling, and framework configurations
|
||||
- `core-common`: Shared utilities and base classes
|
||||
- `core-quartz`: Scheduled task management
|
||||
- `core-generator`: Code generation tools
|
||||
- `core-flowable`: Workflow engine integration
|
||||
- `core-admin`: Administrative functions
|
||||
|
||||
### Frontend Architecture
|
||||
|
||||
The frontend uses Vue 3 with composition API and modern tooling:
|
||||
|
||||
1. **State Management:** Pinia for global state with modules for app, dict, permission, settings, tagsView, and user
|
||||
|
||||
2. **Routing:** Vue Router 4.3.0 with public routes and dynamic permission-based routes
|
||||
|
||||
3. **API Integration:** Axios with request/response interceptors and API services organized by module
|
||||
|
||||
4. **Component Architecture:** Element Plus as UI framework with custom components in `src/components/`
|
||||
|
||||
## Key Configuration Files
|
||||
|
||||
### Backend Configuration
|
||||
|
||||
- Main config: `openhis-server-new/openhis-application/src/main/resources/application.yml`
|
||||
- Environment-specific: `application-dev.yml`, `application-test.yml`, `application-prd.yml`
|
||||
- Database connection settings, Redis configuration, server settings, and MyBatis-Plus configuration
|
||||
|
||||
### Frontend Configuration
|
||||
|
||||
- Environment files: `.env.*` in `openhis-ui-vue3/`
|
||||
- Vite configuration: `vite.config.js`
|
||||
- Main entry: `src/main.js`
|
||||
- Router: `src/router/index.js`
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Adding a New Backend Feature
|
||||
|
||||
1. Create domain entity in appropriate module under `openhis-domain/[module]/domain/`
|
||||
2. Create mapper interface in `openhis-domain/[module]/mapper/`
|
||||
3. Create service interface and implementation in `openhis-domain/[module]/service/`
|
||||
4. Create controller in `openhis-application/src/main/java/com/openhis/web/[module]/`
|
||||
5. Test endpoints via Swagger UI
|
||||
|
||||
### Adding a New Frontend Page
|
||||
|
||||
1. Create Vue component in `openhis-ui-vue3/src/views/[module]/`
|
||||
2. Add API service methods in `openhis-ui-vue3/src/api/`
|
||||
3. Add route to `openhis-ui-vue3/src/router/index.js`
|
||||
4. Add Pinia store module if state management needed
|
||||
|
||||
## Important Notes
|
||||
|
||||
- The system uses logical deletion with a `validFlag` field (1 = active, 0 = deleted)
|
||||
- JWT tokens are stored in the `Authorization` header
|
||||
- The system supports WebView environments with C# accessor integration
|
||||
- File uploads are configured with max 10MB per file and 20MB total request size
|
||||
- Password lockout occurs after 5 failed attempts with a 10-minute lock time
|
||||
- The system includes a code generator accessible via `/tool/gen` route
|
||||
- Printing functionality is implemented using the hiprint plugin
|
||||
68
README.md
68
README.md
@@ -1,68 +0,0 @@
|
||||
# 平台介绍
|
||||
|
||||
## 🏠【关于我们】
|
||||
|
||||

|
||||
|
||||
天天开源致⼒于打造中国应⽤管理 软件开源⽣态,⾯向医疗、企业、教育三⼤⾏业信息化需求,提供优质的开源软件产品与解决⽅案。平台现已发布OpenHIS、OpenCOM、OpenEDU系列开源产品,并持续招募⽣态合作伙伴,期待共同构建开源创新的⾏业协作模式,加速⾏业的数字化进程。
|
||||
|
||||
天天开源的前⾝是新致开源,最早于2022年6⽉发布开源医疗软件平台OpenHIS.org.cn,于2023年6⽉发布开源企业软件平台OpenCOM.com.cn。2025年7⽉,新致开源品牌更新为天天开源,我们始终秉持开源、专业、协作的理念,致⼒于为医疗、教育、中⼩企业等⾏业提供优质的开源解决⽅案。
|
||||
|
||||
了解我们a:https://open.tntlinking.com/about?site=gitee
|
||||
|
||||
## 💾【部署包下载】
|
||||
|
||||
请访问官网产品中心下载部署包:https://open.tntlinking.com/resource/productCenter?site=gitee
|
||||
|
||||
## 📚【支持文档】
|
||||
|
||||
技术支持资源:https://open.tntlinking.com/resource/technicalSupport?site=gitee
|
||||
(含演示环境、操作手册、部署手册、开发手册、常见问题等)
|
||||
|
||||
产品介绍:https://open.tntlinking.com/resource/industryKnowledge?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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
202
audit_field_best_practices.md
Normal file
202
audit_field_best_practices.md
Normal file
@@ -0,0 +1,202 @@
|
||||
# OpenHIS 系统审计字段填充最佳实践
|
||||
|
||||
## 概述
|
||||
本文档介绍如何在 OpenHIS 系统中确保所有实体的审计字段(create_by、create_time、update_by、update_time)能够正确自动填充。
|
||||
|
||||
## 自动填充机制
|
||||
|
||||
### 1. 基础实体类
|
||||
所有需要审计字段的实体类都应该继承自 `HisBaseEntity`:
|
||||
|
||||
```java
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
|
||||
@Data
|
||||
@TableName("adm_practitioner")
|
||||
public class Practitioner extends HisBaseEntity {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
// 其他业务字段...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 自动填充处理器
|
||||
系统使用 `MybastisColumnsHandler` 来自动填充审计字段:
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class MybastisColumnsHandler implements MetaObjectHandler {
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
// 填充创建时间和创建人
|
||||
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
|
||||
this.strictInsertFill(metaObject, "create_time", Date.class, new Date());
|
||||
|
||||
String username = getCurrentUsername(); // 获取当前用户名
|
||||
this.strictInsertFill(metaObject, "createBy", String.class, username);
|
||||
this.strictInsertFill(metaObject, "create_by", String.class, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
// 填充更新时间和更新人
|
||||
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
|
||||
this.strictUpdateFill(metaObject, "update_time", Date.class, new Date());
|
||||
|
||||
String username = getCurrentUsername(); // 获取当前用户名
|
||||
this.strictUpdateFill(metaObject, "updateBy", String.class, username);
|
||||
this.strictUpdateFill(metaObject, "update_by", String.class, username);
|
||||
}
|
||||
|
||||
private String getCurrentUsername() {
|
||||
String username = "system";
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null) {
|
||||
username = loginUser.getUsername();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return username;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 确保自动填充正常工作的要点
|
||||
|
||||
### 1. 检查实体类继承关系
|
||||
确保所有实体类都正确继承了 `HisBaseEntity`:
|
||||
|
||||
```java
|
||||
// 正确的做法
|
||||
public class Practitioner extends HisBaseEntity { ... }
|
||||
|
||||
// 如果不能继承 HisBaseEntity,则需要手动添加审计字段
|
||||
public class CustomEntity {
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private String createBy;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date createTime;
|
||||
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private String updateBy;
|
||||
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private Date updateTime;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 验证安全上下文
|
||||
确保在执行数据库操作时有有效的安全上下文:
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class PractitionerService {
|
||||
public void savePractitioner(Practitioner practitioner) {
|
||||
// 确保调用此方法时用户已登录
|
||||
// SecurityUtils.getLoginUser() 应该能返回有效的 LoginUser 对象
|
||||
|
||||
// MyBatis-Plus 会在保存时自动调用 MybastisColumnsHandler
|
||||
practitionerMapper.insert(practitioner);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 检查配置
|
||||
确保自动填充处理器被正确配置:
|
||||
|
||||
```yaml
|
||||
# application.yml
|
||||
mybatis-plus:
|
||||
global-config:
|
||||
db-config:
|
||||
# 其他配置...
|
||||
configuration:
|
||||
# 其他配置...
|
||||
```
|
||||
|
||||
### 4. 手动填充(特殊情况)
|
||||
在某些特殊情况下,如果自动填充不工作,可以手动设置:
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class PractitionerService {
|
||||
public void savePractitionerManually(Practitioner practitioner) {
|
||||
// 手动设置审计字段
|
||||
Date now = new Date();
|
||||
String currentUser = getCurrentUsername();
|
||||
|
||||
practitioner.setCreateTime(now);
|
||||
practitioner.setCreateBy(currentUser);
|
||||
practitioner.setUpdateTime(now);
|
||||
practitioner.setUpdateBy(currentUser);
|
||||
|
||||
practitionerMapper.insert(practitioner);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 常见问题及解决方案
|
||||
|
||||
### 问题1:自动填充不生效
|
||||
**原因:**
|
||||
- 实体类没有继承 `HisBaseEntity`
|
||||
- `MybastisColumnsHandler` 没有被Spring管理(缺少@Component注解)
|
||||
- 没有有效的安全上下文
|
||||
|
||||
**解决方案:**
|
||||
- 确保实体类继承 `HisBaseEntity`
|
||||
- 检查 `MybastisColumnsHandler` 是否有 `@Component` 注解
|
||||
- 确保在调用保存方法时用户已登录
|
||||
|
||||
### 问题2:获取不到当前用户
|
||||
**原因:**
|
||||
- 用户未登录
|
||||
- 安全上下文配置错误
|
||||
|
||||
**解决方案:**
|
||||
- 在调用保存方法前确保用户已登录
|
||||
- 检查安全配置是否正确
|
||||
|
||||
### 问题3:批量操作时审计字段未填充
|
||||
**原因:**
|
||||
- 批量操作可能绕过了自动填充机制
|
||||
|
||||
**解决方案:**
|
||||
- 对于批量操作,手动设置审计字段
|
||||
- 或者使用 MyBatis-Plus 的批量操作方法,确保它们支持自动填充
|
||||
|
||||
## 测试验证
|
||||
|
||||
创建一个简单的测试来验证自动填充是否正常工作:
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
public class AuditFieldTest {
|
||||
@Autowired
|
||||
private PractitionerMapper practitionerMapper;
|
||||
|
||||
@Test
|
||||
public void testAuditFieldsAutoFill() {
|
||||
Practitioner practitioner = new Practitioner();
|
||||
practitioner.setName("Test Practitioner");
|
||||
|
||||
// 保存实体
|
||||
practitionerMapper.insert(practitioner);
|
||||
|
||||
// 验证审计字段是否被正确填充
|
||||
assertThat(practitioner.getCreateBy()).isNotNull();
|
||||
assertThat(practitioner.getCreateTime()).isNotNull();
|
||||
|
||||
// 清理测试数据
|
||||
practitionerMapper.deleteById(practitioner.getId());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 总结
|
||||
通过遵循以上最佳实践,可以确保 OpenHIS 系统中的所有实体在保存时都能正确填充审计字段,避免因缺少这些字段而引发的数据库约束错误。
|
||||
113
audit_field_solution.md
Normal file
113
audit_field_solution.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# 关于数据库审计字段(create_by, create_time等)的处理方案
|
||||
|
||||
## 问题描述
|
||||
在使用OpenHIS系统时,可能会遇到如下错误:
|
||||
```
|
||||
org.postgresql.util.PSQLException: ERROR: null value in column "create_by" of relation "adm_practitioner" violates not-null constraint
|
||||
```
|
||||
|
||||
## 问题分析
|
||||
1. 数据库表中的审计字段(如create_by, create_time)设置了NOT NULL约束
|
||||
2. 应用程序层面使用了MyBatis-Plus的自动填充功能来设置这些字段
|
||||
3. 当自动填充机制失效时,就会出现违反非空约束的错误
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 方案一:修复自动填充机制(推荐)
|
||||
系统已经实现了自动填充机制,位于 `MybastisColumnsHandler.java`:
|
||||
|
||||
```java
|
||||
// 设置数据新增时候的,字段自动赋值规则
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
// 同时填充驼峰和下划线命名的字段,以兼容不同的配置
|
||||
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
|
||||
this.strictInsertFill(metaObject, "create_time", Date.class, new Date());
|
||||
|
||||
String username = "system";
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null) {
|
||||
username = loginUser.getUsername();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
// 使用 fillStrategy 确保即使字段为 null 也会被填充
|
||||
this.strictInsertFill(metaObject, "createBy", String.class, username);
|
||||
this.strictInsertFill(metaObject, "create_by", String.class, username);
|
||||
// 如果 strictInsertFill 没有生效,使用 setFieldValByName 强制设置
|
||||
if (metaObject.hasGetter("createBy") && metaObject.getValue("createBy") == null) {
|
||||
this.setFieldValByName("createBy", username, metaObject);
|
||||
}
|
||||
if (metaObject.hasGetter("create_by") && metaObject.getValue("create_by") == null) {
|
||||
this.setFieldValByName("create_by", username, metaObject);
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
确保所有实体类都继承自 `HisBaseEntity` 或 `BaseEntity`,这样就能自动获得审计字段。
|
||||
|
||||
### 方案二:移除数据库约束(谨慎使用)
|
||||
如果确实需要允许审计字段为NULL,可以移除数据库约束:
|
||||
|
||||
```sql
|
||||
-- 移除 adm_practitioner 表中 create_by 列的 NOT NULL 约束
|
||||
ALTER TABLE "public"."adm_practitioner"
|
||||
ALTER COLUMN "create_by" DROP NOT NULL;
|
||||
|
||||
-- 同样处理 create_time 列(如果需要)
|
||||
ALTER TABLE "public"."adm_practitioner"
|
||||
ALTER COLUMN "create_time" DROP NOT NULL;
|
||||
```
|
||||
|
||||
### 方案三:批量修复所有表的约束
|
||||
如果多个表都存在这个问题,可以使用以下脚本:
|
||||
|
||||
```sql
|
||||
-- 为所有表的审计字段移除NOT NULL约束
|
||||
-- 注意:执行前请备份数据库!
|
||||
|
||||
-- 1. 检查所有包含审计字段的表
|
||||
SELECT
|
||||
table_name,
|
||||
column_name,
|
||||
is_nullable
|
||||
FROM
|
||||
information_schema.columns
|
||||
WHERE
|
||||
column_name IN ('create_by', 'create_time', 'update_by', 'update_time')
|
||||
AND table_schema = 'public'
|
||||
AND is_nullable = 'NO'; -- NO 表示 NOT NULL 约束
|
||||
|
||||
-- 2. 根据需要移除特定表的约束
|
||||
-- 示例:移除多个表的create_by约束
|
||||
ALTER TABLE "public"."adm_practitioner" ALTER COLUMN "create_by" DROP NOT NULL;
|
||||
ALTER TABLE "public"."adm_patient" ALTER COLUMN "create_by" DROP NOT NULL;
|
||||
-- 添加更多表的处理...
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 确保实体类继承基础类
|
||||
所有实体类应继承 `HisBaseEntity` 或 `BaseEntity`:
|
||||
|
||||
```java
|
||||
@Data
|
||||
@TableName("adm_practitioner")
|
||||
public class Practitioner extends HisBaseEntity {
|
||||
// 其他字段...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 检查安全上下文
|
||||
确保在保存数据时有有效的安全上下文,这样自动填充处理器才能获取到当前用户信息。
|
||||
|
||||
### 3. 验证自动填充配置
|
||||
确保 `MybastisColumnsHandler` 在Spring容器中被正确注册(使用@Component注解)。
|
||||
|
||||
## 总结
|
||||
- 推荐保持数据库中的NOT NULL约束,确保数据完整性
|
||||
- 依赖MyBatis-Plus的自动填充机制来设置审计字段
|
||||
- 确保所有实体类继承基础实体类
|
||||
- 在必要时才考虑移除数据库约束
|
||||
104
check_display_order.sql
Normal file
104
check_display_order.sql
Normal file
@@ -0,0 +1,104 @@
|
||||
-- 检查流水号(display_order)是否按“科室+医生+当天”正确递增
|
||||
--
|
||||
-- 说明:
|
||||
-- 1. display_order 存的是纯数字(1, 2, 3...),不带时间戳前缀
|
||||
-- 2. 时间戳前缀(如 20260109)是在前端显示时加上的
|
||||
-- 3. 后端用 Redis key "ORG-{科室ID}-DOC-{医生ID}" 按天自增
|
||||
--
|
||||
-- 如何判断逻辑是否正确:
|
||||
-- 同一科室、同一医生、同一天的记录,display_order 应该递增(1, 2, 3...)
|
||||
-- 不同科室、不同医生、不同天的记录,可能都是 1(这是正常的)
|
||||
|
||||
-- ========================================
|
||||
-- 查询1:按“科室+医生+日期”分组,看每组内的 display_order 是否递增
|
||||
-- ========================================
|
||||
SELECT
|
||||
DATE(start_time) AS 日期,
|
||||
organization_id AS 科室ID,
|
||||
registrar_id AS 医生ID,
|
||||
COUNT(*) AS 该组记录数,
|
||||
MIN(display_order) AS 最小序号,
|
||||
MAX(display_order) AS 最大序号,
|
||||
STRING_AGG(display_order::text, ', ' ORDER BY start_time) AS 序号列表,
|
||||
STRING_AGG(id::text, ', ' ORDER BY start_time) AS 记录ID列表
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
AND start_time >= CURRENT_DATE - INTERVAL '7 days' -- 只看最近7天
|
||||
AND display_order IS NOT NULL
|
||||
GROUP BY DATE(start_time), organization_id, registrar_id
|
||||
ORDER BY 日期 DESC, 科室ID, 医生ID;
|
||||
|
||||
-- ========================================
|
||||
-- 查询2:详细查看每条记录,看同组内的序号是否连续
|
||||
-- ========================================
|
||||
SELECT
|
||||
id AS 记录ID,
|
||||
DATE(start_time) AS 日期,
|
||||
organization_id AS 科室ID,
|
||||
registrar_id AS 医生ID,
|
||||
start_time AS 挂号时间,
|
||||
display_order AS 流水号,
|
||||
-- 计算:同组内的序号应该是 1, 2, 3...,看是否有重复或跳号
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY DATE(start_time), organization_id, registrar_id
|
||||
ORDER BY start_time
|
||||
) AS 应该是第几个,
|
||||
CASE
|
||||
WHEN display_order = ROW_NUMBER() OVER (
|
||||
PARTITION BY DATE(start_time), organization_id, registrar_id
|
||||
ORDER BY start_time
|
||||
) THEN '✓ 正常'
|
||||
ELSE '✗ 异常'
|
||||
END AS 是否正常
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
AND start_time >= CURRENT_DATE - INTERVAL '7 days'
|
||||
AND display_order IS NOT NULL
|
||||
ORDER BY DATE(start_time) DESC, organization_id, registrar_id, start_time;
|
||||
|
||||
-- ========================================
|
||||
-- 查询3:只看今天的数据(最直观)
|
||||
-- ========================================
|
||||
SELECT
|
||||
id AS 记录ID,
|
||||
organization_id AS 科室ID,
|
||||
registrar_id AS 医生ID,
|
||||
start_time AS 挂号时间,
|
||||
display_order AS 流水号
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
AND DATE(start_time) = CURRENT_DATE
|
||||
AND display_order IS NOT NULL
|
||||
ORDER BY organization_id, registrar_id, start_time;
|
||||
|
||||
-- ========================================
|
||||
-- 查询4:发现问题 - 找出同组内 display_order 重复的记录
|
||||
-- ========================================
|
||||
WITH ranked AS (
|
||||
SELECT
|
||||
id,
|
||||
DATE(start_time) AS reg_date,
|
||||
organization_id,
|
||||
registrar_id,
|
||||
start_time,
|
||||
display_order,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY DATE(start_time), organization_id, registrar_id
|
||||
ORDER BY start_time
|
||||
) AS should_be_order
|
||||
FROM adm_encounter
|
||||
WHERE delete_flag = '0'
|
||||
AND start_time >= CURRENT_DATE - INTERVAL '7 days'
|
||||
AND display_order IS NOT NULL
|
||||
)
|
||||
SELECT
|
||||
reg_date AS 日期,
|
||||
organization_id AS 科室ID,
|
||||
registrar_id AS 医生ID,
|
||||
COUNT(*) AS 重复数量,
|
||||
STRING_AGG(id::text || '->' || display_order::text, ', ') AS 问题记录
|
||||
FROM ranked
|
||||
WHERE display_order != should_be_order
|
||||
GROUP BY reg_date, organization_id, registrar_id
|
||||
ORDER BY reg_date DESC;
|
||||
|
||||
38
debug_api_return.md
Normal file
38
debug_api_return.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# 检查后端API返回数据结构
|
||||
|
||||
## 问题分析
|
||||
尽管我们更新了DTO和SQL查询,前端仍然没有显示创建时间,可能的原因:
|
||||
1. API响应中没有包含createTime字段
|
||||
2. SQL查询没有正确返回createTime字段
|
||||
3. 数据库中createTime字段本身为null
|
||||
4. JSON序列化问题
|
||||
|
||||
## 检查步骤
|
||||
|
||||
### 1. 检查数据库中数据
|
||||
首先检查数据库中sys_user表的createTime字段是否正确填充:
|
||||
```sql
|
||||
SELECT user_id, user_name, nick_name, create_time
|
||||
FROM sys_user
|
||||
WHERE create_time IS NOT NULL
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
### 2. 检查API端点
|
||||
API端点是:GET /base-data-manage/practitioner/user-practitioner-page
|
||||
这个端点在PractitionerController中定义,调用practitionerAppService.getUserPractitionerPage()
|
||||
|
||||
### 3. 检查SQL查询
|
||||
在PractitionerAppMapper.xml中,我们已经添加了createTime字段:
|
||||
```xml
|
||||
T2.create_time
|
||||
```
|
||||
|
||||
### 4. 验证DTO映射
|
||||
UserAndPractitionerDto中已添加createTime字段:
|
||||
```java
|
||||
private Date createTime;
|
||||
```
|
||||
|
||||
### 5. 检查JSON序列化
|
||||
检查是否有@JsonFormat注解或其他序列化配置问题
|
||||
290
deep_dive_autofill_solution.md
Normal file
290
deep_dive_autofill_solution.md
Normal file
@@ -0,0 +1,290 @@
|
||||
# 深度排查 MyBatis-Plus 自动填充不生效问题
|
||||
|
||||
## 问题概述
|
||||
尽管对 MyBatis-Plus 的自动填充处理器进行了多次优化和配置,但 `create_by` 和 `create_time` 字段仍然没有被自动填充。
|
||||
|
||||
## 深度排查步骤
|
||||
|
||||
### 1. 检查 AOP 代理是否生效
|
||||
MyBatis-Plus 的自动填充功能依赖于 AOP 代理。如果实体类的方法被直接调用而非通过代理调用,自动填充可能不会生效。
|
||||
|
||||
### 2. 验证 Service 层实现
|
||||
确保使用的是 MyBatis-Plus 提供的通用 Service 方法,而不是自定义的 SQL。
|
||||
|
||||
### 3. 检查 @TableField 注解配置
|
||||
确认实体类中的字段注解配置正确。
|
||||
|
||||
### 4. 检查事务配置
|
||||
某些事务配置可能会影响 AOP 代理的生效。
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 方案一:在 Service 层手动设置审计字段
|
||||
|
||||
创建一个工具类来统一处理审计字段的设置:
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class AuditFieldUtil {
|
||||
|
||||
public static void setCreateInfo(Object entity) {
|
||||
if (entity == null) return;
|
||||
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
String username = loginUser != null ? loginUser.getUsername() : "system";
|
||||
Date currentTime = new Date();
|
||||
|
||||
// 使用反射设置字段值
|
||||
Field createByField = getField(entity.getClass(), "createBy");
|
||||
if (createByField != null) {
|
||||
createByField.setAccessible(true);
|
||||
if (createByField.get(entity) == null || "".equals(createByField.get(entity))) {
|
||||
createByField.set(entity, username);
|
||||
}
|
||||
}
|
||||
|
||||
Field createTimeField = getField(entity.getClass(), "createTime");
|
||||
if (createTimeField != null) {
|
||||
createTimeField.setAccessible(true);
|
||||
if (createTimeField.get(entity) == null) {
|
||||
createTimeField.set(entity, currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理下划线命名的字段
|
||||
Field createByFieldUnderscore = getField(entity.getClass(), "create_by");
|
||||
if (createByFieldUnderscore != null) {
|
||||
createByFieldUnderscore.setAccessible(true);
|
||||
if (createByFieldUnderscore.get(entity) == null || "".equals(createByFieldUnderscore.get(entity))) {
|
||||
createByFieldUnderscore.set(entity, username);
|
||||
}
|
||||
}
|
||||
|
||||
Field createTimeFieldUnderscore = getField(entity.getClass(), "create_time");
|
||||
if (createTimeFieldUnderscore != null) {
|
||||
createTimeFieldUnderscore.setAccessible(true);
|
||||
if (createTimeFieldUnderscore.get(entity) == null) {
|
||||
createTimeFieldUnderscore.set(entity, currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("设置审计字段时发生异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static Field getField(Class<?> clazz, String fieldName) {
|
||||
try {
|
||||
return clazz.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
if (clazz.getSuperclass() != null) {
|
||||
return getField(clazz.getSuperclass(), fieldName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
然后在 Service 实现中使用:
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class PractitionerServiceImpl extends ServiceImpl<PractitionerMapper, Practitioner>
|
||||
implements IPractitionerService {
|
||||
|
||||
@Autowired
|
||||
private AuditFieldUtil auditFieldUtil;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean save(Practitioner entity) {
|
||||
// 在保存前手动设置审计字段
|
||||
auditFieldUtil.setCreateInfo(entity);
|
||||
return super.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean saveBatch(Collection<Practitioner> entityList) {
|
||||
entityList.forEach(auditFieldUtil::setCreateInfo);
|
||||
return super.saveBatch(entityList);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 方案二:重写 BaseMapper 方法
|
||||
|
||||
如果 Service 层的方法不起作用,可以直接在 Mapper 层处理:
|
||||
|
||||
```java
|
||||
@Mapper
|
||||
public interface PractitionerMapper extends BaseMapper<Practitioner> {
|
||||
|
||||
@Insert({
|
||||
"<script>",
|
||||
"INSERT INTO adm_practitioner (",
|
||||
"id, active_flag, name, name_json, gender_enum, birth_date, deceased_date,",
|
||||
"phone, address, address_province, address_city, address_district, address_street,",
|
||||
"address_json, py_str, wb_str, bus_no, yb_no, user_id, tenant_id, delete_flag,",
|
||||
"create_by, create_time, update_by, update_time, org_id,",
|
||||
"phar_prac_cert_no, prsc_dr_cert_code, dr_profttl_code, kpd_code, signature, pos_no",
|
||||
") VALUES (",
|
||||
"#{id}, #{activeFlag}, #{name}, #{nameJson}, #{genderEnum}, #{birthDate}, #{deceasedDate},",
|
||||
"#{phone}, #{address}, #{addressProvince}, #{addressCity}, #{addressDistrict}, #{addressStreet},",
|
||||
"#{addressJson}, #{pyStr}, #{wbStr}, #{busNo}, #{ybNo}, #{userId}, #{tenantId}, #{deleteFlag},",
|
||||
"#{createBy}, #{createTime}, #{updateBy}, #{updateTime}, #{orgId},",
|
||||
"#{pharPracCertNo}, #{prscDrCertCode}, #{drProfttlCode}, #{kpdCode}, #{signature}, #{posNo}",
|
||||
")",
|
||||
"</script>"
|
||||
})
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insertWithAuditFields(Practitioner record);
|
||||
}
|
||||
```
|
||||
|
||||
### 方案三:使用 MyBatis 拦截器
|
||||
|
||||
创建一个 MyBatis 拦截器来自动填充字段:
|
||||
|
||||
```java
|
||||
@Intercepts({
|
||||
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
|
||||
})
|
||||
@Component
|
||||
public class AuditFieldInterceptor implements Interceptor {
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
Object[] args = invocation.getArgs();
|
||||
MappedStatement ms = (MappedStatement) args[0];
|
||||
Object parameter = args[1];
|
||||
|
||||
String sqlCommandType = ms.getSqlCommandType().toString();
|
||||
|
||||
if ("INSERT".equals(sqlCommandType)) {
|
||||
setCreateAuditFields(parameter);
|
||||
} else if ("UPDATE".equals(sqlCommandType)) {
|
||||
setUpdateAuditFields(parameter);
|
||||
}
|
||||
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
private void setCreateAuditFields(Object parameter) {
|
||||
if (parameter == null) return;
|
||||
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
String username = loginUser != null ? loginUser.getUsername() : "system";
|
||||
Date currentTime = new Date();
|
||||
|
||||
// 设置 createBy 和 createTime
|
||||
setFieldValue(parameter, "createBy", username);
|
||||
setFieldValue(parameter, "create_time", username);
|
||||
setFieldValue(parameter, "createTime", currentTime);
|
||||
setFieldValue(parameter, "create_time", currentTime);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void setUpdateAuditFields(Object parameter) {
|
||||
if (parameter == null) return;
|
||||
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
String username = loginUser != null ? loginUser.getUsername() : "system";
|
||||
Date currentTime = new Date();
|
||||
|
||||
// 设置 updateBy 和 updateTime
|
||||
setFieldValue(parameter, "updateBy", username);
|
||||
setFieldValue(parameter, "update_by", username);
|
||||
setFieldValue(parameter, "updateTime", currentTime);
|
||||
setFieldValue(parameter, "update_time", currentTime);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void setFieldValue(Object obj, String fieldName, Object value) {
|
||||
try {
|
||||
Field field = getField(obj.getClass(), fieldName);
|
||||
if (field != null) {
|
||||
field.setAccessible(true);
|
||||
if (field.get(obj) == null) { // 只在原值为 null 时设置
|
||||
field.set(obj, value);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 忽略无法设置的字段
|
||||
}
|
||||
}
|
||||
|
||||
private Field getField(Class<?> clazz, String fieldName) {
|
||||
try {
|
||||
return clazz.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
if (clazz.getSuperclass() != null) {
|
||||
return getField(clazz.getSuperclass(), fieldName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object plugin(Object target) {
|
||||
if (target instanceof Executor) {
|
||||
return Plugin.wrap(target, this);
|
||||
} else {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperties(Properties properties) {}
|
||||
}
|
||||
```
|
||||
|
||||
## 推荐实施顺序
|
||||
|
||||
1. 首先尝试方案一(Service 层手动设置),这是最简单且可控的方式
|
||||
2. 如果方案一不行,尝试方案三(MyBatis 拦截器),它在更底层起作用
|
||||
3. 方案二是最后的选择,需要重写具体的插入逻辑
|
||||
|
||||
## 验证方法
|
||||
|
||||
创建一个测试来验证自动填充是否生效:
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
public class AuditFieldTest {
|
||||
|
||||
@Autowired
|
||||
private IPractitionerService practitionerService;
|
||||
|
||||
@Test
|
||||
public void testAuditFieldFill() {
|
||||
Practitioner practitioner = new Practitioner();
|
||||
practitioner.setName("Test Practitioner");
|
||||
|
||||
// 记录保存前的值
|
||||
System.out.println("保存前 - createBy: " + practitioner.getCreateBy());
|
||||
System.out.println("保存前 - createTime: " + practitioner.getCreateTime());
|
||||
|
||||
boolean success = practitionerService.save(practitioner);
|
||||
|
||||
// 从数据库重新查询以验证
|
||||
Practitioner saved = practitionerService.getById(practitioner.getId());
|
||||
System.out.println("保存后 - createBy: " + saved.getCreateBy());
|
||||
System.out.println("保存后 - createTime: " + saved.getCreateTime());
|
||||
|
||||
Assertions.assertTrue(success);
|
||||
Assertions.assertNotNull(saved.getCreateBy());
|
||||
Assertions.assertNotNull(saved.getCreateTime());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
通过这些方案,应该能够解决自动填充不生效的问题。
|
||||
143
diagnose_autofill_issue.md
Normal file
143
diagnose_autofill_issue.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# 诊断 MyBatis-Plus 自动填充问题
|
||||
|
||||
## 问题现象
|
||||
尽管 `MybastisColumnsHandler` 已经实现并配置了自动填充功能,但 `create_by` 和 `create_time` 字段仍然没有被正确填充。
|
||||
|
||||
## 可能的原因及解决方案
|
||||
|
||||
### 1. 检查组件扫描配置
|
||||
确保 `MybastisColumnsHandler` 类被Spring容器正确管理:
|
||||
|
||||
```java
|
||||
@Component // 确保这个注解存在
|
||||
public class MybastisColumnsHandler implements MetaObjectHandler {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 检查包扫描路径
|
||||
在主应用类中确保扫描到了处理器所在的包:
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.openhis.*.mapper") // 确保扫描到你的mapper
|
||||
@ComponentScan(basePackages = {"com.core", "com.openhis"}) // 确保扫描到处理器
|
||||
public class OpenHisApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(OpenHisApplication.class, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 验证实体类配置
|
||||
确保实体类正确继承了 `HisBaseEntity` 并且字段上有正确的注解:
|
||||
|
||||
```java
|
||||
@Data
|
||||
@TableName("adm_practitioner")
|
||||
public class Practitioner extends HisBaseEntity {
|
||||
// 不需要在子类中重复定义 createBy, createTime 等字段
|
||||
// 因为它们已在 HisBaseEntity 中定义并带有 @TableField(fill = FieldFill.INSERT)
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 检查安全上下文
|
||||
自动填充处理器依赖于安全上下文来获取当前用户。确保在执行保存操作时用户已登录:
|
||||
|
||||
```java
|
||||
// 在保存之前,确保用户已登录
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
// 用户未登录,可能需要手动设置审计字段
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 手动测试自动填充
|
||||
创建一个简单的测试来验证自动填充是否正常工作:
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
public class AutoFillTest {
|
||||
|
||||
@Autowired
|
||||
private PractitionerMapper practitionerMapper;
|
||||
|
||||
@Test
|
||||
public void testAutoFill() {
|
||||
Practitioner practitioner = new Practitioner();
|
||||
practitioner.setName("Test Practitioner");
|
||||
|
||||
// 检查在保存前字段是否为空
|
||||
System.out.println("Before insert - createBy: " + practitioner.getCreateBy());
|
||||
System.out.println("Before insert - createTime: " + practitioner.getCreateTime());
|
||||
|
||||
// 执行插入操作
|
||||
int result = practitionerMapper.insert(practitioner);
|
||||
|
||||
// 检查保存后字段是否被填充
|
||||
System.out.println("After insert - createBy: " + practitioner.getCreateBy());
|
||||
System.out.println("After insert - createTime: " + practitioner.getCreateTime());
|
||||
|
||||
assertThat(result).isEqualTo(1);
|
||||
assertThat(practitioner.getCreateBy()).isNotNull();
|
||||
assertThat(practitioner.getCreateTime()).isNotNull();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 临时解决方案
|
||||
如果自动填充仍然不工作,可以在服务层手动设置这些字段:
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class PractitionerServiceImpl extends ServiceImpl<PractitionerMapper, Practitioner>
|
||||
implements IPractitionerService {
|
||||
|
||||
@Override
|
||||
public void savePractitioner(Practitioner practitioner) {
|
||||
// 手动设置审计字段
|
||||
if (practitioner.getCreateBy() == null || practitioner.getCreateBy().isEmpty()) {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null) {
|
||||
practitioner.setCreateBy(loginUser.getUsername());
|
||||
} else {
|
||||
practitioner.setCreateBy("system"); // 默认值
|
||||
}
|
||||
}
|
||||
|
||||
if (practitioner.getCreateTime() == null) {
|
||||
practitioner.setCreateTime(new Date());
|
||||
}
|
||||
|
||||
// 执行保存操作
|
||||
this.save(practitioner);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 检查 MyBatis-Plus 版本兼容性
|
||||
确保使用的 MyBatis-Plus 版本与自动填充功能兼容。当前项目使用的是 3.5.5 版本,应该支持自动填充功能。
|
||||
|
||||
### 8. 调试自动填充处理器
|
||||
在 `MybastisColumnsHandler` 中添加日志来调试是否被调用:
|
||||
|
||||
```java
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
System.out.println("MybastisColumnsHandler.insertFill() called"); // 调试日志
|
||||
|
||||
Date currentTime = new Date();
|
||||
this.strictInsertFill(metaObject, "createTime", Date.class, currentTime);
|
||||
this.strictInsertFill(metaObject, "create_time", Date.class, currentTime);
|
||||
|
||||
String username = getCurrentUsername();
|
||||
System.out.println("Setting createBy to: " + username); // 调试日志
|
||||
|
||||
this.strictInsertFill(metaObject, "createBy", String.class, username);
|
||||
this.strictInsertFill(metaObject, "create_by", String.class, username);
|
||||
|
||||
// ... 其他代码
|
||||
}
|
||||
```
|
||||
|
||||
通过以上步骤,应该能够诊断并解决自动填充不工作的问题。
|
||||
145
enhanced_MybastisColumnsHandler.java
Normal file
145
enhanced_MybastisColumnsHandler.java
Normal file
@@ -0,0 +1,145 @@
|
||||
package com.core.framework.handler;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* MyBatis-Plus 自动填充处理器
|
||||
* 用于自动填充创建时间和更新时间,以及创建人和更新人
|
||||
*/
|
||||
@Component
|
||||
public class MybastisColumnsHandler implements MetaObjectHandler {
|
||||
|
||||
// 设置数据新增时的字段自动赋值规则
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
// 填充创建时间
|
||||
Date currentTime = new Date();
|
||||
this.strictInsertFill(metaObject, "createTime", Date.class, currentTime);
|
||||
this.strictInsertFill(metaObject, "create_time", Date.class, currentTime);
|
||||
|
||||
// 获取当前登录用户名
|
||||
String username = getCurrentUsername();
|
||||
|
||||
// 填充创建人
|
||||
this.strictInsertFill(metaObject, "createBy", String.class, username);
|
||||
this.strictInsertFill(metaObject, "create_by", String.class, username);
|
||||
|
||||
// 确保tenantId被设置
|
||||
Integer tenantId = getCurrentTenantId();
|
||||
if (tenantId == null) {
|
||||
throw new RuntimeException("无法获取当前租户ID,请确保用户已登录或正确设置租户上下文");
|
||||
}
|
||||
this.strictInsertFill(metaObject, "tenantId", Integer.class, tenantId);
|
||||
this.strictInsertFill(metaObject, "tenant_id", Integer.class, tenantId);
|
||||
}
|
||||
|
||||
// 设置数据修改时的字段自动赋值规则
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
// 填充更新时间
|
||||
Date currentTime = new Date();
|
||||
this.strictUpdateFill(metaObject, "updateTime", Date.class, currentTime);
|
||||
this.strictUpdateFill(metaObject, "update_time", Date.class, currentTime);
|
||||
|
||||
// 填充更新人
|
||||
String username = getCurrentUsername();
|
||||
this.strictUpdateFill(metaObject, "updateBy", String.class, username);
|
||||
this.strictUpdateFill(metaObject, "update_by", String.class, username);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户名
|
||||
* @return 当前登录用户名,如果无法获取则返回 "system"
|
||||
*/
|
||||
private String getCurrentUsername() {
|
||||
String username = "system"; // 默认值
|
||||
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null) {
|
||||
username = loginUser.getUsername();
|
||||
} else {
|
||||
// 尝试从请求中获取用户信息
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
// 可以在这里添加额外的逻辑来从请求中获取用户信息
|
||||
// 例如从请求头、session等获取用户信息
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 记录异常但不中断处理流程
|
||||
System.err.println("获取当前登录用户时发生异常: " + e.getMessage());
|
||||
// 可以考虑记录日志
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前租户 ID
|
||||
*/
|
||||
private Integer getCurrentTenantId() {
|
||||
Integer result = null;
|
||||
|
||||
// 首先尝试从线程局部变量中获取租户ID(适用于定时任务等场景)
|
||||
Integer threadLocalTenantId = TenantContext.getCurrentTenant();
|
||||
if (threadLocalTenantId != null) {
|
||||
result = threadLocalTenantId;
|
||||
} else {
|
||||
// 获取当前登录用户的租户ID(优先使用SecurityUtils中储存的LoginUser的租户ID)
|
||||
try {
|
||||
if (SecurityUtils.getAuthentication() != null) {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null) {
|
||||
result = loginUser.getTenantId();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 记录异常但不中断处理
|
||||
System.err.println("获取当前登录用户租户ID时发生异常: " + e.getMessage());
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
// 尝试从请求头中获取租户ID
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
if (request != null) {
|
||||
// 从请求头获取租户ID,假设header名称为"X-Tenant-ID" ; 登录接口前端把租户id放到请求头里
|
||||
String tenantIdHeader = request.getHeader("X-Tenant-ID");
|
||||
String requestMethodName = request.getHeader("Request-Method-Name");
|
||||
// 登录
|
||||
if ("login".equals(requestMethodName)) {
|
||||
if (tenantIdHeader != null && !tenantIdHeader.isEmpty()) {
|
||||
try {
|
||||
result = Integer.parseInt(tenantIdHeader);
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("解析请求头中的租户ID时发生异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果仍然没有获取到租户ID,返回默认值
|
||||
if (result == null) {
|
||||
System.out.println("警告: 未能获取当前租户ID,将使用默认租户ID 1");
|
||||
result = 1; // 默认租户ID
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
13
fragment.java
Normal file
13
fragment.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.openhis;
|
||||
|
||||
/**
|
||||
* 示例类 - 引用 OpenHisApplication
|
||||
*/
|
||||
public class Fragment {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 引用 OpenHisApplication
|
||||
Class<?> applicationClass = com.openhis.OpenHisApplication.class;
|
||||
System.out.println("Application class: " + applicationClass.getName());
|
||||
}
|
||||
}
|
||||
332
md/前端UI规范文档.md
Normal file
332
md/前端UI规范文档.md
Normal 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. 遵循响应式设计原则
|
||||
273
md/需求/100-门诊手术中临时医嘱生成界面PRD_2026-1-23.md
Normal file
273
md/需求/100-门诊手术中临时医嘱生成界面PRD_2026-1-23.md
Normal file
@@ -0,0 +1,273 @@
|
||||
## 门诊手术中临时医嘱生成界面PRD文档
|
||||
|
||||
### 一、页面概述
|
||||
|
||||
**页面名称**:门诊手术中临时医嘱生成界面
|
||||
**页面目标**:帮助麻醉医师在手术过程中快速生成临时医嘱,完成药品计费引用、医嘱预览和电子签名确认的全流程操作
|
||||
**适用场景**:门诊手术过程中需要追加药品医嘱时使用
|
||||
**页面类型**:表单页+数据展示页
|
||||
|
||||
**核心功能**:
|
||||
|
||||
1. 患者手术信息展示
|
||||
2. 已引用计费药品列表展示与汇总
|
||||
3. 临时医嘱预览与编辑功能
|
||||
4. 医师电子签名确认流程
|
||||
5. 数据刷新与退出操作
|
||||
**用户价值**:简化手术中医嘱生成流程,确保医嘱准确性,实现无纸化操作,提高手术室工作效率
|
||||
原型图地址:https://static.pm-ai.cn/prototype/20260122/e1d7f10b85e9efea543bf47bd6831600/index.html
|
||||
**流程图:**
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["Start"]) --> Enter["进入门诊手术中临时医嘱生成界面"]
|
||||
Enter --> ShowBase["展示患者基本信息"]
|
||||
ShowBase --> ShowQuoted["显示已引用计费药品列表"]
|
||||
ShowQuoted --> ShowPreview["显示医嘱预览表格"]
|
||||
ShowPreview --> UserOp{用户操作}
|
||||
|
||||
UserOp -- "引用计费" --> GetLatest{"获取最新计费药品数据\n获取成功?"}
|
||||
GetLatest -- "否" --> ErrTip1["显示错误提示"]
|
||||
GetLatest -- "是" --> UpdateTable["更新药品表格和汇总"]
|
||||
|
||||
UserOp -- "编辑" --> PopEdit["弹出医嘱编辑表单"]
|
||||
PopEdit --> EditVal{"验证通过?"}
|
||||
EditVal -- "否" --> ErrTip2["返回错误提示"]
|
||||
EditVal -- "是" --> SaveClick{"点击保存?"}
|
||||
SaveClick -- "是" --> GenTemp["生成临时药品医嘱"]
|
||||
SaveClick -- "否" --> UserOp
|
||||
|
||||
GenTemp --> UpdatePreview["更新医嘱预览表格"]
|
||||
UpdatePreview --> UpdateRecord["更新手术记录"]
|
||||
UpdateRecord --> ShowResult["显示生成结果"]
|
||||
|
||||
UserOp -- "一键签名并生成医嘱" --> PopPwd["弹出账户密码输入框"]
|
||||
PopPwd --> PopConfirm{"弹出确认对话框"}
|
||||
PopConfirm -- "否" --> UserOp
|
||||
PopConfirm -- "是" --> GenTemp
|
||||
|
||||
UserOp -- "刷新" --> Reload["重新加载界面数据"]
|
||||
Reload --> ShowQuoted
|
||||
|
||||
UserOp -- "退出" --> ExitConfirm{"确认退出?"}
|
||||
ExitConfirm -- "否" --> UserOp
|
||||
ExitConfirm -- "是" --> ReturnUp["返回上级页面"]
|
||||
|
||||
ErrTip1 --> UserOp
|
||||
ErrTip2 --> PopEdit
|
||||
ShowResult --> UserOp
|
||||
ReturnUp --> End([结束])
|
||||
```
|
||||
|
||||
### 二、整体布局分析
|
||||
|
||||
**页面宽度**:自适应布局
|
||||
|
||||
**要区域划分**:
|
||||
|
||||
1. 顶部信息区(15%):患者基本信息+操作按钮区
|
||||
2. 计费药品展示区(35%):已引用计费药品表格+金额汇总
|
||||
3. 医嘱预览区(35%):待生成医嘱的预览表格
|
||||
4. 签名确认区(15%):医师签名信息+操作按钮
|
||||
**布局特点**:上下分块布局,采用卡片式设计,主要区域间有明确分隔线
|
||||
**响应式要求**:768px以下时患者信息改为纵向排列,操作按钮换行显示
|
||||
|
||||
### 三、页面区域详细描述
|
||||
|
||||
#### 1. 顶部信息区
|
||||
|
||||
**区域位置**:页面顶部
|
||||
**区域尺寸**:高度180px(包含20px内边距)
|
||||
**区域功能**:展示患者基本信息+提供主要操作入口
|
||||
**包含元素**:
|
||||
|
||||
- **标题栏**:
|
||||
- 元素类型:标题文本
|
||||
- 显示内容:“门诊术中临时医嘱”
|
||||
- 样式特征:白色文字,1.5rem字号,居中显示,渐变蓝色背景
|
||||
- **患者信息卡**:
|
||||
- 元素类型:信息展示区块
|
||||
- 显示内容:患者姓名、就诊卡号、手术单号、科室、医师、角色
|
||||
- 患者:样例值-张三
|
||||
- 就诊卡号:样例值-202507010122
|
||||
- 手术单号:样例值- S202507010135
|
||||
- 科室: 样例值-手术室(OR101)-取值于手术安排的手术间号字段
|
||||
- 医师:样例值-李麻(3015)
|
||||
- 角色:样例值-麻醉医师
|
||||
- 样式特征:半透明白色背景,圆角8px,内部flex布局
|
||||
- **操作按钮组**:
|
||||
- **[刷新按钮]**:
|
||||
- 元素类型:主要操作按钮
|
||||
- 显示内容:↻ 刷新
|
||||
- 交互行为:点击后重新加载当前界面的数据
|
||||
- 样式特征:蓝色渐变背景,悬停有上浮效果
|
||||
- **[引用计费按钮]**:
|
||||
- 元素类型:次要操作按钮
|
||||
- 显示内容:引用计费
|
||||
- 交互行为:点击后拉取当前患者最新计费药品的数据
|
||||
|
||||
#### 2. 计费药品展示区
|
||||
|
||||
**区域位置**:顶部信息区下方
|
||||
**区域尺寸**:高度约420px(包含标题和表格)
|
||||
**区域功能**:展示待生成医嘱的计费药品清单
|
||||
**包含元素**:
|
||||
|
||||
- **表格标题**:
|
||||
- 显示内容:“一、已引用计费药品(待生成医嘱)”
|
||||
- 样式特征:1.2rem字号,底部边框线
|
||||
- **药品数据表格**:
|
||||
|
||||
**取值于门诊术中计费界面生成的药品计费数据(adm_charge_item(费用项管理)、med_medication_request(药品请求管理)等),具体与系统实际业务数据为主。**
|
||||
|
||||
**(参考)关联字段:adm_charge_item. encounter_id = med_medication_request. encounter_id and –就诊ID**
|
||||
|
||||
**adm_charge_item. service_table = 'med_medication_request' and --记录药品数据**
|
||||
|
||||
**adm_charge_item. bus_no = med_medication_request. bus_no -- adm_charge_item. bus_no的值之前多加了‘CI’**
|
||||
|
||||
- 展示方式:斑马纹表格
|
||||
- 数据字段:
|
||||
- 序号:数字 - 自动生成
|
||||
- 药品名称:文本 - 如"罗哌卡因注射液"
|
||||
- 规格:文本 - 如"10ml"
|
||||
- 数量:数字 - 可编辑
|
||||
- 批号:文本 - 如"L240715"
|
||||
- 单价:数字 - 如"38"
|
||||
- 小计:数字 - 自动计算
|
||||
- 医保:标签 - “甲/乙/自费”
|
||||
- 样式特征:表头浅灰色背景,医保类型有颜色区分(蓝色=医保,绿色=自费)
|
||||
- **金额汇总栏**:
|
||||
- 显示内容:
|
||||
- 医保内金额(蓝色强调)
|
||||
- 自费金额(绿色强调)
|
||||
- 总计金额(红色强调)
|
||||
- 位置:表格底部右对齐
|
||||
|
||||
#### 3. 医嘱预览区
|
||||
|
||||
**区域位置**:计费药品展示区下方
|
||||
**区域尺寸**:高度约420px(包含标题和表格)
|
||||
**区域功能**:展示即将生成的药品医嘱
|
||||
**包含元素**:
|
||||
|
||||
\*生成门诊药品医嘱表相关的数据,满足**计费药品明细 ↔ 药品医嘱** 一一对应的要求。
|
||||
|
||||
可以对照参考:需结合门诊医生站开立药品医嘱时生成的药品医嘱表
|
||||
|
||||
- **表格标题**:
|
||||
- 显示内容:“二、临时医嘱预览(已生成)”
|
||||
- **医嘱表格**:
|
||||
- 展示方式:斑马纹表格
|
||||
- 数据字段:
|
||||
- 序号:数字
|
||||
- 医嘱名称:文本(取已引用计费药品的药品名称)
|
||||
- 剂量:数字(自动计算=规格×数量)
|
||||
- 单位:文本(根据药品类型自动判断)
|
||||
- 用法:下拉选择(不可编辑)
|
||||
- 频次:固定"临时"
|
||||
- 执行时间:自动生成当前时间
|
||||
- 操作:编辑/删除按钮
|
||||
- 操作功能:
|
||||
- 编辑:弹出表单修改剂量、用法等字段
|
||||
- 删除:二次确认后移除该条医嘱
|
||||
|
||||
#### 4. 签名确认区
|
||||
|
||||
**区域位置**:页面底部
|
||||
**区域尺寸**:高度约180px
|
||||
**区域功能**:完成医嘱确认和电子签名
|
||||
**包含元素**:
|
||||
|
||||
- **签名信息卡**:
|
||||
- 显示内容:医师姓名工号、签名状态、签名时间
|
||||
- 样式特征:浅灰色背景,圆角边框
|
||||
- **[一键签名按钮]**:
|
||||
- 元素类型:主要操作按钮
|
||||
- 显示内容:“一键签名并生成医嘱”
|
||||
- 交互行为:点击后弹出账户密码输入框
|
||||
- 样式特征:绿色背景,悬停效果
|
||||
- **[取消按钮]**:
|
||||
- 元素类型:次要操作按钮
|
||||
- 显示内容:“取消”
|
||||
- 交互行为:返回上级页面
|
||||
|
||||
### 四、交互功能详细说明
|
||||
|
||||
#### 1. 引用计费功能
|
||||
|
||||
**功能描述**:从术中计费药品获取患者当前最新的计费药品数据
|
||||
**触发条件**:点击"引用计费"按钮
|
||||
**操作流程**:
|
||||
|
||||
1. 点击按钮获取患者当前最新的计费药品数据
|
||||
2. 成功返回后更新药品表格数据
|
||||
3. 自动计算并更新费用汇总
|
||||
|
||||
**反馈机制**:成功提示弹窗"已成功引用最新计费药品信息!"
|
||||
**异常处理**:请求失败时显示错误提示“获取计费数据失败,请重试”,保留原数据
|
||||
|
||||
#### 2. 医嘱生成功能
|
||||
|
||||
**功能描述**:将计费药品转为正式医嘱
|
||||
**触发条件**:点击"一键签名并生成医嘱"按钮
|
||||
**操作流程**:
|
||||
|
||||
1. 自动生成药品医嘱预览(带默认用法和剂量)
|
||||
2. 弹出账户密码输入框
|
||||
3. 验证通过后生成临时药品医嘱数据
|
||||
4. 成功返回后显示生成结果
|
||||
**数据转换规则**:
|
||||
- 剂量 = 规格数值 × 数量(如"10ml"×2 → 20ml)
|
||||
- 单位:根据药品名称自动判断(默认获取当前药品在《药品目录》维护剂量单位的值)
|
||||
- 用法:根据药品名称自动判断(默认获取当前药品在《药品目录》维护用法的值,如果未维护默认空)
|
||||
- 医嘱名称:取值药品名称
|
||||
- 频次:默认ST
|
||||
- 执行时间:默认当前系统时间
|
||||
|
||||
#### 3. 医嘱编辑功能
|
||||
|
||||
**功能描述**:修改已生成的医嘱明细
|
||||
**触发条件**:点击"编辑"按钮
|
||||
**操作流程**:
|
||||
|
||||
1. 弹出编辑表单(带当前值医嘱值)
|
||||
2. 修改后点击保存更新表格
|
||||
3. 自动重新计算相关字段得值
|
||||
**字段限制**:
|
||||
- 剂量:必须为数字
|
||||
- 用法:限定下拉选项,取值于字典管理:用药途径(用法)的值
|
||||
- 频次:固定为"ST"不可编辑
|
||||
|
||||
### 五、数据结构说明
|
||||
|
||||
**关键数据字段**:
|
||||
|
||||
| **字段名** | **说明** | **数据类型** | **示例值** | **是否必填** | **备注** |
|
||||
|---------------|----------|--------------|--------------------|--------------|------------------|
|
||||
| patientId | 患者ID | string | “202507010122” | 是 | 就诊卡号 |
|
||||
| surgeryNo | 手术单号 | string | “S202507010135” | 是 | |
|
||||
| medicineName | 药品名称 | string | “罗哌卡因注射液” | 是 | |
|
||||
| spec | 规格 | string | “10ml” | 是 | 需包含数值和单位 |
|
||||
| batchNo | 批号 | string | “L240715” | 是 | |
|
||||
| insuranceType | 医保类型 | string | “乙” | 是 | 甲/乙/自费 |
|
||||
| usage | 用法 | string | “静脉推注” | 是 | |
|
||||
| execTime | 执行时间 | datetime | “2025-07-01 08:41” | 是 | 精确到分钟 |
|
||||
|
||||
### 六、开发实现要点
|
||||
|
||||
**样式规范**:
|
||||
|
||||
- **主色调**:\#4a90e2(按钮/标题)
|
||||
- **辅助色**:\#5cb85c(成功操作)、\#e74c3c(警告)
|
||||
- **字体规范**:标题1.5rem/正文0.95rem,行高1.6
|
||||
- **间距系统**:区块padding20px,元素间距15px
|
||||
- **表格样式**:斑马纹,行高56px,单元格padding15px 20px
|
||||
|
||||
**技术要求**:
|
||||
|
||||
- **浏览器兼容**:Chrome/Firefox/Edge最新版
|
||||
|
||||
**注意事项**:
|
||||
|
||||
1. 医嘱生成后需同步更新手术记录
|
||||
2. 所有金额显示保留两位小数
|
||||
505
md/需求/102-门诊医生站传染病报告卡登记-2026-1-28.md
Normal file
505
md/需求/102-门诊医生站传染病报告卡登记-2026-1-28.md
Normal file
@@ -0,0 +1,505 @@
|
||||
## 门诊医生站传染性报卡登记PRD文档
|
||||
|
||||
### 一、页面概述
|
||||
|
||||
**页面名称**:门诊医生站传染性报卡登记
|
||||
**页面目标**:帮助医生完成法定传染病病例的电子报告卡填写与提交
|
||||
**适用场景**:医生确诊或疑似发现法定传染病病例时,进行报卡登记
|
||||
**页面类型**:表单页(复杂表单)
|
||||
|
||||
**核心功能**:
|
||||
|
||||
1. 患者基本信息录入(含身份验证)
|
||||
2. 传染病分类选择与疾病诊断信息登记
|
||||
3. 病例分类与流行病学信息记录
|
||||
4. 数据校验与表单提交
|
||||
5. 地址四级联动选择(省-市-区县-街道)
|
||||
|
||||
**用户价值**:
|
||||
|
||||
- 规范传染病报告流程,确保数据完整准确
|
||||
- 减少手工填写错误,提高上报效率
|
||||
- 自动关联患者基本信息,减少重复录入
|
||||
- 内置校验规则防止漏报错报
|
||||
|
||||
**原型图地址**:https://static.pm-ai.cn/prototype/20260128/6041dcc237645108aa9e917e8d57705f/index.html
|
||||
**流程图**:
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A(["开始报卡"]) --> B["填写患者基本信息"]
|
||||
B --> C{"身份证格式错误"}
|
||||
C -- 是 --> D["提示请输入有效身份证号码"]
|
||||
C -- 否 --> E{"患者年龄≤14岁"}
|
||||
E -- 是 --> F["显示家长姓名输入框"]
|
||||
E -- 否 --> G["隐藏家长姓名输入框"]
|
||||
F --> H["填写现住地址"]
|
||||
G --> H
|
||||
H --> I{"地址加载失败"}
|
||||
I -- 是 --> J["显示手动输入选项"]
|
||||
I -- 否 --> K["选择疾病分类"]
|
||||
J --> K
|
||||
K --> L{"选择特定疾病"}
|
||||
L -- 是 --> M["显示疾病分型选择"]
|
||||
L -- 否 --> N["跳过分型选择"]
|
||||
M --> O["填写发病/诊断日期"]
|
||||
N --> O
|
||||
O --> P{"日期逻辑错误"}
|
||||
P -- 是 --> Q["提示发病日期不能晚于诊断日期"]
|
||||
P -- 否 --> R["填写报告信息"]
|
||||
Q --> R
|
||||
R --> S["表单校验"]
|
||||
S --> T{"校验失败"}
|
||||
T -- 是 --> U["显示错误提示"]
|
||||
T -- 否 --> V["保存报卡"]
|
||||
U --> S
|
||||
V --> W{"点击重置按钮"}
|
||||
W -- 是 --> X["保留关键信息重置其他字段"]
|
||||
X --> S
|
||||
V --> Y{"点击关闭按钮"}
|
||||
Y -- 是 --> Z{"确认关闭"}
|
||||
Z -- 是 --> AA(["结束流程"])
|
||||
Z -- 否 --> V
|
||||
```
|
||||
|
||||
|
||||
### 二、整体布局分析
|
||||
|
||||
**页面宽度**:自适应布局
|
||||
**主要区域划分**:
|
||||
|
||||
1. **顶部标题区**(5%):展示表单标题和卡片编号
|
||||
2. **患者信息区**(30%):患者基本信息、联系方式、现住地址等
|
||||
3. **疾病信息区**(50%):疾病分类选择、发病/诊断日期、疾病分型等
|
||||
4. **报告信息区**(10%):报告单位、医生、填卡日期等
|
||||
|
||||
**操作按钮区**(5%):保存、重置、关闭按钮
|
||||
**布局特点**:上下布局,采用响应式网格,表单分组清晰,必填项高亮标识
|
||||
|
||||
### 三、页面区域详细描述
|
||||
|
||||
#### 1. 标题区
|
||||
|
||||
**区域位置**:页面顶部
|
||||
**区域尺寸**:高度60px
|
||||
**区域功能**:展示表单标题和唯一编号标识
|
||||
**包含元素**:
|
||||
|
||||
- 表单标题
|
||||
|
||||
- - 元素类型:标题文本
|
||||
- 默认内容:“中华人民共和国传染病报告卡”
|
||||
- 样式要求:20px字号,深蓝色(#2c3e50),居中加粗
|
||||
|
||||
- 卡片编号
|
||||
|
||||
- - 元素类型:输入框
|
||||
- 默认值:空
|
||||
- 提示文字:“单位自编,与网络直报一致”
|
||||
- 交互行为:支持手动输入12位编号
|
||||
- 样式要求:12px灰色文字,带下划线分隔线
|
||||
|
||||
#### 2. 患者基本信息区
|
||||
|
||||
**区域位置**:标题区下方
|
||||
**区域功能**:采集患者核心身份信息、联系方式、居住地等
|
||||
**包含元素**:
|
||||
|
||||
- 患者姓名输入框
|
||||
|
||||
- - 元素类型:文本输入框,自动引入当前就诊患者信息的姓名
|
||||
- 校验规则:必填项,支持中文姓名2-10字
|
||||
|
||||
- 家长姓名输入框
|
||||
|
||||
- - 元素类型:文本输入框
|
||||
- 条件显示:当系统计算年龄≤14岁时自动显示必填标识
|
||||
|
||||
- 身份证号输入框
|
||||
|
||||
- - 元素类型:文本输入框,自动引入当前就诊患者信息的身份证号
|
||||
- 校验规则:必填项,自动校验18位身份证格式
|
||||
|
||||
- 性别选择
|
||||
|
||||
- - 元素类型:单选按钮组
|
||||
- 选项:男/女/未知,自动匹配当前就诊患者信息的性别
|
||||
- 默认值:必填项
|
||||
|
||||
- 出生日期输入
|
||||
|
||||
- - 元素类型:复合输入区域
|
||||
- 包含:年(4位)/月(2位)/日(2位)三个输入框,自动匹配当前就诊患者信息的出生年月
|
||||
- 联动逻辑:自动计算实足年龄并填充到年龄输入框
|
||||
|
||||
- 工作单位输入框
|
||||
|
||||
- - 元素类型:文本输入框,自动引入当前就诊患者信息的工作单位
|
||||
- 特殊场景:学生自动关联学校信息
|
||||
|
||||
- 联系电话
|
||||
|
||||
- - 元素类型:电话输入框,自动引入当前就诊患者信息的联系方式
|
||||
- 校验规则:必填,11位手机号或带区号固话
|
||||
|
||||
- 紧急联系人电话
|
||||
|
||||
- - 元素类型:电话输入框
|
||||
- 校验规则:必填,11位手机号或带区号固话
|
||||
|
||||
- 病人属于
|
||||
|
||||
- - 复选框类型:通过现地址自动判断
|
||||
- 校验规则:必填
|
||||
|
||||
- 职业
|
||||
|
||||
- - 下拉选项类型:取值于字典管理的字典名称为“职业”维护的数据
|
||||
- 校验规则:必填
|
||||
|
||||
|
||||
|
||||
#### 3. 现住地址选择区
|
||||
|
||||
**区域功能**:四级联动地址选择(省-市-区县-街道)
|
||||
**交互逻辑**:
|
||||
|
||||
1. 省份选择后动态加载对应城市
|
||||
2. 城市选择后动态加载区县
|
||||
3. 区县选择后动态加载街道
|
||||
4. 村(居)和门牌号为手动输入
|
||||
**数据要求**:
|
||||
|
||||
- 初始默认值:省-市-区县-街道(自动引入当前就诊患者信息的现住址)
|
||||
- 异常处理:当上级未选择时禁用下级选择
|
||||
|
||||
**字典取值跟新增患者的现住址保持一致(患者管理-)患者列表)**
|
||||

|
||||
|
||||
|
||||
#### 4. 疾病信息区
|
||||
|
||||
**区域功能**:选择传染病类型及相关临床信息
|
||||
**包含元素**:
|
||||
|
||||
- **疾病分类选择**:
|
||||
|
||||
- - 布局方式:网格布局(3列)
|
||||
- 分类:甲类/乙类/丙类传染病
|
||||
- 交互行为:多选但同类别互斥
|
||||
- 特殊处理:选择炭疽/肺结核/病毒性肝炎/疟疾/梅毒/血吸虫病等疾病时激活分型选择
|
||||
|
||||
- **疾病复选框互斥逻辑:**
|
||||
|
||||
- - 选择炭疽病时显示分型选项(肺炭疽/皮肤炭疽/胃肠炭疽/未分型)
|
||||
- 选择肺结核时显示分型选项(涂阳/仅培阳/菌阴/未痰检)
|
||||
- 选择病毒性肝炎时显示分型选项(甲/乙/丙/戊型)
|
||||
- 选择疟疾时显示分型选项(间日疟/恶性疟/三日疟/卵形疟/未分型)
|
||||
- 选择梅毒时显示分型选项(Ⅰ期/Ⅱ期/Ⅲ期/胎传/隐性)
|
||||
- 选择血吸虫病时显示分型选项(急性/慢性/晚期/未分型)
|
||||
|
||||
- **分型选择**:
|
||||
|
||||
- - 元素类型:动态下拉框
|
||||
- 数据源:根据疾病类型动态加载
|
||||
- 示例:肺结核→涂阳/仅培阳/菌阴/未痰检
|
||||
|
||||
- **其他法定管理以及重点监测传染病输入框:**
|
||||
|
||||
- - 手动输入非列表疾病
|
||||
- 自动关联传染病代码库
|
||||
|
||||
- **发病日期**:
|
||||
|
||||
- - 元素类型:日期选择器
|
||||
- 验证规则:不得晚于诊断日期
|
||||
|
||||
- **诊断日期**:
|
||||
|
||||
- - 元素类型:日期选择器
|
||||
- 取值:默认当前系统时间
|
||||
|
||||
- **死亡日期**:
|
||||
|
||||
- - 元素类型:日期选择器
|
||||
- 填写规则:根据实际情况填写
|
||||
|
||||
- **病例分类**
|
||||
|
||||
- - 复选框类型: 1疑似病例/2临床诊断病例/3确诊病例/4病原携带/5阳性检测结果
|
||||
- 校验规则:必填
|
||||
|
||||
|
||||
|
||||
#### 5. 报告信息区
|
||||
|
||||
**区域功能**:记录报告单位和责任人信息等
|
||||
**包含元素**:
|
||||
|
||||
- **报告单位**:
|
||||
|
||||
- - 元素类型:文本输入
|
||||
- 默认值:当前登录医院
|
||||
- 交互行为:只读
|
||||
|
||||
- **联系电话**:
|
||||
|
||||
- - 元素类型:文本输入
|
||||
- 默认值:当前登录医院的联系电话
|
||||
- 交互行为:可编辑
|
||||
|
||||
- **报告医生**:
|
||||
|
||||
- - 元素类型:文本输入
|
||||
- 默认值:当前登录医生
|
||||
- 验证规则:必填
|
||||
|
||||
- **填卡日期**
|
||||
|
||||
- - 默认当前系统日期,显示为"YYYY-MM-DD"格式
|
||||
|
||||
- **修订病名**
|
||||
|
||||
- - 元素类型:文本输入
|
||||
- 默认值:空
|
||||
- 填写:自定义编辑
|
||||
|
||||
- **退卡原因**
|
||||
|
||||
- - 元素类型:文本输入
|
||||
- 默认值:空
|
||||
- 填写:自定义编辑
|
||||
|
||||
- **备注**
|
||||
|
||||
- - 元素类型:文本输入
|
||||
- 默认值:空
|
||||
- 填写:自定义编辑
|
||||
|
||||
#### 6. 操作按钮区
|
||||
|
||||
**区域位置**:页面底部
|
||||
**包含元素**:
|
||||
|
||||
- **保存按钮**:
|
||||
|
||||
- - 元素类型:主要操作按钮
|
||||
- 交互行为:触发表单验证,通过后保存
|
||||
- 样式特征:蓝色(#3498db),圆角8px
|
||||
|
||||
- **重置按钮**:
|
||||
|
||||
- - 交互行为:清除非基础信息字段
|
||||
- 特殊处理:保留患者姓名、身份证等关键信息
|
||||
|
||||
- **关闭按钮**:
|
||||
|
||||
- - 交互行为:二次确认后关闭页面
|
||||
- 样式特征:红色(#e74c3c)
|
||||
|
||||
### 四、交互功能详细说明
|
||||
|
||||
#### 1. 地址联动选择
|
||||
|
||||
**触发条件**:选择省级行政区
|
||||
**操作流程**:
|
||||
|
||||
1. 选择省份→加载该省下所有城市
|
||||
2. 选择城市→加载该市所有区县
|
||||
3. 选择区县→加载街道列表
|
||||
**异常处理**:网络错误时显示"加载失败,请手动输入"
|
||||
|
||||
#### 2. 疾病分型联动
|
||||
|
||||
**触发条件**:选择特定疾病
|
||||
**数据映射**:
|
||||
|
||||
| **疾病类型** | **分型选项** |
|
||||
| ------------ | ---------------------------------- |
|
||||
| 肺结核 | 涂阳/仅培阳/菌阴/未痰检 |
|
||||
| 梅毒 | I期/II期/III期/胎传/隐性 |
|
||||
| 炭疽 | 肺炭疽/皮肤炭疽/胃肠炭疽/未分型 |
|
||||
| 病毒性肝炎 | 甲/乙/丙/戊型 |
|
||||
| 疟疾 | 间日疟/恶性疟/三日疟/卵形疟/未分型 |
|
||||
| 血吸虫病 | 急性/慢性/晚期/未分型 |
|
||||
|
||||
#### 3. 表单验证
|
||||
|
||||
**全局验证**:
|
||||
|
||||
1. 提交时检查必填字段
|
||||
2. 验证身份证号格式
|
||||
3. 确保至少选择一种疾病
|
||||
**字段级验证**:
|
||||
|
||||
- 电话号码:11位数字,错误提示“请输入有效的联系电话”
|
||||
- 发病日期≤诊断日期≤填卡日期,错误提示“发病日期不能晚于诊断日期”
|
||||
- 身份证号18位且符合校验算法,错误提示“请输入有效的身份证号码”
|
||||
|
||||
### 五、数据结构说明
|
||||
|
||||
**传染病报卡表(infectious_card)**
|
||||
|
||||
| **字段** | **类型** | **国标含义** | **来源****/****说明** |
|
||||
|---------------------| -------------- |-----------------|--------------------------------------|
|
||||
| card_no | VARCHAR(20) PK | 卡片编号 | 机构代码+年月日+4位流水 |
|
||||
| visit_id | BIGINT FK | 本次就诊ID | adm_encounter.id |
|
||||
| diag_id | BIGINT FK | 诊断记录唯一ID | adm_encounter_diagnosis.condition_id |
|
||||
| pat_id | BIGINT FK | 患者主索引 | adm_patient.id |
|
||||
| id_type | TINYINT | 证件类型 | |
|
||||
| id_no | VARCHAR(30) | 证件号码 | 18位校验 |
|
||||
| pat_name | VARCHAR(50) | 患者姓名 | |
|
||||
| parent_name | VARCHAR(50) | 家长姓名 | ≤14岁必填 |
|
||||
| sex | CHAR(1) | 性别 | 1男/2女/0未知 |
|
||||
| birthday | DATE | 出生日期 | |
|
||||
| age | INT | 实足年龄 | 函数计算 |
|
||||
| age_unit | CHAR(1) | 年龄单位 | 岁/月/天-》1岁/2月/3天 |
|
||||
| workplace | VARCHAR(100) | 工作单位 | 学生填学校 |
|
||||
| phone | VARCHAR(20) | 联系电话 | 患者本人电话 |
|
||||
| contact_phone | VARCHAR(20) | 紧急联系人电话 | |
|
||||
| address_prov | VARCHAR(6) | 现住址省 | GB2260 |
|
||||
| address_city | VARCHAR(6) | 现住址市 | 同上 |
|
||||
| address_county | VARCHAR(6) | 现住址县 | 同上 |
|
||||
| address_town | VARCHAR(9) | 现住址街道 | 同上 |
|
||||
| address_village | VARCHAR(80) | 现住址村/居委 | |
|
||||
| address_house | VARCHAR(40) | 现住址门牌号 | |
|
||||
| patient_belong | TINYINT | 病人属于 | 系统判定,1本县区/2本市其他/3本省其他/4外省/5港澳台/6外籍 |
|
||||
| occupation | VARCHAR(4) | 职业 | GB/T 6565,取值于字典管理的字典名称为“职业”维护的数据 |
|
||||
| disease_code | VARCHAR(8) | 疾病名称 | WS 218-2020,见下表 |
|
||||
| disease_type | VARCHAR(8) | 分型 | 见下表,6类必分型疾病必填 |
|
||||
| other_disease | VARCHAR(50) | 其他法定管理以及重点监测传染病 | |
|
||||
| case_class | TINYINT | 病例分类 | 1疑似病例/2临床诊断病例/3确诊病例/4病原携带/5阳性检测结果 |
|
||||
| onset_date | DATE | 发病日期 | 默认诊断时间,病原携带者填初检日期 |
|
||||
| diag_date | DATETIME | 诊断日期 | 精确到小时 |
|
||||
| death_date | DATE | 死亡日期 | 死亡病例必填 |
|
||||
| correct_name | VARCHAR(50) | 订正病名 | 订正报告必填 |
|
||||
| withdraw_reason | VARCHAR(100) | 退卡原因 | 退卡时必填 |
|
||||
| report_org | VARCHAR(18) | 报告单位 | 统一信用代码(医院名称) |
|
||||
| report_org_phone | VARCHAR(20) | 联系电话 | 报告单位电话:医院总值班/防保科座机 |
|
||||
| report_doc | VARCHAR(20) | 报告医生 | 医生姓名 |
|
||||
| report_date | DATE | 填卡日期 | 当天日期 |
|
||||
| status | TINYINT | 状态 | 0暂存1已提交2已审核3已上报4失败5作废 |
|
||||
| fail_msg | VARCHAR(500) | 失败原因 | 国家平台返回 |
|
||||
| xml_content | TEXT | 上报XML | 日志 |
|
||||
| create_time | DATETIME | 创建时间 | |
|
||||
| update_time | DATETIME | 更新时间 | |
|
||||
| card_name_code | TINYINT | 报卡名称代码 | 数值对照(取值于字典管理-》报卡名称代码)1-中华人民共和国传染病报告卡 |
|
||||
| registration source | TINYINT | 登记来源 | 1门诊/2住院 |
|
||||
| dept_id | TINYINT | 科室ID | 患者当前就诊科室 |
|
||||
| doctor_id | TINYINT | 医生ID | 患者当前开单医生 |
|
||||
|
||||
**甲类传染病(2 种)―― 01xxxx**
|
||||
|
||||
| **disease_code** | **疾病名称** | **国家平台码** |
|
||||
| ---------------- | ------------ | -------------- |
|
||||
| 0101 | 鼠疫 | 甲类 |
|
||||
| 0102 | 霍乱 | 甲类 |
|
||||
|
||||
存值示例:`0101`(鼠疫)、`0102`(霍乱)
|
||||
|
||||
|
||||
|
||||
**乙类传染病(27 种)―― 02xxxx**
|
||||
|
||||
| **disease_code** | **疾病名称** | **国家平台码** |
|
||||
| ---------------- | -------------------- | ------------------ |
|
||||
| 0201 | 传染性非典型肺炎 | 乙类(按甲类管理) |
|
||||
| 0202 | 艾滋病 | 乙类 |
|
||||
| 0203 | 病毒性肝炎 | 乙类 |
|
||||
| 0204 | 脊髓灰质炎 | 乙类(按甲类管理) |
|
||||
| 0205 | 人感染高致病性禽流感 | 乙类(按甲类管理) |
|
||||
| 0206 | 麻疹 | 乙类 |
|
||||
| 0207 | 流行性出血热 | 乙类 |
|
||||
| 0208 | 狂犬病 | 乙类 |
|
||||
| 0209 | 流行性乙型脑炎 | 乙类 |
|
||||
| 0210 | 登革热 | 乙类 |
|
||||
| 0211 | 炭疽 | 乙类(按甲类管理) |
|
||||
| 0212 | 细菌性和阿米巴性痢疾 | 乙类 |
|
||||
| 0213 | 肺结核 | 乙类 |
|
||||
| 0214 | 伤寒和副伤寒 | 乙类 |
|
||||
| 0215 | 流行性脑脊髓膜炎 | 乙类 |
|
||||
| 0216 | 百日咳 | 乙类 |
|
||||
| 0217 | 白喉 | 乙类 |
|
||||
| 0218 | 新生儿破伤风 | 乙类 |
|
||||
| 0219 | 猩红热 | 乙类 |
|
||||
| 0220 | 布鲁氏菌病 | 乙类 |
|
||||
| 0221 | 淋病 | 乙类 |
|
||||
| 0222 | 梅毒 | 乙类 |
|
||||
| 0223 | 钩端螺旋体病 | 乙类 |
|
||||
| 0224 | 血吸虫病 | 乙类 |
|
||||
| 0225 | 疟疾 | 乙类 |
|
||||
|
||||
存值示例:乙肝→`0203`;肺结核→`0213`;梅毒→`0222`
|
||||
|
||||
|
||||
|
||||
**丙类传染病(11 种)―― 03xxxx**
|
||||
|
||||
| **disease_code** | **疾病名称** | **国家平台码** |
|
||||
| ---------------- | ---------------------- | -------------- |
|
||||
| 0301 | 流行性感冒 | 丙类 |
|
||||
| 0302 | 流行性腮腺炎 | 丙类 |
|
||||
| 0303 | 风疹 | 丙类 |
|
||||
| 0304 | 急性出血性结膜炎 | 丙类 |
|
||||
| 0305 | 麻风病 | 丙类 |
|
||||
| 0306 | 流行性和地方性斑疹伤寒 | 丙类 |
|
||||
| 0307 | 黑热病 | 丙类 |
|
||||
| 0308 | 包虫病 | 丙类 |
|
||||
| 0309 | 丝虫病 | 丙类 |
|
||||
| 0310 | 其它感染性腹泻病 | 丙类 |
|
||||
| 0311 | 手足口病 | 丙类 |
|
||||
|
||||
存值示例:手足口病→`0311`;流感→`0301`
|
||||
|
||||
|
||||
|
||||
**分型码与名称对照(系统存值用)**
|
||||
|
||||
| **大类疾病** | **disease_code** | **分型中文** | **disease_type** **存值** |
|
||||
| -------------- | ---------------- | ------------ | ------------------------- |
|
||||
| **病毒性肝炎** | 0203 | 甲型 | 020301 |
|
||||
| | | 乙型 | 020302 |
|
||||
| | | 丙型 | 020303 |
|
||||
| | | 戊型 | 020304 |
|
||||
| | | 未分型 | 020305 |
|
||||
| **炭疽** | 0211 | 肺炭疽 | 021101 |
|
||||
| | | 皮肤炭疽 | 021102 |
|
||||
| | | 胃肠炭疽 | 021103 |
|
||||
| | | 未分型 | 021104 |
|
||||
| **肺结核** | 0213 | 涂阳 | 021301 |
|
||||
| | | 仅培阳 | 021302 |
|
||||
| | | 菌阴 | 021303 |
|
||||
| | | 未痰检 | 021304 |
|
||||
| **梅毒** | 0222 | Ⅰ期 | 022201 |
|
||||
| | | Ⅱ期 | 022202 |
|
||||
| | | Ⅲ期 | 022203 |
|
||||
| | | 胎传 | 022204 |
|
||||
| | | 隐性 | 022205 |
|
||||
| **疟疾** | 0225 | 间日疟 | 022501 |
|
||||
| | | 恶性疟 | 022502 |
|
||||
| | | 三日疟 | 022503 |
|
||||
| | | 卵形疟 | 022504 |
|
||||
| | | 未分型 | 022505 |
|
||||
| **血吸虫病** | 0224 | 急性 | 022401 |
|
||||
| | | 慢性 | 022402 |
|
||||
| | | 晚期 | 022403 |
|
||||
| | | 未分型 | 022404 |
|
||||
|
||||
|
||||
|
||||
### 六、开发实现要点
|
||||
|
||||
**样式规范**:
|
||||
|
||||
- 主色调:#3498db(按钮/重要标签)
|
||||
- 错误状态:#e74c3c(边框+文字)
|
||||
- 表单间距:8px垂直间距,16px水平间距
|
||||
|
||||
**技术要求**:
|
||||
|
||||
- 支持Chrome/Firefox/Edge最新版
|
||||
|
||||
**注意事项**:
|
||||
|
||||
1. 身份证号不需脱敏显示
|
||||
|
||||
|
||||
287
md/需求/94-手术室维护界面_2026-1-9.md
Normal file
287
md/需求/94-手术室维护界面_2026-1-9.md
Normal file
@@ -0,0 +1,287 @@
|
||||
## 手术室维护界面PRD文档
|
||||
|
||||
### 一、页面概述
|
||||
|
||||
**页面名称**:手术室维护界面
|
||||
**页面目标**:提供手术室基础数据的维护功能,包括新增、编辑、启用/停用手术室信息,为手术安排提供基础数据支持
|
||||
**适用场景**:医院管理员需要新增、修改、启用/停用手术室信息时使用
|
||||
**页面类型**:列表页+表单页(含模态框)
|
||||
|
||||
**原型图地址:**https://static.pm-ai.cn/prototype/20260104/ee5d222231effefcb39624d1646a2e20/index.html
|
||||
|
||||
**核心功能**:
|
||||
|
||||
1. 手术室列表展示与查询
|
||||
2. 新增手术室信息
|
||||
3. 编辑现有手术室信息
|
||||
4. 启用/停用手术室状态
|
||||
5. 数据有效性校验
|
||||
|
||||
**用户价值**:
|
||||
|
||||
- 管理员可集中管理所有手术室基础信息
|
||||
- 确保手术安排时能获取准确的手术室数据
|
||||
- 通过状态管理控制手术室可用性
|
||||
|
||||
**流程图:**
|
||||
|
||||

|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[手术室维护界面] --> B[手术室列表展示]
|
||||
|
||||
B --> C[新增手术室]
|
||||
|
||||
B --> D[编辑手术室]
|
||||
|
||||
B --> E[启用/停用手术室]
|
||||
|
||||
B --> F[查询手术室]
|
||||
|
||||
C --> G[点击新增按钮]
|
||||
|
||||
G --> H[打开新增模态框]
|
||||
|
||||
H --> I[填写表单字段]
|
||||
|
||||
I --> J{必填字段校验}
|
||||
|
||||
J -->|通过| K[提交数据]
|
||||
|
||||
J -->|不通过| L[提示请填写所有必填项]
|
||||
|
||||
K --> M[表格新增数据行]
|
||||
|
||||
D --> N[点击修改按钮]
|
||||
|
||||
N --> O[打开编辑模态框]
|
||||
|
||||
O --> P[修改表单字段]
|
||||
|
||||
P --> Q{必填字段校验}
|
||||
|
||||
Q -->|通过| R[保存数据]
|
||||
|
||||
Q -->|不通过| S[提示请填写所有必填项]
|
||||
|
||||
R --> T[更新表格对应行]
|
||||
|
||||
E --> U[点击启用/停用按钮]
|
||||
|
||||
U --> V{二次确认}
|
||||
|
||||
V -->|确认| W[切换状态标签]
|
||||
|
||||
V -->|取消| X[取消操作]
|
||||
|
||||
W --> Y[更新按钮状态]
|
||||
|
||||
F --> Z[输入查询条件]
|
||||
|
||||
Z --> AA[筛选表格数据]
|
||||
|
||||
K --> AB{房间号重复校验}
|
||||
|
||||
AB -->|不重复| AC[提示房间号已存在]
|
||||
|
||||
AB -->|重复| AD[更新按钮状态]
|
||||
```
|
||||
|
||||
### 二、整体布局分析
|
||||
|
||||
**页面宽度**:自适应布局
|
||||
**主要区域划分**:
|
||||
|
||||
1. 页头区域(15%高度)
|
||||
2. 表格展示区(85%高度)
|
||||
**布局特点**:上下布局,表格采用固定表头+滚动内容区设计
|
||||
**响应式要求**:移动端适配时改为纵向堆叠布局,操作按钮组变为纵向排列
|
||||
|
||||
### 三、页面区域详细描述
|
||||
|
||||
#### 1. 页头区域
|
||||
|
||||
**区域位置**:页面顶部
|
||||
**区域尺寸**:高度60px,宽度100%
|
||||
**区域功能**:展示标题和主要操作入口
|
||||
**包含元素**:
|
||||
|
||||
- **标题文本**
|
||||
- 元素类型:H1标题
|
||||
- 显示内容:"手术室列表"
|
||||
- 样式特征:1.75rem/600字重,深灰色(#333)
|
||||
- **新增按钮**
|
||||
- 元素类型:主要操作按钮
|
||||
- 显示内容:"新增"(带+图标)
|
||||
- 交互行为:点击触发新增模态框
|
||||
- 样式特征:蓝色背景(#5a7cff),白色文字,8px圆角,悬停上浮1px
|
||||
|
||||
#### 2. 表格展示区(手术室列表表格)
|
||||
|
||||
**区域位置**:页头下方
|
||||
**区域尺寸**:高度自适应,宽度100%
|
||||
**区域功能**:展示手术室数据并支持行级操作
|
||||
**包含元素**:
|
||||
|
||||
- **数据表格**
|
||||
- 展示方式:固定表头表格
|
||||
- 数据字段:
|
||||
- 房间号:文本 - OR01 - 不可操作
|
||||
- 手术室名称:文本 - 第一手术室 - 不可操作
|
||||
- 类型:文本 - 普通/日间/复合 - 不可操作
|
||||
- 所属科室:文本 - 外科 - 不可操作
|
||||
- 状态:标签 - 有效/无效 - 通过操作按钮切换
|
||||
- 操作功能:每行包含"修改"和"状态切换(停用-黄色/启用-绿色)"按钮
|
||||
- **表格样式**:
|
||||
- 表头:浅灰色背景(#f8f9fa),大写字母,14px字号
|
||||
- 行悬停:浅灰色背景(#f8f9fa)
|
||||
- 状态标签:
|
||||
- 有效:绿色背景+文字(#28a745)
|
||||
- 无效:灰色背景+文字(#6c757d)
|
||||
|
||||
#### 3. 新增手术室弹窗
|
||||
|
||||
**区域位置**:页面居中模态弹窗
|
||||
**区域功能**:收集新增手术室所需信息
|
||||
**包含元素**:
|
||||
|
||||
- 表单字段:
|
||||
1. 房间号输入框
|
||||
2. 类型:文本输入
|
||||
3. 必填:是
|
||||
4. 示例值:OR04
|
||||
5. 校验规则:非空校验
|
||||
6. 手术室名称输入框
|
||||
- 类型:文本输入
|
||||
- 必填:是
|
||||
- 示例值:第四手术室
|
||||
1. 手术室类型下拉框
|
||||
- 类型:单选下拉
|
||||
- 选项:普通/日间/复合/特殊
|
||||
- 默认值:普通
|
||||
1. 所属科室下拉框
|
||||
- 类型:单选下拉
|
||||
- 必填:是
|
||||
- 选项:外科/妇产科等8个科室
|
||||
- 默认提示:"请选择科室"
|
||||
- 操作按钮:
|
||||
- 取消按钮(灰色边框)
|
||||
- 确认按钮(蓝色填充)
|
||||
- 校验逻辑:必填字段非空校验
|
||||
- 成功反馈:提示"手术室添加成功"
|
||||
- 失败反馈:提示"请填写所有必填项"
|
||||
|
||||
#### 4. 编辑手术室弹窗
|
||||
|
||||
**区域位置**:页面居中模态弹窗
|
||||
**区域功能**:修改现有手术室信息
|
||||
**包含元素**:
|
||||
|
||||
- 表单字段(同新增弹窗,带初始值)
|
||||
- 操作按钮:
|
||||
- 取消按钮
|
||||
- 保存按钮
|
||||
- 校验逻辑:同新增弹窗
|
||||
- 成功反馈:提示"手术室信息已更新"
|
||||
|
||||
### 四、交互功能详细说明
|
||||
|
||||
#### 1. 新增手术室
|
||||
|
||||
**功能描述**:添加新的手术室记录
|
||||
**触发条件**:点击页头"新增"按钮
|
||||
**操作流程**:
|
||||
|
||||
1. 打开新增模态框
|
||||
2. 填写必填字段(房间号、名称、科室)
|
||||
3. 点击确认提交(插入his_or_room表)
|
||||
4. 表格末尾新增数据行
|
||||
**异常处理**:
|
||||
- 必填项为空时弹出"请填写所有必填项"提示
|
||||
- 房间号重复需在后端校验并提示
|
||||
|
||||
#### 2. 编辑手术室
|
||||
|
||||
**功能描述**:修改现有手术室信息
|
||||
**触发条件**:点击行内"修改"按钮
|
||||
**操作流程**:
|
||||
|
||||
1. 打开编辑模态框(自动填充当前行数据)
|
||||
2. 用户修改数据
|
||||
3. 点击"保存"时校验并更新对应行数据
|
||||
**状态保持**:记录当前编辑行索引确保数据更新准确
|
||||
|
||||
#### 3. 状态切换
|
||||
|
||||
**功能描述**:启用/停用手术室
|
||||
**触发条件**:点击"停用"或"启用"按钮
|
||||
**操作流程**:
|
||||
|
||||
1. 弹出二次确认对话框
|
||||
2. 用户确认后切换状态标签
|
||||
3. 按钮变为相反操作(停用↔启用)
|
||||
4. 、停用手术室
|
||||
- **步骤**:
|
||||
1. 查询需要停用的手术室记录。
|
||||
2. 将 valid_flag 设置为 0(无效)。
|
||||
- **示例**:
|
||||
|
||||
UPDATE his_or_room
|
||||
|
||||
SET valid_flag = '0'
|
||||
|
||||
WHERE room_code = 'OR06';
|
||||
|
||||
5\. 启用手术室
|
||||
|
||||
**步骤**:
|
||||
|
||||
1. 查询需要启用的手术室记录。
|
||||
1. 将 valid_flag 设置为 1(有效)。
|
||||
- **示例**:
|
||||
|
||||
UPDATE his_or_room
|
||||
|
||||
SET valid_flag = '1'
|
||||
|
||||
WHERE room_code = 'OR06';
|
||||
|
||||
**防误操作**:所有状态变更需二次确认
|
||||
|
||||
### 五、数据结构说明(HIS_OR_ROOM手术室字典表)
|
||||
|
||||
| **字段名称** | **数据类型** | **是否为空** | **说明/典型值** | **外键/来源** |
|
||||
|--------------|--------------|--------------|-----------------|-------------------------------|
|
||||
| room_id | VARCHAR(10) | N | 主键 | 自增主键 |
|
||||
| room_code | VARCHAR(10) | N | 手术室房间号 | 自定义编码,如 OR01、OR02 |
|
||||
| room_name | VARCHAR(100) | N | 手术室名称 | 如 "第一手术室"、"第二手术室" |
|
||||
| room_type | VARCHAR(10) | N | 手术室类型 | 普通、日间、复合 |
|
||||
| dept_code | VARCHAR(10) | N | 所属科室 | FK → 科室管理的科室代码 |
|
||||
| valid_flag | CHAR(1) | N | 是否有效 | 1有效,0无效 |
|
||||
| created_time | DATETIME | N | 创建时间 | 默认当前时间 |
|
||||
| updated_time | DATETIME | N | 更新时间 | 默认当前时间,自动更新 |
|
||||
|
||||
### 六、开发实现要点
|
||||
|
||||
**样式规范**:
|
||||
|
||||
- 主色调:#5a7cff(按钮/交互元素)
|
||||
- 辅助色:#7b8a8b(次要文本)
|
||||
- 字体:
|
||||
- 标题:1.75rem/600字重
|
||||
- 正文:0.875rem/400字重
|
||||
- 间距系统:
|
||||
- 卡片内边距:24px
|
||||
- 表单字段间距:16px
|
||||
|
||||
**技术要求**:
|
||||
|
||||
**注意事项**:
|
||||
|
||||
1. 数据安全:
|
||||
- 所有变更操作需记录操作日志
|
||||
- 停用状态的手术室需在前端标记不可预约
|
||||
2. 性能优化:
|
||||
- 表格数据分页加载
|
||||
- 模态框使用懒加载
|
||||
387
md/需求/95-门诊医生站开立会诊申请单界面PRD_2026-01-15.md
Normal file
387
md/需求/95-门诊医生站开立会诊申请单界面PRD_2026-01-15.md
Normal file
@@ -0,0 +1,387 @@
|
||||
## 门诊医生站开立会诊申请单界面PRD文档
|
||||
|
||||
### 一、页面概述
|
||||
|
||||
**页面名称**:门诊医生站开立会诊申请单界面**页面目标**:帮助门诊医生完成会诊申请单的创建、编辑、提交和作废操作,实现多科室会诊流程的电子化管理**适用场景**:
|
||||
|
||||
1. 门诊医生需要邀请其他科室专家进行会诊时
|
||||
2. 会诊申请单需要修改或补充信息时
|
||||
3. 会诊流程需要跟踪管理时
|
||||
**页面类型**:表单页+列表页复合型界面
|
||||
|
||||
**核心功能**:
|
||||
|
||||
1. 会诊申请单的新增、保存、提交、作废功能
|
||||
2. 会诊科室/专家可视化选择
|
||||
3. 申请单数据表格展示与交互
|
||||
4. 表单数据自动填充与校验
|
||||
5. 申请单打印输出
|
||||
|
||||
**用户价值**:
|
||||
|
||||
- 规范会诊申请流程,减少纸质单据使用
|
||||
- 通过智能填充减少医生重复录入
|
||||
- 实时查看会诊申请状态(新开/已提交/已确认/已签名/已完成/已取消)
|
||||
|
||||
原型图地址:https://static.pm-ai.cn/prototype/20260115/4eb1bd5367f9d5610b32c0ecc6c793f5/index.html
|
||||
|
||||
流程图:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
%% ---------- 开始 ----------
|
||||
START(["开始"]) --> A["医生进入会诊申请单界面"]
|
||||
|
||||
%% ---------- 操作选择 ----------
|
||||
A --> B{"操作选择"}
|
||||
B -->|"打印"| C["选择已有申请单"]
|
||||
B -->|"提交/取消提交"| D{"校验状态为“已提交”?"}
|
||||
B -->|"删除"| E["弹出确认对话框"]
|
||||
B -->|"结束"| F{"校验状态为“已提交”?"}
|
||||
B -->|"编辑"| G["修改表单内容"]
|
||||
B -->|"新增"| H["清空表单(保留患者信息)"]
|
||||
|
||||
%% ---------- 打印分支 ----------
|
||||
C --> I["高亮选中行"]
|
||||
I --> J["生成打印视图"]
|
||||
J --> K["输出打印样式"]
|
||||
K --> L(["取消"])
|
||||
|
||||
%% ---------- 提交/取消提交分支 ----------
|
||||
D -->|"不通过"| M["提示“请完善必填信息”"]
|
||||
D -->|"通过"| N["更新状态为“已提交/新开”"]
|
||||
|
||||
%% ---------- 删除分支 ----------
|
||||
E --> O{"确认?"}
|
||||
O -->|"是"| P["标记状态为“已取消”"]
|
||||
O -->|"否"| L
|
||||
|
||||
%% ---------- 结束分支 ----------
|
||||
F -->|"不通过"| Q["提示“请先提交申请”"]
|
||||
F -->|"通过"| R["标记状态为“已完成”"]
|
||||
|
||||
%% ---------- 编辑分支 ----------
|
||||
G --> S{"校验必填字段"}
|
||||
S -->|"不通过"| M
|
||||
S -->|"通过"| T["保存到表格"]
|
||||
|
||||
%% ---------- 新增/保存通用路径 ----------
|
||||
H --> U["填写表单"]
|
||||
U --> V["选择会诊科室/专家"]
|
||||
V --> W["自动填充邀请对象"]
|
||||
W --> X["填写病史及目的"]
|
||||
X --> Y["点击保存"]
|
||||
Y --> Z{"校验必填字段"}
|
||||
Z -->|"不通过"| M
|
||||
Z -->|"通过"| AA["生成会诊申请记录"]
|
||||
AA --> AB["保存到表格"]
|
||||
AB --> AC["新增/更新记录"]
|
||||
|
||||
%% ---------- 循环 ----------
|
||||
AC --> A
|
||||
N --> A
|
||||
P --> A
|
||||
R --> A
|
||||
T --> A
|
||||
M --> A
|
||||
Q --> A
|
||||
L --> A
|
||||
```
|
||||
|
||||
### 二、整体布局分析
|
||||
|
||||
**页面宽度**:自适应宽度(主内容区采用7:3比例分割)
|
||||
**主要区域划分**:
|
||||
|
||||
1. 顶部操作栏(48px固定高度)
|
||||
2. 会诊申请单列表区(高度自适应)
|
||||
3. 主内容区(分左右结构,7:3比例)
|
||||
|
||||
- 左侧:会诊申请单表单区
|
||||
- 右侧:会诊科室/专家选择区
|
||||
**布局特点**:响应式上下+左右混合布局,主要对齐方式为左对齐
|
||||
|
||||
### 三、页面区域详细描述
|
||||
|
||||
#### 1. 顶部操作栏区域
|
||||
|
||||
**区域位置**:页面顶部固定位置**区域尺寸**:高度48px,宽度100%**区域功能**:提供全局操作功能入口**包含元素**:
|
||||
|
||||
- **打印按钮**
|
||||
- 元素类型:操作按钮
|
||||
- 显示内容:“打印”
|
||||
- 交互行为:点击后生成A4打印视图,自动适配医院抬头格式
|
||||
- 样式特征:绿色背景(\#13C2C2),圆角4px,32px高度
|
||||
- **新增按钮**
|
||||
- 元素类型:操作按钮
|
||||
- 显示内容:“新增”
|
||||
- 交互行为:点击清空表单(保留当前患者基本信息)
|
||||
- 样式特征:蓝色背景(\#1890FF)
|
||||
- **结束按钮**
|
||||
- 元素类型:危险操作按钮
|
||||
- 显示内容:“结束”
|
||||
- 交互行为:点击结束已提交的会诊流程,标记申请单状态为"已结束",禁用后续操作
|
||||
- 样式特征:红色背景(\#FF4D4F)
|
||||
- 限制条件:需先选中已提交的会诊单
|
||||
- **保存按钮**
|
||||
- 元素类型:主要操作按钮
|
||||
- 显示内容:“保存”
|
||||
- 交互行为:点击保存当前表单数据,校验必填字段后保存至表格,自动生成时间戳
|
||||
- 样式特征:绿色背景(\#52C41A)
|
||||
|
||||
#### 2. 会诊申请单列表区
|
||||
|
||||
**区域位置**:顶部操作栏下方**区域尺寸**:高度自适应,宽度100%**区域功能**:展示当前医生的会诊申请记录**包含元素**:
|
||||
|
||||
- **申请单表格**
|
||||
- 展示方式:带边框表格
|
||||
- 数据字段:
|
||||
|
||||
- 序号:文本 - 自增序号 - 不可操作
|
||||
- 急:布尔 - ✓表示紧急 - 不可操作
|
||||
- 申请单号:文本 - CS20260105001 - 不可操作
|
||||
- 会诊时间:日期 - 2026-01-05 15:08 - 不可操作
|
||||
- 邀请对象:文本 - 吴院长 - 不可操作
|
||||
- 申请科室:文本 - 内科 - 不可操作
|
||||
- 申请医师:文本 - 张医生 - 不可操作
|
||||
- 申请时间:日期 - 2026-01-05 15:08 - 不可操作
|
||||
- 提交状态:布尔 - 复选框 - 仅查看
|
||||
- 结束状态:布尔 - 复选框 - 仅查看
|
||||
- 操作功能:
|
||||
|
||||
- - o 提交/取消提交按钮
|
||||
|
||||
```
|
||||
样式要求:蓝色小按钮,禁用状态显示灰色
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
交互行为:切换提交状态,需二次确认
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
o 删除图标
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
样式要求:红色垃圾桶图标,hover时放大10%
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
交互行为:弹出确认对话框后作废该记录
|
||||
```
|
||||
|
||||
|
||||
[删除]**将状态改为“已取消”****
|
||||
|
||||
UPDATE ConsultationRequest
|
||||
SET ConsultationStatus = 50,cancelnatureDate = <作废会诊时间>
|
||||
WHERE ConsultationID = <会诊申请单ID> and ConsultationStatus <> 40 ;
|
||||
|
||||
- 交互特性:
|
||||
- 行点击选中效果(蓝色高亮+左侧边框)
|
||||
- 行hover浅灰色背景
|
||||
- 提交按钮状态联动(切换提交状态,需二次确认)
|
||||
|
||||
#### 3. 会诊申请单表单区
|
||||
|
||||
**区域位置**:主内容区左侧**区域尺寸**:占主内容区70%宽度**区域功能**:会诊申请单的详细表单填写**包含元素**:
|
||||
|
||||
- **基础信息区**
|
||||
- 申请单号:只读文本,【保存】时自动生成规则CS+年月日时分秒+4位随机数
|
||||
- 申请时间:只读文本,自动获取系统当前时间
|
||||
- 病人信息:病人姓名/性别/年龄/就诊卡号/申请医师/申请科室(不可编辑),自动获取当前患者档案信息。
|
||||
- **会诊信息区**
|
||||
- 会诊时间:时间控件可编辑
|
||||
- 紧急标识:复选框控件
|
||||
- 申请医师:默认当前登录医生
|
||||
- 申请科室:默认当前医生登录的开单科室
|
||||
- 门诊诊断:自动获取医生开立的门诊诊断(主诊断)
|
||||
- **病史及目的**
|
||||
- 多行文本域,最小高度100px
|
||||
- **会诊邀请**
|
||||
- 会诊邀请对象:支持多选(逗号分隔)-》(可从右侧会诊邀请对象选择)
|
||||
- **会诊记录区**
|
||||
- 会诊意见:只读文本域
|
||||
- 会诊确认参加医师:只读字段
|
||||
- 所属医生、代表科室、签名医生、签名时间:只读字段
|
||||
|
||||
#### 4. 会诊邀请对象选择区(侧边栏)
|
||||
|
||||
**区域位置**:主内容区右侧**区域尺寸**:占主内容区30%宽度**区域功能**:快速选择会诊科室和专家**包含元素**:
|
||||
|
||||
- **会诊科室列表**
|
||||
- 展示方式:带边框可滚动列表
|
||||
- 交互行为:选择科室后动态加载对应专家
|
||||
- **会诊专家列表**
|
||||
- 展示方式:带边框可滚动列表
|
||||
- 交互行为:点击专家自动填入会诊邀请对象字段(防重复:已选专家提示"请勿重复选择")
|
||||
- 特殊逻辑:支持多选(自动用逗号分隔)
|
||||
|
||||
### 四、交互功能详细说明
|
||||
|
||||
#### 1. 会诊申请单提交流程
|
||||
|
||||
**功能描述**:完成会诊申请单的提交操作**触发条件**:点击表格行的"提交"按钮**操作流程**:
|
||||
|
||||
1. 医生点击行内"提交"按钮
|
||||
2. 系统校验必填字段(会诊时间、邀请对象)
|
||||
3. 提交状态复选框变为已勾选
|
||||
4. 按钮文字变为"取消提交"
|
||||
5. 禁用该行编辑功能
|
||||
|
||||
【提交】**将状态从“新开”改为“已提交”**
|
||||
|
||||
UPDATE ConsultationRequest
|
||||
SET ConsultationStatus = 10,ConfirmingPhysician = <提交会诊医生姓名> ,ConfirmingPhysicianID = <提交会诊医生ID> ,ConfirmingDate = <提交会诊时间>
|
||||
WHERE ConsultationID = <会诊申请单ID> and ConsultationStatus = 0 ;
|
||||
|
||||
【取消提交】**将状态从“已提交”改为“新开”**
|
||||
|
||||
UPDATE ConsultationRequest
|
||||
SET ConsultationStatus = 0,ConfirmingPhysician = '',ConfirmingPhysicianID = '',ConfirmingDate = ''
|
||||
WHERE ConsultationID = <会诊申请单ID> and ConsultationStatus = 10 ;
|
||||
|
||||
**异常处理**:
|
||||
|
||||
- 必填字段缺失:弹出"请完善会诊时间和邀请对象信息"
|
||||
- 重复提交:提示"该申请已提交,请勿重复操作"
|
||||
|
||||
#### 2. 会诊流程结束功能
|
||||
|
||||
**功能描述**:标记会诊流程已结束**触发条件**:选中已提交的申请单后点击顶部"结束"按钮**操作流程**:
|
||||
|
||||
1. 医生选中已提交的申请单(行高亮)
|
||||
2. 点击顶部"结束"按钮
|
||||
3. 系统校验提交状态为已提交
|
||||
4. 结束状态复选框变为已勾选
|
||||
5. 禁用该行的取消提交功能
|
||||
|
||||
【结束】**将状态从“已签名”改为“已完成”**
|
||||
|
||||
UPDATE ConsultationRequest
|
||||
SET ConsultationStatus = 40,Signature = <结束会诊医生姓名> ,SignatureDate=<结束会诊时间>
|
||||
WHERE ConsultationID = <会诊申请单ID> and ConsultationStatus = 30 ;
|
||||
|
||||
**异常处理**:
|
||||
|
||||
- 未选中记录:提示"请先选择要结束的会诊申请"
|
||||
- 未提交记录:提示"请先提交该会诊申请"
|
||||
|
||||
#### 3. 申请单保存功能
|
||||
|
||||
**功能描述**:保存会诊申请单数据**触发条件**:点击顶部"保存"按钮**操作流程**:
|
||||
|
||||
1. 系统自动生成申请单号(如为空)
|
||||
2. 保存当前表单所有字段值
|
||||
3. 新增记录插入表格末尾
|
||||
4. 已有记录更新对应行数据
|
||||
|
||||
【保存】
|
||||
|
||||
①、写入门诊医嘱表(医嘱状态为新开,医嘱名称为"门诊会诊")
|
||||
|
||||
②、写入门诊会诊申请单表(ConsultationRequest)
|
||||
|
||||
**数据校验**:
|
||||
|
||||
- 必填字段:病人姓名、会诊时间、申请科室、会诊时间、会诊邀请对象、简要病史及会诊目的
|
||||
- 未选会诊对象:提示"请至少选择1位会诊专家"
|
||||
- 过期时间:提示"会诊时间不能早于当前时间"
|
||||
|
||||
#### 4. 会诊邀请对象选择联动
|
||||
|
||||
**触发方式**:点击科室列表项
|
||||
**数据联动**:
|
||||
|
||||
1. 根据选中会诊科室过滤会诊专家列表
|
||||
2. 记忆已选专家(跨科室切换时不丢失)
|
||||
|
||||
**技术要点**:
|
||||
|
||||
- 使用对象存储会诊科室-会诊专家映射关系
|
||||
- 采用事件委托处理动态生成的列表项
|
||||
|
||||
### 五、数据结构说明
|
||||
|
||||
门诊会诊申请单表(ConsultationRequest)
|
||||
|
||||
|
||||
| **字段名称** | **数据类型** | **长度** | **描述** | **取值范围** |
|
||||
|-----------------------------| ------------ | -------- |----------------| --------------------------------------------------------- |
|
||||
| **PatientID** | Text | 20 | 患者唯一标识 | 患者就诊卡号 (取值患者档案) |
|
||||
| **ConsultationID** | Text | 20 | 会诊申请单唯一标识 | 系统自动生成的唯一编号,生成规则CS+年月日时分秒+4位随机数 |
|
||||
| **VisitID** | BIGINT | 20 | 门诊就诊流水号(逻辑外键) | 取值于本次门诊就诊记录表的主键 |
|
||||
| **OrderID** | BIGINT | 20 | 门诊医嘱表主键(一对一外键) | 门诊医嘱表 |
|
||||
| **PatientName** | Text | 50 | 患者姓名 | 患者的姓名 (取值患者档案) |
|
||||
| **Gender** | Text | 10 | 患者性别 | 男/女/其他 (取值患者档案) |
|
||||
| **Age** | Integer | - | 患者年龄 | 取值患者档案 |
|
||||
| **Department** | Text | 50 | 申请会诊的科室 | 当前科室名称 |
|
||||
| **RequestingPhysician** | Text | 50 | 申请会诊的医生 | 当前医生姓名 |
|
||||
| **ConsultationrequestDate** | DateTime | - | 会诊申请时间 | YYYY-MM-DD HH:MM:SS
|
||||
| **ConsultationPurpose** | Text | 255 | 简要病史及会诊目的 | 文本描述,自定义编辑 |
|
||||
| **ProvisionalDiagnosis** | Text | 255 | 门诊诊断 | 文本描述,自动获取医生开立的门诊诊断(主诊断) |
|
||||
| **ConsultationDate** | DateTime | - | 会诊时间 | YYYY-MM-DD HH:MM:SS |
|
||||
| **ConsultationStatus** | Text | 20 | 会诊状态 | 新开/已提交/已确认/已签名/已完成/已取消 |
|
||||
| **ConsultationUrgency** | Text | 20 | 是否紧急 | 勾选框:一般/紧急 |
|
||||
| **ConsultationOpinion** | Text | 255 | 会诊意见 | 文本描述 |
|
||||
| **ConfirmingPhysician** | Text | 50 | 提交会诊的医生 | 医生姓名 |
|
||||
| **ConfirmingPhysicianID** | Text | 20 | 提交会诊的医生ID | 医生唯一标识 |
|
||||
| **ConfirmingDate** | DateTime | - | 提交会诊日期 | YYYY-MM-DD HH:MM:SS |
|
||||
| **Signature** | Text | 50 | 结束会诊医生 | 医生姓名 |
|
||||
| **SignatureDate** | DateTime | - | 结束会诊日期 | YYYY-MM-DD HH:MM:SS |
|
||||
| **cancelnatureDate** | DateTime | - | 作废会诊日期 | YYYY-MM-DD HH:MM:SS |
|
||||
| InvitedObject | Text | 50 | 会诊邀请对象 | |
|
||||
|
||||
**诊状态用于记录会诊申请在不同阶段的状态,以下是常见的会诊状态及其说明:**
|
||||
|
||||
|
||||
| **状态名称** | **状态值** | **描述** |
|
||||
| ------------ | ---------- | ---------------------------------------------------------------------- |
|
||||
| **新开** | 0 | 会诊申请单已保存 |
|
||||
| **已提交** | 10 | 会诊申请已提交,但尚未被会诊医生确认。 |
|
||||
| **已确认** | 20 | 会诊医生已确认会诊申请,并准备进行会诊。 |
|
||||
| **已签名** | 30 | 会诊完成后进行签名 |
|
||||
| **已完成** | 40 | 会诊已经完成,会诊意见已记录。 |
|
||||
| **已取消** | 50 | 会诊申请被取消,可能由于患者情况变化或其他原因,申请医生进行作废操作。 |
|
||||
|
||||
**门诊医嘱表在相关会诊操作步骤的相关事务**
|
||||
|
||||
把“门诊会诊申请”当成**一种特殊医嘱**(OrderType = 'Consult')由系统**在同一事务内**自动插入 门诊医嘱表,再挂到 `ConsultationRequest` **注意:按照现有系统的门诊医嘱表进行设置相关字段的值**
|
||||
|
||||
|
||||
| **节点** | **是否自动** | **说明** |
|
||||
| --------------------- | ------------ | --------------------------------------------------------------------------------------------------- |
|
||||
| 医生点击【保存】 | ✅ | 后台事务:先插门诊医嘱表(医嘱状态为“新开”),再插`ConsultationRequest`.Status=0 |
|
||||
| 医生点击【提交】 | ✅ | 仅更新两表状态 → 门诊医嘱表的医嘱状态和`ConsultationRequest.Status=10` (已提交),不重复生成医嘱 |
|
||||
| 医生点击【作废/删除】 | ✅ | 自动将门诊医嘱表的医嘱状态字段置为“作废”,级联`ConsultationRequest.Status=50` |
|
||||
| 医生点击【结束】 | ✅ | 将 门诊医嘱表的医嘱状态字段置为“已完成”,同时写`ConsultationRequest.Status=40` |
|
||||
|
||||
### 六、开发实现要点
|
||||
|
||||
**样式规范**:
|
||||
|
||||
- **主色调**:\#1890FF(操作按钮)
|
||||
- **辅助色**:\#13C2C2(打印)、\#52C41A(保存)、\#FF4D4F(结束)
|
||||
- **字体规范**:14px/1.5,中文字体优先使用"PingFang SC"
|
||||
- **间距系统**:16px基准,表单行间距12px
|
||||
- **组件样式**:
|
||||
- 按钮:4px圆角,32px高度
|
||||
- 输入框:4px圆角,1px \#D9D9D9边框
|
||||
- 表格行:选中状态\#E6F7FF背景+左侧3px蓝色边框
|
||||
|
||||
**技术要求**:
|
||||
|
||||
- **浏览器兼容**:支持Chrome/Firefox/Edge最新版
|
||||
- **性能要求**:表单提交响应时间\<1秒
|
||||
|
||||
**注意事项**:
|
||||
|
||||
1. 时间字段需统一处理为YYYY-MM-DD HH:mm:ss格式
|
||||
2. 申请单号生成需加锁防止重复
|
||||
3. 移动端需优化表格横向滚动体验
|
||||
4. 打印功能需特殊样式处理(隐藏操作按钮)
|
||||
310
md/需求/96-门诊医生站会诊申请确认界面_2026-01-15.md
Normal file
310
md/需求/96-门诊医生站会诊申请确认界面_2026-01-15.md
Normal file
@@ -0,0 +1,310 @@
|
||||
## 门诊医生站会诊申请确认界面PRD文档
|
||||
|
||||
### 一、页面概述
|
||||
|
||||
**页面名称**:门诊医生站会诊申请确认界面
|
||||
**页面目标**:帮助医生完成会诊申请的确认、签名和打印操作,展示会诊申请详细信息
|
||||
**适用场景**:医生在收到会诊申请后,查看申请信息并给出会诊意见
|
||||
**页面类型**:表单页+列表页复合型页面
|
||||
|
||||
**核心功能**:
|
||||
|
||||
1. 会诊申请单列表展示与选择
|
||||
2. 会诊确认与取消确认功能
|
||||
3. 签名功能
|
||||
4. 会诊记录单打印
|
||||
5. 会诊意见编辑与保存
|
||||
|
||||
**用户价值**:
|
||||
|
||||
- 规范会诊申请流程
|
||||
- 电子化确认和签名提高效率
|
||||
- 完整记录会诊意见便于后续诊疗
|
||||
- 打印功能满足纸质存档需求
|
||||
**原型图地址:**https://static.pm-ai.cn/prototype/20260115/7c45e175239257e0f04c9081bf2ca204/index.html
|
||||
**流程图:**
|
||||
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["医生进入会诊申请确认界面"]) --> LoadList["加载会诊申请列表"]
|
||||
LoadList --> HasUntreated{"是否有未处理申请?"}
|
||||
|
||||
HasUntreated -- "否" --> ShowNoTip["显示无申请提示"]
|
||||
HasUntreated -- "是" --> SelectApp["医生选择会诊申请"]
|
||||
|
||||
SelectApp --> ShowDetail["显示会诊申请详情"]
|
||||
ShowDetail --> EditOpinion["医生编辑会诊意见"]
|
||||
|
||||
EditOpinion --> ConfirmClick{"点击确认按钮?"}
|
||||
ConfirmClick -- "否" --> SignClick{"点击签名按钮?"}
|
||||
ConfirmClick -- "是" --> ValidateConfirm{"校验必填字段"}
|
||||
|
||||
ValidateConfirm -- "不通过" --> TipFill["提示\n请先填写会诊意见"]
|
||||
ValidateConfirm -- "通过" --> CheckConfirmed{"是否已确认?"}
|
||||
|
||||
CheckConfirmed -- "是" --> UpdateConfirmed["更新状态为\n已确认"]
|
||||
UpdateConfirmed --> AutoFill["自动填充医生科室信息"]
|
||||
AutoFill --> DisableCancel["禁用取消确认功能"]
|
||||
|
||||
CheckConfirmed -- "否" --> KeepState["保持当前状态"]
|
||||
|
||||
SignClick -- "否" --> PrintClick{"点击打印按钮?"}
|
||||
SignClick -- "是" --> ValidateSign{"校验通过?"}
|
||||
|
||||
ValidateSign -- "不通过" --> TipConfirmFirst["提示\n请先确认会诊申请"]
|
||||
ValidateSign -- "通过" --> UpdateSigned["更新状态为\n已签名"]
|
||||
UpdateSigned --> RecordSign["记录签名医生和时间"]
|
||||
|
||||
PrintClick -- "否" --> RefreshClick{"点击刷新按钮?"}
|
||||
PrintClick -- "是" --> GenPrintView["生成打印优化视图"]
|
||||
GenPrintView --> BrowserPrint["调用浏览器打印功能"]
|
||||
|
||||
RefreshClick -- "是" --> LoadList
|
||||
RefreshClick -- "否" --> KeepState
|
||||
|
||||
TipFill --> EditOpinion
|
||||
TipConfirmFirst --> EditOpinion
|
||||
KeepState --> End(["结束"])
|
||||
BrowserPrint --> End
|
||||
DisableCancel --> End
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### 二、整体布局分析
|
||||
|
||||
**页面宽度**:自适应布局
|
||||
**主要区域划分**:
|
||||
|
||||
1. 顶部标签导航(高度48px)
|
||||
2. 操作按钮区(高度36px+间距)
|
||||
3. 会诊申请列表区(高度自适应)
|
||||
4. 会诊记录单表单区(高度自适应)
|
||||
**布局特点**:上下布局,采用网格系统对齐,左侧对齐为主
|
||||
|
||||
### 三、页面区域详细描述
|
||||
|
||||
#### 1. 顶部标签导航区域
|
||||
|
||||
**区域位置**:页面顶部
|
||||
**区域尺寸**:高度48px,宽度100%
|
||||
**区域功能**:页面导航标识
|
||||
**包含元素**:
|
||||
|
||||
- **会诊确认标签**
|
||||
- 元素类型:文本标签
|
||||
- 显示内容:“会诊确认”
|
||||
- 交互行为:无点击交互(当前页面)
|
||||
- 样式特征:蓝色下划线,16px字体,700字重
|
||||
|
||||
#### 2. 操作按钮区域
|
||||
|
||||
**区域位置**:标签导航下方
|
||||
**区域尺寸**:高度36px,宽度100%
|
||||
**区域功能**:提供页面主要操作入口
|
||||
**包含元素**:
|
||||
|
||||
- **打印按钮**
|
||||
- 元素类型:操作按钮
|
||||
- 显示内容:“打印”
|
||||
- 交互行为:点击触发打印会诊记录单
|
||||
- 样式特征:绿色背景,白色文字,圆角6px
|
||||
- **刷新按钮**
|
||||
- 元素类型:操作按钮
|
||||
- 显示内容:“刷新”
|
||||
- 交互行为:点击重新加载页面数据
|
||||
- 样式特征:白色背景,灰色边框,黑色文字
|
||||
- **确认按钮**
|
||||
- 元素类型:状态切换按钮
|
||||
- 显示内容:“确认”/“取消确认”
|
||||
- 交互行为:
|
||||
- 点击后变为"取消确认"状态(红色样式)
|
||||
- 已签名时禁用取消操作
|
||||
- 样式特征:蓝色背景,白色文字
|
||||
- 限制条件:需选中表格行才可操作
|
||||
- **签名按钮**
|
||||
- 元素类型:操作按钮
|
||||
- 显示内容:“签名”
|
||||
- 交互行为:
|
||||
- 需先确认才能签名
|
||||
- 签名后自动记录签名时间和签名医生
|
||||
- 样式特征:蓝色背景,白色文字
|
||||
- 限制条件:需先完成确认操作
|
||||
|
||||
#### 3. 会诊申请列表区域
|
||||
|
||||
**区域位置**:按钮区域下方
|
||||
**区域尺寸**:高度自适应,宽度100%
|
||||
**区域功能**:展示待处理的会诊申请列表
|
||||
**包含元素**:
|
||||
|
||||
- **申请列表表格** (取值于门诊会诊申请单表(ConsultationRequest))
|
||||
- 检索要求:医生登录门诊医生站打开会诊申请确认界面时只能检索出当前登录医生姓名包含在会诊邀请对象内(只能查看自己受会诊邀请对象)
|
||||
- 展示方式:带斑马纹表格
|
||||
- 表头字段:
|
||||
- 序号 \| 紧急 \| 申请单号 \| 病人姓名 \| 会诊时间 \| 邀请对象 \| 申请科室 \| 申请医师 \| 申请时间 \| 确认 \| 签名
|
||||
- 数据字段:
|
||||
- 序号:文本 - 自动编号 - “1” - 不可操作
|
||||
- 紧急:复选框 - 布尔值 - 未勾选 - 可操作
|
||||
- 申请单号:文本 - 字符串 - “CS20250812001” - 不可操作
|
||||
- 病人姓名:文本 - 字符串 - “陈明” - 不可操作
|
||||
- 会诊时间:日期 - 日期时间 - “2025-08-12 17:48” - 不可操作
|
||||
- 邀请对象:文本 - 字符串 - “演示测试” - 不可操作
|
||||
- 申请科室:文本 - 字符串 - “内科” - 不可操作
|
||||
- 申请医师:文本 - 字符串 - “徐斌” - 不可操作
|
||||
- 申请时间:日期 - 日期时间 - “2025-08-12 17:48” - 不可操作
|
||||
- 确认:复选框 - 布尔值 - 勾选框 – 不可操作
|
||||
- 签名:复选框 - 布尔值 - 勾选框 – 不可操作
|
||||
- 操作功能:点击行选中查看会诊申请详情
|
||||
- 样式特征:斑马纹交替背景,悬停高亮
|
||||
|
||||
#### 4. 会诊记录单表单区域
|
||||
|
||||
**区域位置**:列表区域下方
|
||||
**区域尺寸**:高度自适应,宽度100%
|
||||
**区域功能**:展示和编辑会诊详细信息
|
||||
**包含元素**:
|
||||
|
||||
- **基础信息区**
|
||||
- 布局方式:8列网格
|
||||
- 包含字段:
|
||||
- 病人姓名/性别/年龄/就诊卡号
|
||||
- 申请单号/申请科室
|
||||
- 会诊时间/紧急标志
|
||||
- 会诊邀请对象
|
||||
- 提交医生/提交时间
|
||||
- **病史及目的区**
|
||||
- 元素类型:文本区域
|
||||
- 显示内容:患者主诉和会诊目的
|
||||
- 交互行为:只读展示
|
||||
- **会诊确认参加医师**
|
||||
- **会诊意见区**
|
||||
- 元素类型:可编辑文本域
|
||||
- 显示内容:会诊意见文本
|
||||
- 交互行为:支持多行编辑
|
||||
- 样式特征:浅灰色背景,120px最小高度
|
||||
- **确认/签名信息区**
|
||||
- 包含字段:
|
||||
- 所属医生/代表科室(确认后自动填充当前医生和科室)
|
||||
- 签名医生/签名时间(自动填充签名医生和签名时间(系统当前时间))
|
||||
|
||||
### 四、交互功能详细说明
|
||||
|
||||
#### 1. 会诊申请选择功能
|
||||
|
||||
**触发方式**:点击表格行
|
||||
**执行流程**:
|
||||
|
||||
1. 高亮选中行(浅蓝色背景)
|
||||
2. 同步该行数据到下方表单
|
||||
3. 根据确认状态更新按钮文字
|
||||
4. 加载存储的会诊意见到文本域
|
||||
|
||||
**异常处理**:
|
||||
|
||||
- 无选中行时禁用确认/签名按钮
|
||||
- 已签名行禁止取消确认
|
||||
|
||||
#### 2. 会诊确认功能
|
||||
|
||||
**触发方式**:点击确认按钮
|
||||
|
||||
**执行流程**:
|
||||
|
||||
1. 校验必填字段(会诊意见、会诊确认参加医师)
|
||||
2. 保存会诊意见等相关内容到行数据(写入门诊会诊申请确认表(ConsultationConfirmation))
|
||||
3. 勾选确认复选框
|
||||
4. 更新按钮为"取消确认"状态
|
||||
5. 所属医生和代表科室(自动填充当前医生和科室)
|
||||
|
||||
**异常处理**:
|
||||
|
||||
- 未填写会诊意见时提示"请先填写会诊意见"
|
||||
- 保存失败时保持原状态并提示错误
|
||||
|
||||
#### 2. 电子签名功能
|
||||
|
||||
**功能描述**:医生对确认的会诊进行电子签名
|
||||
**触发条件**:已确认的会诊申请点击"签名"按钮
|
||||
**操作流程**:
|
||||
|
||||
1. 医生确认会诊申请
|
||||
2. 点击"签名"按钮
|
||||
3. 校验确认状态
|
||||
4. 表格中"签名"列复选框被勾选
|
||||
5. 自动记录签名医生(当前用户)
|
||||
6. 自动填充签名时间为系统时间
|
||||
7. 禁用取消确认功能
|
||||
**成功反馈**:表单区显示签名信息
|
||||
**失败处理**:提示"请先确认会诊申请"
|
||||
|
||||
#### 3. 打印会诊记录单
|
||||
|
||||
**功能描述**:打印格式化的会诊记录
|
||||
**触发条件**:点击"打印"按钮
|
||||
**操作流程**:
|
||||
|
||||
1. 点击"打印"按钮
|
||||
2. 系统生成打印优化视图
|
||||
3. 调用浏览器打印功能
|
||||
**特殊处理**:隐藏交互元素,优化打印布局
|
||||
|
||||
### 五、数据结构说明
|
||||
|
||||
**门诊会诊申请确认表(**ConsultationConfirmation**)**
|
||||
|
||||
| **字段名称** | **数据类型** | **长度** | **描述** | **约束/说明** |
|
||||
|-----------------------------|--------------|----------|--------------------|------------------------------------------------------------------------------------|
|
||||
| **ConsultationID** | INTEGER | 20 | 会诊申请单唯一标识 | FOREIGN KEY REFERENCES ConsultationRequest(ConsultationID) |
|
||||
| **ConfirmingPhysicianID** | TEXT | -20 | 确认会诊的医生ID | 操作【确认】按钮的当前医生ID |
|
||||
| **ConfirmingPhysicianName** | TEXT | -20 | 确认会诊的医生姓名 | 操作【确认】按钮的当前医生姓名 |
|
||||
| **ConfirmingDeptName** | TEXT | 20 | 代表科室 | 操作【确认】按钮的当前开单科室 |
|
||||
| **ConfirmingDate** | DateTime | - | 确认会诊的日期 | 操作【确认】按钮当前系统时间 |
|
||||
| **ConsultationStatus** | TEXT | 20 | 会诊状态 | CHECK (ConsultationStatus IN ('已确认', '取消确认', '已签名', '已完成')), NOT NULL |
|
||||
| **ConsultationOpinion** | TEXT | 500 | 会诊意见 | |
|
||||
| **ConfirmingPhysician** | TEXT | 100 | 会诊确认参加医师 | |
|
||||
| **Signature** | TEXT | 20 | 签名医生 | |
|
||||
| **SignatureDate** | DateTime | - | 签名时间 | - |
|
||||
|
||||
ConsultationConfirmation.ConsultationStatu会诊状态
|
||||
|
||||
| **状态值** | **状态名** | **描述** |
|
||||
|------------|------------|-----------------------------------|
|
||||
| **0** | 取消确认 | 作废 |
|
||||
| **20** | 已确认 | 会诊医生已查看/同意,可写初步意见 |
|
||||
| **30** | 已签名 | 已电子签名,意见最终生效 |
|
||||
| **40** | 已完成 | 会诊报告已回写,流程关闭 |
|
||||
|
||||
**按钮涉及的事务**
|
||||
|
||||
| **按钮** | **涉及表** | **执行事务** | **锁/并发** | **成功状态** | **失败处理** |
|
||||
|--------------|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------|------------------|--------------------------|
|
||||
| **确认** | 1、ConsultationRequest<br>2、门诊医嘱<br>3、ConsultationConfirmation | 1、ConsultationRequest.ConsultationStatus =20<br>2、医嘱 状态='已执行'<br>3、写入ConsultationConfirmation表相关的数据 | SELECT ... FOR UPDATE | 已提交 → 已确认 | 任何异常 → 整体 ROLLBACK |
|
||||
| **取消确认** | 1、ConsultationRequest<br>2、门诊医嘱<br>3、ConsultationConfirmation | 1、ConsultationRequest.ConsultationStatus =10<br>2、医嘱 状态='已提交'<br>3、ConsultationConfirmation. ConsultationStatus = 0 | 同上 | 已确认→ 取消确认 | 同上回滚 |
|
||||
| **签名** | 1、ConsultationRequest<br>2、门诊医嘱<br>3、ConsultationConfirmation | 1、ConsultationRequest.ConsultationStatus =30<br>2、医嘱 Status='已完成'<br>3、写入ConsultationConfirmation. Signature, SignatureDate,ConsultationStatus =30 | 同上 | 已确认 → 已签名 | 同上回滚 |
|
||||
|
||||
### 六、开发实现要点
|
||||
|
||||
**样式规范**:
|
||||
|
||||
- **主色调**:\#4A89DC(按钮蓝色)
|
||||
- **辅助色**:\#4CAF50(成功绿色)
|
||||
- **字体规范**:14px/1.5 常规,16px 标题
|
||||
- **间距系统**:8px基础间距,24px区块间距
|
||||
- **组件样式**:
|
||||
- 按钮:6px圆角,1px边框
|
||||
- 输入框:4px圆角,1px \#E0E0E0边框
|
||||
|
||||
**技术要求**:
|
||||
|
||||
- **浏览器兼容**:Chrome/Firefox/Edge最新版
|
||||
- **性能要求**:列表加载时间\<1s
|
||||
|
||||
**注意事项**:
|
||||
|
||||
1. 确认和签名状态需要联动控制
|
||||
2. 打印功能需要特殊样式处理
|
||||
3. 时间字段需统一使用YYYY-MM-DD HH:mm:ss格式
|
||||
4. 移动端需优化表单布局
|
||||
267
md/需求/97-门诊会诊申请管理界面_2026-1-19.md
Normal file
267
md/需求/97-门诊会诊申请管理界面_2026-1-19.md
Normal file
@@ -0,0 +1,267 @@
|
||||
## 门诊会诊申请管理界面PRD文档
|
||||
|
||||
### 一、页面概述
|
||||
|
||||
**页面名称**:门诊会诊申请管理界面
|
||||
**页面目标**:提供会诊申请的全流程管理功能,包括申请记录查询、编辑申请、查看详情、状态变更等核心操作
|
||||
**适用场景**:门诊医生需要查看会诊申请或管理已有申请记录时使用
|
||||
**页面类型**:列表页+表单弹窗复合型页面
|
||||
|
||||
**核心功能**:
|
||||
|
||||
1. 多条件组合筛选会诊申请记录
|
||||
2. 会诊申请表格展示与操作(编辑/查看/删除)
|
||||
3. 会诊申请单的填写与提交
|
||||
4. 会诊状态标记(提交/结束)
|
||||
**用户价值**:规范会诊申请流程,减少纸质单据流转,提高多科室协作效率
|
||||
原型图地址:https://static.pm-ai.cn/prototype/20260116/aed1f102d614677f100c0d1fe3104999/index.html
|
||||
**流程图:**
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([Start]) --> A[进入门诊会诊申请管理界面]
|
||||
A --> B{用户操作类型}
|
||||
B -->|筛选查询| C[设置筛选条件]
|
||||
B -->|编辑申请| D[点击编辑按钮]
|
||||
B -->|查看详情| E[点击查看按钮]
|
||||
B -->|删除申请| G[点击删除按钮]
|
||||
|
||||
C --> H{验证筛选条件}
|
||||
H -->|有效| I[展示筛选结果]
|
||||
H -->|无效| J[显示错误提示]
|
||||
J --> C
|
||||
|
||||
I --> K[用户浏览列表]
|
||||
|
||||
D --> L{检查会诊状态}
|
||||
L -->|未结束| M[打开编辑弹窗]
|
||||
L -->|已结束| N[提示不可编辑]
|
||||
N --> O[关闭弹窗]
|
||||
|
||||
M --> P[修改表单内容]
|
||||
P --> Q{表单验证}
|
||||
Q -->|通过| R[保存修改]
|
||||
Q -->|不通过| S[标红错误字段]
|
||||
S --> P
|
||||
|
||||
E --> T{检查会诊状态}
|
||||
T -->|未结束| U[打开只读弹窗]
|
||||
T -->|已结束| U
|
||||
|
||||
G --> Z{删除验证}
|
||||
Z -->|可删除| AA[确认删除]
|
||||
Z -->|不可删除| AB[提示删除失败]
|
||||
AB --> K
|
||||
|
||||
AA --> AC[更新状态为已取消]
|
||||
AC --> AD[更新列表显示]
|
||||
|
||||
R --> AD
|
||||
AD --> K
|
||||
K --> AE([End])
|
||||
```
|
||||
|
||||
### 二、整体布局分析
|
||||
|
||||
**页面宽度**:自适应布局
|
||||
**主要区域划分**:
|
||||
|
||||
1. 顶部筛选区(高度自适应,约80px)
|
||||
2. 表格展示区(高度自适应,占主要空间)
|
||||
3. 底部页码区(固定高度56px)
|
||||
**布局特点**:上下布局+弹性布局,采用左右对齐方式
|
||||
|
||||
### 三、页面区域详细描述
|
||||
|
||||
#### 1. 顶部筛选区
|
||||
|
||||
**区域位置**:页面顶部
|
||||
**区域尺寸**:100%宽度,高度自适应
|
||||
**区域功能**:提供多维度筛选和快速搜索功能
|
||||
**包含元素**:
|
||||
|
||||
- **时间类型选择器**
|
||||
- 元素类型:下拉选择框
|
||||
- 显示内容:默认"会诊时间",可选"申请时间"
|
||||
- 交互行为:点击展开下拉选项
|
||||
- 样式特征:宽度120px,高度32px,圆角4px
|
||||
- **时间范围选择器**(开始/结束时间)
|
||||
- 元素类型:日期时间输入框
|
||||
- 显示内容:placeholder提示"开始时间"/“结束时间”
|
||||
- 交互行为:点击弹出日期选择面板
|
||||
- 样式特征:宽度180px,高度32px
|
||||
- **申请科室/申请医生选择器**
|
||||
- 元素类型:带datalist的输入框
|
||||
- 显示内容:placeholder提示"选择或输入科室/医生"
|
||||
- 交互行为:输入时显示匹配选项
|
||||
- 数据来源:动态生成申请科室/医生候选列表,取值于门诊会诊申请单表(ConsultationRequest.Department/ RequestingPhysician)
|
||||
- **会诊状态筛选器**
|
||||
- 元素类型:下拉选择框
|
||||
- 可选值:全部/未提交/提交/结束
|
||||
- 默认值:全部
|
||||
- **病人姓名搜索框**
|
||||
- 元素类型:文本输入框
|
||||
- 显示内容:placeholder提示"病人姓名"
|
||||
- 交互行为:支持模糊搜索
|
||||
- **操作按钮组**
|
||||
- 查询按钮
|
||||
- 样式:蓝色背景,带搜索图标
|
||||
- 交互:触发筛选条件应用
|
||||
- 重置按钮
|
||||
- 样式:灰色背景,带刷新图标
|
||||
- 交互:清空所有筛选条件
|
||||
- 打印按钮
|
||||
- 样式:深灰色背景,带打印图标
|
||||
- 交互:调起浏览器打印功能
|
||||
|
||||
#### 2. 表格展示区
|
||||
|
||||
**区域位置**:页面中部
|
||||
**区域尺寸**:100%宽度,高度自适应
|
||||
**区域功能**:展示会诊申请列表数据,支持行内操作
|
||||
**包含元素**:
|
||||
|
||||
取值于门诊会诊申请单表(ConsultationRequest)和门诊会诊申请单表(ConsultationRequest)
|
||||
|
||||
- **数据表格**
|
||||
- 展示方式:11列固定表头表格
|
||||
- 数据字段:
|
||||
- ID:文本 - 15 -申请单号
|
||||
- 急:复选框 - 布尔值 - 示例false – 不可编辑
|
||||
- 病人姓名:文本 - 朱某某 - 红色高亮
|
||||
- 会诊时间:日期 - 2026-01-05 15:08
|
||||
- 申请科室:文本 - 内科
|
||||
- 邀请对象:文本 - 吴院长
|
||||
- 申请时间:日期 - 2026-01-05 15:08
|
||||
- 申请医师:文本 - 演示测试
|
||||
- 提交:复选框 - 布尔值 - 示例false
|
||||
- 结束:复选框 - 布尔值 - 示例false
|
||||
- 操作功能:
|
||||
- 编辑按钮(✏️):点击打开编辑弹窗
|
||||
- 查看按钮(👁️):点击打开只读弹窗
|
||||
- 删除按钮(🗑️):点击确认删除
|
||||
- 【删除】将状态改为“已取消”
|
||||
- UPDATE ConsultationRequest
|
||||
- SET ConsultationStatus = 50,cancelnatureDate = \<作废会诊时间\>
|
||||
- WHERE ConsultationID = \<会诊申请单ID\> and ConsultationStatus \<\> 40 ;
|
||||
- 交互行为:
|
||||
- 行悬停效果:浅蓝色背景
|
||||
- 复选框点击:即时更新状态(需防抖处理)
|
||||
|
||||
#### 3. 底部页码区
|
||||
|
||||
**区域位置**:页面底部
|
||||
**区域尺寸**:100%宽度,固定高度56px
|
||||
**区域功能**:分页控制和数据统计
|
||||
**包含元素**:
|
||||
|
||||
- **总数统计**:总数统计文本(如:“总数:15”)
|
||||
- **分页控制器**:
|
||||
- 上一页按钮(\<)
|
||||
- 当前页按钮(1)active状态
|
||||
- 下一页按钮(\>)
|
||||
- 交互反馈:hover时边框变蓝
|
||||
|
||||
#### 4. 会诊申请弹窗(模态框)
|
||||
|
||||
**触发方式**:点击表格行操作列的编辑/查看按钮
|
||||
**区域功能**:展示/编辑会诊申请详细信息
|
||||
**包含元素**:
|
||||
|
||||
- 头部区域
|
||||
- 标题:“会诊申请单”
|
||||
- 关闭按钮(×图标)
|
||||
- 表单区域(分两栏布局)
|
||||
- 基础信息区:
|
||||
- 申请单号(只读)
|
||||
- 申请时间(不可编辑)
|
||||
- 病人姓名(不可编辑)
|
||||
- 性别/年龄(不可编辑)
|
||||
- 就诊卡号(不可编辑)
|
||||
- 会诊信息区:
|
||||
- 会诊时间(日期时间选择器)
|
||||
- 申请医师(不可编辑)
|
||||
- 紧急程度(复选框)
|
||||
- 申请科室(不可编辑)
|
||||
- 门诊诊断(不可编辑)
|
||||
- 会诊邀请对象
|
||||
- 会诊确认参加医师
|
||||
- 所属医生
|
||||
- 代表科室
|
||||
- 签名医生
|
||||
- 签名时间
|
||||
- 文本域:
|
||||
- 病史及会诊目的(多行文本)
|
||||
- 会诊意见(多行文本)
|
||||
- 底部按钮区:
|
||||
- 取消按钮(左对齐)
|
||||
- 保存按钮(右对齐,蓝色)
|
||||
|
||||
### 四、交互功能详细说明
|
||||
|
||||
#### 1. 会诊申请编辑功能
|
||||
|
||||
**功能描述**:修改已有会诊申请信息
|
||||
**触发条件**:点击表格行中的"✏️"按钮
|
||||
**操作流程**:
|
||||
|
||||
1. 检查会诊状态是否为"结束",若已结束则提示不可编辑
|
||||
2. 弹出会诊申请编辑弹窗,填充当前行数据的会诊申请和确认相关的数据
|
||||
3. 用户修改表单内容(必填字段校验)
|
||||
4. 点击"保存"按钮提交修改
|
||||
**异常处理**:
|
||||
- 必填字段为空时,标红提示
|
||||
- 保存失败时显示toast提示"保存失败,请重试"
|
||||
|
||||
#### 2. 会诊申请查看功能
|
||||
|
||||
**功能描述**:查看会诊申请详细信息
|
||||
**触发条件**:点击表格行中的"👁️"按钮
|
||||
**操作流程**:
|
||||
|
||||
1. 弹出只读弹窗,显示完整申请信息
|
||||
2. 所有字段禁用编辑
|
||||
3. 仅显示"取消"按钮用于关闭弹窗
|
||||
|
||||
#### 3. 数据筛选功能
|
||||
|
||||
**功能描述**:多条件组合查询会诊记录
|
||||
**触发条件**:点击"查询"按钮
|
||||
**数据过滤逻辑**:
|
||||
|
||||
- 时间范围:根据选择的时间类型(会诊/申请)进行筛选
|
||||
- 申请科室/申请医生:支持模糊匹配
|
||||
- 会诊状态筛选:支持多选逻辑(未提交/提交/结束)
|
||||
1. 收集所有筛选条件值
|
||||
2. 发起异步请求(示例中为前端过滤)
|
||||
3. 更新表格数据展示
|
||||
**异常处理**:
|
||||
- 时间范围不合法:提示"结束时间不能早于开始时间"
|
||||
- 无查询结果:显示空白表格+提示文字
|
||||
**性能优化**:前端本地缓存数据,减少服务器请求
|
||||
|
||||
### 五、数据结构说明
|
||||
|
||||
门诊会诊申请单表(ConsultationRequest)和门诊会诊申请单表(ConsultationRequest)
|
||||
|
||||
### 六、开发实现要点
|
||||
|
||||
**样式规范**:
|
||||
|
||||
- **主色调**:\#5D9CEC(按钮/交互元素)
|
||||
- **辅助色**:\#8E8E8E(次要按钮)
|
||||
- **字体规范**:14px/1.5(主要内容),16px/1.5(标题)
|
||||
- **间距系统**:16px(元素间距),24px(区块间距)
|
||||
- **组件样式**:
|
||||
- 按钮:圆角6px,内边距0 16px
|
||||
- 输入框:1px实线边框\#D9D9D9,圆角4px
|
||||
|
||||
**技术要求**:
|
||||
|
||||
- **浏览器兼容**:支持Chrome/Firefox/Edge最新版
|
||||
- **性能要求**:列表数据筛选响应时间\<200ms
|
||||
|
||||
**注意事项**:
|
||||
|
||||
1. 状态变更逻辑:已结束的记录不可编辑
|
||||
2. 时间字段需要做时区转换处理
|
||||
3. 申请科室/申请医生选择器需要支持拼音首字母检索
|
||||
62
md/需求/99-门诊手术中计费界面PRD_2026-1-22.md
Normal file
62
md/需求/99-门诊手术中计费界面PRD_2026-1-22.md
Normal file
@@ -0,0 +1,62 @@
|
||||
**门诊手术中计费PRD文档**
|
||||
|
||||
**目标:**
|
||||
|
||||
支持手术中追加计费(耗材、药品等)、退费等场景使用
|
||||
|
||||
术后一站式结算(发票、清单、医保等)
|
||||
|
||||
**流程图:**
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["医生开立手术申请单"] --> B{"系统生成计费包"}
|
||||
B --> C["患者缴费"]
|
||||
C --> D["手术室确认"]
|
||||
D --> E{"术中追加/退费?"}
|
||||
|
||||
E -- "是" --> F{"术中计费"}
|
||||
F -- "耗材" --> F2["护士扫码追加耗材\n实时计价 更新库存"]
|
||||
F -- "药品" --> F3["麻醉师追加药品\n实时计价 更新库存"]
|
||||
F -- "诊疗项目" --> F4["追加麻醉时长/项目\n实时计价"]
|
||||
|
||||
|
||||
F2 --> F6["生成术中追加计费单"]
|
||||
F3 --> F6
|
||||
F4 --> F6
|
||||
|
||||
|
||||
F6 --> G{"患者支付?"}
|
||||
G -- "是" --> P["提示支付成功"]--> J
|
||||
G -- "否" --> H["提示支付失败\n保持待支付"]
|
||||
H --> D
|
||||
|
||||
E -- "否" --> I["手术完成"]
|
||||
I --> J["术后统一结算"]
|
||||
J --> K["发票/清单/分割单"]
|
||||
K --> L["财务对账"]
|
||||
```
|
||||
|
||||
**注意:**待门诊手术安排界面(禅道需求编号:93)完成后再执行
|
||||
|
||||

|
||||
|
||||
图1:门诊手术安排界面(禅道需求编号:93)
|
||||
|
||||

|
||||
|
||||
图2:门诊管理-》门诊划价:手术计费界面复制《门诊划价》界面红色框内容
|
||||
|
||||
1、如上图1、2所示:在门诊手术安排界面增加【计费】按钮,实现对门诊手术中追加的费用进行记账,手术计费界面如图2所示复制《门诊划价》界面红色框内容进行个性化改造,患者信息:取值于手术安排界面选中行的患者信息,计费账号为当前系统登录的账号。
|
||||
|
||||
\*比如:在手术计费界面给患者1计费成功后,重新从手术按钮界面选中患者1点击【计费】打开界面时显示当前患者已计费成功的手术费用。
|
||||
|
||||
写入事务注意:
|
||||
|
||||
adm_charge_item费用项管理表
|
||||
|
||||
①、术中费用仍走“门诊就诊管理”的就诊ID(adm_encounter.id = adm_charge_item.encounter_id)。
|
||||
|
||||
2\. 为了事后能追溯“这些费用是术中发生的”,在费用项管理表明细上加一个 “来源业务单据(SourceBillNo)” 字段(adm_charge_item.generate_source_enum = 2(帐单生成来源为手术计费),SourceBillNo = 手术申请单号)。
|
||||
|
||||
3\. 其他内容按照《门诊划价》的业务数据流程走。
|
||||
BIN
md/需求/media/2756f39fb624c7f686d56b675b4d4d10.png
Normal file
BIN
md/需求/media/2756f39fb624c7f686d56b675b4d4d10.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 224 KiB |
BIN
md/需求/media/4fa3fca6b8362de7b938ded77d6e4982.png
Normal file
BIN
md/需求/media/4fa3fca6b8362de7b938ded77d6e4982.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 219 KiB |
BIN
md/需求/media/clip_image001.png
Normal file
BIN
md/需求/media/clip_image001.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 268 KiB |
BIN
md/需求/media/e577cd26f9a82835f3ac3690259eb357.png
Normal file
BIN
md/需求/media/e577cd26f9a82835f3ac3690259eb357.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 216 KiB |
@@ -1,2 +0,0 @@
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">OpenHis v0.0.1</h1>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.openhis</groupId>
|
||||
<artifactId>openhis-server</artifactId>
|
||||
@@ -25,6 +25,11 @@
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<optional>true</optional> <!-- 表示依赖不会传递 -->
|
||||
</dependency>
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- swagger3-->
|
||||
<dependency>
|
||||
@@ -38,9 +43,10 @@
|
||||
<artifactId>swagger-models</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Mysql驱动包 -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 核心模块-->
|
||||
@@ -66,45 +72,12 @@
|
||||
<groupId>com.core</groupId>
|
||||
<artifactId>core-flowable</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 通用工具-->
|
||||
<dependency>
|
||||
<groupId>com.core</groupId>
|
||||
<artifactId>core-common</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<!-- <build>-->
|
||||
<!-- <plugins>-->
|
||||
<!-- <plugin>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
|
||||
<!-- <version>2.5.15</version>-->
|
||||
<!-- <configuration>-->
|
||||
<!-- <fork>true</fork> <!– 如果没有该配置,devtools不会生效 –>-->
|
||||
<!-- </configuration>-->
|
||||
<!-- <executions>-->
|
||||
<!-- <execution>-->
|
||||
<!-- <goals>-->
|
||||
<!-- <goal>repackage</goal>-->
|
||||
<!-- </goals>-->
|
||||
<!-- </execution>-->
|
||||
<!-- </executions>-->
|
||||
<!-- </plugin>-->
|
||||
<!-- <plugin>-->
|
||||
<!-- <groupId>org.apache.maven.plugins</groupId>-->
|
||||
<!-- <artifactId>maven-war-plugin</artifactId>-->
|
||||
<!-- <version>3.1.0</version>-->
|
||||
<!-- <configuration>-->
|
||||
<!-- <failOnMissingWebXml>false</failOnMissingWebXml>-->
|
||||
<!-- <warName>${project.artifactId}</warName>-->
|
||||
<!-- </configuration>-->
|
||||
<!-- </plugin>-->
|
||||
<!-- <plugin>-->
|
||||
<!-- <groupId>org.apache.maven.plugins</groupId>-->
|
||||
<!-- <artifactId>maven-compiler-plugin</artifactId>-->
|
||||
<!-- <configuration>-->
|
||||
<!-- <source>8</source>-->
|
||||
<!-- <target>8</target>-->
|
||||
<!-- </configuration>-->
|
||||
<!-- </plugin>-->
|
||||
<!-- </plugins>-->
|
||||
<!-- <finalName>${project.artifactId}</finalName>-->
|
||||
<!-- </build>-->
|
||||
|
||||
</project>
|
||||
@@ -1,18 +1,5 @@
|
||||
package com.core.web.controller.common;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.FastByteArrayOutputStream;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.core.common.config.CoreConfig;
|
||||
import com.core.common.constant.CacheConstants;
|
||||
import com.core.common.constant.Constants;
|
||||
@@ -22,6 +9,17 @@ import com.core.common.utils.sign.Base64;
|
||||
import com.core.common.utils.uuid.IdUtils;
|
||||
import com.core.system.service.ISysConfigService;
|
||||
import com.google.code.kaptcha.Producer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.FastByteArrayOutputStream;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 验证码操作处理
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package com.core.web.controller.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.core.common.config.CoreConfig;
|
||||
import com.core.common.constant.Constants;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import com.core.common.utils.file.FileUploadUtils;
|
||||
import com.core.common.utils.file.FileUtils;
|
||||
import com.core.framework.config.ServerConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -16,13 +17,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.core.common.config.CoreConfig;
|
||||
import com.core.common.constant.Constants;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import com.core.common.utils.file.FileUploadUtils;
|
||||
import com.core.common.utils.file.FileUtils;
|
||||
import com.core.framework.config.ServerConfig;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通用请求处理
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
package com.core.web.controller.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.core.common.annotation.Anonymous;
|
||||
import com.core.common.config.CoreConfig;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
@@ -16,6 +8,13 @@ import com.core.common.utils.StringUtils;
|
||||
import com.core.common.utils.file.FileUploadUtils;
|
||||
import com.core.common.utils.file.FileUtils;
|
||||
import com.core.framework.config.ServerConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/file")
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package com.core.web.controller.monitor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.core.common.constant.CacheConstants;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import com.core.system.domain.SysCache;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.core.common.constant.CacheConstants;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import com.core.system.domain.SysCache;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 缓存监控
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package com.core.web.controller.monitor;
|
||||
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.core.framework.web.domain.Server;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.core.framework.web.domain.Server;
|
||||
|
||||
/**
|
||||
* 服务器监控
|
||||
*
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
package com.core.web.controller.monitor;
|
||||
|
||||
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;
|
||||
@@ -17,15 +9,23 @@ import com.core.common.utils.poi.ExcelUtil;
|
||||
import com.core.framework.web.service.SysPasswordService;
|
||||
import com.core.system.domain.SysLogininfor;
|
||||
import com.core.system.service.ISysLogininforService;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 系统访问记录
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/monitor/logininfor")
|
||||
|
||||
public class SysLogininforController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private ISysLogininforService logininforService;
|
||||
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
package com.core.web.controller.monitor;
|
||||
|
||||
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.SysOperLog;
|
||||
import com.core.system.service.ISysOperLogService;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 操作日志记录
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
package com.core.web.controller.monitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
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.constant.CacheConstants;
|
||||
import com.core.common.core.controller.BaseController;
|
||||
@@ -20,6 +11,14 @@ import com.core.common.enums.BusinessType;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import com.core.system.domain.SysUserOnline;
|
||||
import com.core.system.service.ISysUserOnlineService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 在线用户监控
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
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.validation.annotation.Validated;
|
||||
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;
|
||||
@@ -17,6 +8,13 @@ import com.core.common.enums.BusinessType;
|
||||
import com.core.common.utils.poi.ExcelUtil;
|
||||
import com.core.system.domain.SysConfig;
|
||||
import com.core.system.service.ISysConfigService;
|
||||
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 javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 参数配置 信息操作处理
|
||||
@@ -63,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
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 com.core.common.annotation.Log;
|
||||
import com.core.common.constant.UserConstants;
|
||||
import com.core.common.core.controller.BaseController;
|
||||
@@ -16,6 +8,13 @@ import com.core.common.core.domain.entity.SysDept;
|
||||
import com.core.common.enums.BusinessType;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import com.core.system.service.ISysDeptService;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
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 java.util.List;
|
||||
|
||||
/**
|
||||
* 部门信息
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.validation.annotation.Validated;
|
||||
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;
|
||||
@@ -20,6 +10,14 @@ import com.core.common.utils.StringUtils;
|
||||
import com.core.common.utils.poi.ExcelUtil;
|
||||
import com.core.system.service.ISysDictDataService;
|
||||
import com.core.system.service.ISysDictTypeService;
|
||||
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 javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据字典信息
|
||||
@@ -62,11 +60,12 @@ public class SysDictDataController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型查询字典数据信息
|
||||
* 根据字典类型查询字典数据信息(支持拼音搜索)
|
||||
*/
|
||||
@GetMapping(value = "/type/{dictType}")
|
||||
public AjaxResult dictType(@PathVariable String dictType) {
|
||||
List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
|
||||
public AjaxResult dictType(@PathVariable String dictType,
|
||||
@RequestParam(value = "searchKey", required = false) String searchKey) {
|
||||
List<SysDictData> data = dictTypeService.selectDictDataByType(dictType, searchKey);
|
||||
if (StringUtils.isNull(data)) {
|
||||
data = new ArrayList<SysDictData>();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
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.validation.annotation.Validated;
|
||||
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;
|
||||
@@ -17,6 +8,13 @@ import com.core.common.core.page.TableDataInfo;
|
||||
import com.core.common.enums.BusinessType;
|
||||
import com.core.common.utils.poi.ExcelUtil;
|
||||
import com.core.system.service.ISysDictTypeService;
|
||||
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 javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据字典信息
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.core.common.config.CoreConfig;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 首页
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.core.common.constant.Constants;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.core.common.core.domain.entity.SysMenu;
|
||||
@@ -20,6 +11,15 @@ 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;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**已评审
|
||||
* 登录验证
|
||||
@@ -40,6 +40,9 @@ public class SysLoginController {
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
@Autowired
|
||||
private ISysTenantService tenantService;
|
||||
|
||||
/**已评审
|
||||
* 登录方法
|
||||
*
|
||||
@@ -73,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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 com.core.common.annotation.Log;
|
||||
import com.core.common.constant.UserConstants;
|
||||
import com.core.common.core.controller.BaseController;
|
||||
@@ -15,6 +8,12 @@ import com.core.common.core.domain.entity.SysMenu;
|
||||
import com.core.common.enums.BusinessType;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import com.core.system.service.ISysMenuService;
|
||||
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 java.util.List;
|
||||
|
||||
/**
|
||||
* 菜单信息
|
||||
@@ -34,7 +33,9 @@ public class SysMenuController extends BaseController {
|
||||
@GetMapping("/list")
|
||||
public AjaxResult list(SysMenu menu) {
|
||||
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
|
||||
return success(menus);
|
||||
// 构建带完整路径的菜单树
|
||||
List<SysMenu> menuTreeWithFullPath = menuService.buildMenuTreeWithFullPath(menus);
|
||||
return success(menuTreeWithFullPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,4 +117,25 @@ public class SysMenuController extends BaseController {
|
||||
}
|
||||
return toAjax(menuService.deleteMenuById(menuId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单完整路径
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:menu:query')")
|
||||
@GetMapping("/fullPath/{menuId}")
|
||||
public AjaxResult getFullPath(@PathVariable("menuId") Long menuId) {
|
||||
String fullPath = menuService.getMenuFullPath(menuId);
|
||||
return success(fullPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成完整路径
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:menu:query')")
|
||||
@PostMapping("/generateFullPath")
|
||||
public AjaxResult generateFullPath(@RequestParam(required = false) Long parentId,
|
||||
@RequestParam String currentPath) {
|
||||
String fullPath = menuService.generateFullPath(parentId, currentPath);
|
||||
return success(fullPath);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
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.core.page.TableDataInfo;
|
||||
import com.core.common.enums.BusinessType;
|
||||
import com.core.system.domain.SysNotice;
|
||||
import com.core.system.service.ISysNoticeService;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 公告 信息操作处理
|
||||
@@ -26,6 +28,9 @@ public class SysNoticeController extends BaseController {
|
||||
@Autowired
|
||||
private ISysNoticeService noticeService;
|
||||
|
||||
@Autowired
|
||||
private ISysNoticeReadService noticeReadService;
|
||||
|
||||
/**
|
||||
* 获取通知公告列表
|
||||
*/
|
||||
@@ -37,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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据通知公告编号获取详细信息
|
||||
*/
|
||||
@@ -54,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));
|
||||
}
|
||||
|
||||
@@ -77,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
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.validation.annotation.Validated;
|
||||
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;
|
||||
@@ -17,6 +8,13 @@ import com.core.common.enums.BusinessType;
|
||||
import com.core.common.utils.poi.ExcelUtil;
|
||||
import com.core.system.domain.SysPost;
|
||||
import com.core.system.service.ISysPostService;
|
||||
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 javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 岗位信息操作处理
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
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.config.CoreConfig;
|
||||
import com.core.common.core.controller.BaseController;
|
||||
@@ -17,6 +13,9 @@ import com.core.common.utils.file.FileUploadUtils;
|
||||
import com.core.common.utils.file.MimeTypeUtils;
|
||||
import com.core.framework.web.service.TokenService;
|
||||
import com.core.system.service.ISysUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 个人信息 业务处理
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.core.common.core.controller.BaseController;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.core.common.core.domain.model.RegisterBody;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import com.core.framework.web.service.SysRegisterService;
|
||||
import com.core.system.service.ISysConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 注册验证
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
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.validation.annotation.Validated;
|
||||
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;
|
||||
@@ -26,6 +17,13 @@ import com.core.system.domain.SysUserRole;
|
||||
import com.core.system.service.ISysDeptService;
|
||||
import com.core.system.service.ISysRoleService;
|
||||
import com.core.system.service.ISysUserService;
|
||||
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 javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 角色信息
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
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.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.core.common.annotation.Anonymous;
|
||||
import com.core.common.core.controller.BaseController;
|
||||
@@ -13,6 +7,11 @@ import com.core.common.core.domain.R;
|
||||
import com.core.common.core.domain.entity.SysUser;
|
||||
import com.core.system.domain.SysTenant;
|
||||
import com.core.system.service.ISysTenantService;
|
||||
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
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
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.core.controller.BaseController;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.system.domain.dto.SaveTenantOptionDetailDto;
|
||||
import com.core.system.domain.dto.TenantOptionDto;
|
||||
import com.core.system.service.ISysTenantOptionService;
|
||||
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
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
package com.core.web.controller.system;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
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.core.common.annotation.Log;
|
||||
import com.core.common.core.controller.BaseController;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
@@ -27,6 +15,16 @@ import com.core.system.service.ISysDeptService;
|
||||
import com.core.system.service.ISysPostService;
|
||||
import com.core.system.service.ISysRoleService;
|
||||
import com.core.system.service.ISysUserService;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
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.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package com.core.web.controller.tool;
|
||||
|
||||
import com.core.common.core.controller.BaseController;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import io.swagger.annotations.*;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import com.core.common.core.controller.BaseController;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.common.utils.StringUtils;
|
||||
|
||||
import io.swagger.annotations.*;
|
||||
|
||||
/**
|
||||
* swagger 用户测试方法
|
||||
*
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
package com.core.web.core.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.core.common.config.CoreConfig;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.models.auth.In;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.core.common.config.CoreConfig;
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.models.auth.In;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
@@ -20,6 +15,9 @@ import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Swagger2的接口配置
|
||||
*
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.core.web.util;
|
||||
import com.core.common.core.domain.model.LoginUser;
|
||||
import com.core.common.enums.TenantOptionDict;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.core.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 租户配置工具类
|
||||
@@ -30,7 +31,12 @@ public class TenantOptionUtil {
|
||||
if (loginUser.getOptionMap() == null || loginUser.getOptionMap().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return loginUser.getOptionMap().get(optionDict.getCode());
|
||||
// return loginUser.getOptionMap().get(optionDict.getCode());
|
||||
|
||||
// TODO:2025/10/17 李永兴提出的sys_option切换TenantOption临时防止报错方案,最晚2025年11月底删除
|
||||
String newValue = loginUser.getOptionMap().get(optionDict.getCode());
|
||||
String oldValue = loginUser.getOptionJson().getString(optionDict.getCode());
|
||||
return StringUtils.isEmpty(newValue) ? oldValue : newValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,33 @@
|
||||
common通用工具
|
||||
</description>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
<arg>--add-modules</arg>
|
||||
<arg>java.base</arg>
|
||||
</compilerArgs>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.34</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- mybatis-plus 增强CRUD -->
|
||||
@@ -54,6 +81,12 @@
|
||||
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- jsr250 annotations -->
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 自定义验证注解 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -77,10 +110,6 @@
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- io常用工具类 -->
|
||||
<dependency>
|
||||
@@ -140,6 +169,12 @@
|
||||
<dependency>
|
||||
<groupId>com.belerweb</groupId>
|
||||
<artifactId>pinyin4j</artifactId>
|
||||
<version>2.5.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.core.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import com.core.common.enums.DataSourceType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 自定义多数据源切换注解
|
||||
*
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package com.core.common.annotation;
|
||||
|
||||
import com.core.common.utils.poi.ExcelHandlerAdapter;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
|
||||
import com.core.common.utils.poi.ExcelHandlerAdapter;
|
||||
|
||||
/**
|
||||
* 自定义导出Excel数据注解
|
||||
*
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.core.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Excel额外表头信息注解
|
||||
*
|
||||
* @author swb
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ExcelExtra {
|
||||
/**
|
||||
* 表头名称
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* 日期格式,如:yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
String dateFormat() default "";
|
||||
|
||||
/**
|
||||
* 排序(越小越靠前)
|
||||
*/
|
||||
int sort() default 0;
|
||||
|
||||
/**
|
||||
* 默认值
|
||||
*/
|
||||
String defaultValue() default "";
|
||||
|
||||
/**
|
||||
* 是否导出
|
||||
*/
|
||||
boolean isExport() default true;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.core.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import com.core.common.enums.BusinessType;
|
||||
import com.core.common.enums.OperatorType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 自定义操作日志记录注解
|
||||
*
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.core.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import com.core.common.constant.CacheConstants;
|
||||
import com.core.common.enums.LimitType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 限流注解
|
||||
*
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.core.common.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.core.common.config.serializer.SensitiveJsonSerializer;
|
||||
import com.core.common.enums.DesensitizedType;
|
||||
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 数据脱敏注解
|
||||
*
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package com.core.common.config.serializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.core.common.annotation.Sensitive;
|
||||
import com.core.common.core.domain.model.LoginUser;
|
||||
import com.core.common.enums.DesensitizedType;
|
||||
@@ -14,6 +11,9 @@ import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 数据脱敏序列化过滤
|
||||
*
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.core.common.constant;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 通用常量信息
|
||||
*
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
package com.core.common.core.controller;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
|
||||
import com.core.common.constant.HttpStatus;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.core.common.core.domain.model.LoginUser;
|
||||
@@ -22,6 +13,14 @@ import com.core.common.utils.StringUtils;
|
||||
import com.core.common.utils.sql.SqlUtil;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* web层通用数据处理
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.core.common.core.domain;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.core.common.constant.HttpStatus;
|
||||
import com.core.common.utils.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 操作消息提醒
|
||||
*
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.core.common.core.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Entity基类
|
||||
*
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
package com.core.common.core.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Entity基类
|
||||
*
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Data
|
||||
@@ -28,6 +28,7 @@ public class HisBaseEntity implements Serializable {
|
||||
|
||||
/** 创建时间 */
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/** 更新者 */
|
||||
@@ -36,6 +37,7 @@ public class HisBaseEntity implements Serializable {
|
||||
|
||||
/** 更新时间 */
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
/** 租户ID */
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.core.common.core.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.core.common.constant.HttpStatus;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 响应信息主体
|
||||
*
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.core.common.core.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.core.common.core.domain.entity.SysDept;
|
||||
import com.core.common.core.domain.entity.SysMenu;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Treeselect树结构实体类
|
||||
*
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
package com.core.common.core.domain.entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.core.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import com.core.common.core.domain.BaseEntity;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部门表 sys_dept
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package com.core.common.core.domain.entity;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import com.core.common.annotation.Excel;
|
||||
import com.core.common.annotation.Excel.ColumnType;
|
||||
import com.core.common.constant.UserConstants;
|
||||
import com.core.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 字典数据表 sys_dict_data
|
||||
@@ -53,6 +52,9 @@ public class SysDictData extends BaseEntity {
|
||||
@Excel(name = "状态", readConverterExp = "0=正常,1=停用")
|
||||
private String status;
|
||||
|
||||
/** 拼音首字母 */
|
||||
private String pyStr;
|
||||
|
||||
public Long getDictCode() {
|
||||
return dictCode;
|
||||
}
|
||||
@@ -136,13 +138,21 @@ public class SysDictData extends BaseEntity {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getPyStr() {
|
||||
return pyStr;
|
||||
}
|
||||
|
||||
public void setPyStr(String pyStr) {
|
||||
this.pyStr = pyStr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).append("dictCode", getDictCode())
|
||||
.append("dictSort", getDictSort()).append("dictLabel", getDictLabel()).append("dictValue", getDictValue())
|
||||
.append("dictType", getDictType()).append("cssClass", getCssClass()).append("listClass", getListClass())
|
||||
.append("isDefault", getIsDefault()).append("status", getStatus()).append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime()).append("updateBy", getUpdateBy())
|
||||
.append("isDefault", getIsDefault()).append("status", getStatus()).append("pyStr", getPyStr())
|
||||
.append("createBy", getCreateBy()).append("createTime", getCreateTime()).append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime()).append("remark", getRemark()).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package com.core.common.core.domain.entity;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import com.core.common.annotation.Excel;
|
||||
import com.core.common.annotation.Excel.ColumnType;
|
||||
import com.core.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 字典类型表 sys_dict_type
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package com.core.common.core.domain.entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.core.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import com.core.common.core.domain.BaseEntity;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 菜单权限表 sys_menu
|
||||
@@ -71,6 +69,9 @@ public class SysMenu extends BaseEntity {
|
||||
/** 子菜单 */
|
||||
private List<SysMenu> children = new ArrayList<SysMenu>();
|
||||
|
||||
/** 完整路径 */
|
||||
private String fullPath;
|
||||
|
||||
public Long getMenuId() {
|
||||
return menuId;
|
||||
}
|
||||
@@ -214,6 +215,14 @@ public class SysMenu extends BaseEntity {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public String getFullPath() {
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
public void setFullPath(String fullPath) {
|
||||
this.fullPath = fullPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).append("menuId", getMenuId())
|
||||
@@ -221,8 +230,8 @@ public class SysMenu extends BaseEntity {
|
||||
.append("path", getPath()).append("component", getComponent()).append("query", getQuery())
|
||||
.append("routeName", getRouteName()).append("isFrame", getIsFrame()).append("IsCache", getIsCache())
|
||||
.append("menuType", getMenuType()).append("visible", getVisible()).append("status ", getStatus())
|
||||
.append("perms", getPerms()).append("icon", getIcon()).append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime()).append("updateBy", getUpdateBy())
|
||||
.append("perms", getPerms()).append("icon", getIcon()).append("fullPath", getFullPath())
|
||||
.append("createBy", getCreateBy()).append("createTime", getCreateTime()).append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime()).append("remark", getRemark()).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
package com.core.common.core.domain.entity;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import com.core.common.annotation.Excel;
|
||||
import com.core.common.annotation.Excel.ColumnType;
|
||||
import com.core.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 角色表 sys_role
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
package com.core.common.core.domain.entity;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.core.common.annotation.Excel;
|
||||
import com.core.common.annotation.Excel.ColumnType;
|
||||
import com.core.common.annotation.Excel.Type;
|
||||
@@ -19,8 +11,15 @@ import com.core.common.core.domain.BaseEntity;
|
||||
import com.core.common.xss.Xss;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户对象 sys_user
|
||||
@@ -32,6 +31,7 @@ public class SysUser extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 用户ID */
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
|
||||
private Long userId;
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
package com.core.common.core.domain.model;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.core.common.core.domain.entity.SysRole;
|
||||
import com.core.common.core.domain.entity.SysUser;
|
||||
import lombok.Data;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.core.common.core.domain.entity.SysRole;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.core.common.core.domain.entity.SysUser;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 登录用户身份权限
|
||||
*
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package com.core.common.core.redis;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.core.common.exception.UtilException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.*;
|
||||
import org.springframework.data.redis.core.BoundSetOperations;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* spring redis 工具类
|
||||
*
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.core.common.core.text;
|
||||
|
||||
import com.core.common.utils.StringUtils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.core.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 字符集工具类
|
||||
*
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.core.common.core.text;
|
||||
|
||||
import com.core.common.utils.StringUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.RoundingMode;
|
||||
@@ -8,10 +11,6 @@ import java.nio.charset.Charset;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import com.core.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 类型转换器
|
||||
*
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.core.common.enums;
|
||||
|
||||
/**
|
||||
* Desc: 调价类型枚举
|
||||
* @Author raymond
|
||||
* @Date 09:14 2025/10/16
|
||||
* @return
|
||||
**/
|
||||
public enum AdjustPriceEnum {
|
||||
|
||||
MEDICINE(0, "药品"),
|
||||
CONSUMABLES(1, "耗材"),
|
||||
DIAGNOSIS(2, "诊疗"),
|
||||
REGISTER(3, "挂号");
|
||||
|
||||
|
||||
private final Integer code;
|
||||
private final String info;
|
||||
|
||||
AdjustPriceEnum(Integer code, String info) {
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.openhis.common.enums;
|
||||
package com.core.common.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 删除标识
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Getter
|
||||
public enum DelFlag {
|
||||
/**
|
||||
* 未删除
|
||||
@@ -25,6 +28,23 @@ public enum DelFlag {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public static DelFlag getByValue(Integer value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
for (DelFlag val : values()) {
|
||||
if (val.value.equals(value)) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 手动添加 getter 方法以解决 Lombok 兼容性问题
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
@@ -32,20 +52,4 @@ public enum DelFlag {
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static DelFlag getByValue(Integer value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
for (DelFlag val : values()) {
|
||||
if (val.getValue().equals(value)) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.core.common.enums;
|
||||
|
||||
/**
|
||||
* 删除标志
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
public enum DeleteFlag {
|
||||
NOT_DELETED("0", "未删除"), DELETED("1", "已删除");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
DeleteFlag(String code, String info) {
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.core.common.enums;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.core.common.utils.DesensitizedUtil;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 脱敏类型
|
||||
*
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.core.common.enums;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* 请求方式
|
||||
*
|
||||
|
||||
@@ -6,98 +6,39 @@ package com.core.common.enums;
|
||||
* @author system
|
||||
*/
|
||||
public enum TenantOptionDict {
|
||||
|
||||
/**
|
||||
* 医院名称
|
||||
*/
|
||||
HOSPITAL_NAME("hospitalName", "医院名称", 0),
|
||||
YB_HOSPITAL_NAME("hospitalName", "医保-医院名称", 0),
|
||||
/**
|
||||
* 医疗机构等级
|
||||
* 医保-医疗机构等级(3101接口)
|
||||
*/
|
||||
MEDINS_LV("medinsLv", "医疗机构等级", 1),
|
||||
YB_MEDINS_LV("medinsLv", "医保_医疗机构等级", 1),
|
||||
/**
|
||||
* 定点医药机构编号
|
||||
*/
|
||||
FIXMEDINS_CODE("fixmedinsCode", "定点医药机构编号", 2),
|
||||
YB_FIXMEDINS_CODE("fixmedinsCode", "医保_定点医药机构编号", 2),
|
||||
/**
|
||||
* 电子发票appid
|
||||
*/
|
||||
APP_ID("app_id", "电子发票appid", 3),
|
||||
EINVOICE_APP_ID("app_id", "电子发票-appid", 3),
|
||||
/**
|
||||
* 电子发票key
|
||||
*/
|
||||
KEY("key", "电子发票key", 4),
|
||||
EINVOICE_KEY("key", "电子发票-key", 4),
|
||||
/**
|
||||
* 电子发票url
|
||||
*/
|
||||
URL("url", "电子发票url", 5),
|
||||
EINVOICE_URL("url", "电子发票-url", 5),
|
||||
/**
|
||||
* 医保开关
|
||||
*/
|
||||
YB_SWITCH("yb_switch", "医保开关", 6),
|
||||
/**
|
||||
* 客户端私钥
|
||||
*/
|
||||
CLI_PRV_KEY("cliPrvKey", "客户端私钥", 7),
|
||||
/**
|
||||
* 客户端公钥
|
||||
*/
|
||||
CLI_PUB_KEY("cliPubKey", "客户端公钥", 8),
|
||||
/**
|
||||
* 服务端公钥
|
||||
*/
|
||||
SERVER_PUB_KEY("serverPubKey", "服务端公钥", 9),
|
||||
/**
|
||||
* 定点医药机构名称
|
||||
*/
|
||||
FIXMEDINS_NAME("fixmedinsName", "定点医药机构名称", 10),
|
||||
/**
|
||||
* 行政区划
|
||||
*/
|
||||
ADMVS("admvs", "行政区划", 11),
|
||||
/**
|
||||
* 授权范围
|
||||
*/
|
||||
SCOPE("scope", "授权范围", 12),
|
||||
/**
|
||||
* 授权类型
|
||||
*/
|
||||
GRANT_TYPE("grantType", "授权类型", 13),
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
PASSWORD("password", "密码", 14),
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
USERNAME("username", "用户名", 15),
|
||||
/**
|
||||
* 客户端安全码
|
||||
*/
|
||||
CLIENT_SECRET("clientSecret", "客户端安全码", 16),
|
||||
/**
|
||||
* 客户端ID
|
||||
*/
|
||||
CLIENT_ID("clientId", "客户端ID", 17),
|
||||
/**
|
||||
* 生产环境客户端公钥
|
||||
*/
|
||||
PROD_CLI_PUB_KEY("prod_cliPubKey", "生产环境客户端公钥", 18),
|
||||
/**
|
||||
* 生产环境客户端私钥
|
||||
*/
|
||||
PROD_CLI_PRV_KEY("prod_cliPrvKey", "生产环境客户端私钥", 19),
|
||||
/**
|
||||
* 生产环境客户端ID
|
||||
*/
|
||||
PROD_CLIENT_ID("prod_clientId", "生产环境客户端ID", 20),
|
||||
/**
|
||||
* 文件路径
|
||||
*/
|
||||
FILE_PATH("filePath", "文件路径", 21),
|
||||
/**
|
||||
* 电子地址
|
||||
*/
|
||||
ELE_ADDRESS("eleAddress", "电子地址", 22),
|
||||
ELE_ADDRESS("eleAddress", "电子处方-请求地址", 22),
|
||||
/**
|
||||
* 服务地址
|
||||
*/
|
||||
@@ -109,63 +50,35 @@ public enum TenantOptionDict {
|
||||
/**
|
||||
* 是否加密
|
||||
*/
|
||||
IS_ENCRYPT("isEncrypt", "是否加密", 25),
|
||||
YB_IS_ENCRYPT("isEncrypt", "医保-是否加密", 25),
|
||||
/**
|
||||
* 医保区划
|
||||
*/
|
||||
INSUPLC_ADMDVS("insuplc_admdvs", "医保区划", 26),
|
||||
YB_INSUPLC_ADMDVS("insuplc_admdvs", "医保-区划", 26),
|
||||
/**
|
||||
* 电子处方appId
|
||||
*/
|
||||
PRE_APP_ID("pre_app_id", "电子处方appId", 27),
|
||||
ELE_PRE_APP_ID("pre_app_id", "电子处方-appId", 27),
|
||||
/**
|
||||
* 电子处方appSecret
|
||||
*/
|
||||
PRE_APP_SECRET("pre_app_secret", "电子处方appSecret", 28),
|
||||
ELE_PRE_APP_SECRET("pre_app_secret", "电子处方-appSecret", 28),
|
||||
/**
|
||||
* 电子处方私钥
|
||||
*/
|
||||
APP_PRVKEY("APP_PRVKEY", "电子处方私钥", 29),
|
||||
ELE_APP_PRVKEY("APP_PRVKEY", "电子处方-私钥", 29),
|
||||
/**
|
||||
* 电子处方公钥
|
||||
*/
|
||||
PLAF_PUBKEY("PLAF_PUBKEY", "电子处方公钥", 30),
|
||||
/**
|
||||
* 医保客户端ID
|
||||
*/
|
||||
YB_CLIENT_ID("ybClientId", "医保客户端ID", 31),
|
||||
/**
|
||||
* 医保客户端安全码
|
||||
*/
|
||||
YB_CLIENT_SECRET("ybClientSecret", "医保客户端安全码", 32),
|
||||
/**
|
||||
* 医保用户名
|
||||
*/
|
||||
YB_USERNAME("ybUsername", "医保用户名", 33),
|
||||
/**
|
||||
* 医保密码
|
||||
*/
|
||||
YB_PASSWORD("ybPassword", "医保密码", 34),
|
||||
/**
|
||||
* 医保授权类型
|
||||
*/
|
||||
YB_GRANT_TYPE("ybGrantType", "医保授权类型", 35),
|
||||
/**
|
||||
* 医保授权范围
|
||||
*/
|
||||
YB_SCOPE("ybScope", "医保授权范围", 36),
|
||||
/**
|
||||
* 医保密钥
|
||||
*/
|
||||
YB_CLI_PRV_KEY("ybCliPrvKey", "医保密钥", 37),
|
||||
/**
|
||||
* 医保服务URL
|
||||
*/
|
||||
YB_URL("ybUrl", "医保服务URL", 38),
|
||||
ELE_PLAF_PUBKEY("PLAF_PUBKEY", "电子处方-公钥", 30),
|
||||
/**
|
||||
* 医院等级
|
||||
*/
|
||||
HOSPITAL_LV("hospital_lv", "医院等级", 39),
|
||||
EINVOICE_HOSPITAL_LV("hospital_lv", "电子发票-医院等级", 39),
|
||||
/**
|
||||
* 无视LIS&PACS报错
|
||||
*/
|
||||
LIS_PACS_ERROR_IGNORE("lisPacsErrorIgnore", "无视LIS&PACS报错", 40),
|
||||
/**
|
||||
* LIS接口地址
|
||||
*/
|
||||
@@ -191,13 +104,13 @@ public enum TenantOptionDict {
|
||||
*/
|
||||
PACS_APP_SECRET("pacsAppSecret", "PACSAppSecret", 45),
|
||||
/**
|
||||
* PACSAppSecret
|
||||
* 电子发票-中转服务的路径
|
||||
*/
|
||||
INVOICE_URL("invoiceUrl", "电子发票中转服务的路径", 46),
|
||||
INVOICE_FORWARD_URL("invoiceUrl", "电子发票-中转服务的路径", 46),
|
||||
/**
|
||||
* PACSAppSecret
|
||||
* 电子发票-中转服务开关
|
||||
*/
|
||||
FORWARD_SWITCH("forwardSwitch", "电子发票中转服务开关", 47),
|
||||
FORWARD_SWITCH("forwardSwitch", "电子发票-中转服务开关", 47),
|
||||
/**
|
||||
* 食源性开关
|
||||
*/
|
||||
@@ -229,7 +142,164 @@ public enum TenantOptionDict {
|
||||
/**
|
||||
* BPC请求URL
|
||||
*/
|
||||
BPC_REQUEST_URL("bpcRequestUrl", "BPC请求URL", 55);
|
||||
BPC_REQUEST_URL("bpcRequestUrl", "BPC请求URL", 55),
|
||||
/**
|
||||
* 电子发票开关
|
||||
*/
|
||||
INVOICE_SWITCH("invoiceSwitch", "电子发票开关", 56),
|
||||
/**
|
||||
* 医嘱定价来源
|
||||
*/
|
||||
ORDER_PRICING_SOURCE("orderPricingSource", "定价来源 batchSellingPrice/retailPrice", 57),
|
||||
/**
|
||||
* 三方支付(签到)
|
||||
*/
|
||||
THREE_PART_SIGN_URL("threePartSignUrl", "三方支付【签到】请求路径", 58),
|
||||
/**
|
||||
* 三方支付(签到)
|
||||
*/
|
||||
THREE_PART_SIGN_STATIC_PARAM("threePartSignStaticParam", "三方支付【签到】固定参数", 59),
|
||||
/**
|
||||
* 三方支付(签到)
|
||||
*/
|
||||
THREE_PART_SIGN_ACTIVE_PARAM("threePartSignActiveParam", "三方支付【签到】可变参数", 60),
|
||||
/**
|
||||
* 三方支付(签到)
|
||||
*/
|
||||
THREE_PART_SIGN_MAPPING_METHOD("threePartSignMappingMethod", "三方支付【签到】请求方式", 61),
|
||||
/**
|
||||
* 三方支付(消费)
|
||||
*/
|
||||
THREE_PART_PAY_URL("threePartPayUrl", "三方支付【消费】请求路径", 62),
|
||||
/**
|
||||
* 三方支付(消费)
|
||||
*/
|
||||
THREE_PART_PAY_STATIC_PARAM("threePartPayStaticParam", "三方支付【消费】固定参数", 63),
|
||||
/**
|
||||
* 三方支付(消费)
|
||||
*/
|
||||
THREE_PART_PAY_ACTIVE_PARAM("threePartPayActiveParam", "三方支付【消费】可变参数", 64),
|
||||
/**
|
||||
* 三方支付(消费)
|
||||
*/
|
||||
THREE_PART_PAY_MAPPING_METHOD("threePartPayMappingMethod", "三方支付【消费】请求方式", 65),
|
||||
/**
|
||||
* 三方支付(退费)
|
||||
*/
|
||||
THREE_PART_RETURN_URL("threePartReturnUrl", "三方支付【退费】请求路径", 66),
|
||||
/**
|
||||
* 三方支付(退费)
|
||||
*/
|
||||
THREE_PART_RETURN_STATIC_PARAM("threePartReturnStaticParam", "三方支付【退费】固定参数", 67),
|
||||
/**
|
||||
* 三方支付(退费)
|
||||
*/
|
||||
THREE_PART_RETURN_ACTIVE_PARAM("threePartReturnActiveParam", "三方支付【退费】可变参数", 68),
|
||||
/**
|
||||
* 三方支付(退费)
|
||||
*/
|
||||
THREE_PART_RETURN_MAPPING_METHOD("threePartReturnMappingMethod", "三方支付【退费】请求方式", 69),
|
||||
/**
|
||||
* 三方支付(隔天退费)
|
||||
*/
|
||||
THREE_PART_NEXT_DAY_RETURN_URL("threePartNextDayReturnUrl", "三方支付【隔天退费】请求路径", 70),
|
||||
/**
|
||||
* 三方支付(隔天退费)
|
||||
*/
|
||||
THREE_PART_NEXT_DAY_RETURN_STATIC_PARAM("threePartNextDayReturnStaticParam", "三方支付【隔天退费】固定参数", 71),
|
||||
/**
|
||||
* 三方支付(隔天退费)
|
||||
*/
|
||||
THREE_PART_NEXT_DAY_RETURN_ACTIVE_PARAM("threePartNextDayReturnActiveParam", "三方支付【隔天退费】可变参数", 72),
|
||||
/**
|
||||
* 三方支付(隔天退费)
|
||||
*/
|
||||
THREE_PART_NEXT_DAY_RETURN_MAPPING_METHOD("threePartNextDayReturnMappingMethod", "三方支付【隔天退费】请求方式", 73),
|
||||
/**
|
||||
* 三方支付路径(支付结果查询)
|
||||
*/
|
||||
THREE_PART_PAY_QUERY_URL("threePartPayQueryUrl", "三方支付【支付结果查询】请求路径", 74),
|
||||
/**
|
||||
* 三方支付(支付结果查询)
|
||||
*/
|
||||
THREE_PART_PAY_QUERY_STATIC_PARAM("threePartPayQueryStaticParam", "三方支付【支付结果查询】固定参数", 75),
|
||||
/**
|
||||
* 三方支付(支付结果查询)
|
||||
*/
|
||||
THREE_PART_PAY_QUERY_ACTIVE_PARAM("threePartPayQueryActiveParam", "三方支付【支付结果查询】可变参数", 76),
|
||||
/**
|
||||
* 三方支付(支付结果查询)
|
||||
*/
|
||||
THREE_PART_PAY_QUERY_MAPPING_METHOD("threePartPayQueryMappingMethod", "三方支付【支付结果查询】请求方式", 77),
|
||||
/**
|
||||
* 三方支付路径(退费结果查询)
|
||||
*/
|
||||
THREE_PART_RETURN_QUERY_URL("threePartReturnQueryUrl", "三方支付【退费结果查询】请求路径", 78),
|
||||
/**
|
||||
* 三方支付(退费结果查询)
|
||||
*/
|
||||
THREE_PART_RETURN_QUERY_STATIC_PARAM("threePartReturnQueryStaticParam", "三方支付【退费结果查询】固定参数", 79),
|
||||
/**
|
||||
* 三方支付(退费结果查询)
|
||||
*/
|
||||
THREE_PART_RETURN_QUERY_ACTIVE_PARAM("threePartReturnQueryActiveParam", "三方支付【退费结果查询】可变参数", 80),
|
||||
/**
|
||||
* 三方支付(退费结果查询)
|
||||
*/
|
||||
THREE_PART_RETURN_QUERY_MAPPING_METHOD("threePartReturnQueryMappingMethod", "三方支付【退费结果查询】请求方式", 81),
|
||||
/**
|
||||
* 三方支付路径(隔天退费结果查询)
|
||||
*/
|
||||
THREE_PART_NEXT_DAY_RETURN_QUERY_URL("threePartNextDayReturnQueryUrl", "三方支付【隔天退费结果查询】请求路径", 82),
|
||||
/**
|
||||
* 三方支付(隔天退费结果查询)
|
||||
*/
|
||||
THREE_PART_NEXT_DAY_RETURN_QUERY_STATIC_PARAM("threePartNextDayReturnQueryStaticParam", "三方支付【隔天退费结果查询】固定参数", 83),
|
||||
/**
|
||||
* 三方支付(隔天退费结果查询)
|
||||
*/
|
||||
THREE_PART_NEXT_DAY_RETURN_QUERY_ACTIVE_PARAM("threePartNextDayReturnQueryActiveParam", "三方支付【隔天退费结果查询】可变参数", 84),
|
||||
/**
|
||||
* 三方支付(隔天退费结果查询)
|
||||
*/
|
||||
THREE_PART_NEXT_DAY_RETURN_QUERY_MAPPING_METHOD("threePartNextDayReturnQueryMappingMethod", "三方支付【隔天退费结果查询】请求方式",
|
||||
85),
|
||||
/**
|
||||
* 三方支付(签出)
|
||||
*/
|
||||
THREE_PART_SIGN_OUT_URL("threePartSignOutUrl", "三方支付【签出】请求路径", 86),
|
||||
/**
|
||||
* 三方支付(签出)
|
||||
*/
|
||||
THREE_PART_SIGN_OUT_STATIC_PARAM("threePartSignOutStaticParam", "三方支付【签出】固定参数", 87),
|
||||
/**
|
||||
* 三方支付(签出)
|
||||
*/
|
||||
THREE_PART_SIGN_OUT_ACTIVE_PARAM("threePartSignOutActiveParam", "三方支付【签出】可变参数", 88),
|
||||
/**
|
||||
* 三方支付(签出)
|
||||
*/
|
||||
THREE_PART_SIGN_OUT_MAPPING_METHOD("threePartSignOutMappingMethod", "三方支付【签出】请求方式", 89),
|
||||
/**
|
||||
* 三方支付(签出)
|
||||
*/
|
||||
YB_INPATIENT_SETTLEMENT_UP_URL("ybInpatientSetlUp", "选填4101或4101A", 90),
|
||||
/**
|
||||
* PACS查看报告地址
|
||||
*/
|
||||
PACS_REPORT_URL("pacsReportUrl", "PACS查看报告地址", 91),
|
||||
/**
|
||||
* LIS查看报告地址
|
||||
*/
|
||||
LIS_REPORT_URL("lisReportUrl", "LIS查看报告地址", 92),
|
||||
/**
|
||||
* 开药时药房允许多选开关
|
||||
*/
|
||||
PHARMACY_MULTIPLE_CHOICE_SWITCH("pharmacyMultipleChoiceSwitch", "开药时药房允许多选开关", 93),
|
||||
/**
|
||||
* PEIS服务地址
|
||||
*/
|
||||
PEIS_SERVER_URL("peisServerUrl", "PEIS服务地址", 94);
|
||||
|
||||
private final String code;
|
||||
private final String name;
|
||||
@@ -241,18 +311,6 @@ public enum TenantOptionDict {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
public static TenantOptionDict getByCode(String code) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
@@ -264,4 +322,16 @@ public enum TenantOptionDict {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public final class ServiceException extends RuntimeException {
|
||||
/**
|
||||
* 错误明细,内部调试错误
|
||||
*
|
||||
* 和 {@link CommonResult#getDetailMessage()} 一致的设计
|
||||
* 和
|
||||
*/
|
||||
private String detailMessage;
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package com.core.common.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import com.core.common.utils.StringUtils;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Repeatable 过滤器
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package com.core.common.filter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import com.core.common.constant.Constants;
|
||||
import com.core.common.utils.http.HttpHelper;
|
||||
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
import com.core.common.constant.Constants;
|
||||
import com.core.common.utils.http.HttpHelper;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* 构建可重复读取inputStream的request
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package com.core.common.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.core.common.enums.HttpMethod;
|
||||
import com.core.common.utils.StringUtils;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.core.common.enums.HttpMethod;
|
||||
import com.core.common.utils.StringUtils;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 防止XSS攻击的过滤器
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user