From 1747291f415c7f39d3f0b659a50e18562839a927 Mon Sep 17 00:00:00 2001 From: weixin_45799331 Date: Wed, 11 Feb 2026 14:16:30 +0800 Subject: [PATCH] =?UTF-8?q?96-=E9=97=A8=E8=AF=8A=E5=8C=BB=E7=94=9F?= =?UTF-8?q?=E7=AB=99=E4=BC=9A=E8=AF=8A=E7=94=B3=E8=AF=B7=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E5=92=8C97-=E9=97=A8=E8=AF=8A=E4=BC=9A?= =?UTF-8?q?=E8=AF=8A=E7=94=B3=E8=AF=B7=E7=AE=A1=E7=90=86=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E5=85=A8=E9=83=A8=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.agents/skills/java-spring-boot/SKILL.md | 208 ------- .../java-spring-boot/assets/config.yaml | 41 -- .../java-spring-boot/assets/schema.json | 60 -- .../java-spring-boot/references/GUIDE.md | 95 --- .../java-spring-boot/references/PATTERNS.md | 87 --- .../java-spring-boot/scripts/validate.py | 131 ----- .../skills/spring-boot-engineer/SKILL.md | 94 --- .../spring-boot-engineer/references/cloud.md | 498 ---------------- .../spring-boot-engineer/references/data.md | 381 ------------ .../references/security.md | 459 --------------- .../references/testing.md | 545 ------------------ .../spring-boot-engineer/references/web.md | 295 ---------- ...missing_fields_to_consultation_request.sql | 63 -- .../create_consultation_tables_postgresql.sql | 109 ---- ...e_consultation_tables_postgresql_final.sql | 135 ----- ...e_consultation_tables_postgresql_fixed.sql | 141 ----- .../insert_consultation_menu_fixed.sql | 70 --- .../impl/ClinicRoomAppServiceImpl.java | 22 +- .../impl/DoctorScheduleAppServiceImpl.java | 62 +- .../appservice/impl/TicketAppServiceImpl.java | 35 +- .../controller/DoctorScheduleController.java | 9 - .../SurgicalScheduleController.java | 28 - .../ConsultationConfirmationController.java | 95 --- .../controller/ConsultationController.java | 4 +- .../ConsultationRecordController.java | 92 --- .../ConsultationRequestController.java | 170 ------ .../domain/ConsultationRequest.java | 4 +- .../mapper/ConsultationRequestMapper.java | 15 +- .../DoctorStationAdviceAppServiceImpl.java | 31 +- .../InfectiousDiseaseReportController.java | 161 ------ .../impl/PaymentRecServiceImpl.java | 42 +- ...dMedicalInsuranceSettlementController.java | 94 --- .../src/main/resources/application-dev.yml | 13 +- .../src/main/resources/application.yml | 14 +- .../SurgicalScheduleAppMapper.xml | 1 - .../ConsultationRequestMapper.xml | 11 +- .../src/main/resources/spy.properties | 5 - .../openhis/common/enums/GenerateSource.java | 7 +- .../administration/domain/ChargeItem.java | 5 - .../service/IClinicRoomService.java | 19 - .../service/impl/ClinicRoomServiceImpl.java | 17 - .../clinical/domain/InfectiousCard.java | 378 ------------ .../clinical/mapper/InfectiousCardMapper.java | 15 - .../service/IInfectiousCardService.java | 24 - .../impl/InfectiousCardServiceImpl.java | 67 --- .../domain/ConsultationConfirmation.java | 156 ----- .../domain/ConsultationRecord.java | 120 ---- .../domain/ConsultationRequest.java | 273 --------- .../ConsultationConfirmationMapper.java | 14 - .../mapper/ConsultationRecordMapper.java | 14 - .../IConsultationConfirmationService.java | 12 - .../service/IConsultationRecordService.java | 12 - .../service/IConsultationRequestService.java | 41 -- .../ConsultationConfirmationServiceImpl.java | 16 - .../impl/ConsultationRecordServiceImpl.java | 16 - .../impl/ConsultationRequestServiceImpl.java | 93 --- .../ConsultationConfirmationMapper.xml | 25 - .../consultation/ConsultationRecordMapper.xml | 25 - .../ConsultationRequestMapper.xml | 50 -- ...ptimize_consultation_tables_postgresql.sql | 80 --- .../add_source_bill_no_to_adm_charge_item.sql | 12 - .../create_yb_day_end_settlement_table.sql | 71 --- .../sql/insert_yb_day_end_settlement_menu.sql | 27 - .../update_consultation_menu_postgresql.sql | 118 ---- ...consultation_menu_postgresql_corrected.sql | 118 ---- .../layout/components/Sidebar/SidebarItem.vue | 12 +- ....timestamp-1770788344262-2691367b6bac9.mjs | 143 +++++ 67 files changed, 213 insertions(+), 6087 deletions(-) delete mode 100644 openhis-server-new/.agents/skills/java-spring-boot/SKILL.md delete mode 100644 openhis-server-new/.agents/skills/java-spring-boot/assets/config.yaml delete mode 100644 openhis-server-new/.agents/skills/java-spring-boot/assets/schema.json delete mode 100644 openhis-server-new/.agents/skills/java-spring-boot/references/GUIDE.md delete mode 100644 openhis-server-new/.agents/skills/java-spring-boot/references/PATTERNS.md delete mode 100644 openhis-server-new/.agents/skills/java-spring-boot/scripts/validate.py delete mode 100644 openhis-server-new/.agents/skills/spring-boot-engineer/SKILL.md delete mode 100644 openhis-server-new/.agents/skills/spring-boot-engineer/references/cloud.md delete mode 100644 openhis-server-new/.agents/skills/spring-boot-engineer/references/data.md delete mode 100644 openhis-server-new/.agents/skills/spring-boot-engineer/references/security.md delete mode 100644 openhis-server-new/.agents/skills/spring-boot-engineer/references/testing.md delete mode 100644 openhis-server-new/.agents/skills/spring-boot-engineer/references/web.md delete mode 100644 openhis-server-new/add_missing_fields_to_consultation_request.sql delete mode 100644 openhis-server-new/create_consultation_tables_postgresql.sql delete mode 100644 openhis-server-new/create_consultation_tables_postgresql_final.sql delete mode 100644 openhis-server-new/create_consultation_tables_postgresql_fixed.sql delete mode 100644 openhis-server-new/insert_consultation_menu_fixed.sql delete mode 100644 openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationConfirmationController.java delete mode 100644 openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationRecordController.java delete mode 100644 openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationRequestController.java rename openhis-server-new/{openhis-domain/src/main/java/com/openhis => openhis-application/src/main/java/com/openhis/web}/consultation/mapper/ConsultationRequestMapper.java (52%) delete mode 100644 openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/InfectiousDiseaseReportController.java delete mode 100644 openhis-server-new/openhis-application/src/main/java/com/openhis/web/ybmanage/controller/DayEndMedicalInsuranceSettlementController.java delete mode 100644 openhis-server-new/openhis-application/src/main/resources/spy.properties delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/domain/InfectiousCard.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/mapper/InfectiousCardMapper.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/IInfectiousCardService.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/InfectiousCardServiceImpl.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationConfirmation.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationRecord.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationRequest.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationConfirmationMapper.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationRecordMapper.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationConfirmationService.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationRecordService.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationRequestService.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationConfirmationServiceImpl.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationRecordServiceImpl.java delete mode 100644 openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationRequestServiceImpl.java delete mode 100644 openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationConfirmationMapper.xml delete mode 100644 openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationRecordMapper.xml delete mode 100644 openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml delete mode 100644 openhis-server-new/optimize_consultation_tables_postgresql.sql delete mode 100644 openhis-server-new/sql/add_source_bill_no_to_adm_charge_item.sql delete mode 100644 openhis-server-new/sql/create_yb_day_end_settlement_table.sql delete mode 100644 openhis-server-new/sql/insert_yb_day_end_settlement_menu.sql delete mode 100644 openhis-server-new/update_consultation_menu_postgresql.sql delete mode 100644 openhis-server-new/update_consultation_menu_postgresql_corrected.sql create mode 100644 openhis-ui-vue3/vite.config.js.timestamp-1770788344262-2691367b6bac9.mjs diff --git a/openhis-server-new/.agents/skills/java-spring-boot/SKILL.md b/openhis-server-new/.agents/skills/java-spring-boot/SKILL.md deleted file mode 100644 index f3b1df54..00000000 --- a/openhis-server-new/.agents/skills/java-spring-boot/SKILL.md +++ /dev/null @@ -1,208 +0,0 @@ ---- -name: java-spring-boot -description: Build production Spring Boot applications - REST APIs, Security, Data, Actuator -sasmp_version: "1.3.0" -version: "3.0.0" -bonded_agent: 03-java-spring -bond_type: PRIMARY_BOND -allowed-tools: Read, Write, Bash, Glob, Grep - -# Parameter Validation -parameters: - spring_version: - type: string - default: "3.2" - description: Spring Boot version - module: - type: string - enum: [web, security, data, actuator, cloud] - description: Spring module focus ---- - -# Java Spring Boot Skill - -Build production-ready Spring Boot applications with modern best practices. - -## Overview - -This skill covers Spring Boot development including REST APIs, security configuration, data access, actuator monitoring, and cloud integration. Follows Spring Boot 3.x patterns with emphasis on production readiness. - -## When to Use This Skill - -Use when you need to: -- Create REST APIs with Spring MVC/WebFlux -- Configure Spring Security (OAuth2, JWT) -- Set up database access with Spring Data -- Enable monitoring with Actuator -- Integrate with Spring Cloud - -## Topics Covered - -### Spring Boot Core -- Auto-configuration and starters -- Application properties and profiles -- Bean lifecycle and configuration -- DevTools and hot reload - -### REST API Development -- @RestController and @RequestMapping -- Request/response handling -- Validation with Bean Validation -- Exception handling with @ControllerAdvice - -### Spring Security -- SecurityFilterChain configuration -- OAuth2 and JWT authentication -- Method security (@PreAuthorize) -- CORS and CSRF configuration - -### Spring Data JPA -- Repository pattern -- Query methods and @Query -- Pagination and sorting -- Auditing and transactions - -### Actuator & Monitoring -- Health checks and probes -- Metrics with Micrometer -- Custom endpoints -- Prometheus integration - -## Quick Reference - -```java -// REST Controller -@RestController -@RequestMapping("/api/users") -@Validated -public class UserController { - - @GetMapping("/{id}") - public ResponseEntity getUser(@PathVariable Long id) { - return userService.findById(id) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); - } - - @PostMapping - public ResponseEntity createUser(@Valid @RequestBody UserRequest request) { - User user = userService.create(request); - URI location = URI.create("/api/users/" + user.getId()); - return ResponseEntity.created(location).body(user); - } -} - -// Security Configuration -@Configuration -@EnableWebSecurity -public class SecurityConfig { - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - return http - .csrf(csrf -> csrf.disable()) - .sessionManagement(s -> s.sessionCreationPolicy(STATELESS)) - .authorizeHttpRequests(auth -> auth - .requestMatchers("/actuator/health/**").permitAll() - .requestMatchers("/api/public/**").permitAll() - .anyRequest().authenticated()) - .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())) - .build(); - } -} - -// Exception Handler -@RestControllerAdvice -public class GlobalExceptionHandler { - - @ExceptionHandler(EntityNotFoundException.class) - public ProblemDetail handleNotFound(EntityNotFoundException ex) { - return ProblemDetail.forStatusAndDetail(NOT_FOUND, ex.getMessage()); - } -} -``` - -## Configuration Templates - -```yaml -# application.yml -spring: - application: - name: ${APP_NAME:my-service} - profiles: - active: ${SPRING_PROFILES_ACTIVE:local} - jpa: - open-in-view: false - properties: - hibernate: - jdbc.batch_size: 50 - -management: - endpoints: - web: - exposure: - include: health,info,metrics,prometheus - endpoint: - health: - probes: - enabled: true - -server: - error: - include-stacktrace: never -``` - -## Common Patterns - -### Layer Architecture -``` -Controller → Service → Repository → Database - ↓ ↓ ↓ - DTOs Entities Entities -``` - -### Validation Patterns -```java -public record CreateUserRequest( - @NotBlank @Size(max = 100) String name, - @Email @NotBlank String email, - @NotNull @Min(18) Integer age -) {} -``` - -## Troubleshooting - -### Common Issues - -| Problem | Cause | Solution | -|---------|-------|----------| -| Bean not found | Missing @Component | Add annotation or @Bean | -| Circular dependency | Constructor injection | Use @Lazy or refactor | -| 401 Unauthorized | Security config | Check permitAll paths | -| Slow startup | Heavy auto-config | Exclude unused starters | - -### Debug Properties -```properties -debug=true -logging.level.org.springframework.security=DEBUG -spring.jpa.show-sql=true -``` - -### Debug Checklist -``` -□ Check /actuator/conditions -□ Verify active profiles -□ Review security filter chain -□ Check bean definitions -□ Test health endpoints -``` - -## Usage - -``` -Skill("java-spring-boot") -``` - -## Related Skills -- `java-testing` - Spring test patterns -- `java-jpa-hibernate` - Data access diff --git a/openhis-server-new/.agents/skills/java-spring-boot/assets/config.yaml b/openhis-server-new/.agents/skills/java-spring-boot/assets/config.yaml deleted file mode 100644 index 2df75205..00000000 --- a/openhis-server-new/.agents/skills/java-spring-boot/assets/config.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# java-spring-boot Configuration -# Category: general -# Generated: 2025-12-30 - -skill: - name: java-spring-boot - version: "1.0.0" - category: general - -settings: - # Default settings for java-spring-boot - enabled: true - log_level: info - - # Category-specific defaults - validation: - strict_mode: false - auto_fix: false - - output: - format: markdown - include_examples: true - -# Environment-specific overrides -environments: - development: - log_level: debug - validation: - strict_mode: false - - production: - log_level: warn - validation: - strict_mode: true - -# Integration settings -integrations: - # Enable/disable integrations - git: true - linter: true - formatter: true diff --git a/openhis-server-new/.agents/skills/java-spring-boot/assets/schema.json b/openhis-server-new/.agents/skills/java-spring-boot/assets/schema.json deleted file mode 100644 index d709a4de..00000000 --- a/openhis-server-new/.agents/skills/java-spring-boot/assets/schema.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "java-spring-boot Configuration Schema", - "type": "object", - "properties": { - "skill": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string", - "pattern": "^\\d+\\.\\d+\\.\\d+$" - }, - "category": { - "type": "string", - "enum": [ - "api", - "testing", - "devops", - "security", - "database", - "frontend", - "algorithms", - "machine-learning", - "cloud", - "containers", - "general" - ] - } - }, - "required": [ - "name", - "version" - ] - }, - "settings": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean", - "default": true - }, - "log_level": { - "type": "string", - "enum": [ - "debug", - "info", - "warn", - "error" - ] - } - } - } - }, - "required": [ - "skill" - ] -} \ No newline at end of file diff --git a/openhis-server-new/.agents/skills/java-spring-boot/references/GUIDE.md b/openhis-server-new/.agents/skills/java-spring-boot/references/GUIDE.md deleted file mode 100644 index 590755cb..00000000 --- a/openhis-server-new/.agents/skills/java-spring-boot/references/GUIDE.md +++ /dev/null @@ -1,95 +0,0 @@ -# Java Spring Boot Guide - -## Overview - -This guide provides comprehensive documentation for the **java-spring-boot** skill in the custom-plugin-java plugin. - -## Category: General - -## Quick Start - -### Prerequisites - -- Familiarity with general concepts -- Development environment set up -- Plugin installed and configured - -### Basic Usage - -```bash -# Invoke the skill -claude "java-spring-boot - [your task description]" - -# Example -claude "java-spring-boot - analyze the current implementation" -``` - -## Core Concepts - -### Key Principles - -1. **Consistency** - Follow established patterns -2. **Clarity** - Write readable, maintainable code -3. **Quality** - Validate before deployment - -### Best Practices - -- Always validate input data -- Handle edge cases explicitly -- Document your decisions -- Write tests for critical paths - -## Common Tasks - -### Task 1: Basic Implementation - -```python -# Example implementation pattern -def implement_java_spring_boot(input_data): - """ - Implement java-spring-boot functionality. - - Args: - input_data: Input to process - - Returns: - Processed result - """ - # Validate input - if not input_data: - raise ValueError("Input required") - - # Process - result = process(input_data) - - # Return - return result -``` - -### Task 2: Advanced Usage - -For advanced scenarios, consider: - -- Configuration customization via `assets/config.yaml` -- Validation using `scripts/validate.py` -- Integration with other skills - -## Troubleshooting - -### Common Issues - -| Issue | Cause | Solution | -|-------|-------|----------| -| Skill not found | Not installed | Run plugin sync | -| Validation fails | Invalid config | Check config.yaml | -| Unexpected output | Missing context | Provide more details | - -## Related Resources - -- SKILL.md - Skill specification -- config.yaml - Configuration options -- validate.py - Validation script - ---- - -*Last updated: 2025-12-30* diff --git a/openhis-server-new/.agents/skills/java-spring-boot/references/PATTERNS.md b/openhis-server-new/.agents/skills/java-spring-boot/references/PATTERNS.md deleted file mode 100644 index 431a1c14..00000000 --- a/openhis-server-new/.agents/skills/java-spring-boot/references/PATTERNS.md +++ /dev/null @@ -1,87 +0,0 @@ -# Java Spring Boot Patterns - -## Design Patterns - -### Pattern 1: Input Validation - -Always validate input before processing: - -```python -def validate_input(data): - if data is None: - raise ValueError("Data cannot be None") - if not isinstance(data, dict): - raise TypeError("Data must be a dictionary") - return True -``` - -### Pattern 2: Error Handling - -Use consistent error handling: - -```python -try: - result = risky_operation() -except SpecificError as e: - logger.error(f"Operation failed: {e}") - handle_error(e) -except Exception as e: - logger.exception("Unexpected error") - raise -``` - -### Pattern 3: Configuration Loading - -Load and validate configuration: - -```python -import yaml - -def load_config(config_path): - with open(config_path) as f: - config = yaml.safe_load(f) - validate_config(config) - return config -``` - -## Anti-Patterns to Avoid - -### ❌ Don't: Swallow Exceptions - -```python -# BAD -try: - do_something() -except: - pass -``` - -### ✅ Do: Handle Explicitly - -```python -# GOOD -try: - do_something() -except SpecificError as e: - logger.warning(f"Expected error: {e}") - return default_value -``` - -## Category-Specific Patterns: General - -### Recommended Approach - -1. Start with the simplest implementation -2. Add complexity only when needed -3. Test each addition -4. Document decisions - -### Common Integration Points - -- Configuration: `assets/config.yaml` -- Validation: `scripts/validate.py` -- Documentation: `references/GUIDE.md` - ---- - -*Pattern library for java-spring-boot skill* diff --git a/openhis-server-new/.agents/skills/java-spring-boot/scripts/validate.py b/openhis-server-new/.agents/skills/java-spring-boot/scripts/validate.py deleted file mode 100644 index bf5f0659..00000000 --- a/openhis-server-new/.agents/skills/java-spring-boot/scripts/validate.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env python3 -""" -Validation script for java-spring-boot skill. -Category: general -""" - -import os -import sys -import yaml -import json -from pathlib import Path - - -def validate_config(config_path: str) -> dict: - """ - Validate skill configuration file. - - Args: - config_path: Path to config.yaml - - Returns: - dict: Validation result with 'valid' and 'errors' keys - """ - errors = [] - - if not os.path.exists(config_path): - return {"valid": False, "errors": ["Config file not found"]} - - try: - with open(config_path, 'r') as f: - config = yaml.safe_load(f) - except yaml.YAMLError as e: - return {"valid": False, "errors": [f"YAML parse error: {e}"]} - - # Validate required fields - if 'skill' not in config: - errors.append("Missing 'skill' section") - else: - if 'name' not in config['skill']: - errors.append("Missing skill.name") - if 'version' not in config['skill']: - errors.append("Missing skill.version") - - # Validate settings - if 'settings' in config: - settings = config['settings'] - if 'log_level' in settings: - valid_levels = ['debug', 'info', 'warn', 'error'] - if settings['log_level'] not in valid_levels: - errors.append(f"Invalid log_level: {settings['log_level']}") - - return { - "valid": len(errors) == 0, - "errors": errors, - "config": config if not errors else None - } - - -def validate_skill_structure(skill_path: str) -> dict: - """ - Validate skill directory structure. - - Args: - skill_path: Path to skill directory - - Returns: - dict: Structure validation result - """ - required_dirs = ['assets', 'scripts', 'references'] - required_files = ['SKILL.md'] - - errors = [] - - # Check required files - for file in required_files: - if not os.path.exists(os.path.join(skill_path, file)): - errors.append(f"Missing required file: {file}") - - # Check required directories - for dir in required_dirs: - dir_path = os.path.join(skill_path, dir) - if not os.path.isdir(dir_path): - errors.append(f"Missing required directory: {dir}/") - else: - # Check for real content (not just .gitkeep) - files = [f for f in os.listdir(dir_path) if f != '.gitkeep'] - if not files: - errors.append(f"Directory {dir}/ has no real content") - - return { - "valid": len(errors) == 0, - "errors": errors, - "skill_name": os.path.basename(skill_path) - } - - -def main(): - """Main validation entry point.""" - skill_path = Path(__file__).parent.parent - - print(f"Validating java-spring-boot skill...") - print(f"Path: {skill_path}") - - # Validate structure - structure_result = validate_skill_structure(str(skill_path)) - print(f"\nStructure validation: {'PASS' if structure_result['valid'] else 'FAIL'}") - if structure_result['errors']: - for error in structure_result['errors']: - print(f" - {error}") - - # Validate config - config_path = skill_path / 'assets' / 'config.yaml' - if config_path.exists(): - config_result = validate_config(str(config_path)) - print(f"\nConfig validation: {'PASS' if config_result['valid'] else 'FAIL'}") - if config_result['errors']: - for error in config_result['errors']: - print(f" - {error}") - else: - print("\nConfig validation: SKIPPED (no config.yaml)") - - # Summary - all_valid = structure_result['valid'] - print(f"\n==================================================") - print(f"Overall: {'VALID' if all_valid else 'INVALID'}") - - return 0 if all_valid else 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/openhis-server-new/.agents/skills/spring-boot-engineer/SKILL.md b/openhis-server-new/.agents/skills/spring-boot-engineer/SKILL.md deleted file mode 100644 index d2cf9125..00000000 --- a/openhis-server-new/.agents/skills/spring-boot-engineer/SKILL.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -name: spring-boot-engineer -description: Use when building Spring Boot 3.x applications, microservices, or reactive Java applications. Invoke for Spring Data JPA, Spring Security 6, WebFlux, Spring Cloud integration. -license: MIT -metadata: - author: https://github.com/Jeffallan - version: "1.0.0" - domain: backend - triggers: Spring Boot, Spring Framework, Spring Cloud, Spring Security, Spring Data JPA, Spring WebFlux, Microservices Java, Java REST API, Reactive Java - role: specialist - scope: implementation - output-format: code - related-skills: java-architect, database-optimizer, microservices-architect, devops-engineer ---- - -# Spring Boot Engineer - -Senior Spring Boot engineer with expertise in Spring Boot 3+, cloud-native Java development, and enterprise microservices architecture. - -## Role Definition - -You are a senior Spring Boot engineer with 10+ years of enterprise Java experience. You specialize in Spring Boot 3.x with Java 17+, reactive programming, Spring Cloud ecosystem, and building production-grade microservices. You focus on creating scalable, secure, and maintainable applications with comprehensive testing and observability. - -## When to Use This Skill - -- Building REST APIs with Spring Boot -- Implementing reactive applications with WebFlux -- Setting up Spring Data JPA repositories -- Implementing Spring Security 6 authentication -- Creating microservices with Spring Cloud -- Optimizing Spring Boot performance -- Writing comprehensive tests with Spring Boot Test - -## Core Workflow - -1. **Analyze requirements** - Identify service boundaries, APIs, data models, security needs -2. **Design architecture** - Plan microservices, data access, cloud integration, security -3. **Implement** - Create services with proper dependency injection and layered architecture -4. **Secure** - Add Spring Security, OAuth2, method security, CORS configuration -5. **Test** - Write unit, integration, and slice tests with high coverage -6. **Deploy** - Configure for cloud deployment with health checks and observability - -## Reference Guide - -Load detailed guidance based on context: - -| Topic | Reference | Load When | -|-------|-----------|-----------| -| Web Layer | `references/web.md` | Controllers, REST APIs, validation, exception handling | -| Data Access | `references/data.md` | Spring Data JPA, repositories, transactions, projections | -| Security | `references/security.md` | Spring Security 6, OAuth2, JWT, method security | -| Cloud Native | `references/cloud.md` | Spring Cloud, Config, Discovery, Gateway, resilience | -| Testing | `references/testing.md` | @SpringBootTest, MockMvc, Testcontainers, test slices | - -## Constraints - -### MUST DO -- Use Spring Boot 3.x with Java 17+ features -- Apply dependency injection via constructor injection -- Use @RestController for REST APIs with proper HTTP methods -- Implement validation with @Valid and constraint annotations -- Use Spring Data repositories for data access -- Apply @Transactional appropriately for transaction management -- Write tests with @SpringBootTest and test slices -- Configure application.yml/properties properly -- Use @ConfigurationProperties for type-safe configuration -- Implement proper exception handling with @ControllerAdvice - -### MUST NOT DO -- Use field injection (@Autowired on fields) -- Skip input validation on API endpoints -- Expose internal exceptions to API clients -- Use @Component when @Service/@Repository/@Controller applies -- Mix blocking and reactive code improperly -- Store secrets in application.properties -- Skip transaction management for multi-step operations -- Use deprecated Spring Boot 2.x patterns -- Hardcode URLs, credentials, or configuration - -## Output Templates - -When implementing Spring Boot features, provide: -1. Entity/model classes with JPA annotations -2. Repository interfaces extending Spring Data -3. Service layer with business logic -4. Controller with REST endpoints -5. DTO classes for API requests/responses -6. Configuration classes if needed -7. Test classes with appropriate test slices -8. Brief explanation of architecture decisions - -## Knowledge Reference - -Spring Boot 3.x, Spring Framework 6, Spring Data JPA, Spring Security 6, Spring Cloud, Project Reactor (WebFlux), JPA/Hibernate, Bean Validation, RestTemplate/WebClient, Actuator, Micrometer, JUnit 5, Mockito, Testcontainers, Docker, Kubernetes diff --git a/openhis-server-new/.agents/skills/spring-boot-engineer/references/cloud.md b/openhis-server-new/.agents/skills/spring-boot-engineer/references/cloud.md deleted file mode 100644 index 7be0b169..00000000 --- a/openhis-server-new/.agents/skills/spring-boot-engineer/references/cloud.md +++ /dev/null @@ -1,498 +0,0 @@ -# Cloud Native - Spring Cloud - -## Spring Cloud Config Server - -```java -// Config Server -@SpringBootApplication -@EnableConfigServer -public class ConfigServerApplication { - public static void main(String[] args) { - SpringApplication.run(ConfigServerApplication.class, args); - } -} - -// application.yml -server: - port: 8888 - -spring: - cloud: - config: - server: - git: - uri: https://github.com/example/config-repo - default-label: main - search-paths: '{application}' - username: ${GIT_USERNAME} - password: ${GIT_PASSWORD} - native: - search-locations: classpath:/config - security: - user: - name: config-user - password: ${CONFIG_PASSWORD} - -// Config Client -@SpringBootApplication -public class ClientApplication { - public static void main(String[] args) { - SpringApplication.run(ClientApplication.class, args); - } -} - -// application.yml (Config Client) -spring: - application: - name: user-service - config: - import: "configserver:http://localhost:8888" - cloud: - config: - username: config-user - password: ${CONFIG_PASSWORD} - fail-fast: true - retry: - max-attempts: 6 - initial-interval: 1000 -``` - -## Dynamic Configuration Refresh - -```java -@RestController -@RefreshScope -public class ConfigController { - @Value("${app.feature.enabled:false}") - private boolean featureEnabled; - - @Value("${app.max-connections:100}") - private int maxConnections; - - @GetMapping("/config") - public Map getConfig() { - return Map.of( - "featureEnabled", featureEnabled, - "maxConnections", maxConnections - ); - } -} - -// Refresh configuration via Actuator endpoint: -// POST /actuator/refresh -``` - -## Service Discovery - Eureka - -```java -// Eureka Server -@SpringBootApplication -@EnableEurekaServer -public class EurekaServerApplication { - public static void main(String[] args) { - SpringApplication.run(EurekaServerApplication.class, args); - } -} - -// application.yml (Eureka Server) -server: - port: 8761 - -eureka: - instance: - hostname: localhost - client: - register-with-eureka: false - fetch-registry: false - service-url: - defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ - -// Eureka Client -@SpringBootApplication -@EnableDiscoveryClient -public class UserServiceApplication { - public static void main(String[] args) { - SpringApplication.run(UserServiceApplication.class, args); - } -} - -// application.yml (Eureka Client) -spring: - application: - name: user-service - -eureka: - client: - service-url: - defaultZone: http://localhost:8761/eureka/ - registry-fetch-interval-seconds: 5 - instance: - prefer-ip-address: true - lease-renewal-interval-in-seconds: 10 - lease-expiration-duration-in-seconds: 30 -``` - -## Spring Cloud Gateway - -```java -@SpringBootApplication -public class GatewayApplication { - public static void main(String[] args) { - SpringApplication.run(GatewayApplication.class, args); - } - - @Bean - public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { - return builder.routes() - .route("user-service", r -> r - .path("/api/users/**") - .filters(f -> f - .rewritePath("/api/users/(?.*)", "/users/${segment}") - .addRequestHeader("X-Gateway", "Spring-Cloud-Gateway") - .circuitBreaker(config -> config - .setName("userServiceCircuitBreaker") - .setFallbackUri("forward:/fallback/users") - ) - .retry(config -> config - .setRetries(3) - .setStatuses(HttpStatus.SERVICE_UNAVAILABLE) - ) - ) - .uri("lb://user-service") - ) - .route("order-service", r -> r - .path("/api/orders/**") - .filters(f -> f - .rewritePath("/api/orders/(?.*)", "/orders/${segment}") - .requestRateLimiter(config -> config - .setRateLimiter(redisRateLimiter()) - .setKeyResolver(userKeyResolver()) - ) - ) - .uri("lb://order-service") - ) - .build(); - } - - @Bean - public RedisRateLimiter redisRateLimiter() { - return new RedisRateLimiter(10, 20); // replenishRate, burstCapacity - } - - @Bean - public KeyResolver userKeyResolver() { - return exchange -> Mono.just( - exchange.getRequest().getHeaders().getFirst("X-User-Id") - ); - } -} - -// application.yml (Gateway) -spring: - cloud: - gateway: - discovery: - locator: - enabled: true - lower-case-service-id: true - default-filters: - - DedupeResponseHeader=Access-Control-Allow-Origin - globalcors: - cors-configurations: - '[/**]': - allowed-origins: "*" - allowed-methods: - - GET - - POST - - PUT - - DELETE - allowed-headers: "*" -``` - -## Circuit Breaker - Resilience4j - -```java -@Service -@RequiredArgsConstructor -public class ExternalApiService { - private final WebClient webClient; - - @CircuitBreaker(name = "externalApi", fallbackMethod = "getFallbackData") - @Retry(name = "externalApi") - @RateLimiter(name = "externalApi") - public Mono getData(String id) { - return webClient - .get() - .uri("/data/{id}", id) - .retrieve() - .bodyToMono(ExternalData.class) - .timeout(Duration.ofSeconds(3)); - } - - private Mono getFallbackData(String id, Exception e) { - log.warn("Fallback triggered for id: {}, error: {}", id, e.getMessage()); - return Mono.just(new ExternalData(id, "Fallback data", LocalDateTime.now())); - } -} - -// application.yml -resilience4j: - circuitbreaker: - instances: - externalApi: - register-health-indicator: true - sliding-window-size: 10 - minimum-number-of-calls: 5 - permitted-number-of-calls-in-half-open-state: 3 - automatic-transition-from-open-to-half-open-enabled: true - wait-duration-in-open-state: 5s - failure-rate-threshold: 50 - event-consumer-buffer-size: 10 - - retry: - instances: - externalApi: - max-attempts: 3 - wait-duration: 1s - enable-exponential-backoff: true - exponential-backoff-multiplier: 2 - - ratelimiter: - instances: - externalApi: - limit-for-period: 10 - limit-refresh-period: 1s - timeout-duration: 0s -``` - -## Distributed Tracing - Micrometer Tracing - -```java -// application.yml -management: - tracing: - sampling: - probability: 1.0 - zipkin: - tracing: - endpoint: http://localhost:9411/api/v2/spans - -logging: - pattern: - level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]" - -// Custom spans -@Service -@RequiredArgsConstructor -public class OrderService { - private final Tracer tracer; - private final OrderRepository orderRepository; - - public Order processOrder(OrderRequest request) { - Span span = tracer.nextSpan().name("processOrder").start(); - try (Tracer.SpanInScope ws = tracer.withSpan(span)) { - span.tag("order.type", request.type()); - span.tag("order.items", String.valueOf(request.items().size())); - - // Business logic - Order order = createOrder(request); - - span.event("order.created"); - return order; - } finally { - span.end(); - } - } -} -``` - -## Load Balancing with Spring Cloud LoadBalancer - -```java -@Configuration -@LoadBalancerClient(name = "user-service", configuration = UserServiceLoadBalancerConfig.class) -public class LoadBalancerConfiguration { -} - -@Configuration -public class UserServiceLoadBalancerConfig { - - @Bean - public ReactorLoadBalancer randomLoadBalancer( - LoadBalancerClientFactory clientFactory, - ObjectProvider properties) { - return new RandomLoadBalancer( - clientFactory.getLazyProvider("user-service", ServiceInstanceListSupplier.class), - "user-service" - ); - } -} - -@Service -@RequiredArgsConstructor -public class UserClientService { - private final WebClient.Builder webClientBuilder; - - public Mono getUser(Long id) { - return webClientBuilder - .baseUrl("http://user-service") - .build() - .get() - .uri("/users/{id}", id) - .retrieve() - .bodyToMono(User.class); - } -} -``` - -## Health Checks & Actuator - -```java -@Component -public class CustomHealthIndicator implements HealthIndicator { - - @Override - public Health health() { - boolean serviceUp = checkExternalService(); - - if (serviceUp) { - return Health.up() - .withDetail("externalService", "Available") - .withDetail("timestamp", LocalDateTime.now()) - .build(); - } else { - return Health.down() - .withDetail("externalService", "Unavailable") - .withDetail("error", "Connection timeout") - .build(); - } - } - - private boolean checkExternalService() { - // Check external dependency - return true; - } -} - -// application.yml -management: - endpoints: - web: - exposure: - include: health,info,metrics,prometheus - endpoint: - health: - show-details: always - probes: - enabled: true - health: - livenessState: - enabled: true - readinessState: - enabled: true - metrics: - export: - prometheus: - enabled: true - tags: - application: ${spring.application.name} -``` - -## Kubernetes Deployment - -```yaml -# deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: user-service -spec: - replicas: 3 - selector: - matchLabels: - app: user-service - template: - metadata: - labels: - app: user-service - spec: - containers: - - name: user-service - image: user-service:1.0.0 - ports: - - containerPort: 8080 - env: - - name: SPRING_PROFILES_ACTIVE - value: "kubernetes" - - name: JAVA_OPTS - value: "-Xmx512m -Xms256m" - livenessProbe: - httpGet: - path: /actuator/health/liveness - port: 8080 - initialDelaySeconds: 60 - periodSeconds: 10 - readinessProbe: - httpGet: - path: /actuator/health/readiness - port: 8080 - initialDelaySeconds: 30 - periodSeconds: 5 - resources: - requests: - memory: "512Mi" - cpu: "500m" - limits: - memory: "1Gi" - cpu: "1000m" ---- -apiVersion: v1 -kind: Service -metadata: - name: user-service -spec: - selector: - app: user-service - ports: - - port: 80 - targetPort: 8080 - type: ClusterIP -``` - -## Docker Configuration - -```dockerfile -# Dockerfile (Multi-stage) -FROM eclipse-temurin:17-jdk-alpine AS build -WORKDIR /workspace/app - -COPY mvnw . -COPY .mvn .mvn -COPY pom.xml . -COPY src src - -RUN ./mvnw install -DskipTests -RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar) - -FROM eclipse-temurin:17-jre-alpine -VOLUME /tmp -ARG DEPENDENCY=/workspace/app/target/dependency -COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib -COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF -COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app - -ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.Application"] -``` - -## Quick Reference - -| Component | Purpose | -|-----------|---------| -| **Config Server** | Centralized configuration management | -| **Eureka** | Service discovery and registration | -| **Gateway** | API gateway with routing, filtering, load balancing | -| **Circuit Breaker** | Fault tolerance and fallback patterns | -| **Load Balancer** | Client-side load balancing | -| **Tracing** | Distributed tracing across services | -| **Actuator** | Production-ready monitoring and management | -| **Kubernetes** | Container orchestration and deployment | diff --git a/openhis-server-new/.agents/skills/spring-boot-engineer/references/data.md b/openhis-server-new/.agents/skills/spring-boot-engineer/references/data.md deleted file mode 100644 index f7643b22..00000000 --- a/openhis-server-new/.agents/skills/spring-boot-engineer/references/data.md +++ /dev/null @@ -1,381 +0,0 @@ -# Data Access - Spring Data JPA - -## JPA Entity Pattern - -```java -@Entity -@Table(name = "users", indexes = { - @Index(name = "idx_email", columnList = "email", unique = true), - @Index(name = "idx_username", columnList = "username") -}) -@EntityListeners(AuditingEntityListener.class) -@Getter @Setter -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class User { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(nullable = false, unique = true, length = 100) - private String email; - - @Column(nullable = false, length = 100) - private String password; - - @Column(nullable = false, unique = true, length = 50) - private String username; - - @Column(nullable = false) - @Builder.Default - private Boolean active = true; - - @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) - @Builder.Default - private List
addresses = new ArrayList<>(); - - @ManyToMany - @JoinTable( - name = "user_roles", - joinColumns = @JoinColumn(name = "user_id"), - inverseJoinColumns = @JoinColumn(name = "role_id") - ) - @Builder.Default - private Set roles = new HashSet<>(); - - @CreatedDate - @Column(nullable = false, updatable = false) - private LocalDateTime createdAt; - - @LastModifiedDate - @Column(nullable = false) - private LocalDateTime updatedAt; - - @Version - private Long version; - - // Helper methods for bidirectional relationships - public void addAddress(Address address) { - addresses.add(address); - address.setUser(this); - } - - public void removeAddress(Address address) { - addresses.remove(address); - address.setUser(null); - } -} -``` - -## Spring Data JPA Repository - -```java -@Repository -public interface UserRepository extends JpaRepository, - JpaSpecificationExecutor { - - Optional findByEmail(String email); - - Optional findByUsername(String username); - - boolean existsByEmail(String email); - - boolean existsByUsername(String username); - - @Query("SELECT u FROM User u LEFT JOIN FETCH u.roles WHERE u.email = :email") - Optional findByEmailWithRoles(@Param("email") String email); - - @Query("SELECT u FROM User u WHERE u.active = true AND u.createdAt >= :since") - List findActiveUsersSince(@Param("since") LocalDateTime since); - - @Modifying - @Query("UPDATE User u SET u.active = false WHERE u.lastLoginAt < :threshold") - int deactivateInactiveUsers(@Param("threshold") LocalDateTime threshold); - - // Projection for read-only DTOs - @Query("SELECT new com.example.dto.UserSummary(u.id, u.username, u.email) " + - "FROM User u WHERE u.active = true") - List findAllActiveSummaries(); -} -``` - -## Repository with Specifications - -```java -public class UserSpecifications { - - public static Specification hasEmail(String email) { - return (root, query, cb) -> - email == null ? null : cb.equal(root.get("email"), email); - } - - public static Specification isActive() { - return (root, query, cb) -> cb.isTrue(root.get("active")); - } - - public static Specification createdAfter(LocalDateTime date) { - return (root, query, cb) -> - date == null ? null : cb.greaterThanOrEqualTo(root.get("createdAt"), date); - } - - public static Specification hasRole(String roleName) { - return (root, query, cb) -> { - Join roles = root.join("roles", JoinType.INNER); - return cb.equal(roles.get("name"), roleName); - }; - } -} - -// Usage in service -@Service -@RequiredArgsConstructor -public class UserService { - private final UserRepository userRepository; - - public Page searchUsers(UserSearchCriteria criteria, Pageable pageable) { - Specification spec = Specification - .where(UserSpecifications.hasEmail(criteria.email())) - .and(UserSpecifications.isActive()) - .and(UserSpecifications.createdAfter(criteria.createdAfter())); - - return userRepository.findAll(spec, pageable); - } -} -``` - -## Transaction Management - -```java -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class OrderService { - private final OrderRepository orderRepository; - private final PaymentService paymentService; - private final InventoryService inventoryService; - private final NotificationService notificationService; - - @Transactional - public Order createOrder(OrderCreateRequest request) { - // All operations in single transaction - Order order = Order.builder() - .customerId(request.customerId()) - .status(OrderStatus.PENDING) - .build(); - - request.items().forEach(item -> { - inventoryService.reserveStock(item.productId(), item.quantity()); - order.addItem(item); - }); - - order = orderRepository.save(order); - - try { - paymentService.processPayment(order); - order.setStatus(OrderStatus.PAID); - } catch (PaymentException e) { - order.setStatus(OrderStatus.PAYMENT_FAILED); - throw e; // Transaction will rollback - } - - return orderRepository.save(order); - } - - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void logOrderEvent(Long orderId, String event) { - // Separate transaction - will commit even if parent rolls back - OrderEvent orderEvent = new OrderEvent(orderId, event); - orderEventRepository.save(orderEvent); - } - - @Transactional(noRollbackFor = NotificationException.class) - public void completeOrder(Long orderId) { - Order order = orderRepository.findById(orderId) - .orElseThrow(() -> new ResourceNotFoundException("Order not found")); - - order.setStatus(OrderStatus.COMPLETED); - orderRepository.save(order); - - // Won't rollback transaction if notification fails - try { - notificationService.sendCompletionEmail(order); - } catch (NotificationException e) { - log.error("Failed to send notification for order {}", orderId, e); - } - } -} -``` - -## Auditing Configuration - -```java -@Configuration -@EnableJpaAuditing -public class JpaAuditingConfig { - - @Bean - public AuditorAware auditorProvider() { - return () -> { - Authentication authentication = SecurityContextHolder - .getContext() - .getAuthentication(); - - if (authentication == null || !authentication.isAuthenticated()) { - return Optional.of("system"); - } - - return Optional.of(authentication.getName()); - }; - } -} - -@MappedSuperclass -@EntityListeners(AuditingEntityListener.class) -@Getter @Setter -public abstract class AuditableEntity { - - @CreatedDate - @Column(nullable = false, updatable = false) - private LocalDateTime createdAt; - - @CreatedBy - @Column(nullable = false, updatable = false, length = 100) - private String createdBy; - - @LastModifiedDate - @Column(nullable = false) - private LocalDateTime updatedAt; - - @LastModifiedBy - @Column(nullable = false, length = 100) - private String updatedBy; -} -``` - -## Projections - -```java -// Interface-based projection -public interface UserSummary { - Long getId(); - String getUsername(); - String getEmail(); - - @Value("#{target.firstName + ' ' + target.lastName}") - String getFullName(); -} - -// Class-based projection (DTO) -public record UserSummaryDto( - Long id, - String username, - String email -) {} - -// Usage -public interface UserRepository extends JpaRepository { - List findAllBy(); - - List findAllBy(Class type); -} - -// Service usage -List summaries = userRepository.findAllBy(); -List dtos = userRepository.findAllBy(UserSummaryDto.class); -``` - -## Query Optimization - -```java -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class UserQueryService { - private final UserRepository userRepository; - private final EntityManager entityManager; - - // N+1 problem solved with JOIN FETCH - @Query("SELECT DISTINCT u FROM User u " + - "LEFT JOIN FETCH u.addresses " + - "LEFT JOIN FETCH u.roles " + - "WHERE u.active = true") - List findAllActiveWithAssociations(); - - // Batch fetching - @BatchSize(size = 25) - @OneToMany(mappedBy = "user") - private List orders; - - // EntityGraph for dynamic fetching - @EntityGraph(attributePaths = {"addresses", "roles"}) - List findAllByActiveTrue(); - - // Pagination to avoid loading all data - public Page findAllUsers(Pageable pageable) { - return userRepository.findAll(pageable); - } - - // Native query for complex queries - @Query(value = """ - SELECT u.* FROM users u - INNER JOIN orders o ON u.id = o.user_id - WHERE o.created_at >= :since - GROUP BY u.id - HAVING COUNT(o.id) >= :minOrders - """, nativeQuery = true) - List findFrequentBuyers(@Param("since") LocalDateTime since, - @Param("minOrders") int minOrders); -} -``` - -## Database Migrations (Flyway) - -```sql --- V1__create_users_table.sql -CREATE TABLE users ( - id BIGSERIAL PRIMARY KEY, - email VARCHAR(100) NOT NULL UNIQUE, - password VARCHAR(100) NOT NULL, - username VARCHAR(50) NOT NULL UNIQUE, - active BOOLEAN NOT NULL DEFAULT true, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - version BIGINT NOT NULL DEFAULT 0 -); - -CREATE INDEX idx_users_email ON users(email); -CREATE INDEX idx_users_username ON users(username); -CREATE INDEX idx_users_active ON users(active); - --- V2__create_addresses_table.sql -CREATE TABLE addresses ( - id BIGSERIAL PRIMARY KEY, - user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - street VARCHAR(200) NOT NULL, - city VARCHAR(100) NOT NULL, - country VARCHAR(2) NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE INDEX idx_addresses_user_id ON addresses(user_id); -``` - -## Quick Reference - -| Annotation | Purpose | -|------------|---------| -| `@Entity` | Marks class as JPA entity | -| `@Table` | Specifies table details and indexes | -| `@Id` | Marks primary key field | -| `@GeneratedValue` | Auto-generated primary key strategy | -| `@Column` | Column constraints and mapping | -| `@OneToMany/@ManyToOne` | One-to-many/many-to-one relationships | -| `@ManyToMany` | Many-to-many relationships | -| `@JoinColumn/@JoinTable` | Join column/table configuration | -| `@Transactional` | Declares transaction boundaries | -| `@Query` | Custom JPQL/native queries | -| `@Modifying` | Marks query as UPDATE/DELETE | -| `@EntityGraph` | Defines fetch graph for associations | -| `@Version` | Optimistic locking version field | diff --git a/openhis-server-new/.agents/skills/spring-boot-engineer/references/security.md b/openhis-server-new/.agents/skills/spring-boot-engineer/references/security.md deleted file mode 100644 index 9a4bfa32..00000000 --- a/openhis-server-new/.agents/skills/spring-boot-engineer/references/security.md +++ /dev/null @@ -1,459 +0,0 @@ -# Security - Spring Security 6 - -## Security Configuration - -```java -@Configuration -@EnableWebSecurity -@EnableMethodSecurity -public class SecurityConfig { - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .csrf(csrf -> csrf - .ignoringRequestMatchers("/api/auth/**") - .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) - ) - .cors(cors -> cors.configurationSource(corsConfigurationSource())) - .authorizeHttpRequests(auth -> auth - .requestMatchers("/api/auth/**", "/actuator/health").permitAll() - .requestMatchers("/api/admin/**").hasRole("ADMIN") - .requestMatchers("/api/users/**").hasAnyRole("USER", "ADMIN") - .anyRequest().authenticated() - ) - .sessionManagement(session -> session - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - ) - .exceptionHandling(ex -> ex - .authenticationEntryPoint(authenticationEntryPoint()) - .accessDeniedHandler(accessDeniedHandler()) - ) - .addFilterBefore(jwtAuthenticationFilter(), - UsernamePasswordAuthenticationFilter.class); - - return http.build(); - } - - @Bean - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(List.of("http://localhost:3000")); - configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); - configuration.setAllowedHeaders(List.of("*")); - configuration.setAllowCredentials(true); - configuration.setMaxAge(3600L); - - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - return source; - } - - @Bean - public AuthenticationManager authenticationManager( - AuthenticationConfiguration config) throws Exception { - return config.getAuthenticationManager(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(12); - } -} -``` - -## JWT Authentication Filter - -```java -@Component -@RequiredArgsConstructor -public class JwtAuthenticationFilter extends OncePerRequestFilter { - private final JwtService jwtService; - private final UserDetailsService userDetailsService; - - @Override - protected void doFilterInternal( - @NonNull HttpServletRequest request, - @NonNull HttpServletRequest response, - @NonNull FilterChain filterChain) throws ServletException, IOException { - - final String authHeader = request.getHeader("Authorization"); - final String jwt; - final String username; - - if (authHeader == null || !authHeader.startsWith("Bearer ")) { - filterChain.doFilter(request, response); - return; - } - - jwt = authHeader.substring(7); - - try { - username = jwtService.extractUsername(jwt); - - if (username != null && SecurityContextHolder.getContext() - .getAuthentication() == null) { - UserDetails userDetails = userDetailsService.loadUserByUsername(username); - - if (jwtService.isTokenValid(jwt, userDetails)) { - UsernamePasswordAuthenticationToken authToken = - new UsernamePasswordAuthenticationToken( - userDetails, - null, - userDetails.getAuthorities() - ); - - authToken.setDetails( - new WebAuthenticationDetailsSource().buildDetails(request) - ); - - SecurityContextHolder.getContext().setAuthentication(authToken); - } - } - } catch (JwtException e) { - log.error("JWT validation failed", e); - } - - filterChain.doFilter(request, response); - } -} -``` - -## JWT Service - -```java -@Service -public class JwtService { - @Value("${jwt.secret}") - private String secretKey; - - @Value("${jwt.expiration}") - private long jwtExpiration; - - @Value("${jwt.refresh-expiration}") - private long refreshExpiration; - - public String extractUsername(String token) { - return extractClaim(token, Claims::getSubject); - } - - public T extractClaim(String token, Function claimsResolver) { - final Claims claims = extractAllClaims(token); - return claimsResolver.apply(claims); - } - - public String generateToken(UserDetails userDetails) { - Map extraClaims = new HashMap<>(); - extraClaims.put("roles", userDetails.getAuthorities().stream() - .map(GrantedAuthority::getAuthority) - .collect(Collectors.toList())); - - return generateToken(extraClaims, userDetails); - } - - public String generateToken( - Map extraClaims, - UserDetails userDetails) { - return buildToken(extraClaims, userDetails, jwtExpiration); - } - - public String generateRefreshToken(UserDetails userDetails) { - return buildToken(new HashMap<>(), userDetails, refreshExpiration); - } - - private String buildToken( - Map extraClaims, - UserDetails userDetails, - long expiration) { - return Jwts - .builder() - .setClaims(extraClaims) - .setSubject(userDetails.getUsername()) - .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + expiration)) - .signWith(getSignInKey(), SignatureAlgorithm.HS256) - .compact(); - } - - public boolean isTokenValid(String token, UserDetails userDetails) { - final String username = extractUsername(token); - return username.equals(userDetails.getUsername()) && !isTokenExpired(token); - } - - private boolean isTokenExpired(String token) { - return extractExpiration(token).before(new Date()); - } - - private Date extractExpiration(String token) { - return extractClaim(token, Claims::getExpiration); - } - - private Claims extractAllClaims(String token) { - return Jwts - .parserBuilder() - .setSigningKey(getSignInKey()) - .build() - .parseClaimsJws(token) - .getBody(); - } - - private Key getSignInKey() { - byte[] keyBytes = Decoders.BASE64.decode(secretKey); - return Keys.hmacShaKeyFor(keyBytes); - } -} -``` - -## UserDetailsService Implementation - -```java -@Service -@RequiredArgsConstructor -public class CustomUserDetailsService implements UserDetailsService { - private final UserRepository userRepository; - - @Override - @Transactional(readOnly = true) - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - User user = userRepository.findByEmailWithRoles(username) - .orElseThrow(() -> new UsernameNotFoundException( - "User not found with email: " + username)); - - return org.springframework.security.core.userdetails.User - .builder() - .username(user.getEmail()) - .password(user.getPassword()) - .authorities(user.getRoles().stream() - .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName())) - .collect(Collectors.toList())) - .accountExpired(false) - .accountLocked(!user.getActive()) - .credentialsExpired(false) - .disabled(!user.getActive()) - .build(); - } -} -``` - -## Authentication Controller - -```java -@RestController -@RequestMapping("/api/auth") -@RequiredArgsConstructor -public class AuthenticationController { - private final AuthenticationService authenticationService; - - @PostMapping("/register") - public ResponseEntity register( - @Valid @RequestBody RegisterRequest request) { - AuthenticationResponse response = authenticationService.register(request); - return ResponseEntity.status(HttpStatus.CREATED).body(response); - } - - @PostMapping("/login") - public ResponseEntity login( - @Valid @RequestBody LoginRequest request) { - AuthenticationResponse response = authenticationService.login(request); - return ResponseEntity.ok(response); - } - - @PostMapping("/refresh") - public ResponseEntity refreshToken( - @RequestBody RefreshTokenRequest request) { - AuthenticationResponse response = authenticationService.refreshToken(request); - return ResponseEntity.ok(response); - } - - @PostMapping("/logout") - @PreAuthorize("isAuthenticated()") - public ResponseEntity logout() { - SecurityContextHolder.clearContext(); - return ResponseEntity.noContent().build(); - } -} -``` - -## Authentication Service - -```java -@Service -@RequiredArgsConstructor -@Transactional -public class AuthenticationService { - private final UserRepository userRepository; - private final PasswordEncoder passwordEncoder; - private final JwtService jwtService; - private final AuthenticationManager authenticationManager; - - public AuthenticationResponse register(RegisterRequest request) { - if (userRepository.existsByEmail(request.email())) { - throw new DuplicateResourceException("Email already registered"); - } - - User user = User.builder() - .email(request.email()) - .password(passwordEncoder.encode(request.password())) - .username(request.username()) - .active(true) - .roles(Set.of(Role.builder().name("USER").build())) - .build(); - - user = userRepository.save(user); - - String accessToken = jwtService.generateToken(convertToUserDetails(user)); - String refreshToken = jwtService.generateRefreshToken(convertToUserDetails(user)); - - return new AuthenticationResponse(accessToken, refreshToken); - } - - public AuthenticationResponse login(LoginRequest request) { - authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken( - request.email(), - request.password() - ) - ); - - User user = userRepository.findByEmail(request.email()) - .orElseThrow(() -> new UsernameNotFoundException("User not found")); - - String accessToken = jwtService.generateToken(convertToUserDetails(user)); - String refreshToken = jwtService.generateRefreshToken(convertToUserDetails(user)); - - return new AuthenticationResponse(accessToken, refreshToken); - } - - public AuthenticationResponse refreshToken(RefreshTokenRequest request) { - String username = jwtService.extractUsername(request.refreshToken()); - - User user = userRepository.findByEmail(username) - .orElseThrow(() -> new UsernameNotFoundException("User not found")); - - UserDetails userDetails = convertToUserDetails(user); - - if (!jwtService.isTokenValid(request.refreshToken(), userDetails)) { - throw new InvalidTokenException("Invalid refresh token"); - } - - String accessToken = jwtService.generateToken(userDetails); - - return new AuthenticationResponse(accessToken, request.refreshToken()); - } - - private UserDetails convertToUserDetails(User user) { - return org.springframework.security.core.userdetails.User - .builder() - .username(user.getEmail()) - .password(user.getPassword()) - .authorities(user.getRoles().stream() - .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName())) - .collect(Collectors.toList())) - .build(); - } -} -``` - -## Method Security - -```java -@Service -@RequiredArgsConstructor -public class UserService { - private final UserRepository userRepository; - - @PreAuthorize("hasRole('ADMIN')") - public List getAllUsers() { - return userRepository.findAll(); - } - - @PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id") - public User getUserById(Long userId) { - return userRepository.findById(userId) - .orElseThrow(() -> new ResourceNotFoundException("User not found")); - } - - @PreAuthorize("isAuthenticated()") - @PostAuthorize("returnObject.email == authentication.principal.username") - public User updateProfile(Long userId, UserUpdateRequest request) { - User user = getUserById(userId); - // Update logic - return userRepository.save(user); - } - - @Secured({"ROLE_ADMIN", "ROLE_MANAGER"}) - public void deleteUser(Long userId) { - userRepository.deleteById(userId); - } -} -``` - -## OAuth2 Resource Server (JWT) - -```java -@Configuration -@EnableWebSecurity -public class OAuth2ResourceServerConfig { - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests(auth -> auth - .requestMatchers("/public/**").permitAll() - .anyRequest().authenticated() - ) - .oauth2ResourceServer(oauth2 -> oauth2 - .jwt(jwt -> jwt - .jwtAuthenticationConverter(jwtAuthenticationConverter()) - ) - ); - - return http.build(); - } - - @Bean - public JwtDecoder jwtDecoder() { - return JwtDecoders.fromIssuerLocation("https://auth.example.com"); - } - - @Bean - public JwtAuthenticationConverter jwtAuthenticationConverter() { - JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = - new JwtGrantedAuthoritiesConverter(); - grantedAuthoritiesConverter.setAuthoritiesClaimName("roles"); - grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); - - JwtAuthenticationConverter jwtAuthenticationConverter = - new JwtAuthenticationConverter(); - jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter( - grantedAuthoritiesConverter); - - return jwtAuthenticationConverter; - } -} -``` - -## Quick Reference - -| Annotation | Purpose | -|------------|---------| -| `@EnableWebSecurity` | Enables Spring Security | -| `@EnableMethodSecurity` | Enables method-level security annotations | -| `@PreAuthorize` | Checks authorization before method execution | -| `@PostAuthorize` | Checks authorization after method execution | -| `@Secured` | Role-based method security | -| `@WithMockUser` | Mock authenticated user in tests | -| `@AuthenticationPrincipal` | Inject current user in controller | - -## Security Best Practices - -- Always use HTTPS in production -- Store JWT secret in environment variables -- Use strong password encoding (BCrypt with strength 12+) -- Implement token refresh mechanism -- Add rate limiting to authentication endpoints -- Validate all user inputs -- Log security events -- Keep dependencies updated -- Use CSRF protection for state-changing operations -- Implement proper session timeout diff --git a/openhis-server-new/.agents/skills/spring-boot-engineer/references/testing.md b/openhis-server-new/.agents/skills/spring-boot-engineer/references/testing.md deleted file mode 100644 index 9bfec1de..00000000 --- a/openhis-server-new/.agents/skills/spring-boot-engineer/references/testing.md +++ /dev/null @@ -1,545 +0,0 @@ -# Testing - Spring Boot Test - -## Unit Testing with JUnit 5 - -```java -@ExtendWith(MockitoExtension.class) -class UserServiceTest { - - @Mock - private UserRepository userRepository; - - @Mock - private PasswordEncoder passwordEncoder; - - @InjectMocks - private UserService userService; - - @Test - @DisplayName("Should create user successfully") - void shouldCreateUser() { - // Given - UserCreateRequest request = new UserCreateRequest( - "test@example.com", - "Password123", - "testuser", - 25 - ); - - User user = User.builder() - .id(1L) - .email(request.email()) - .username(request.username()) - .build(); - - when(userRepository.existsByEmail(request.email())).thenReturn(false); - when(passwordEncoder.encode(request.password())).thenReturn("encodedPassword"); - when(userRepository.save(any(User.class))).thenReturn(user); - - // When - UserResponse response = userService.create(request); - - // Then - assertThat(response).isNotNull(); - assertThat(response.email()).isEqualTo(request.email()); - - verify(userRepository).existsByEmail(request.email()); - verify(passwordEncoder).encode(request.password()); - verify(userRepository).save(any(User.class)); - } - - @Test - @DisplayName("Should throw exception when email already exists") - void shouldThrowExceptionWhenEmailExists() { - // Given - UserCreateRequest request = new UserCreateRequest( - "test@example.com", - "Password123", - "testuser", - 25 - ); - - when(userRepository.existsByEmail(request.email())).thenReturn(true); - - // When & Then - assertThatThrownBy(() -> userService.create(request)) - .isInstanceOf(DuplicateResourceException.class) - .hasMessageContaining("Email already registered"); - - verify(userRepository, never()).save(any(User.class)); - } -} -``` - -## Integration Testing with @SpringBootTest - -```java -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@ActiveProfiles("test") -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -class UserIntegrationTest { - - @Autowired - private TestRestTemplate restTemplate; - - @Autowired - private UserRepository userRepository; - - @BeforeEach - void setUp() { - userRepository.deleteAll(); - } - - @Test - @Order(1) - @DisplayName("Should create user via API") - void shouldCreateUserViaApi() { - // Given - UserCreateRequest request = new UserCreateRequest( - "test@example.com", - "Password123", - "testuser", - 25 - ); - - // When - ResponseEntity response = restTemplate.postForEntity( - "/api/v1/users", - request, - UserResponse.class - ); - - // Then - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED); - assertThat(response.getBody()).isNotNull(); - assertThat(response.getBody().email()).isEqualTo(request.email()); - assertThat(response.getHeaders().getLocation()).isNotNull(); - } - - @Test - @Order(2) - @DisplayName("Should return validation error for invalid request") - void shouldReturnValidationError() { - // Given - UserCreateRequest request = new UserCreateRequest( - "invalid-email", - "short", - "u", - 15 - ); - - // When - ResponseEntity response = restTemplate.postForEntity( - "/api/v1/users", - request, - ValidationErrorResponse.class - ); - - // Then - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); - assertThat(response.getBody()).isNotNull(); - assertThat(response.getBody().errors()).isNotEmpty(); - } -} -``` - -## Web Layer Testing with MockMvc - -```java -@WebMvcTest(UserController.class) -@Import(SecurityConfig.class) -class UserControllerTest { - - @Autowired - private MockMvc mockMvc; - - @MockBean - private UserService userService; - - @Autowired - private ObjectMapper objectMapper; - - @Test - @WithMockUser(roles = "ADMIN") - @DisplayName("Should get all users") - void shouldGetAllUsers() throws Exception { - // Given - Page users = new PageImpl<>(List.of( - new UserResponse(1L, "user1@example.com", "user1", 25, true, null, null), - new UserResponse(2L, "user2@example.com", "user2", 30, true, null, null) - )); - - when(userService.findAll(any(Pageable.class))).thenReturn(users); - - // When & Then - mockMvc.perform(get("/api/v1/users") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.content").isArray()) - .andExpect(jsonPath("$.content.length()").value(2)) - .andExpect(jsonPath("$.content[0].email").value("user1@example.com")) - .andDo(print()); - } - - @Test - @WithMockUser(roles = "ADMIN") - @DisplayName("Should create user") - void shouldCreateUser() throws Exception { - // Given - UserCreateRequest request = new UserCreateRequest( - "test@example.com", - "Password123", - "testuser", - 25 - ); - - UserResponse response = new UserResponse( - 1L, - request.email(), - request.username(), - request.age(), - true, - LocalDateTime.now(), - LocalDateTime.now() - ); - - when(userService.create(any(UserCreateRequest.class))).thenReturn(response); - - // When & Then - mockMvc.perform(post("/api/v1/users") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isCreated()) - .andExpect(header().exists("Location")) - .andExpect(jsonPath("$.email").value(request.email())) - .andExpect(jsonPath("$.username").value(request.username())) - .andDo(print()); - } - - @Test - @WithMockUser(roles = "USER") - @DisplayName("Should return 403 for non-admin user") - void shouldReturn403ForNonAdmin() throws Exception { - mockMvc.perform(get("/api/v1/users") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isForbidden()); - } -} -``` - -## Data JPA Testing - -```java -@DataJpaTest -@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) -@ActiveProfiles("test") -class UserRepositoryTest { - - @Autowired - private UserRepository userRepository; - - @Autowired - private TestEntityManager entityManager; - - @Test - @DisplayName("Should find user by email") - void shouldFindUserByEmail() { - // Given - User user = User.builder() - .email("test@example.com") - .password("password") - .username("testuser") - .active(true) - .build(); - - entityManager.persistAndFlush(user); - - // When - Optional found = userRepository.findByEmail("test@example.com"); - - // Then - assertThat(found).isPresent(); - assertThat(found.get().getEmail()).isEqualTo("test@example.com"); - } - - @Test - @DisplayName("Should check if email exists") - void shouldCheckIfEmailExists() { - // Given - User user = User.builder() - .email("test@example.com") - .password("password") - .username("testuser") - .active(true) - .build(); - - entityManager.persistAndFlush(user); - - // When - boolean exists = userRepository.existsByEmail("test@example.com"); - - // Then - assertThat(exists).isTrue(); - } - - @Test - @DisplayName("Should fetch user with roles") - void shouldFetchUserWithRoles() { - // Given - Role adminRole = Role.builder().name("ADMIN").build(); - entityManager.persist(adminRole); - - User user = User.builder() - .email("admin@example.com") - .password("password") - .username("admin") - .active(true) - .roles(Set.of(adminRole)) - .build(); - - entityManager.persistAndFlush(user); - entityManager.clear(); - - // When - Optional found = userRepository.findByEmailWithRoles("admin@example.com"); - - // Then - assertThat(found).isPresent(); - assertThat(found.get().getRoles()).hasSize(1); - assertThat(found.get().getRoles()).extracting(Role::getName).contains("ADMIN"); - } -} -``` - -## Testcontainers for Database - -```java -@SpringBootTest -@Testcontainers -@ActiveProfiles("test") -class UserServiceIntegrationTest { - - @Container - static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15-alpine") - .withDatabaseName("testdb") - .withUsername("test") - .withPassword("test"); - - @DynamicPropertySource - static void configureProperties(DynamicPropertyRegistry registry) { - registry.add("spring.datasource.url", postgres::getJdbcUrl); - registry.add("spring.datasource.username", postgres::getUsername); - registry.add("spring.datasource.password", postgres::getPassword); - } - - @Autowired - private UserService userService; - - @Autowired - private UserRepository userRepository; - - @BeforeEach - void setUp() { - userRepository.deleteAll(); - } - - @Test - @DisplayName("Should create and find user in real database") - void shouldCreateAndFindUser() { - // Given - UserCreateRequest request = new UserCreateRequest( - "test@example.com", - "Password123", - "testuser", - 25 - ); - - // When - UserResponse created = userService.create(request); - UserResponse found = userService.findById(created.id()); - - // Then - assertThat(found).isNotNull(); - assertThat(found.email()).isEqualTo(request.email()); - } -} -``` - -## Testing Reactive Endpoints with WebTestClient - -```java -@WebFluxTest(UserReactiveController.class) -class UserReactiveControllerTest { - - @Autowired - private WebTestClient webTestClient; - - @MockBean - private UserReactiveService userService; - - @Test - @DisplayName("Should get user reactively") - void shouldGetUserReactively() { - // Given - UserResponse user = new UserResponse( - 1L, - "test@example.com", - "testuser", - 25, - true, - LocalDateTime.now(), - LocalDateTime.now() - ); - - when(userService.findById(1L)).thenReturn(Mono.just(user)); - - // When & Then - webTestClient.get() - .uri("/api/v1/users/{id}", 1L) - .accept(MediaType.APPLICATION_JSON) - .exchange() - .expectStatus().isOk() - .expectBody(UserResponse.class) - .value(response -> { - assertThat(response.id()).isEqualTo(1L); - assertThat(response.email()).isEqualTo("test@example.com"); - }); - } - - @Test - @DisplayName("Should create user reactively") - void shouldCreateUserReactively() { - // Given - UserCreateRequest request = new UserCreateRequest( - "test@example.com", - "Password123", - "testuser", - 25 - ); - - UserResponse response = new UserResponse( - 1L, - request.email(), - request.username(), - request.age(), - true, - LocalDateTime.now(), - LocalDateTime.now() - ); - - when(userService.create(any(UserCreateRequest.class))).thenReturn(Mono.just(response)); - - // When & Then - webTestClient.post() - .uri("/api/v1/users") - .contentType(MediaType.APPLICATION_JSON) - .body(Mono.just(request), UserCreateRequest.class) - .exchange() - .expectStatus().isCreated() - .expectHeader().exists("Location") - .expectBody(UserResponse.class) - .value(user -> { - assertThat(user.email()).isEqualTo(request.email()); - }); - } -} -``` - -## Testing Configuration - -```java -// application-test.yml -spring: - datasource: - url: jdbc:h2:mem:testdb - driver-class-name: org.h2.Driver - jpa: - hibernate: - ddl-auto: create-drop - show-sql: true - properties: - hibernate: - format_sql: true - security: - user: - name: test - password: test - -logging: - level: - org.hibernate.SQL: DEBUG - org.hibernate.type.descriptor.sql.BasicBinder: TRACE - -// Test Configuration Class -@TestConfiguration -public class TestConfig { - - @Bean - @Primary - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(4); // Faster for tests - } - - @Bean - public Clock fixedClock() { - return Clock.fixed( - Instant.parse("2024-01-01T00:00:00Z"), - ZoneId.of("UTC") - ); - } -} -``` - -## Test Fixtures with @DataJpaTest - -```java -@Component -public class TestDataFactory { - - public static User createUser(String email, String username) { - return User.builder() - .email(email) - .password("encodedPassword") - .username(username) - .active(true) - .createdAt(LocalDateTime.now()) - .updatedAt(LocalDateTime.now()) - .build(); - } - - public static UserCreateRequest createUserRequest() { - return new UserCreateRequest( - "test@example.com", - "Password123", - "testuser", - 25 - ); - } -} -``` - -## Quick Reference - -| Annotation | Purpose | -|------------|---------| -| `@SpringBootTest` | Full application context integration test | -| `@WebMvcTest` | Test MVC controllers with mocked services | -| `@WebFluxTest` | Test reactive controllers | -| `@DataJpaTest` | Test JPA repositories with in-memory database | -| `@MockBean` | Add mock bean to Spring context | -| `@WithMockUser` | Mock authenticated user for security tests | -| `@Testcontainers` | Enable Testcontainers support | -| `@ActiveProfiles` | Activate specific Spring profiles for test | - -## Testing Best Practices - -- Write tests following AAA pattern (Arrange, Act, Assert) -- Use descriptive test names with @DisplayName -- Mock external dependencies, use real DB with Testcontainers -- Achieve 85%+ code coverage -- Test happy path and edge cases -- Use @Transactional for test data cleanup -- Separate unit tests from integration tests -- Use parameterized tests for multiple scenarios -- Test security rules and validation -- Keep tests fast and independent diff --git a/openhis-server-new/.agents/skills/spring-boot-engineer/references/web.md b/openhis-server-new/.agents/skills/spring-boot-engineer/references/web.md deleted file mode 100644 index 2eabbbb1..00000000 --- a/openhis-server-new/.agents/skills/spring-boot-engineer/references/web.md +++ /dev/null @@ -1,295 +0,0 @@ -# Web Layer - Controllers & REST APIs - -## REST Controller Pattern - -```java -@RestController -@RequestMapping("/api/v1/users") -@Validated -@RequiredArgsConstructor -public class UserController { - private final UserService userService; - - @GetMapping - public ResponseEntity> getUsers( - @PageableDefault(size = 20, sort = "createdAt") Pageable pageable) { - Page users = userService.findAll(pageable); - return ResponseEntity.ok(users); - } - - @GetMapping("/{id}") - public ResponseEntity getUser(@PathVariable Long id) { - UserResponse user = userService.findById(id); - return ResponseEntity.ok(user); - } - - @PostMapping - public ResponseEntity createUser( - @Valid @RequestBody UserCreateRequest request) { - UserResponse user = userService.create(request); - URI location = ServletUriComponentsBuilder - .fromCurrentRequest() - .path("/{id}") - .buildAndExpand(user.id()) - .toUri(); - return ResponseEntity.created(location).body(user); - } - - @PutMapping("/{id}") - public ResponseEntity updateUser( - @PathVariable Long id, - @Valid @RequestBody UserUpdateRequest request) { - UserResponse user = userService.update(id, request); - return ResponseEntity.ok(user); - } - - @DeleteMapping("/{id}") - @ResponseStatus(HttpStatus.NO_CONTENT) - public void deleteUser(@PathVariable Long id) { - userService.delete(id); - } -} -``` - -## Request DTOs with Validation - -```java -public record UserCreateRequest( - @NotBlank(message = "Email is required") - @Email(message = "Email must be valid") - String email, - - @NotBlank(message = "Password is required") - @Size(min = 8, max = 100, message = "Password must be 8-100 characters") - @Pattern(regexp = "^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).*$", - message = "Password must contain uppercase, lowercase, and digit") - String password, - - @NotBlank(message = "Username is required") - @Size(min = 3, max = 50) - @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "Username must be alphanumeric") - String username, - - @Min(value = 18, message = "Must be at least 18") - @Max(value = 120, message = "Must be at most 120") - Integer age -) {} - -public record UserUpdateRequest( - @Email(message = "Email must be valid") - String email, - - @Size(min = 3, max = 50) - String username -) {} -``` - -## Response DTOs - -```java -public record UserResponse( - Long id, - String email, - String username, - Integer age, - Boolean active, - LocalDateTime createdAt, - LocalDateTime updatedAt -) { - public static UserResponse from(User user) { - return new UserResponse( - user.getId(), - user.getEmail(), - user.getUsername(), - user.getAge(), - user.getActive(), - user.getCreatedAt(), - user.getUpdatedAt() - ); - } -} -``` - -## Global Exception Handling - -```java -@RestControllerAdvice -@Slf4j -public class GlobalExceptionHandler { - - @ExceptionHandler(ResourceNotFoundException.class) - public ResponseEntity handleNotFound( - ResourceNotFoundException ex, WebRequest request) { - log.error("Resource not found: {}", ex.getMessage()); - ErrorResponse error = new ErrorResponse( - HttpStatus.NOT_FOUND.value(), - ex.getMessage(), - request.getDescription(false), - LocalDateTime.now() - ); - return new ResponseEntity<>(error, HttpStatus.NOT_FOUND); - } - - @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handleValidation( - MethodArgumentNotValidException ex) { - Map errors = ex.getBindingResult() - .getFieldErrors() - .stream() - .collect(Collectors.toMap( - FieldError::getField, - error -> error.getDefaultMessage() != null - ? error.getDefaultMessage() - : "Invalid value" - )); - - ValidationErrorResponse response = new ValidationErrorResponse( - HttpStatus.BAD_REQUEST.value(), - "Validation failed", - errors, - LocalDateTime.now() - ); - return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); - } - - @ExceptionHandler(DataIntegrityViolationException.class) - public ResponseEntity handleDataIntegrity( - DataIntegrityViolationException ex, WebRequest request) { - log.error("Data integrity violation", ex); - ErrorResponse error = new ErrorResponse( - HttpStatus.CONFLICT.value(), - "Data integrity violation - resource may already exist", - request.getDescription(false), - LocalDateTime.now() - ); - return new ResponseEntity<>(error, HttpStatus.CONFLICT); - } - - @ExceptionHandler(Exception.class) - public ResponseEntity handleGlobalException( - Exception ex, WebRequest request) { - log.error("Unexpected error", ex); - ErrorResponse error = new ErrorResponse( - HttpStatus.INTERNAL_SERVER_ERROR.value(), - "An unexpected error occurred", - request.getDescription(false), - LocalDateTime.now() - ); - return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR); - } -} - -record ErrorResponse( - int status, - String message, - String path, - LocalDateTime timestamp -) {} - -record ValidationErrorResponse( - int status, - String message, - Map errors, - LocalDateTime timestamp -) {} -``` - -## Custom Validation - -```java -@Target({ElementType.FIELD, ElementType.PARAMETER}) -@Retention(RetentionPolicy.RUNTIME) -@Constraint(validatedBy = UniqueEmailValidator.class) -public @interface UniqueEmail { - String message() default "Email already exists"; - Class[] groups() default {}; - Class[] payload() default {}; -} - -@Component -@RequiredArgsConstructor -public class UniqueEmailValidator implements ConstraintValidator { - private final UserRepository userRepository; - - @Override - public boolean isValid(String email, ConstraintValidatorContext context) { - if (email == null) return true; - return !userRepository.existsByEmail(email); - } -} -``` - -## WebClient for External APIs - -```java -@Configuration -public class WebClientConfig { - @Bean - public WebClient webClient(WebClient.Builder builder) { - return builder - .baseUrl("https://api.example.com") - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .filter(logRequest()) - .build(); - } - - private ExchangeFilterFunction logRequest() { - return ExchangeFilterFunction.ofRequestProcessor(request -> { - log.info("Request: {} {}", request.method(), request.url()); - return Mono.just(request); - }); - } -} - -@Service -@RequiredArgsConstructor -public class ExternalApiService { - private final WebClient webClient; - - public Mono fetchData(String id) { - return webClient - .get() - .uri("/data/{id}", id) - .retrieve() - .onStatus(HttpStatusCode::is4xxClientError, response -> - Mono.error(new ResourceNotFoundException("External resource not found"))) - .onStatus(HttpStatusCode::is5xxServerError, response -> - Mono.error(new ServiceUnavailableException("External service unavailable"))) - .bodyToMono(ExternalDataResponse.class) - .timeout(Duration.ofSeconds(5)) - .retry(3); - } -} -``` - -## CORS Configuration - -```java -@Configuration -public class WebConfig implements WebMvcConfigurer { - - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/api/**") - .allowedOrigins("http://localhost:3000", "https://example.com") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("*") - .allowCredentials(true) - .maxAge(3600); - } -} -``` - -## Quick Reference - -| Annotation | Purpose | -|------------|---------| -| `@RestController` | Marks class as REST controller (combines @Controller + @ResponseBody) | -| `@RequestMapping` | Maps HTTP requests to handler methods | -| `@GetMapping/@PostMapping` | HTTP method-specific mappings | -| `@PathVariable` | Extracts values from URI path | -| `@RequestParam` | Extracts query parameters | -| `@RequestBody` | Binds request body to method parameter | -| `@Valid` | Triggers validation on request body | -| `@RestControllerAdvice` | Global exception handling for REST controllers | -| `@ResponseStatus` | Sets HTTP status code for method | diff --git a/openhis-server-new/add_missing_fields_to_consultation_request.sql b/openhis-server-new/add_missing_fields_to_consultation_request.sql deleted file mode 100644 index bd0a9c95..00000000 --- a/openhis-server-new/add_missing_fields_to_consultation_request.sql +++ /dev/null @@ -1,63 +0,0 @@ --- 为现有的consultation_request表添加缺失的字段 -DO $$ -BEGIN - -- 检查并添加缺失的字段 - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'confirming_physician_name') THEN - ALTER TABLE consultation_request ADD COLUMN confirming_physician_name VARCHAR(100); - COMMENT ON COLUMN consultation_request.confirming_physician_name IS '确认会诊的医生姓名'; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'confirming_department_name') THEN - ALTER TABLE consultation_request ADD COLUMN confirming_department_name VARCHAR(100); - COMMENT ON COLUMN consultation_request.confirming_department_name IS '代表科室'; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'confirming_physician_participation') THEN - ALTER TABLE consultation_request ADD COLUMN confirming_physician_participation VARCHAR(100); - COMMENT ON COLUMN consultation_request.confirming_physician_participation IS '会诊确认参加医师'; - END IF; - - -- 检查并更新consultation_status字段的注释 - DROP COMMENT ON COLUMN consultation_request.consultation_status; - COMMENT ON COLUMN consultation_request.consultation_status IS '会诊状态:0-新开,10-已提交,20-已确认,30-已签名,40-已完成,50-已取消'; - - -- 检查并更新consultation_urgency字段的默认值 - ALTER TABLE consultation_request ALTER COLUMN consultation_urgency SET DEFAULT '一般'; - - -- 检查并更新字段注释 - COMMENT ON COLUMN consultation_request.consultation_id IS '会诊申请单号:CS+年月日时分秒+4位随机数'; - COMMENT ON COLUMN consultation_request.consultation_request_date IS '会诊申请时间'; - COMMENT ON COLUMN consultation_request.consultation_date IS '会诊时间'; - COMMENT ON COLUMN consultation_request.consultation_purpose IS '简要病史及会诊目的'; - COMMENT ON COLUMN consultation_request.consultation_opinion IS '会诊意见'; - COMMENT ON COLUMN consultation_request.consultation_status IS '会诊状态:0-新开,10-已提交,20-已确认,30-已签名,40-已完成,50-已取消'; - COMMENT ON COLUMN consultation_request.consultation_urgency IS '是否紧急:一般/紧急'; - COMMENT ON COLUMN consultation_request.confirming_physician IS '提交会诊的医生姓名'; - COMMENT ON COLUMN consultation_request.confirming_physician_id IS '提交会诊的医生ID'; - COMMENT ON COLUMN consultation_request.confirming_date IS '提交会诊日期'; - COMMENT ON COLUMN consultation_request.signature IS '结束会诊医生姓名/签名医生'; - COMMENT ON COLUMN consultation_request.signature_physician_id IS '结束会诊医生ID'; - COMMENT ON COLUMN consultation_request.signature_date IS '结束会诊日期/签名时间'; - COMMENT ON COLUMN consultation_request.cancel_nature_date IS '作废会诊日期'; - COMMENT ON COLUMN consultation_request.cancel_reason IS '作废原因'; - COMMENT ON COLUMN consultation_request.consultation_activity_id IS '会诊项目ID,关联wor_activity_definition表,用于确定会诊类型和价格'; - COMMENT ON COLUMN consultation_request.consultation_activity_name IS '会诊项目名称,如:院内会诊、远程会诊等'; - -END $$; - --- 为现有表添加必要的索引 -DO $$ -BEGIN - -- 检查并创建索引 - IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE tablename = 'consultation_request' AND indexname = 'idx_consultation_request_status_date') THEN - CREATE INDEX idx_consultation_request_status_date ON consultation_request (consultation_status, consultation_request_date); - END IF; - - IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE tablename = 'consultation_request' AND indexname = 'idx_consultation_request_physician') THEN - CREATE INDEX idx_consultation_request_physician ON consultation_request (requesting_physician_id); - END IF; - - IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE tablename = 'consultation_request' AND indexname = 'idx_consultation_request_invited_object') THEN - CREATE INDEX idx_consultation_request_invited_object ON consultation_request (invited_object); - END IF; -END $$; \ No newline at end of file diff --git a/openhis-server-new/create_consultation_tables_postgresql.sql b/openhis-server-new/create_consultation_tables_postgresql.sql deleted file mode 100644 index ab7cece4..00000000 --- a/openhis-server-new/create_consultation_tables_postgresql.sql +++ /dev/null @@ -1,109 +0,0 @@ --- 会诊申请表 -CREATE TABLE IF NOT EXISTS consultation_request ( - id BIGSERIAL PRIMARY KEY, - patient_id VARCHAR(50) NOT NULL, -- 患者ID - patient_name VARCHAR(100), -- 患者姓名 - gender VARCHAR(10), -- 患者性别 - age INTEGER, -- 患者年龄 - visit_id BIGINT, -- 门诊就诊流水号 - order_id BIGINT, -- 门诊医嘱表主键 - department_id BIGINT, -- 申请科室ID - department_name VARCHAR(100), -- 申请科室名称 - attending_doctor_id BIGINT, -- 主管医生ID - attending_doctor_name VARCHAR(100), -- 主管医生姓名 - consultation_purpose TEXT, -- 会诊目的 - provisional_diagnosis TEXT, -- 门诊诊断 - current_condition TEXT, -- 目前病情 - consultation_department_ids TEXT, -- 申请会诊科室ID列表,逗号分隔 - consultation_doctor_ids TEXT, -- 申请会诊医生ID列表,逗号分隔 - consultation_type INTEGER DEFAULT 1, -- 会诊类型:1-普通会诊,2-紧急会诊,3-多学科会诊 - priority_level INTEGER DEFAULT 1, -- 优先级:1-低,2-中,3-高 - consultation_status INTEGER DEFAULT 0, -- 会诊状态:0-新开,10-已提交,20-已确认,30-已签名,40-已完成,50-已取消 - consultation_opinion TEXT, -- 会诊意见 - confirming_physician VARCHAR(100), -- 提交会诊的医生 - confirming_physician_id BIGINT, -- 提交会诊的医生ID - confirming_date TIMESTAMP, -- 提交会诊时间 - consultation_urgency VARCHAR(20) DEFAULT '一般', -- 是否紧急 - application_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 申请日期 - consultation_request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 会诊申请时间 - confirmed_date TIMESTAMP, -- 确认日期 - scheduled_date TIMESTAMP, -- 计划会诊日期 - actual_date TIMESTAMP, -- 实际会诊日期 - location VARCHAR(200), -- 会诊地点 - confirming_physician_name VARCHAR(100), -- 确认会诊的医生姓名 - signature VARCHAR(100), -- 签名医生 - signature_date TIMESTAMP, -- 签名时间 - confirming_department_name VARCHAR(100), -- 代表科室 - confirming_physician_participation VARCHAR(100), -- 会诊确认参加医师 - creator_id BIGINT, -- 创建人ID - creator_name VARCHAR(100), -- 创建人姓名 - create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间 - updater_id BIGINT, -- 更新人ID - updater_name VARCHAR(100), -- 更新人姓名 - update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 更新时间 - valid_flag INTEGER DEFAULT 1, -- 有效标志:1-有效,0-无效 - tenant_id INTEGER DEFAULT 1 -- 租户ID -); - --- 会诊确认表 -CREATE TABLE IF NOT EXISTS consultation_confirmation ( - id BIGSERIAL PRIMARY KEY, - consultation_request_id BIGINT NOT NULL, -- 会诊申请ID - confirming_department_id BIGINT NOT NULL, -- 确认科室ID - confirming_department_name VARCHAR(100) NOT NULL, -- 确认科室名称 - confirming_doctor_id BIGINT NOT NULL, -- 确认医生ID - confirming_doctor_name VARCHAR(100) NOT NULL, -- 确认医生姓名 - confirmation_status INTEGER DEFAULT 1, -- 确认状态:1-待确认,2-同意,3-拒绝 - confirmation_reason TEXT, -- 确认/拒绝理由 - confirmation_date TIMESTAMP, -- 确认日期 - consultation_opinion TEXT, -- 会诊意见 - confirming_physician_participation VARCHAR(100), -- 会诊确认参加医师 - confirming_physician_name VARCHAR(100), -- 所属医生 - confirming_department_name_field VARCHAR(100), -- 代表科室 - signature VARCHAR(100), -- 签名医生 - signature_date TIMESTAMP, -- 签名时间 - creator_id BIGINT NOT NULL, -- 创建人ID - creator_name VARCHAR(100) NOT NULL, -- 创建人姓名 - create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间 - updater_id BIGINT, -- 更新人ID - updater_name VARCHAR(100), -- 更新人姓名 - update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 更新时间 - valid_flag INTEGER DEFAULT 1, -- 有效标志:1-有效,0-无效 - tenant_id INTEGER DEFAULT 1, -- 租户ID - FOREIGN KEY (consultation_request_id) REFERENCES consultation_request(id) -); - --- 会诊记录表 -CREATE TABLE IF NOT EXISTS consultation_record ( - id BIGSERIAL PRIMARY KEY, - consultation_request_id BIGINT NOT NULL, -- 会诊申请ID - participant_doctor_id BIGINT NOT NULL, -- 参与医生ID - participant_doctor_name VARCHAR(100) NOT NULL, -- 参与医生姓名 - participant_department_id BIGINT NOT NULL, -- 参与科室ID - participant_department_name VARCHAR(100) NOT NULL, -- 参与科室名称 - opinion TEXT, -- 会诊意见 - suggestion TEXT, -- 会诊建议 - record_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 记录日期 - creator_id BIGINT NOT NULL, -- 创建人ID - creator_name VARCHAR(100) NOT NULL, -- 创建人姓名 - create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间 - updater_id BIGINT, -- 更新人ID - updater_name VARCHAR(100), -- 更新人姓名 - update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 更新时间 - valid_flag INTEGER DEFAULT 1, -- 有效标志:1-有效,0-无效 - tenant_id INTEGER DEFAULT 1, -- 租户ID - FOREIGN KEY (consultation_request_id) REFERENCES consultation_request(id) -); - --- 创建索引 -CREATE INDEX IF NOT EXISTS idx_consultation_request_patient_id ON consultation_request(patient_id); -CREATE INDEX IF NOT EXISTS idx_consultation_request_dept_id ON consultation_request(department_id); -CREATE INDEX IF NOT EXISTS idx_consultation_request_status ON consultation_request(consultation_status); -CREATE INDEX IF NOT EXISTS idx_consultation_request_date ON consultation_request(application_date); - -CREATE INDEX IF NOT EXISTS idx_consultation_confirmation_req_id ON consultation_confirmation(consultation_request_id); -CREATE INDEX IF NOT EXISTS idx_consultation_confirmation_doctor_id ON consultation_confirmation(confirming_doctor_id); -CREATE INDEX IF NOT EXISTS idx_consultation_confirmation_status ON consultation_confirmation(confirmation_status); - -CREATE INDEX IF NOT EXISTS idx_consultation_record_req_id ON consultation_record(consultation_request_id); -CREATE INDEX IF NOT EXISTS idx_consultation_record_doctor_id ON consultation_record(participant_doctor_id); \ No newline at end of file diff --git a/openhis-server-new/create_consultation_tables_postgresql_final.sql b/openhis-server-new/create_consultation_tables_postgresql_final.sql deleted file mode 100644 index d833221f..00000000 --- a/openhis-server-new/create_consultation_tables_postgresql_final.sql +++ /dev/null @@ -1,135 +0,0 @@ --- 会诊申请表 -CREATE TABLE IF NOT EXISTS consultation_request ( - id BIGSERIAL PRIMARY KEY, - patient_id VARCHAR(50) NOT NULL, -- 患者ID - patient_name VARCHAR(100), -- 患者姓名 - gender VARCHAR(10), -- 患者性别 - age INTEGER, -- 患者年龄 - visit_id BIGINT, -- 门诊就诊流水号 - order_id BIGINT, -- 门诊医嘱表主键 - department_id BIGINT, -- 申请科室ID - department_name VARCHAR(100), -- 申请科室名称 - attending_doctor_id BIGINT, -- 主管医生ID - attending_doctor_name VARCHAR(100), -- 主管医生姓名 - consultation_purpose TEXT, -- 会诊目的 - provisional_diagnosis TEXT, -- 门诊诊断 - current_condition TEXT, -- 目前病情 - consultation_department_ids TEXT, -- 申请会诊科室ID列表,逗号分隔 - consultation_doctor_ids TEXT, -- 申请会诊医生ID列表,逗号分隔 - consultation_type INTEGER DEFAULT 1, -- 会诊类型:1-普通会诊,2-紧急会诊,3-多学科会诊 - priority_level INTEGER DEFAULT 1, -- 优先级:1-低,2-中,3-高 - consultation_status INTEGER DEFAULT 0, -- 会诊状态:0-新开,10-已提交,20-已确认,30-已签名,40-已完成,50-已取消 - consultation_opinion TEXT, -- 会诊意见 - confirming_physician VARCHAR(100), -- 提交会诊的医生 - confirming_physician_id BIGINT, -- 提交会诊的医生ID - confirming_date TIMESTAMP, -- 提交会诊时间 - consultation_urgency VARCHAR(20) DEFAULT '一般', -- 是否紧急 - application_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 申请日期 - consultation_request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 会诊申请时间 - confirmed_date TIMESTAMP, -- 确认日期 - scheduled_date TIMESTAMP, -- 计划会诊日期 - actual_date TIMESTAMP, -- 实际会诊日期 - location VARCHAR(200), -- 会诊地点 - confirming_physician_name VARCHAR(100), -- 确认会诊的医生姓名 - signature VARCHAR(100), -- 签名医生 - signature_date TIMESTAMP, -- 签名时间 - confirming_department_name VARCHAR(100), -- 代表科室 - confirming_physician_participation VARCHAR(100), -- 会诊确认参加医师 - creator_id BIGINT, -- 创建人ID - creator_name VARCHAR(100), -- 创建人姓名 - create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间 - updater_id BIGINT, -- 更新人ID - updater_name VARCHAR(100), -- 更新人姓名 - update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 更新时间 - valid_flag INTEGER DEFAULT 1, -- 有效标志:1-有效,0-无效 - tenant_id INTEGER DEFAULT 1 -- 租户ID -); - --- 会诊确认表 -CREATE TABLE IF NOT EXISTS consultation_confirmation ( - id BIGSERIAL PRIMARY KEY, - consultation_request_id BIGINT, -- 会诊申请ID - confirming_department_id BIGINT NOT NULL, -- 确认科室ID - confirming_department_name VARCHAR(100) NOT NULL, -- 确认科室名称 - confirming_doctor_id BIGINT NOT NULL, -- 确认医生ID - confirming_doctor_name VARCHAR(100) NOT NULL, -- 确认医生姓名 - confirmation_status INTEGER DEFAULT 1, -- 确认状态:1-待确认,2-同意,3-拒绝 - confirmation_reason TEXT, -- 确认/拒绝理由 - confirmation_date TIMESTAMP, -- 确认日期 - consultation_opinion TEXT, -- 会诊意见 - confirming_physician_participation VARCHAR(100), -- 会诊确认参加医师 - confirming_physician_name VARCHAR(100), -- 所属医生 - confirming_department_name_field VARCHAR(100), -- 代表科室 - signature VARCHAR(100), -- 签名医生 - signature_date TIMESTAMP, -- 签名时间 - creator_id BIGINT NOT NULL, -- 创建人ID - creator_name VARCHAR(100) NOT NULL, -- 创建人姓名 - create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间 - updater_id BIGINT, -- 更新人ID - updater_name VARCHAR(100), -- 更新人姓名 - update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 更新时间 - valid_flag INTEGER DEFAULT 1, -- 有效标志:1-有效,0-无效 - tenant_id INTEGER DEFAULT 1 -- 租户ID -); - --- 会诊记录表 -CREATE TABLE IF NOT EXISTS consultation_record ( - id BIGSERIAL PRIMARY KEY, - consultation_request_id BIGINT, -- 会诊申请ID - participant_doctor_id BIGINT NOT NULL, -- 参与医生ID - participant_doctor_name VARCHAR(100) NOT NULL, -- 参与医生姓名 - participant_department_id BIGINT NOT NULL, -- 参与科室ID - participant_department_name VARCHAR(100) NOT NULL, -- 参与科室名称 - opinion TEXT, -- 会诊意见 - suggestion TEXT, -- 会诊建议 - record_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 记录日期 - creator_id BIGINT NOT NULL, -- 创建人ID - creator_name VARCHAR(100) NOT NULL, -- 创建人姓名 - create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间 - updater_id BIGINT, -- 更新人ID - updater_name VARCHAR(100), -- 更新人姓名 - update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 更新时间 - valid_flag INTEGER DEFAULT 1, -- 有效标志:1-有效,0-无效 - tenant_id INTEGER DEFAULT 1 -- 租户ID -); - --- 创建索引 -CREATE INDEX IF NOT EXISTS idx_consultation_request_patient_id ON consultation_request(patient_id); -CREATE INDEX IF NOT EXISTS idx_consultation_request_dept_id ON consultation_request(department_id); -CREATE INDEX IF NOT EXISTS idx_consultation_request_status ON consultation_request(consultation_status); -CREATE INDEX IF NOT EXISTS idx_consultation_request_date ON consultation_request(application_date); - -CREATE INDEX IF NOT EXISTS idx_consultation_confirmation_req_id ON consultation_confirmation(consultation_request_id); -CREATE INDEX IF NOT EXISTS idx_consultation_confirmation_doctor_id ON consultation_confirmation(confirming_doctor_id); -CREATE INDEX IF NOT EXISTS idx_consultation_confirmation_status ON consultation_confirmation(confirmation_status); - -CREATE INDEX IF NOT EXISTS idx_consultation_record_req_id ON consultation_record(consultation_request_id); -CREATE INDEX IF NOT EXISTS idx_consultation_record_doctor_id ON consultation_record(participant_doctor_id); - --- 添加外键约束 -DO $$ -BEGIN - -- 为consultation_confirmation表添加外键约束 - IF NOT EXISTS ( - SELECT 1 - FROM information_schema.table_constraints - WHERE constraint_name = 'fk_consultation_confirmation_request' - AND table_name = 'consultation_confirmation' - ) THEN - ALTER TABLE consultation_confirmation - ADD CONSTRAINT fk_consultation_confirmation_request - FOREIGN KEY (consultation_request_id) REFERENCES consultation_request(id); - END IF; - - -- 为consultation_record表添加外键约束 - IF NOT EXISTS ( - SELECT 1 - FROM information_schema.table_constraints - WHERE constraint_name = 'fk_consultation_record_request' - AND table_name = 'consultation_record' - ) THEN - ALTER TABLE consultation_record - ADD CONSTRAINT fk_consultation_record_request - FOREIGN KEY (consultation_request_id) REFERENCES consultation_request(id); - END IF; -END $$; \ No newline at end of file diff --git a/openhis-server-new/create_consultation_tables_postgresql_fixed.sql b/openhis-server-new/create_consultation_tables_postgresql_fixed.sql deleted file mode 100644 index 9ef288df..00000000 --- a/openhis-server-new/create_consultation_tables_postgresql_fixed.sql +++ /dev/null @@ -1,141 +0,0 @@ --- 会诊申请表 -CREATE TABLE IF NOT EXISTS consultation_request ( - id BIGSERIAL PRIMARY KEY, - patient_id VARCHAR(50) NOT NULL, -- 患者ID - patient_name VARCHAR(100), -- 患者姓名 - gender VARCHAR(10), -- 患者性别 - age INTEGER, -- 患者年龄 - visit_id BIGINT, -- 门诊就诊流水号 - order_id BIGINT, -- 门诊医嘱表主键 - department_id BIGINT, -- 申请科室ID - department_name VARCHAR(100), -- 申请科室名称 - attending_doctor_id BIGINT, -- 主管医生ID - attending_doctor_name VARCHAR(100), -- 主管医生姓名 - consultation_purpose TEXT, -- 会诊目的 - provisional_diagnosis TEXT, -- 门诊诊断 - current_condition TEXT, -- 目前病情 - consultation_department_ids TEXT, -- 申请会诊科室ID列表,逗号分隔 - consultation_doctor_ids TEXT, -- 申请会诊医生ID列表,逗号分隔 - consultation_type INTEGER DEFAULT 1, -- 会诊类型:1-普通会诊,2-紧急会诊,3-多学科会诊 - priority_level INTEGER DEFAULT 1, -- 优先级:1-低,2-中,3-高 - consultation_status INTEGER DEFAULT 0, -- 会诊状态:0-新开,10-已提交,20-已确认,30-已签名,40-已完成,50-已取消 - consultation_opinion TEXT, -- 会诊意见 - confirming_physician VARCHAR(100), -- 提交会诊的医生 - confirming_physician_id BIGINT, -- 提交会诊的医生ID - confirming_date TIMESTAMP, -- 提交会诊时间 - consultation_urgency VARCHAR(20) DEFAULT '一般', -- 是否紧急 - application_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 申请日期 - consultation_request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 会诊申请时间 - confirmed_date TIMESTAMP, -- 确认日期 - scheduled_date TIMESTAMP, -- 计划会诊日期 - actual_date TIMESTAMP, -- 实际会诊日期 - location VARCHAR(200), -- 会诊地点 - confirming_physician_name VARCHAR(100), -- 确认会诊的医生姓名 - signature VARCHAR(100), -- 签名医生 - signature_date TIMESTAMP, -- 签名时间 - confirming_department_name VARCHAR(100), -- 代表科室 - confirming_physician_participation VARCHAR(100), -- 会诊确认参加医师 - creator_id BIGINT, -- 创建人ID - creator_name VARCHAR(100), -- 创建人姓名 - create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间 - updater_id BIGINT, -- 更新人ID - updater_name VARCHAR(100), -- 更新人姓名 - update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 更新时间 - valid_flag INTEGER DEFAULT 1, -- 有效标志:1-有效,0-无效 - tenant_id INTEGER DEFAULT 1 -- 租户ID -); - --- 会诊确认表 -CREATE TABLE IF NOT EXISTS consultation_confirmation ( - id BIGSERIAL PRIMARY KEY, - consultation_request_id BIGINT NOT NULL, -- 会诊申请ID - confirming_department_id BIGINT NOT NULL, -- 确认科室ID - confirming_department_name VARCHAR(100) NOT NULL, -- 确认科室名称 - confirming_doctor_id BIGINT NOT NULL, -- 确认医生ID - confirming_doctor_name VARCHAR(100) NOT NULL, -- 确认医生姓名 - confirmation_status INTEGER DEFAULT 1, -- 确认状态:1-待确认,2-同意,3-拒绝 - confirmation_reason TEXT, -- 确认/拒绝理由 - confirmation_date TIMESTAMP, -- 确认日期 - consultation_opinion TEXT, -- 会诊意见 - confirming_physician_participation VARCHAR(100), -- 会诊确认参加医师 - confirming_physician_name VARCHAR(100), -- 所属医生 - confirming_department_name_field VARCHAR(100), -- 代表科室 - signature VARCHAR(100), -- 签名医生 - signature_date TIMESTAMP, -- 签名时间 - creator_id BIGINT NOT NULL, -- 创建人ID - creator_name VARCHAR(100) NOT NULL, -- 创建人姓名 - create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间 - updater_id BIGINT, -- 更新人ID - updater_name VARCHAR(100), -- 更新人姓名 - update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 更新时间 - valid_flag INTEGER DEFAULT 1, -- 有效标志:1-有效,0-无效 - tenant_id INTEGER DEFAULT 1, -- 租户ID - -- 注意:外键约束将在所有表创建后再添加 - CONSTRAINT fk_consultation_confirmation_request - FOREIGN KEY (consultation_request_id) REFERENCES consultation_request(id) -); - --- 会诊记录表 -CREATE TABLE IF NOT EXISTS consultation_record ( - id BIGSERIAL PRIMARY KEY, - consultation_request_id BIGINT NOT NULL, -- 会诊申请ID - participant_doctor_id BIGINT NOT NULL, -- 参与医生ID - participant_doctor_name VARCHAR(100) NOT NULL, -- 参与医生姓名 - participant_department_id BIGINT NOT NULL, -- 参与科室ID - participant_department_name VARCHAR(100) NOT NULL, -- 参与科室名称 - opinion TEXT, -- 会诊意见 - suggestion TEXT, -- 会诊建议 - record_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 记录日期 - creator_id BIGINT NOT NULL, -- 创建人ID - creator_name VARCHAR(100) NOT NULL, -- 创建人姓名 - create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间 - updater_id BIGINT, -- 更新人ID - updater_name VARCHAR(100), -- 更新人姓名 - update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 更新时间 - valid_flag INTEGER DEFAULT 1, -- 有效标志:1-有效,0-无效 - tenant_id INTEGER DEFAULT 1, -- 租户ID - -- 注意:外键约束将在所有表创建后再添加 - CONSTRAINT fk_consultation_record_request - FOREIGN KEY (consultation_request_id) REFERENCES consultation_request(id) -); - --- 创建索引 -CREATE INDEX IF NOT EXISTS idx_consultation_request_patient_id ON consultation_request(patient_id); -CREATE INDEX IF NOT EXISTS idx_consultation_request_dept_id ON consultation_request(department_id); -CREATE INDEX IF NOT EXISTS idx_consultation_request_status ON consultation_request(consultation_status); -CREATE INDEX IF NOT EXISTS idx_consultation_request_date ON consultation_request(application_date); - -CREATE INDEX IF NOT EXISTS idx_consultation_confirmation_req_id ON consultation_confirmation(consultation_request_id); -CREATE INDEX IF NOT EXISTS idx_consultation_confirmation_doctor_id ON consultation_confirmation(confirming_doctor_id); -CREATE INDEX IF NOT EXISTS idx_consultation_confirmation_status ON consultation_confirmation(confirmation_status); - -CREATE INDEX IF NOT EXISTS idx_consultation_record_req_id ON consultation_record(consultation_request_id); -CREATE INDEX IF NOT EXISTS idx_consultation_record_doctor_id ON consultation_record(participant_doctor_id); - --- 添加外键约束(如果尚未添加) -DO $$ -BEGIN - -- 为consultation_confirmation表添加外键约束 - IF NOT EXISTS ( - SELECT 1 - FROM information_schema.table_constraints - WHERE constraint_name = 'fk_consultation_confirmation_request' - AND table_name = 'consultation_confirmation' - ) THEN - ALTER TABLE consultation_confirmation - ADD CONSTRAINT fk_consultation_confirmation_request - FOREIGN KEY (consultation_request_id) REFERENCES consultation_request(id); - END IF; - - -- 为consultation_record表添加外键约束 - IF NOT EXISTS ( - SELECT 1 - FROM information_schema.table_constraints - WHERE constraint_name = 'fk_consultation_record_request' - AND table_name = 'consultation_record' - ) THEN - ALTER TABLE consultation_record - ADD CONSTRAINT fk_consultation_record_request - FOREIGN KEY (consultation_request_id) REFERENCES consultation_request(id); - END IF; -END $$; \ No newline at end of file diff --git a/openhis-server-new/insert_consultation_menu_fixed.sql b/openhis-server-new/insert_consultation_menu_fixed.sql deleted file mode 100644 index 076fe5ef..00000000 --- a/openhis-server-new/insert_consultation_menu_fixed.sql +++ /dev/null @@ -1,70 +0,0 @@ --- 插入会诊管理模块的菜单项 --- 首先插入主菜单项 -INSERT INTO sys_menu -(menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -VALUES -('会诊管理', 0, 10, 'consultationmanagement', '', '', 'ConsultationManagement', 1, 0, 'M', '0', '0', '', 'operation', 'admin', NOW(), 'admin', NOW(), '会诊管理菜单'); - --- 获取刚插入的菜单ID -DO $$ -DECLARE - consultation_menu_id BIGINT; - consultation_request_menu_id BIGINT; - consultation_confirmation_menu_id BIGINT; - consultation_record_menu_id BIGINT; -BEGIN - -- 获取会诊管理菜单ID - SELECT menu_id INTO consultation_menu_id FROM sys_menu WHERE menu_name = '会诊管理' LIMIT 1; - - -- 插入会诊申请子菜单 - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊申请', consultation_menu_id, 1, 'consultationrequest', 'clinicmanagement/consultationrequest/index', '', 'ConsultationRequest', 1, 0, 'C', '0', '0', 'consultation:request:view', 'form', 'admin', NOW(), 'admin', NOW(), '会诊申请菜单'); - - -- 获取会诊申请菜单ID - SELECT menu_id INTO consultation_request_menu_id FROM sys_menu WHERE menu_name = '会诊申请' LIMIT 1; - - -- 插入会诊申请按钮权限 - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊申请查询', consultation_request_menu_id, 1, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:request:query', '#', 'admin', NOW(), 'admin', NOW(), ''), - ('会诊申请新增', consultation_request_menu_id, 2, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:request:add', '#', 'admin', NOW(), 'admin', NOW(), ''), - ('会诊申请修改', consultation_request_menu_id, 3, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:request:edit', '#', 'admin', NOW(), 'admin', NOW(), ''), - ('会诊申请删除', consultation_request_menu_id, 4, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:request:remove', '#', 'admin', NOW(), 'admin', NOW(), ''); - - -- 插入会诊确认子菜单 - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊确认', consultation_menu_id, 2, 'consultationconfirmation', 'clinicmanagement/consultationconfirmation/index', '', 'ConsultationConfirmation', 1, 0, 'C', '0', '0', 'consultation:confirmation:view', 'form', 'admin', NOW(), 'admin', NOW(), '会诊确认菜单'); - - -- 获取会诊确认菜单ID - SELECT menu_id INTO consultation_confirmation_menu_id FROM sys_menu WHERE menu_name = '会诊确认' LIMIT 1; - - -- 插入会诊确认按钮权限 - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊确认查询', consultation_confirmation_menu_id, 1, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:confirmation:query', '#', 'admin', NOW(), 'admin', NOW(), ''), - ('会诊确认新增', consultation_confirmation_menu_id, 2, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:confirmation:add', '#', 'admin', NOW(), 'admin', NOW(), ''), - ('会诊确认修改', consultation_confirmation_menu_id, 3, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:confirmation:edit', '#', 'admin', NOW(), 'admin', NOW(), ''), - ('会诊确认删除', consultation_confirmation_menu_id, 4, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:confirmation:remove', '#', 'admin', NOW(), 'admin', NOW(), ''), - ('会诊确认操作', consultation_confirmation_menu_id, 5, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:confirmation:confirm', '#', 'admin', NOW(), 'admin', NOW(), ''); - - -- 插入会诊记录子菜单 - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊记录', consultation_menu_id, 3, 'consultationrecord', 'clinicmanagement/consultationrecord/index', '', 'ConsultationRecord', 1, 0, 'C', '0', '0', 'consultation:record:view', 'form', 'admin', NOW(), 'admin', NOW(), '会诊记录菜单'); - - -- 获取会诊记录菜单ID - SELECT menu_id INTO consultation_record_menu_id FROM sys_menu WHERE menu_name = '会诊记录' LIMIT 1; - - -- 插入会诊记录按钮权限 - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊记录查询', consultation_record_menu_id, 1, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:record:query', '#', 'admin', NOW(), 'admin', NOW(), ''), - ('会诊记录新增', consultation_record_menu_id, 2, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:record:add', '#', 'admin', NOW(), 'admin', NOW(), ''), - ('会诊记录修改', consultation_record_menu_id, 3, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:record:edit', '#', 'admin', NOW(), 'admin', NOW(), ''), - ('会诊记录删除', consultation_record_menu_id, 4, '', '', '', '', 1, 0, 'F', '0', '0', 'consultation:record:remove', '#', 'admin', NOW(), 'admin', NOW(), ''); -END $$; \ No newline at end of file diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/ClinicRoomAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/ClinicRoomAppServiceImpl.java index a6869d5b..a35199cc 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/ClinicRoomAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/ClinicRoomAppServiceImpl.java @@ -26,11 +26,11 @@ public class ClinicRoomAppServiceImpl implements IClinicRoomAppService { if (roomName != null && ObjectUtil.isNotEmpty(roomName)) { clinicRoom.setRoomName(roomName); } - + // 分页查询 Page page = new Page<>(pageNum, pageSize); Page result = clinicRoomService.selectClinicRoomPage(page, clinicRoom); - + return R.ok(result); } @@ -58,12 +58,7 @@ public class ClinicRoomAppServiceImpl implements IClinicRoomAppService { if (clinicRoom.getRemarks() != null && clinicRoom.getRemarks().length() > 500) { return R.fail(400, "备注长度不能超过500个字符"); } - - // 检查诊室名称在同卫生机构下是否已存在 - if (clinicRoomService.existsByOrgNameAndRoomName(clinicRoom.getOrgName(), clinicRoom.getRoomName())) { - return R.fail(400, "当前卫生机构下已存在该诊室名称"); - } - + // 新增诊室 int result = clinicRoomService.insertClinicRoom(clinicRoom); if (result > 0) { @@ -91,18 +86,13 @@ public class ClinicRoomAppServiceImpl implements IClinicRoomAppService { if (clinicRoom.getRemarks() != null && clinicRoom.getRemarks().length() > 500) { return R.fail(400, "备注长度不能超过500个字符"); } - + // 检查诊室是否存在 ClinicRoom existingClinicRoom = clinicRoomService.selectClinicRoomById(clinicRoom.getId()); if (existingClinicRoom == null) { return R.fail(404, "诊室不存在"); } - - // 检查诊室名称在同卫生机构下是否已存在(排除当前记录) - if (clinicRoomService.existsByOrgNameAndRoomNameExcludeId(clinicRoom.getOrgName(), clinicRoom.getRoomName(), clinicRoom.getId())) { - return R.fail(400, "当前卫生机构下已存在该诊室名称"); - } - + // 更新诊室 int result = clinicRoomService.updateClinicRoom(clinicRoom); if (result > 0) { @@ -119,7 +109,7 @@ public class ClinicRoomAppServiceImpl implements IClinicRoomAppService { if (existingClinicRoom == null) { return R.fail(404, "诊室不存在"); } - + // 删除诊室 int result = clinicRoomService.deleteClinicRoomById(id); if (result > 0) { diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/DoctorScheduleAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/DoctorScheduleAppServiceImpl.java index d8cf9bad..6fce757f 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/DoctorScheduleAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/DoctorScheduleAppServiceImpl.java @@ -87,6 +87,30 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService { return R.ok(list); } + @Transactional + @Override + public R getTodayMySchedule() { + // 获取今天的日期 + LocalDate today = LocalDate.now(); + DayOfWeek dayOfWeek = today.getDayOfWeek(); + + // 将 Java 的 DayOfWeek 转换为字符串表示 + String weekdayStr = convertDayOfWeekToString(dayOfWeek); + + // 获取当前登录医生的ID(需要从SecurityUtils获取) + // 如果没有SecurityUtils,可以从参数传入或使用其他方式获取 + // Long currentDoctorId = SecurityUtils.getLoginUser().getPractitionerId(); + + // 查询当前医生今天的排班 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DoctorSchedule::getWeekday, weekdayStr) // 根据星期几查询 + // .eq(DoctorSchedule::getDoctorId, currentDoctorId) // 只查询当前医生的排班 + .eq(DoctorSchedule::getIsStopped, false); // 只查询未停止的排班 + + List list = doctorScheduleService.list(queryWrapper); + return R.ok(list); + } + /** * 将 DayOfWeek 转换为字符串表示 * @param dayOfWeek DayOfWeek枚举 @@ -113,44 +137,6 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService { } } - @Override - public R getTodayMySchedule() { - try { - // 获取当前登录用户信息 - com.core.common.core.domain.model.LoginUser loginUser = com.core.common.utils.SecurityUtils.getLoginUser(); - Long doctorId = loginUser.getPractitionerId(); - - // 如果没有获取到医生ID,尝试使用用户ID - if (doctorId == null || doctorId == 0) { - doctorId = loginUser.getUserId(); - System.out.println("Using userId as doctorId: " + doctorId); - } else { - System.out.println("Using practitionerId as doctorId: " + doctorId); - } - - // 获取今天的日期 - LocalDate today = LocalDate.now(); - System.out.println("Querying schedule for date: " + today); - - // 查询当前医生今天的排班信息(从SchedulePool表) - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(SchedulePool::getScheduleDate, today) // 根据具体日期查询 - .eq(SchedulePool::getDoctorId, doctorId) // 根据医生ID查询 - .eq(SchedulePool::getStatus, 1); // 只查询可用的排班 - - List list = schedulePoolService.list(queryWrapper); - System.out.println("Found " + list.size() + " schedules for doctorId: " + doctorId); - - // 为了支持多种日程类型,我们可以在这里整合来自不同数据源的信息 - // 目前只返回排班信息,但为以后扩展预留空间 - return R.ok(list); - } catch (Exception e) { - System.err.println("Error getting today's schedule: " + e.getMessage()); - e.printStackTrace(); - return R.fail("获取排班信息失败:" + e.getMessage()); - } - } - @Override public R addDoctorSchedule(DoctorSchedule doctorSchedule) { if (ObjectUtil.isEmpty(doctorSchedule)) { diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/TicketAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/TicketAppServiceImpl.java index 8fc6bd76..7c00ceec 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/TicketAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/TicketAppServiceImpl.java @@ -71,39 +71,10 @@ public class TicketAppServiceImpl implements ITicketAppService { } try { - // 3. 号源列表来自排班表(adm_doctor_schedule),需确保clinical_ticket中存在对应记录 - Ticket existingTicket = ticketService.selectTicketById(ticketId); - if (existingTicket == null) { - DoctorSchedule schedule = doctorScheduleMapper.selectById(slotId); - if (schedule == null) { - return R.fail("排班记录不存在"); - } - Ticket newTicket = new Ticket(); - newTicket.setId(slotId); - newTicket.setDoctor(schedule.getDoctor()); - newTicket.setDepartment(String.valueOf(schedule.getDeptId())); - newTicket.setDoctorId(schedule.getDoctorId()); - newTicket.setDepartmentId(schedule.getDeptId()); - String registerItem = schedule.getRegisterItem(); - if (registerItem != null && registerItem.contains("专家")) { - newTicket.setTicketType("expert"); - } else { - newTicket.setTicketType("general"); - } - newTicket.setStatus("unbooked"); - int totalFee = (schedule.getRegisterFee() != null ? schedule.getRegisterFee() : 0) - + (schedule.getDiagnosisFee() != null ? schedule.getDiagnosisFee() : 0); - newTicket.setFee(String.valueOf(totalFee)); - String timeRange = schedule.getStartTime() + "-" + schedule.getEndTime(); - newTicket.setTime(LocalDate.now() + " " + timeRange); - // 使用MyBatis-Plus的save方法,支持手动设置ID - ticketService.save(newTicket); - } - - // 4. 执行预约逻辑 + // 3. 执行原有的预约逻辑 int result = ticketService.bookTicket(params); if (result > 0) { - // 5. 预约成功后,更新排班表状态 + // 4. 预约成功后,更新排班表状态 DoctorSchedule schedule = new DoctorSchedule(); schedule.setId(slotId); // 对应 XML 中的 WHERE id = #{id} schedule.setIsStopped(true); // 设置为已预约 @@ -115,12 +86,14 @@ public class TicketAppServiceImpl implements ITicketAppService { if (updateCount > 0) { return R.ok("预约成功并已更新排班状态"); } else { + // 如果更新失败,可能需要根据业务逻辑决定是否回滚预约 return R.ok("预约成功,但排班状态更新失败"); } } else { return R.fail("预约失败"); } } catch (Exception e) { + // e.printStackTrace(); log.error(e.getMessage()); return R.fail("系统异常:" + e.getMessage()); } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/controller/DoctorScheduleController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/controller/DoctorScheduleController.java index 2a7e8bf6..77ab066a 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/controller/DoctorScheduleController.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/controller/DoctorScheduleController.java @@ -89,13 +89,4 @@ public class DoctorScheduleController { return R.ok(doctorScheduleAppService.getTodayDoctorScheduleList()); } - /* - * 获取当前登录医生今日排班List - * - * */ - @GetMapping("/today-my-schedule") - public R getTodayMySchedule() { - return doctorScheduleAppService.getTodayMySchedule(); - } - } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/controller/SurgicalScheduleController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/controller/SurgicalScheduleController.java index 3666bdcd..746a94a3 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/controller/SurgicalScheduleController.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/controller/SurgicalScheduleController.java @@ -2,14 +2,11 @@ package com.openhis.web.clinicalmanage.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.core.common.core.domain.R; -import com.core.common.core.domain.model.LoginUser; -import com.core.common.utils.SecurityUtils; import com.openhis.web.clinicalmanage.appservice.ISurgicalScheduleAppService; import com.openhis.web.clinicalmanage.dto.OpCreateScheduleDto; import com.openhis.web.clinicalmanage.dto.OpScheduleDto; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.catalina.User; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; @@ -101,29 +98,4 @@ public class SurgicalScheduleController { surgicalScheduleAppService.exportSurgerySchedule(opScheduleDto, response); } - /** - * 验证密码 - * @param password 密码 - * @return 结果 - */ - @PostMapping(value = "/checkPassword") - public R checkPassword(@RequestParam String password) { - try { - //获取当前登录用户信息 - LoginUser loginUser = SecurityUtils.getLoginUser(); - if (loginUser == null) { - return R.fail("用户未登录"); - } - // 直接使用 SecurityUtils.matchesPassword 进行密码验证 - boolean isMatch = SecurityUtils.matchesPassword(password, loginUser.getPassword()); - if (isMatch) { - return R.ok(true); - } else { - return R.fail("密码错误"); - } - } catch (Exception e) { - return R.fail("密码验证失败:" + e.getMessage()); - } - } - } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationConfirmationController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationConfirmationController.java deleted file mode 100644 index 67fcc084..00000000 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationConfirmationController.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.openhis.web.consultation.controller; - -import com.core.common.core.controller.BaseController; -import com.core.common.core.domain.AjaxResult; -import com.core.common.annotation.Log; -import com.core.common.enums.BusinessType; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.openhis.consultation.domain.ConsultationConfirmation; -import com.openhis.consultation.service.IConsultationConfirmationService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import java.time.LocalDateTime; -import java.util.List; - -/** - * 会诊确认 Controller - * - * @author his - */ -@RestController -@RequestMapping("/consultation/confirmation") -public class ConsultationConfirmationController extends BaseController { - @Autowired - private IConsultationConfirmationService consultationConfirmationService; - - /** - * 查询会诊确认列表 - */ - @PreAuthorize("@ss.hasPermi('consultation:confirmation:list')") - @GetMapping("/list") - public AjaxResult list(ConsultationConfirmation consultationConfirmation, - @RequestParam(defaultValue = "1") Integer pageNum, - @RequestParam(defaultValue = "10") Integer pageSize) { - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(consultationConfirmation.getConsultationRequestId() != null, - ConsultationConfirmation::getConsultationRequestId, - consultationConfirmation.getConsultationRequestId()) - .eq(consultationConfirmation.getConfirmationStatus() != null, - ConsultationConfirmation::getConfirmationStatus, - consultationConfirmation.getConfirmationStatus()) - .orderByDesc(ConsultationConfirmation::getCreateTime); - - Page page = new Page<>(pageNum, pageSize); - Page result = consultationConfirmationService.page(page, queryWrapper); - return AjaxResult.success(result); - } - - /** - * 获取会诊确认详细信息 - */ - @PreAuthorize("@ss.hasPermi('consultation:confirmation:query')") - @GetMapping(value = "/{id}") - public AjaxResult getInfo(@PathVariable("id") Long id) { - return AjaxResult.success(consultationConfirmationService.getById(id)); - } - - /** - * 新增会诊确认 - */ - @PreAuthorize("@ss.hasPermi('consultation:confirmation:add')") - @Log(title = "会诊确认", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@RequestBody ConsultationConfirmation consultationConfirmation) { - consultationConfirmation.setCreatorId(getUserId()); - consultationConfirmation.setCreatorName(getUsername()); - return toAjax(consultationConfirmationService.save(consultationConfirmation)); - } - - /** - * 确认会诊(同意或拒绝) - */ - @PreAuthorize("@ss.hasPermi('consultation:confirmation:confirm')") - @Log(title = "会诊确认", businessType = BusinessType.UPDATE) - @PutMapping("/confirm") - public AjaxResult confirm(@RequestBody ConsultationConfirmation consultationConfirmation) { - // 设置确认日期 - consultationConfirmation.setConfirmationDate(LocalDateTime.now()); - consultationConfirmation.setUpdaterId(getUserId()); - consultationConfirmation.setUpdaterName(getUsername()); - return toAjax(consultationConfirmationService.updateById(consultationConfirmation)); - } - - /** - * 删除会诊确认 - */ - @PreAuthorize("@ss.hasPermi('consultation:confirmation:remove')") - @Log(title = "会诊确认", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") - public AjaxResult remove(@PathVariable Long[] ids) { - return toAjax(consultationConfirmationService.removeByIds(java.util.Arrays.asList(ids))); - } -} \ No newline at end of file diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationController.java index fc8fcd8a..de2746f3 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationController.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationController.java @@ -41,12 +41,10 @@ public class ConsultationController { @ApiParam("就诊ID(可选,不传则查询当前医生的所有会诊申请)") @RequestParam(required = false) Long encounterId) { try { - log.info("获取会诊列表,encounterId: {}", encounterId); List list = consultationAppService.getConsultationList(encounterId); - log.info("获取会诊列表成功,共 {} 条记录", list != null ? list.size() : 0); return R.ok(list); } catch (Exception e) { - log.error("获取会诊列表失败,encounterId: {}", encounterId, e); + log.error("获取会诊列表失败", e); return R.fail("获取会诊列表失败: " + e.getMessage()); } } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationRecordController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationRecordController.java deleted file mode 100644 index f138e177..00000000 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationRecordController.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.openhis.web.consultation.controller; - -import com.core.common.core.controller.BaseController; -import com.core.common.core.domain.AjaxResult; -import com.core.common.annotation.Log; -import com.core.common.enums.BusinessType; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.openhis.consultation.domain.ConsultationRecord; -import com.openhis.consultation.service.IConsultationRecordService; -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 - * - * @author his - */ -@RestController -@RequestMapping("/consultation/record") -public class ConsultationRecordController extends BaseController { - @Autowired - private IConsultationRecordService consultationRecordService; - - /** - * 查询会诊记录列表 - */ - @PreAuthorize("@ss.hasPermi('consultation:record:list')") - @GetMapping("/list") - public AjaxResult list(ConsultationRecord consultationRecord, - @RequestParam(defaultValue = "1") Integer pageNum, - @RequestParam(defaultValue = "10") Integer pageSize) { - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(consultationRecord.getConsultationRequestId() != null, - ConsultationRecord::getConsultationRequestId, - consultationRecord.getConsultationRequestId()) - .eq(consultationRecord.getParticipantDoctorId() != null, - ConsultationRecord::getParticipantDoctorId, - consultationRecord.getParticipantDoctorId()) - .orderByDesc(ConsultationRecord::getRecordDate); - - Page page = new Page<>(pageNum, pageSize); - Page result = consultationRecordService.page(page, queryWrapper); - return AjaxResult.success(result); - } - - /** - * 获取会诊记录详细信息 - */ - @PreAuthorize("@ss.hasPermi('consultation:record:query')") - @GetMapping(value = "/{id}") - public AjaxResult getInfo(@PathVariable("id") Long id) { - return AjaxResult.success(consultationRecordService.getById(id)); - } - - /** - * 新增会诊记录 - */ - @PreAuthorize("@ss.hasPermi('consultation:record:add')") - @Log(title = "会诊记录", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@RequestBody ConsultationRecord consultationRecord) { - consultationRecord.setCreatorId(getUserId()); - consultationRecord.setCreatorName(getUsername()); - return toAjax(consultationRecordService.save(consultationRecord)); - } - - /** - * 修改会诊记录 - */ - @PreAuthorize("@ss.hasPermi('consultation:record:edit')") - @Log(title = "会诊记录", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@RequestBody ConsultationRecord consultationRecord) { - consultationRecord.setUpdaterId(getUserId()); - consultationRecord.setUpdaterName(getUsername()); - return toAjax(consultationRecordService.updateById(consultationRecord)); - } - - /** - * 删除会诊记录 - */ - @PreAuthorize("@ss.hasPermi('consultation:record:remove')") - @Log(title = "会诊记录", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") - public AjaxResult remove(@PathVariable Long[] ids) { - return toAjax(consultationRecordService.removeByIds(java.util.Arrays.asList(ids))); - } -} \ No newline at end of file diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationRequestController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationRequestController.java deleted file mode 100644 index 8f250e58..00000000 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationRequestController.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.openhis.web.consultation.controller; - -import com.core.common.core.controller.BaseController; -import com.core.common.core.domain.AjaxResult; -import com.core.common.annotation.Log; -import com.core.common.enums.BusinessType; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.openhis.consultation.domain.ConsultationRequest; -import com.openhis.consultation.service.IConsultationRequestService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import java.time.LocalDateTime; -import java.util.List; - -/** - * 会诊申请 Controller - * - * @author his - */ -@RestController -@RequestMapping("/consultation/request") -public class ConsultationRequestController extends BaseController { - @Autowired - private IConsultationRequestService consultationRequestService; - - /** - * 查询会诊申请列表 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:list')") - @GetMapping("/list") - public AjaxResult list(ConsultationRequest consultationRequest, - @RequestParam(defaultValue = "1") Integer pageNum, - @RequestParam(defaultValue = "10") Integer pageSize) { - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.like(consultationRequest.getPatientName() != null, ConsultationRequest::getPatientName, consultationRequest.getPatientName()) - .eq(consultationRequest.getConsultationStatus() != null, ConsultationRequest::getConsultationStatus, consultationRequest.getConsultationStatus()) - .eq(consultationRequest.getConsultationActivityId() != null, ConsultationRequest::getConsultationActivityId, consultationRequest.getConsultationActivityId()) - .orderByDesc(ConsultationRequest::getConsultationRequestDate); - - Page page = new Page<>(pageNum, pageSize); - Page result = consultationRequestService.page(page, queryWrapper); - return AjaxResult.success(result); - } - - /** - * 获取会诊申请详细信息 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:query')") - @GetMapping(value = "/{id}") - public AjaxResult getInfo(@PathVariable("id") Long id) { - return AjaxResult.success(consultationRequestService.getById(id)); - } - - /** - * 新增会诊申请 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:add')") - @Log(title = "会诊申请", businessType = BusinessType.INSERT) - @PostMapping - public AjaxResult add(@RequestBody ConsultationRequest consultationRequest) { - // 自动生成申请单号 - String consultationId = "CS" + LocalDateTime.now().toString().replaceAll("[^0-9]", "").substring(0, 14) + String.format("%04d", (int)(Math.random() * 10000)); - consultationRequest.setConsultationId(consultationId); - - consultationRequest.setCreateBy(getUsername()); - consultationRequest.setConsultationRequestDate(LocalDateTime.now()); - consultationRequest.setConsultationStatus(0); // 新开状态 - return toAjax(consultationRequestService.save(consultationRequest)); - } - - /** - * 修改会诊申请 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:edit')") - @Log(title = "会诊申请", businessType = BusinessType.UPDATE) - @PutMapping - public AjaxResult edit(@RequestBody ConsultationRequest consultationRequest) { - consultationRequest.setUpdateBy(getUsername()); - return toAjax(consultationRequestService.updateById(consultationRequest)); - } - - /** - * 删除会诊申请 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:remove')") - @Log(title = "会诊申请", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") - public AjaxResult remove(@PathVariable Long[] ids) { - return toAjax(consultationRequestService.removeByIds(java.util.Arrays.asList(ids))); - } - - /** - * 提交会诊申请 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:submit')") - @Log(title = "会诊申请", businessType = BusinessType.UPDATE) - @PutMapping("/submit/{id}") - public AjaxResult submit(@PathVariable Long id) { - int result = consultationRequestService.submitConsultation(id, getUserId(), getUsername()); - return result > 0 ? AjaxResult.success("提交成功") : AjaxResult.error("提交失败"); - } - - /** - * 取消提交会诊申请 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:cancelSubmit')") - @Log(title = "会诊申请", businessType = BusinessType.UPDATE) - @PutMapping("/cancelSubmit/{id}") - public AjaxResult cancelSubmit(@PathVariable Long id) { - int result = consultationRequestService.cancelSubmitConsultation(id); - return result > 0 ? AjaxResult.success("取消提交成功") : AjaxResult.error("取消提交失败"); - } - - /** - * 结束会诊申请 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:end')") - @Log(title = "会诊申请", businessType = BusinessType.UPDATE) - @PutMapping("/end/{id}") - public AjaxResult end(@PathVariable Long id) { - int result = consultationRequestService.endConsultation(id, getUserId(), getUsername()); - return result > 0 ? AjaxResult.success("结束成功") : AjaxResult.error("结束失败,请确保会诊已签名"); - } - - /** - * 作废会诊申请 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:cancel')") - @Log(title = "会诊申请", businessType = BusinessType.UPDATE) - @PutMapping("/cancel/{id}") - public AjaxResult cancel(@PathVariable Long id) { - int result = consultationRequestService.cancelConsultation(id, getUserId(), getUsername()); - return result > 0 ? AjaxResult.success("作废成功") : AjaxResult.error("作废失败"); - } - - /** - * 确认会诊 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:confirm')") - @Log(title = "会诊申请", businessType = BusinessType.UPDATE) - @PutMapping("/confirm") - public AjaxResult confirm(@RequestBody ConsultationRequest request) { - int result = consultationRequestService.confirmConsultation( - request.getId(), - request.getConfirmingPhysicianName(), - String.valueOf(request.getConfirmingPhysicianId()), - request.getConsultationOpinion() - ); - return result > 0 ? AjaxResult.success("确认成功") : AjaxResult.error("确认失败"); - } - - /** - * 签名会诊 - */ - @PreAuthorize("@ss.hasPermi('consultation:request:sign')") - @Log(title = "会诊申请", businessType = BusinessType.UPDATE) - @PutMapping("/sign") - public AjaxResult sign(@RequestBody ConsultationRequest request) { - int result = consultationRequestService.signConsultation( - request.getId(), - request.getSignature(), - getUserId(), - getUsername() - ); - return result > 0 ? AjaxResult.success("签名成功") : AjaxResult.error("签名失败,请确保会诊已确认"); - } -} \ No newline at end of file diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationRequest.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationRequest.java index 74aa3b18..4165d204 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationRequest.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationRequest.java @@ -3,20 +3,18 @@ package com.openhis.web.consultation.domain; import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; -import org.apache.ibatis.type.Alias; import java.io.Serializable; import java.util.Date; /** * 会诊申请单实体类 - * + * * @author system * @date 2026-01-29 */ @Data @TableName("consultation_request") -@Alias("webConsultationRequest") public class ConsultationRequest implements Serializable { private static final long serialVersionUID = 1L; diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationRequestMapper.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationRequestMapper.java similarity index 52% rename from openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationRequestMapper.java rename to openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationRequestMapper.java index 73d26323..cb7aaaa5 100644 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationRequestMapper.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationRequestMapper.java @@ -1,14 +1,17 @@ -package com.openhis.consultation.mapper; +package com.openhis.web.consultation.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.openhis.consultation.domain.ConsultationRequest; +import com.openhis.web.consultation.domain.ConsultationRequest; import org.apache.ibatis.annotations.Mapper; /** - * 会诊申请 Mapper接口 - * - * @author his + * 会诊申请Mapper接口 + * + * @author system + * @date 2026-01-29 */ @Mapper public interface ConsultationRequestMapper extends BaseMapper { -} \ No newline at end of file + +} + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java index b276f344..58d66b77 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java @@ -629,16 +629,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp chargeItem.setId(adviceSaveDto.getChargeItemId()); // 费用项id chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); // 收费状态 chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(medicationRequest.getBusNo())); - // 生成来源:如果前端指定了生成来源,使用前端值;否则使用默认的医生开立 - if (adviceSaveDto.getGenerateSourceEnum() != null) { - chargeItem.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum()); - } else { - chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); - } - // 来源业务单据号:如果前端指定了来源业务单据号,设置该字段 - if (adviceSaveDto.getSourceBillNo() != null) { - chargeItem.setSourceBillNo(adviceSaveDto.getSourceBillNo()); - } + chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源 chargeItem.setPrescriptionNo(adviceSaveDto.getPrescriptionNo()); // 处方号 chargeItem.setPatientId(adviceSaveDto.getPatientId()); // 患者 chargeItem.setContextEnum(adviceSaveDto.getAdviceType()); // 类型 @@ -768,16 +759,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp // 保存耗材费用项 chargeItem = new ChargeItem(); - // 生成来源:如果前端指定了生成来源,使用前端值;否则使用默认的医生开立 - if (adviceSaveDto.getGenerateSourceEnum() != null) { - chargeItem.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum()); - } else { - chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); - } - // 来源业务单据号:如果前端指定了来源业务单据号,设置该字段 - if (adviceSaveDto.getSourceBillNo() != null) { - chargeItem.setSourceBillNo(adviceSaveDto.getSourceBillNo()); - } chargeItem.setId(adviceSaveDto.getChargeItemId()); // 费用项id chargeItem.setTenantId(tenantId); // 补全租户ID chargeItem.setCreateBy(currentUsername); // 补全创建人 @@ -919,16 +900,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp // 保存时保存诊疗费用项 if (is_save) { chargeItem = new ChargeItem(); - // 生成来源:如果前端指定了生成来源,使用前端值;否则使用默认的医生开立 - if (adviceSaveDto.getGenerateSourceEnum() != null) { - chargeItem.setGenerateSourceEnum(adviceSaveDto.getGenerateSourceEnum()); - } else { - chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); - } - // 来源业务单据号:如果前端指定了来源业务单据号,设置该字段 - if (adviceSaveDto.getSourceBillNo() != null) { - chargeItem.setSourceBillNo(adviceSaveDto.getSourceBillNo()); - } chargeItem.setId(adviceSaveDto.getChargeItemId()); // 费用项id chargeItem.setTenantId(tenantId); // 补全租户ID chargeItem.setCreateBy(currentUsername); // 补全创建人 diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/InfectiousDiseaseReportController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/InfectiousDiseaseReportController.java deleted file mode 100644 index 2c15c8f1..00000000 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/controller/InfectiousDiseaseReportController.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.openhis.web.doctorstation.controller; - -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.openhis.clinical.domain.InfectiousCard; -import com.openhis.clinical.service.IInfectiousCardService; -import com.core.common.utils.StringUtils; -import com.core.common.utils.poi.ExcelUtil; -import com.core.common.core.domain.AjaxResult; -import com.core.common.core.page.PageDomain; -import com.core.common.core.page.TableDataInfo; -import com.core.common.core.page.TableSupport; -import com.core.common.utils.PageUtils; -import com.core.common.core.controller.BaseController; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static com.core.common.utils.PageUtils.startPage; - -/** - * 传染病报告卡 Controller - */ -@RestController -@RequestMapping("/doctor-station/diagnosis") -public class InfectiousDiseaseReportController extends BaseController { - - @Autowired - private IInfectiousCardService infectiousCardService; - - /** - * 获取下一个卡片编号 - */ - @GetMapping("/next-card-no") - public AjaxResult getNextCardNo(@RequestParam String orgCode) { - if (StringUtils.isEmpty(orgCode)) { - return AjaxResult.error("机构编码不能为空"); - } - try { - String nextCardNo = infectiousCardService.generateNextCardNo(orgCode); - return AjaxResult.success("获取成功", nextCardNo); - } catch (Exception e) { - return AjaxResult.error("获取卡片编号失败:" + e.getMessage()); - } - } - - /** - * 保存传染病报告卡 - */ - @PostMapping("/save-infectious-disease-report") - public AjaxResult saveInfectiousDiseaseReport(@RequestBody InfectiousCard infectiousCard) { - // 验证必要参数 - if (infectiousCard == null) { - return AjaxResult.error("请求数据不能为空"); - } - - if (StringUtils.isEmpty(infectiousCard.getCardNo())) { - return AjaxResult.error("卡片编号不能为空"); - } - - if (infectiousCard.getPatId() == null) { - return AjaxResult.error("患者ID不能为空"); - } - - if (StringUtils.isEmpty(infectiousCard.getDiseaseCode())) { - return AjaxResult.error("疾病编码不能为空"); - } - - // 验证年龄和家长姓名的关系 - if (infectiousCard.getAge() != null && infectiousCard.getAgeUnit() != null && - infectiousCard.getAgeUnit().equals("1") && infectiousCard.getAge() <= 14) { - if (StringUtils.isEmpty(infectiousCard.getParentName())) { - return AjaxResult.error("14岁及以下患者必须填写家长姓名"); - } - } - - try { - boolean result = infectiousCardService.saveInfectiousCard(infectiousCard); - if (result) { - return AjaxResult.success("传染病报告卡保存成功"); - } else { - return AjaxResult.error("传染病报告卡保存失败"); - } - } catch (Exception e) { - return AjaxResult.error("保存过程中发生异常:" + e.getMessage()); - } - } - - /** - * 根据卡片编号查询传染病报告卡 - */ - @GetMapping("/get-infectious-card/{cardNo}") - public AjaxResult getInfectiousCardByCardNo(@PathVariable String cardNo) { - if (StringUtils.isEmpty(cardNo)) { - return AjaxResult.error("卡片编号不能为空"); - } - - try { - InfectiousCard infectiousCard = infectiousCardService.getById(cardNo); - if (infectiousCard != null) { - return AjaxResult.success(infectiousCard); - } else { - return AjaxResult.error("未找到对应的传染病报告卡"); - } - } catch (Exception e) { - return AjaxResult.error("查询过程中发生异常:" + e.getMessage()); - } - } - - /** - * 提交传染病报告卡(更新状态为已提交) - */ - @PutMapping("/submit-infectious-card/{cardNo}") - public AjaxResult submitInfectiousCard(@PathVariable String cardNo) { - if (StringUtils.isEmpty(cardNo)) { - return AjaxResult.error("卡片编号不能为空"); - } - - try { - InfectiousCard infectiousCard = infectiousCardService.getById(cardNo); - if (infectiousCard == null) { - return AjaxResult.error("未找到对应的传染病报告卡"); - } - - // 只有暂存状态的卡片才能提交 - if (infectiousCard.getStatus() != 0) { - return AjaxResult.error("只有暂存状态的卡片才能提交"); - } - - infectiousCard.setStatus(1); // 设置为已提交状态 - boolean result = infectiousCardService.updateById(infectiousCard); - if (result) { - return AjaxResult.success("传染病报告卡提交成功"); - } else { - return AjaxResult.error("传染病报告卡提交失败"); - } - } catch (Exception e) { - return AjaxResult.error("提交过程中发生异常:" + e.getMessage()); - } - } - - /** - * 查询传染病报告卡列表 - */ - @GetMapping("/list-infectious-cards") - public TableDataInfo listInfectiousCards(InfectiousCard infectiousCard) { - startPage(); - List list = infectiousCardService.list(new LambdaQueryWrapper<>(infectiousCard)); - return getDataTable(list); - } - - /** - * 导出传染病报告卡列表 - */ - @PostMapping("/export-infectious-cards") - public AjaxResult exportInfectiousCards(InfectiousCard infectiousCard) { - List list = infectiousCardService.list(new LambdaQueryWrapper<>(infectiousCard)); - ExcelUtil util = new ExcelUtil<>(InfectiousCard.class); - return util.exportExcel(list, "传染病报告卡数据"); - } -} \ No newline at end of file diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/paymentmanage/appservice/impl/PaymentRecServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/paymentmanage/appservice/impl/PaymentRecServiceImpl.java index bae539d1..1b34b9f6 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/paymentmanage/appservice/impl/PaymentRecServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/paymentmanage/appservice/impl/PaymentRecServiceImpl.java @@ -190,15 +190,8 @@ public class PaymentRecServiceImpl implements IPaymentRecService { @Override public R prePayment(PrePaymentDto prePaymentDto) { logger.info("预结算:参数:" + JSON.toJSONString(prePaymentDto)); - // 查收费项(支持手术计费) - List chargeItemList; - if (prePaymentDto.getGenerateSourceEnum() != null && prePaymentDto.getGenerateSourceEnum() == 2) { - // 手术计费:根据generateSourceEnum和sourceBillNo过滤 - chargeItemList = getChargeItems(prePaymentDto.getChargeItemIds(), prePaymentDto.getGenerateSourceEnum(), prePaymentDto.getSourceBillNo()); - } else { - // 普通门诊划价 - chargeItemList = getChargeItems(prePaymentDto.getChargeItemIds()); - } + // 查收费项 + List chargeItemList = getChargeItems(prePaymentDto.getChargeItemIds()); if (chargeItemList.isEmpty()) { return R.fail("未选择收费项"); } @@ -1906,13 +1899,11 @@ public class PaymentRecServiceImpl implements IPaymentRecService { : outpatientRegistrationAddParam.getYbMdtrtCertType()); iAccountService.save(accountZf); } - String accountContractNo = outpatientRegistrationAddParam.getAccountFormData().getContractNo(); if (!CommonConstants.BusinessName.DEFAULT_CONTRACT_NO - .equals(accountContractNo) + .equals(outpatientRegistrationAddParam.getAccountFormData().getContractNo()) && !CommonConstants.BusinessName.DEFAULT_STUDENT_CONTRACT_NO - .equals(accountContractNo) - && accountContractNo.length() > 11 - && accountContractNo.startsWith("STUDENT")) { + .equals(outpatientRegistrationAddParam.getAccountFormData().getContractNo()) + && outpatientRegistrationAddParam.getAccountFormData().getContractNo().length() > 11) { // 建立学生自费ACCOUNT Account accountStudentZf = new Account(); BeanUtils.copyProperties(accountFormData, accountStudentZf); @@ -1922,9 +1913,8 @@ public class PaymentRecServiceImpl implements IPaymentRecService { : outpatientRegistrationAddParam.getYbMdtrtCertType()); iAccountService.save(accountStudentZf); - // 截取医保合同号时,先验证是否以"STUDENT"开头 - String ybContractNo = accountContractNo.substring("STUDENT".length()); - Contract contractYb = contractService.getContract(ybContractNo); + Contract contractYb = contractService.getContract( + outpatientRegistrationAddParam.getAccountFormData().getContractNo().substring("STUDENT".length())); if (contractYb != null && 1 == contractYb.getYbFlag()) { // 建立纯医保ACCOUNT Account accountYb = new Account(); @@ -2103,24 +2093,6 @@ public class PaymentRecServiceImpl implements IPaymentRecService { return chargeItemService.list(new LambdaQueryWrapper().in(ChargeItem::getId, chargeItemIds)); } - /** - * 获取收费项集合(支持手术计费) - * - * @param chargeItemIds 收费项id集合 - * @param generateSourceEnum 账单生成来源(2:手术计费) - * @param sourceBillNo 来源业务单据号(手术单号) - * @return 收费项集合 - */ - private List getChargeItems(List chargeItemIds, Integer generateSourceEnum, String sourceBillNo) { - LambdaQueryWrapper wrapper = new LambdaQueryWrapper().in(ChargeItem::getId, chargeItemIds); - // 如果是手术计费,需要额外过滤条件 - if (generateSourceEnum != null && generateSourceEnum == 2 && sourceBillNo != null) { - wrapper.eq(ChargeItem::getGenerateSourceEnum, 2) - .eq(ChargeItem::getSourceBillNo, sourceBillNo); - } - return chargeItemService.list(wrapper); - } - /** * 类型转换:收费项chargeItem转成PaymentedItemModel * diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/ybmanage/controller/DayEndMedicalInsuranceSettlementController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/ybmanage/controller/DayEndMedicalInsuranceSettlementController.java deleted file mode 100644 index e64a0252..00000000 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/ybmanage/controller/DayEndMedicalInsuranceSettlementController.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.openhis.web.ybmanage.controller; - -import com.core.common.core.domain.AjaxResult; -import com.core.common.core.domain.PageResult; -import com.core.common.core.page.TableDataInfo; -import com.core.common.core.controller.BaseController; -import com.openhis.yb.domain.DayEndMedicalInsuranceSettlement; -import com.openhis.yb.service.IDayEndMedicalInsuranceSettlementService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -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 - * - * @author - * @date 2026-02-02 - */ -@Api(tags = "日结医保结算") -@RestController -@RequestMapping("/ybmanage/dayEndMedicalInsuranceSettlement") -public class DayEndMedicalInsuranceSettlementController extends BaseController { - - @Autowired - private IDayEndMedicalInsuranceSettlementService dayEndMedicalInsuranceSettlementService; - - /** - * 查询日结医保结算列表 - */ - @ApiOperation("查询日结医保结算列表") - @PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:list')") - @GetMapping("/list") - public TableDataInfo list(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement) { - startPage(); - List list = dayEndMedicalInsuranceSettlementService.selectDayEndMedicalInsuranceSettlementList(dayEndMedicalInsuranceSettlement); - return getDataTable(list); - } - - /** - * 分页查询日结医保结算列表 - */ - @ApiOperation("分页查询日结医保结算列表") - @PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:list')") - @GetMapping("/page") - public PageResult page(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement, - @RequestParam(defaultValue = "1") int pageNum, - @RequestParam(defaultValue = "10") int pageSize) { - return dayEndMedicalInsuranceSettlementService.selectDayEndMedicalInsuranceSettlementPage(dayEndMedicalInsuranceSettlement, pageNum, pageSize); - } - - /** - * 获取日结医保结算详细信息 - */ - @ApiOperation("获取日结医保结算详细信息") - @PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:query')") - @GetMapping(value = "/{id}") - public AjaxResult getInfo(@PathVariable("id") Long id) { - return AjaxResult.success(dayEndMedicalInsuranceSettlementService.selectDayEndMedicalInsuranceSettlementById(id)); - } - - /** - * 新增日结医保结算 - */ - @ApiOperation("新增日结医保结算") - @PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:add')") - @PostMapping - public AjaxResult add(@RequestBody DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement) { - return toAjax(dayEndMedicalInsuranceSettlementService.insertDayEndMedicalInsuranceSettlement(dayEndMedicalInsuranceSettlement)); - } - - /** - * 修改日结医保结算 - */ - @ApiOperation("修改日结医保结算") - @PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:edit')") - @PutMapping - public AjaxResult edit(@RequestBody DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement) { - return toAjax(dayEndMedicalInsuranceSettlementService.updateDayEndMedicalInsuranceSettlement(dayEndMedicalInsuranceSettlement)); - } - - /** - * 删除日结医保结算 - */ - @ApiOperation("删除日结医保结算") - @PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:remove')") - @DeleteMapping("/{ids}") - public AjaxResult remove(@PathVariable Long[] ids) { - return toAjax(dayEndMedicalInsuranceSettlementService.deleteDayEndMedicalInsuranceSettlementByIds(ids)); - } -} \ No newline at end of file diff --git a/openhis-server-new/openhis-application/src/main/resources/application-dev.yml b/openhis-server-new/openhis-application/src/main/resources/application-dev.yml index 176f1f6c..bf984f0d 100644 --- a/openhis-server-new/openhis-application/src/main/resources/application-dev.yml +++ b/openhis-server-new/openhis-application/src/main/resources/application-dev.yml @@ -90,15 +90,4 @@ server: port: 18080 servlet: # 应用的访问路径 - context-path: /openhis - -# 开发环境日志配置 -logging: - level: - com.openhis: info - com.baomidou.mybatisplus: info - com.openhis.mapper: info - com.openhis.domain: info - org.springframework.jdbc.core: info - com.alibaba.druid: info - com.alibaba.druid.sql: info \ No newline at end of file + context-path: /openhis \ No newline at end of file diff --git a/openhis-server-new/openhis-application/src/main/resources/application.yml b/openhis-server-new/openhis-application/src/main/resources/application.yml index 057ea6ab..ebe0302d 100644 --- a/openhis-server-new/openhis-application/src/main/resources/application.yml +++ b/openhis-server-new/openhis-application/src/main/resources/application.yml @@ -32,17 +32,16 @@ server: # 日志配置 logging: level: - com.openhis: debug + com.openhis: info org.springframework: warn # MyBatis和MyBatis-Plus日志 com.baomidou.mybatisplus: debug - com.openhis.mapper: debug - com.openhis.domain: debug + com.openhis.web.regdoctorstation.mapper: info # JDBC日志 - org.springframework.jdbc.core: debug + org.springframework.jdbc.core: info # Druid SQL日志 - com.alibaba.druid: debug - com.alibaba.druid.sql: debug + com.alibaba.druid: info + com.alibaba.druid.sql: info # 用户配置 user: @@ -92,9 +91,6 @@ mybatis-plus: mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml - configuration: - # 开启 SQL 日志输出 - log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: validFlag # 全局逻辑删除的实体字段名 diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/clinicalmanage/SurgicalScheduleAppMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/clinicalmanage/SurgicalScheduleAppMapper.xml index d268a842..adf68611 100644 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/clinicalmanage/SurgicalScheduleAppMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/clinicalmanage/SurgicalScheduleAppMapper.xml @@ -29,7 +29,6 @@ cs.apply_dept_id, cs.apply_dept_name, cs.org_id, - cs.encounter_id, o.name AS org_name, cs.main_surgeon_name AS surgeon_name FROM op_schedule os diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml index 3932f64e..8a104a29 100644 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml @@ -2,10 +2,10 @@ - + - + @@ -22,6 +22,8 @@ + + @@ -43,11 +45,6 @@ - - - - - diff --git a/openhis-server-new/openhis-application/src/main/resources/spy.properties b/openhis-server-new/openhis-application/src/main/resources/spy.properties deleted file mode 100644 index 4fd5882d..00000000 --- a/openhis-server-new/openhis-application/src/main/resources/spy.properties +++ /dev/null @@ -1,5 +0,0 @@ -modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory -logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger -outagedetection=true -outagedetectioninterval=2000 -appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger \ No newline at end of file diff --git a/openhis-server-new/openhis-common/src/main/java/com/openhis/common/enums/GenerateSource.java b/openhis-server-new/openhis-common/src/main/java/com/openhis/common/enums/GenerateSource.java index 2f9123f9..8b21120d 100644 --- a/openhis-server-new/openhis-common/src/main/java/com/openhis/common/enums/GenerateSource.java +++ b/openhis-server-new/openhis-common/src/main/java/com/openhis/common/enums/GenerateSource.java @@ -39,12 +39,7 @@ public enum GenerateSource implements HisEnumInterface { /** * 自动滚费 */ - AUTO_ROLL_FEES(5, "5", "自动滚费"), - - /** - * 手术计费 - */ - SURGERY_BILLING(6, "6", "手术计费"); + AUTO_ROLL_FEES(5, "5", "自动滚费"); private final Integer value; private final String code; diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/domain/ChargeItem.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/domain/ChargeItem.java index 75de7058..25a717cd 100644 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/domain/ChargeItem.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/administration/domain/ChargeItem.java @@ -244,9 +244,4 @@ public class ChargeItem extends HisBaseEntity { */ private BigDecimal manualAdjustedPrice; - /** - * 来源业务单据号(如手术申请单号) - */ - private String sourceBillNo; - } diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/IClinicRoomService.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/IClinicRoomService.java index b40c24b9..58808be9 100644 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/IClinicRoomService.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/IClinicRoomService.java @@ -51,23 +51,4 @@ public interface IClinicRoomService extends IService { * @return 结果 */ int deleteClinicRoomById(Long id); - - /** - * 检查指定卫生机构下是否已存在相同诊室名称(新增时使用) - * - * @param orgName 卫生机构名称 - * @param roomName 诊室名称 - * @return 是否存在 - */ - boolean existsByOrgNameAndRoomName(String orgName, String roomName); - - /** - * 检查指定卫生机构下是否已存在相同诊室名称(编辑时使用,排除当前记录) - * - * @param orgName 卫生机构名称 - * @param roomName 诊室名称 - * @param id 当前记录ID - * @return 是否存在 - */ - boolean existsByOrgNameAndRoomNameExcludeId(String orgName, String roomName, Long id); } diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/impl/ClinicRoomServiceImpl.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/impl/ClinicRoomServiceImpl.java index 802e9dd4..cc66e875 100644 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/impl/ClinicRoomServiceImpl.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/impl/ClinicRoomServiceImpl.java @@ -44,21 +44,4 @@ public class ClinicRoomServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(ClinicRoom::getOrgName, orgName) - .eq(ClinicRoom::getRoomName, roomName); - return count(queryWrapper) > 0; - } - - @Override - public boolean existsByOrgNameAndRoomNameExcludeId(String orgName, String roomName, Long id) { - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(ClinicRoom::getOrgName, orgName) - .eq(ClinicRoom::getRoomName, roomName) - .ne(ClinicRoom::getId, id); - return count(queryWrapper) > 0; - } } diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/domain/InfectiousCard.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/domain/InfectiousCard.java deleted file mode 100644 index 2768bc19..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/domain/InfectiousCard.java +++ /dev/null @@ -1,378 +0,0 @@ -package com.openhis.clinical.domain; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import java.io.Serializable; -import java.time.LocalDate; -import java.time.LocalDateTime; -import lombok.Data; - -/** - * 传染病报卡表 - */ -@Data -@TableName(value = "infectious_card") -public class InfectiousCard implements Serializable { - - /** - * 卡片编号:机构代码+年月日+4位流水 - */ - @TableId(value = "card_no") - private String cardNo; - - /** - * 本次就诊ID (adm_encounter.id) - */ - @TableField(value = "visit_id") - private Long visitId; - - /** - * 诊断记录唯一ID (adm_encounter_diagnosis.condition_id) - */ - @TableField(value = "diag_id") - private Long diagId; - - /** - * 患者ID - */ - @TableField(value = "pat_id") - private Long patId; - - /** - * 证件类型 - */ - @TableField(value = "id_type") - private Integer idType; - - /** - * 证件号码:18位校验 - */ - @TableField(value = "id_no") - private String idNo; - - /** - * 患者姓名 - */ - @TableField(value = "pat_name") - private String patName; - - /** - * 家长姓名:≤14岁必填 - */ - @TableField(value = "parent_name") - private String parentName; - - /** - * 性别 - */ - @TableField(value = "sex") - private String sex; - - /** - * 出生日期 - */ - @TableField(value = "birthday") - private LocalDate birthday; - - /** - * 年龄 - */ - @TableField(value = "age") - private Integer age; - - /** - * 年龄单位:1岁/2月/3天 - */ - @TableField(value = "age_unit") - private String ageUnit; - - /** - * 工作单位 - */ - @TableField(value = "workplace") - private String workplace; - - /** - * 电话 - */ - @TableField(value = "phone") - private String phone; - - /** - * 联系电话 - */ - @TableField(value = "contact_phone") - private String contactPhone; - - /** - * 地址-省 - */ - @TableField(value = "address_prov") - private String addressProv; - - /** - * 地址-市 - */ - @TableField(value = "address_city") - private String addressCity; - - /** - * 地址-县/区 - */ - @TableField(value = "address_county") - private String addressCounty; - - /** - * 地址-乡镇/街道 - */ - @TableField(value = "address_town") - private String addressTown; - - /** - * 地址-村/居委会 - */ - @TableField(value = "address_village") - private String addressVillage; - - /** - * 地址-门牌号 - */ - @TableField(value = "address_house") - private String addressHouse; - - /** - * 患者归属:1本县区/2本市/3本省/4外省/5港澳台/6外籍 - */ - @TableField(value = "patient_belong") - private Integer patientBelong; - - /** - * 职业 - */ - @TableField(value = "occupation") - private String occupation; - - /** - * 疾病编码 - */ - @TableField(value = "disease_code") - private String diseaseCode; - - /** - * 疾病类型 - */ - @TableField(value = "disease_type") - private String diseaseType; - - /** - * 其他疾病 - */ - @TableField(value = "other_disease") - private String otherDisease; - - /** - * 病例分类:1疑似/2临床诊断/3确诊/4病原携带/5阳性 - */ - @TableField(value = "case_class") - private Integer caseClass; - - /** - * 发病日期 - */ - @TableField(value = "onset_date") - private LocalDate onsetDate; - - /** - * 诊断日期 - */ - @TableField(value = "diag_date") - private LocalDateTime diagDate; - - /** - * 死亡日期 - */ - @TableField(value = "death_date") - private LocalDate deathDate; - - /** - * 订正姓名 - */ - @TableField(value = "correct_name") - private String correctName; - - /** - * 撤销原因 - */ - @TableField(value = "withdraw_reason") - private String withdrawReason; - - /** - * 报告机构 - */ - @TableField(value = "report_org") - private String reportOrg; - - /** - * 报告机构电话 - */ - @TableField(value = "report_org_phone") - private String reportOrgPhone; - - /** - * 报告医生 - */ - @TableField(value = "report_doc") - private String reportDoc; - - /** - * 报告日期 - */ - @TableField(value = "report_date") - private LocalDate reportDate; - - /** - * 状态:0暂存 1已提交 2已审核 3已上报 4失败 5作废 - */ - @TableField(value = "status") - private Integer status; - - /** - * 失败信息 - */ - @TableField(value = "fail_msg") - private String failMsg; - - /** - * XML内容 - */ - @TableField(value = "xml_content") - private String xmlContent; - - /** - * 卡片名称代码:默认1-中华人民共和国传染病报告卡 - */ - @TableField(value = "card_name_code") - private Integer cardNameCode; - - /** - * 登记来源 - */ - @TableField(value = "registration_source") - private Integer registrationSource; - - /** - * 科室ID - */ - @TableField(value = "dept_id") - private Long deptId; - - /** - * 医生ID - */ - @TableField(value = "doctor_id") - private Long doctorId; - - /** - * 创建时间 - */ - @TableField(value = "create_time") - private LocalDateTime createTime; - - /** - * 更新时间 - */ - @TableField(value = "update_time") - private LocalDateTime updateTime; - - public static final String COL_CARD_NO = "card_no"; - - public static final String COL_VISIT_ID = "visit_id"; - - public static final String COL_DIAG_ID = "diag_id"; - - public static final String COL_PAT_ID = "pat_id"; - - public static final String COL_ID_TYPE = "id_type"; - - public static final String COL_ID_NO = "id_no"; - - public static final String COL_PAT_NAME = "pat_name"; - - public static final String COL_PARENT_NAME = "parent_name"; - - public static final String COL_SEX = "sex"; - - public static final String COL_BIRTHDAY = "birthday"; - - public static final String COL_AGE = "age"; - - public static final String COL_AGE_UNIT = "age_unit"; - - public static final String COL_WORKPLACE = "workplace"; - - public static final String COL_PHONE = "phone"; - - public static final String COL_CONTACT_PHONE = "contact_phone"; - - public static final String COL_ADDRESS_PROV = "address_prov"; - - public static final String COL_ADDRESS_CITY = "address_city"; - - public static final String COL_ADDRESS_COUNTY = "address_county"; - - public static final String COL_ADDRESS_TOWN = "address_town"; - - public static final String COL_ADDRESS_VILLAGE = "address_village"; - - public static final String COL_ADDRESS_HOUSE = "address_house"; - - public static final String COL_PATIENT_BELONG = "patient_belong"; - - public static final String COL_OCCUPATION = "occupation"; - - public static final String COL_DISEASE_CODE = "disease_code"; - - public static final String COL_DISEASE_TYPE = "disease_type"; - - public static final String COL_OTHER_DISEASE = "other_disease"; - - public static final String COL_CASE_CLASS = "case_class"; - - public static final String COL_ONSET_DATE = "onset_date"; - - public static final String COL_DIAG_DATE = "diag_date"; - - public static final String COL_DEATH_DATE = "death_date"; - - public static final String COL_CORRECT_NAME = "correct_name"; - - public static final String COL_WITHDRAW_REASON = "withdraw_reason"; - - public static final String COL_REPORT_ORG = "report_org"; - - public static final String COL_REPORT_ORG_PHONE = "report_org_phone"; - - public static final String COL_REPORT_DOC = "report_doc"; - - public static final String COL_REPORT_DATE = "report_date"; - - public static final String COL_STATUS = "status"; - - public static final String COL_FAIL_MSG = "fail_msg"; - - public static final String COL_XML_CONTENT = "xml_content"; - - public static final String COL_CARD_NAME_CODE = "card_name_code"; - - public static final String COL_REGISTRATION_SOURCE = "registration_source"; - - public static final String COL_DEPT_ID = "dept_id"; - - public static final String COL_DOCTOR_ID = "doctor_id"; - - public static final String COL_CREATE_TIME = "create_time"; - - public static final String COL_UPDATE_TIME = "update_time"; -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/mapper/InfectiousCardMapper.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/mapper/InfectiousCardMapper.java deleted file mode 100644 index be690d92..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/mapper/InfectiousCardMapper.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.openhis.clinical.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.openhis.clinical.domain.InfectiousCard; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; - -/** - * 传染病报卡表 Mapper 接口 - */ -@Mapper -public interface InfectiousCardMapper extends BaseMapper { - -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/IInfectiousCardService.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/IInfectiousCardService.java deleted file mode 100644 index 604c8289..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/IInfectiousCardService.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.openhis.clinical.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.openhis.clinical.domain.InfectiousCard; - -/** - * 传染病报卡表 Service接口 - */ -public interface IInfectiousCardService extends IService { - - /** - * 保存传染病报告卡 - * @param infectiousCard 传染病报告卡对象 - * @return 保存结果 - */ - boolean saveInfectiousCard(InfectiousCard infectiousCard); - - /** - * 生成下一个卡片编号 - * @param orgCode 医疗机构编码 - * @return 下一个卡片编号(机构编码+YYYYMMDD+4位流水号) - */ - String generateNextCardNo(String orgCode); -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/InfectiousCardServiceImpl.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/InfectiousCardServiceImpl.java deleted file mode 100644 index cd2bab6e..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/InfectiousCardServiceImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.openhis.clinical.service.impl; - -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.openhis.clinical.domain.InfectiousCard; -import com.openhis.clinical.mapper.InfectiousCardMapper; -import com.openhis.clinical.service.IInfectiousCardService; -import org.springframework.stereotype.Service; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -/** - * 传染病报卡表 Service实现 - */ -@Service -public class InfectiousCardServiceImpl extends ServiceImpl implements IInfectiousCardService { - - @Override - public String generateNextCardNo(String orgCode) { - String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); - String prefix = orgCode + dateStr; - - // --- MyBatis-Plus Lambda 重构部分开始 --- - InfectiousCard maxCardEntity = this.lambdaQuery() - .select(InfectiousCard::getCardNo) // 只查询编号列 - .likeRight(InfectiousCard::getCardNo, prefix) // 匹配前缀 (prefix%) - .orderByDesc(InfectiousCard::getCardNo) // 按编号降序 - .last("LIMIT 1") // 取最大的一条 - .one(); // 获取单条实体 - - String maxCardNo = (maxCardEntity != null) ? maxCardEntity.getCardNo() : null; - // --- 重构部分结束 --- - - int nextSerial = 1; - if (maxCardNo != null && maxCardNo.length() >= 4) { - String lastFour = maxCardNo.substring(maxCardNo.length() - 4); - try { - nextSerial = Integer.parseInt(lastFour) + 1; - } catch (NumberFormatException e) { - // 流水号解析失败,使用默认值1 - } - } - return prefix + String.format("%04d", nextSerial); - } - - @Override - public boolean saveInfectiousCard(InfectiousCard infectiousCard) { - // 设置默认值 - if (infectiousCard.getStatus() == null) { - infectiousCard.setStatus(0); - } - - if (infectiousCard.getReportDate() == null) { - infectiousCard.setReportDate(LocalDate.now()); - } - - if (infectiousCard.getCreateTime() == null) { - infectiousCard.setCreateTime(LocalDateTime.now()); - } else { - infectiousCard.setUpdateTime(LocalDateTime.now()); - } - - return this.saveOrUpdate(infectiousCard); - } -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationConfirmation.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationConfirmation.java deleted file mode 100644 index 15440769..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationConfirmation.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.openhis.consultation.domain; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import java.io.Serializable; -import java.time.LocalDateTime; -import lombok.Data; - -/** - * 会诊确认表 - */ -@TableName(value = "consultation_confirmation") -@Data -public class ConsultationConfirmation implements Serializable { - /** - * 主键ID - */ - @TableId(value = "id", type = IdType.AUTO) - private Long id; - - /** - * 会诊申请ID - */ - @TableField(value = "consultation_request_id") - private Long consultationRequestId; - - /** - * 确认科室ID - */ - @TableField(value = "confirming_department_id") - private Long confirmingDepartmentId; - - /** - * 确认科室名称 - */ - @TableField(value = "confirming_department_name") - private String confirmingDepartmentName; - - /** - * 确认医生ID - */ - @TableField(value = "confirming_doctor_id") - private Long confirmingDoctorId; - - /** - * 确认医生姓名 - */ - @TableField(value = "confirming_doctor_name") - private String confirmingDoctorName; - - /** - * 确认状态:1-待确认,2-同意,3-拒绝 - */ - @TableField(value = "confirmation_status") - private Integer confirmationStatus; - - /** - * 确认/拒绝理由 - */ - @TableField(value = "confirmation_reason") - private String confirmationReason; - - /** - * 确认日期 - */ - @TableField(value = "confirmation_date") - private LocalDateTime confirmationDate; - - /** - * 会诊意见 - */ - @TableField(value = "consultation_opinion") - private String consultationOpinion; - - /** - * 会诊确认参加医师 - */ - @TableField(value = "confirming_physician_participation") - private String confirmingPhysicianParticipation; - - /** - * 所属医生 - */ - @TableField(value = "confirming_physician_name") - private String confirmingPhysicianName; - - /** - * 代表科室 - */ - @TableField(value = "confirming_department_name") - private String confirmingDepartmentNameField; - - /** - * 签名医生 - */ - @TableField(value = "signature") - private String signature; - - /** - * 签名时间 - */ - @TableField(value = "signature_date") - private LocalDateTime signatureDate; - - /** - * 创建人ID - */ - @TableField(value = "creator_id") - private Long creatorId; - - /** - * 创建人姓名 - */ - @TableField(value = "creator_name") - private String creatorName; - - /** - * 创建时间 - */ - @TableField(value = "create_time") - private LocalDateTime createTime; - - /** - * 更新人ID - */ - @TableField(value = "updater_id") - private Long updaterId; - - /** - * 更新人姓名 - */ - @TableField(value = "updater_name") - private String updaterName; - - /** - * 更新时间 - */ - @TableField(value = "update_time") - private LocalDateTime updateTime; - - /** - * 有效标志:1-有效,0-无效 - */ - @TableField(value = "valid_flag") - private Integer validFlag; - - /** - * 租户ID - */ - @TableField(value = "tenant_id") - private Integer tenantId; - - private static final long serialVersionUID = 1L; -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationRecord.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationRecord.java deleted file mode 100644 index 474a6101..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationRecord.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.openhis.consultation.domain; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import java.io.Serializable; -import java.time.LocalDateTime; -import lombok.Data; - -/** - * 会诊记录表 - */ -@TableName(value = "consultation_record") -@Data -public class ConsultationRecord implements Serializable { - /** - * 主键ID - */ - @TableId(value = "id", type = IdType.AUTO) - private Long id; - - /** - * 会诊申请ID - */ - @TableField(value = "consultation_request_id") - private Long consultationRequestId; - - /** - * 参与医生ID - */ - @TableField(value = "participant_doctor_id") - private Long participantDoctorId; - - /** - * 参与医生姓名 - */ - @TableField(value = "participant_doctor_name") - private String participantDoctorName; - - /** - * 参与科室ID - */ - @TableField(value = "participant_department_id") - private Long participantDepartmentId; - - /** - * 参与科室名称 - */ - @TableField(value = "participant_department_name") - private String participantDepartmentName; - - /** - * 会诊意见 - */ - @TableField(value = "opinion") - private String opinion; - - /** - * 会诊建议 - */ - @TableField(value = "suggestion") - private String suggestion; - - /** - * 记录日期 - */ - @TableField(value = "record_date") - private LocalDateTime recordDate; - - /** - * 创建人ID - */ - @TableField(value = "creator_id") - private Long creatorId; - - /** - * 创建人姓名 - */ - @TableField(value = "creator_name") - private String creatorName; - - /** - * 创建时间 - */ - @TableField(value = "create_time") - private LocalDateTime createTime; - - /** - * 更新人ID - */ - @TableField(value = "updater_id") - private Long updaterId; - - /** - * 更新人姓名 - */ - @TableField(value = "updater_name") - private String updaterName; - - /** - * 更新时间 - */ - @TableField(value = "update_time") - private LocalDateTime updateTime; - - /** - * 有效标志:1-有效,0-无效 - */ - @TableField(value = "valid_flag") - private Integer validFlag; - - /** - * 租户ID - */ - @TableField(value = "tenant_id") - private Integer tenantId; - - private static final long serialVersionUID = 1L; -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationRequest.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationRequest.java deleted file mode 100644 index 58f19063..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/domain/ConsultationRequest.java +++ /dev/null @@ -1,273 +0,0 @@ -package com.openhis.consultation.domain; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import org.apache.ibatis.type.Alias; - -import java.io.Serializable; -import java.time.LocalDateTime; - -/** - * 会诊申请表 - */ -@TableName(value = "consultation_request") -@Alias("domainConsultationRequest") -@Data -public class ConsultationRequest implements Serializable { - /** - * 主键ID - */ - @TableId(value = "id", type = IdType.AUTO) - private Long id; - - /** - * 会诊申请单号:CS+年月日时分秒+4位随机数 - */ - @TableField(value = "consultation_id") - private String consultationId; - - /** - * 患者ID(外键:adm_patient.id) - */ - @TableField(value = "patient_id") - private Long patientId; - - /** - * 门诊就诊流水号(外键:adm_encounter.id) - */ - @TableField(value = "encounter_id") - private Long encounterId; - - /** - * 关联的医嘱ID(外键:wor_service_request.id) - */ - @TableField(value = "order_id") - private Long orderId; - - /** - * 患者姓名 - */ - @TableField(value = "patient_name") - private String patientName; - - /** - * 患者病历号 - */ - @TableField(value = "patient_bus_no") - private String patientBusNo; - - /** - * 患者就诊卡号 - */ - @TableField(value = "patient_identifier_no") - private String patientIdentifierNo; - - /** - * 性别 - */ - @TableField(value = "gender_enum") - private Integer genderEnum; - - /** - * 年龄 - */ - @TableField(value = "age") - private Integer age; - - /** - * 申请会诊的科室名称 - */ - @TableField(value = "department") - private String department; - - /** - * 申请科室ID - */ - @TableField(value = "department_id") - private Long departmentId; - - /** - * 申请会诊的医生姓名 - */ - @TableField(value = "requesting_physician") - private String requestingPhysician; - - /** - * 申请医生ID - */ - @TableField(value = "requesting_physician_id") - private Long requestingPhysicianId; - - /** - * 会诊申请时间 - */ - @TableField(value = "consultation_request_date") - private LocalDateTime consultationRequestDate; - - /** - * 会诊邀请对象 - */ - @TableField(value = "invited_object") - private String invitedObject; - - /** - * 会诊时间 - */ - @TableField(value = "consultation_date") - private LocalDateTime consultationDate; - - /** - * 简要病史及会诊目的 - */ - @TableField(value = "consultation_purpose") - private String consultationPurpose; - - /** - * 门诊诊断 - */ - @TableField(value = "provisional_diagnosis") - private String provisionalDiagnosis; - - /** - * 会诊意见 - */ - @TableField(value = "consultation_opinion") - private String consultationOpinion; - - /** - * 会诊状态:0-新开,10-已提交,20-已确认,30-已签名,40-已完成,50-已取消 - */ - @TableField(value = "consultation_status") - private Integer consultationStatus; - - /** - * 是否紧急:一般/紧急 - */ - @TableField(value = "consultation_urgency") - private String consultationUrgency; - - /** - * 提交会诊的医生姓名 - */ - @TableField(value = "confirming_physician") - private String confirmingPhysician; - - /** - * 提交会诊的医生ID - */ - @TableField(value = "confirming_physician_id") - private Long confirmingPhysicianId; - - /** - * 提交会诊日期 - */ - @TableField(value = "confirming_date") - private LocalDateTime confirmingDate; - - /** - * 结束会诊医生姓名/签名医生 - */ - @TableField(value = "signature") - private String signature; - - /** - * 结束会诊医生ID - */ - @TableField(value = "signature_physician_id") - private Long signaturePhysicianId; - - /** - * 结束会诊日期/签名时间 - */ - @TableField(value = "signature_date") - private LocalDateTime signatureDate; - - /** - * 作废会诊日期 - */ - @TableField(value = "cancel_nature_date") - private LocalDateTime cancelNatureDate; - - /** - * 作废原因 - */ - @TableField(value = "cancel_reason") - private String cancelReason; - - /** - * 创建时间 - */ - @TableField(value = "create_time") - private LocalDateTime createTime; - - /** - * 更新时间 - */ - @TableField(value = "update_time") - private LocalDateTime updateTime; - - /** - * 创建人 - */ - @TableField(value = "create_by") - private String createBy; - - /** - * 更新人 - */ - @TableField(value = "update_by") - private String updateBy; - - /** - * 租户ID - */ - @TableField(value = "tenant_id") - private Long tenantId; - - /** - * 逻辑删除标识:0-未删除,1-已删除 - */ - @TableField(value = "is_deleted") - private Integer isDeleted; - - /** - * 备注 - */ - @TableField(value = "remark") - private String remark; - - /** - * 会诊项目ID,关联wor_activity_definition表,用于确定会诊类型和价格 - */ - @TableField(value = "consultation_activity_id") - private Long consultationActivityId; - - /** - * 会诊项目名称,如:院内会诊、远程会诊等 - */ - @TableField(value = "consultation_activity_name") - private String consultationActivityName; - - /** - * 确认会诊的医生姓名 - */ - @TableField(value = "confirming_physician_name") - private String confirmingPhysicianName; - - /** - * 代表科室 - */ - @TableField(value = "confirming_department_name") - private String confirmingDepartmentName; - - /** - * 会诊确认参加医师 - */ - @TableField(value = "confirming_physician_participation") - private String confirmingPhysicianParticipation; - - private static final long serialVersionUID = 1L; -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationConfirmationMapper.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationConfirmationMapper.java deleted file mode 100644 index c9da7925..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationConfirmationMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.openhis.consultation.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.openhis.consultation.domain.ConsultationConfirmation; -import org.apache.ibatis.annotations.Mapper; - -/** - * 会诊确认 Mapper接口 - * - * @author his - */ -@Mapper -public interface ConsultationConfirmationMapper extends BaseMapper { -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationRecordMapper.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationRecordMapper.java deleted file mode 100644 index 6e7d9c9b..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/mapper/ConsultationRecordMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.openhis.consultation.mapper; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.openhis.consultation.domain.ConsultationRecord; -import org.apache.ibatis.annotations.Mapper; - -/** - * 会诊记录 Mapper接口 - * - * @author his - */ -@Mapper -public interface ConsultationRecordMapper extends BaseMapper { -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationConfirmationService.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationConfirmationService.java deleted file mode 100644 index dbfc1528..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationConfirmationService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.openhis.consultation.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.openhis.consultation.domain.ConsultationConfirmation; - -/** - * 会诊确认 服务层 - * - * @author his - */ -public interface IConsultationConfirmationService extends IService { -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationRecordService.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationRecordService.java deleted file mode 100644 index bc33fb65..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationRecordService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.openhis.consultation.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.openhis.consultation.domain.ConsultationRecord; - -/** - * 会诊记录 服务层 - * - * @author his - */ -public interface IConsultationRecordService extends IService { -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationRequestService.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationRequestService.java deleted file mode 100644 index a3c08d0e..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/IConsultationRequestService.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.openhis.consultation.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.openhis.consultation.domain.ConsultationRequest; - -/** - * 会诊申请 服务层 - * - * @author his - */ -public interface IConsultationRequestService extends IService { - /** - * 提交会诊申请 - */ - int submitConsultation(Long id, Long userId, String userName); - - /** - * 取消提交会诊申请 - */ - int cancelSubmitConsultation(Long id); - - /** - * 结束会诊申请 - */ - int endConsultation(Long id, Long userId, String userName); - - /** - * 作废会诊申请 - */ - int cancelConsultation(Long id, Long userId, String userName); - - /** - * 确认会诊 - */ - int confirmConsultation(Long id, String physicianName, String physicianId, String opinion); - - /** - * 签名会诊 - */ - int signConsultation(Long id, String signature, Long userId, String userName); -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationConfirmationServiceImpl.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationConfirmationServiceImpl.java deleted file mode 100644 index 42bdb7fc..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationConfirmationServiceImpl.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.openhis.consultation.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.openhis.consultation.domain.ConsultationConfirmation; -import com.openhis.consultation.mapper.ConsultationConfirmationMapper; -import com.openhis.consultation.service.IConsultationConfirmationService; -import org.springframework.stereotype.Service; - -/** - * 会诊确认 服务层实现 - * - * @author his - */ -@Service -public class ConsultationConfirmationServiceImpl extends ServiceImpl implements IConsultationConfirmationService { -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationRecordServiceImpl.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationRecordServiceImpl.java deleted file mode 100644 index efd33342..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationRecordServiceImpl.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.openhis.consultation.service.impl; - -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.openhis.consultation.domain.ConsultationRecord; -import com.openhis.consultation.mapper.ConsultationRecordMapper; -import com.openhis.consultation.service.IConsultationRecordService; -import org.springframework.stereotype.Service; - -/** - * 会诊记录 服务层实现 - * - * @author his - */ -@Service -public class ConsultationRecordServiceImpl extends ServiceImpl implements IConsultationRecordService { -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationRequestServiceImpl.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationRequestServiceImpl.java deleted file mode 100644 index e0f3ba66..00000000 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/consultation/service/impl/ConsultationRequestServiceImpl.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.openhis.consultation.service.impl; - -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.openhis.consultation.domain.ConsultationRequest; -import com.openhis.consultation.mapper.ConsultationRequestMapper; -import com.openhis.consultation.service.IConsultationRequestService; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; - -/** - * 会诊申请 服务层实现 - * - * @author his - */ -@Service -public class ConsultationRequestServiceImpl extends ServiceImpl implements IConsultationRequestService { - - @Override - public int submitConsultation(Long id, Long userId, String userName) { - ConsultationRequest request = this.getById(id); - if (request != null && request.getConsultationStatus() == 0) { // 仅当状态为"新开"时可提交 - request.setConsultationStatus(10); // 设置为"已提交" - request.setConfirmingPhysician(userName); - request.setConfirmingPhysicianId(userId); - request.setConfirmingDate(LocalDateTime.now()); - return this.updateById(request) ? 1 : 0; - } - return 0; // 提交失败 - } - - @Override - public int cancelSubmitConsultation(Long id) { - ConsultationRequest request = this.getById(id); - if (request != null && request.getConsultationStatus() == 10) { // 仅当状态为"已提交"时可取消提交 - request.setConsultationStatus(0); // 设置为"新开" - request.setConfirmingPhysician(null); - request.setConfirmingPhysicianId(null); - request.setConfirmingDate(null); - return this.updateById(request) ? 1 : 0; - } - return 0; // 取消提交失败 - } - - @Override - public int endConsultation(Long id, Long userId, String userName) { - ConsultationRequest request = this.getById(id); - if (request != null && request.getConsultationStatus() == 30) { // 仅当状态为"已签名"时可结束 - request.setConsultationStatus(40); // 设置为"已完成" - request.setSignature(userName); - request.setSignatureDate(LocalDateTime.now()); - return this.updateById(request) ? 1 : 0; - } - return 0; // 结束失败 - } - - @Override - public int cancelConsultation(Long id, Long userId, String userName) { - ConsultationRequest request = this.getById(id); - if (request != null && request.getConsultationStatus() != 40) { // 未完成的申请可作废 - request.setConsultationStatus(50); // 设置为"已取消" - request.setSignature(userName); - request.setSignatureDate(LocalDateTime.now()); - return this.updateById(request) ? 1 : 0; - } - return 0; // 作废失败 - } - - @Override - public int confirmConsultation(Long id, String physicianName, String physicianId, String opinion) { - ConsultationRequest request = this.getById(id); - if (request != null && request.getConsultationStatus() != 40) { // 未完成的申请可确认 - request.setConsultationStatus(20); // 设置为"已确认" - request.setConfirmingPhysicianName(physicianName); - request.setConsultationOpinion(opinion); - return this.updateById(request) ? 1 : 0; - } - return 0; // 确认失败 - } - - @Override - public int signConsultation(Long id, String signature, Long userId, String userName) { - ConsultationRequest request = this.getById(id); - if (request != null && request.getConsultationStatus() == 20) { // 仅当状态为"已确认"时可签名 - request.setConsultationStatus(30); // 设置为"已签名" - request.setSignature(signature); - request.setSignatureDate(LocalDateTime.now()); - return this.updateById(request) ? 1 : 0; - } - return 0; // 签名失败 - } -} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationConfirmationMapper.xml b/openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationConfirmationMapper.xml deleted file mode 100644 index ac8afe62..00000000 --- a/openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationConfirmationMapper.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationRecordMapper.xml b/openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationRecordMapper.xml deleted file mode 100644 index 7e1fcdf9..00000000 --- a/openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationRecordMapper.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml b/openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml deleted file mode 100644 index b12bd06a..00000000 --- a/openhis-server-new/openhis-domain/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/openhis-server-new/optimize_consultation_tables_postgresql.sql b/openhis-server-new/optimize_consultation_tables_postgresql.sql deleted file mode 100644 index 74888a23..00000000 --- a/openhis-server-new/optimize_consultation_tables_postgresql.sql +++ /dev/null @@ -1,80 +0,0 @@ --- 优化会诊申请表结构以满足需求文档 (PostgreSQL格式) --- 添加缺失的字段 -DO $$ -BEGIN - -- 检查并添加字段 - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'visit_id') THEN - ALTER TABLE consultation_request ADD COLUMN visit_id BIGINT; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'order_id') THEN - ALTER TABLE consultation_request ADD COLUMN order_id BIGINT; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'patient_name') THEN - ALTER TABLE consultation_request ADD COLUMN patient_name VARCHAR(100); - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'gender') THEN - ALTER TABLE consultation_request ADD COLUMN gender VARCHAR(10); - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'age') THEN - ALTER TABLE consultation_request ADD COLUMN age INTEGER; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'provisional_diagnosis') THEN - ALTER TABLE consultation_request ADD COLUMN provisional_diagnosis TEXT; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'consultation_opinion') THEN - ALTER TABLE consultation_request ADD COLUMN consultation_opinion TEXT; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'confirming_physician') THEN - ALTER TABLE consultation_request ADD COLUMN confirming_physician VARCHAR(100); - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'confirming_physician_id') THEN - ALTER TABLE consultation_request ADD COLUMN confirming_physician_id BIGINT; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'confirming_date') THEN - ALTER TABLE consultation_request ADD COLUMN confirming_date TIMESTAMP; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'consultation_urgency') THEN - ALTER TABLE consultation_request ADD COLUMN consultation_urgency VARCHAR(20) DEFAULT '一般'; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'consultation_status') THEN - ALTER TABLE consultation_request ADD COLUMN consultation_status INTEGER DEFAULT 0; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'consultation_request_date') THEN - ALTER TABLE consultation_request ADD COLUMN consultation_request_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'confirming_physician_name') THEN - ALTER TABLE consultation_request ADD COLUMN confirming_physician_name VARCHAR(100); - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'signature') THEN - ALTER TABLE consultation_request ADD COLUMN signature VARCHAR(100); - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'signature_date') THEN - ALTER TABLE consultation_request ADD COLUMN signature_date TIMESTAMP; - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'confirming_department_name') THEN - ALTER TABLE consultation_request ADD COLUMN confirming_department_name VARCHAR(100); - END IF; - - IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'consultation_request' AND column_name = 'confirming_physician_participation') THEN - ALTER TABLE consultation_request ADD COLUMN confirming_physician_participation VARCHAR(100); - END IF; - - -- 更新注释 - COMMENT ON COLUMN consultation_request.consultation_status IS '会诊状态:0-新开,10-已提交,20-已确认,30-已签名,40-已完成,50-已取消'; -END $$; \ No newline at end of file diff --git a/openhis-server-new/sql/add_source_bill_no_to_adm_charge_item.sql b/openhis-server-new/sql/add_source_bill_no_to_adm_charge_item.sql deleted file mode 100644 index d983caf7..00000000 --- a/openhis-server-new/sql/add_source_bill_no_to_adm_charge_item.sql +++ /dev/null @@ -1,12 +0,0 @@ --- 在adm_charge_item表添加来源业务单据字段 --- 用于追溯费用是来源于手术计费 --- SourceBillNo = 手术申请单号 - --- 添加SourceBillNo字段 -ALTER TABLE adm_charge_item ADD COLUMN IF NOT EXISTS source_bill_no VARCHAR(64); - --- 添加注释 -COMMENT ON COLUMN adm_charge_item.source_bill_no IS '来源业务单据号(如手术申请单号)'; - --- 创建索引以提高查询性能 -CREATE INDEX IF NOT EXISTS idx_source_bill_no ON adm_charge_item(source_bill_no); diff --git a/openhis-server-new/sql/create_yb_day_end_settlement_table.sql b/openhis-server-new/sql/create_yb_day_end_settlement_table.sql deleted file mode 100644 index 8022b380..00000000 --- a/openhis-server-new/sql/create_yb_day_end_settlement_table.sql +++ /dev/null @@ -1,71 +0,0 @@ --- 创建日结医保结算表 -CREATE TABLE IF NOT EXISTS yb_day_end_settlement ( - id BIGSERIAL PRIMARY KEY, - settlement_no VARCHAR(64) NOT NULL, - settlement_date DATE NOT NULL, - settlement_type VARCHAR(20) DEFAULT 'daily', - insurance_type VARCHAR(50), - total_visits INTEGER DEFAULT 0, - total_amount NUMERIC(15,2) DEFAULT 0.00, - insurance_pay_amount NUMERIC(15,2) DEFAULT 0.00, - account_pay_amount NUMERIC(15,2) DEFAULT 0.00, - personal_pay_amount NUMERIC(15,2) DEFAULT 0.00, - fund_pay_sum_amount NUMERIC(15,2) DEFAULT 0.00, - status CHAR(1) DEFAULT '0', - operator VARCHAR(50), - remark VARCHAR(500), - create_by VARCHAR(64), - create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - update_by VARCHAR(64), - update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); - --- 添加注释 -COMMENT ON TABLE yb_day_end_settlement IS '日结医保结算表'; -COMMENT ON COLUMN yb_day_end_settlement.settlement_no IS '结算单号'; -COMMENT ON COLUMN yb_day_end_settlement.settlement_date IS '结算日期'; -COMMENT ON COLUMN yb_day_end_settlement.settlement_type IS '结算类型 (daily, weekly, monthly)'; -COMMENT ON COLUMN yb_day_end_settlement.insurance_type IS '医保类型 (urbanEmployee, urbanRuralResident, newRuralCooperative, selfPaid)'; -COMMENT ON COLUMN yb_day_end_settlement.total_visits IS '总人次'; -COMMENT ON COLUMN yb_day_end_settlement.total_amount IS '总金额'; -COMMENT ON COLUMN yb_day_end_settlement.insurance_pay_amount IS '医保统筹支付金额'; -COMMENT ON COLUMN yb_day_end_settlement.account_pay_amount IS '个人账户支付金额'; -COMMENT ON COLUMN yb_day_end_settlement.personal_pay_amount IS '个人自付金额'; -COMMENT ON COLUMN yb_day_end_settlement.fund_pay_sum_amount IS '医保基金支付总额'; -COMMENT ON COLUMN yb_day_end_settlement.status IS '状态 (0正常 1停用)'; -COMMENT ON COLUMN yb_day_end_settlement.operator IS '操作员'; -COMMENT ON COLUMN yb_day_end_settlement.remark IS '备注'; -COMMENT ON COLUMN yb_day_end_settlement.create_by IS '创建者'; -COMMENT ON COLUMN yb_day_end_settlement.create_time IS '创建时间'; -COMMENT ON COLUMN yb_day_end_settlement.update_by IS '更新者'; -COMMENT ON COLUMN yb_day_end_settlement.update_time IS '更新时间'; - --- 创建索引 -CREATE INDEX IF NOT EXISTS idx_settlement_date ON yb_day_end_settlement(settlement_date); -CREATE INDEX IF NOT EXISTS idx_settlement_no ON yb_day_end_settlement(settlement_no); -CREATE INDEX IF NOT EXISTS idx_settlement_type ON yb_day_end_settlement(settlement_type); -CREATE INDEX IF NOT EXISTS idx_insurance_type ON yb_day_end_settlement(insurance_type); - --- 创建更新时间函数(如果不存在) -CREATE OR REPLACE FUNCTION update_modified_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.update_time = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ language 'plpgsql'; - --- 创建更新时间触发器 -DO $$ -BEGIN - DROP TRIGGER IF EXISTS update_yb_day_end_settlement_modtime ON yb_day_end_settlement; - CREATE TRIGGER update_yb_day_end_settlement_modtime - BEFORE UPDATE ON yb_day_end_settlement - FOR EACH ROW EXECUTE FUNCTION update_modified_column(); -END $$; - --- 插入示例数据 -INSERT INTO yb_day_end_settlement (settlement_no, settlement_date, settlement_type, insurance_type, total_visits, total_amount, insurance_pay_amount, account_pay_amount, personal_pay_amount, fund_pay_sum_amount, status, operator, remark, create_by) VALUES -('YBDS20260202001', '2026-02-02', 'daily', 'urbanEmployee', 150, 150000.00, 120000.00, 15000.00, 15000.00, 135000.00, '0', 'admin', '2026年2月2日城镇职工医保日结', 'admin'), -('YBDS20260202002', '2026-02-02', 'daily', 'urbanRuralResident', 80, 80000.00, 64000.00, 8000.00, 8000.00, 72000.00, '0', 'admin', '2026年2月2日城乡居民医保日结', 'admin'), -('YBDS20260202003', '2026-02-02', 'daily', 'newRuralCooperative', 60, 60000.00, 48000.00, 6000.00, 6000.00, 54000.00, '0', 'admin', '2026年2月2日新农合医保日结', 'admin'); \ No newline at end of file diff --git a/openhis-server-new/sql/insert_yb_day_end_settlement_menu.sql b/openhis-server-new/sql/insert_yb_day_end_settlement_menu.sql deleted file mode 100644 index ba09bd03..00000000 --- a/openhis-server-new/sql/insert_yb_day_end_settlement_menu.sql +++ /dev/null @@ -1,27 +0,0 @@ --- 添加日结医保结算菜单项到系统菜单表 --- 假设医保管理模块的父级菜单ID为某个值,这里我们先查询医保管理相关的菜单ID - --- 首先查找医保相关的父菜单ID(如果没有找到,需要手动指定一个合适的父菜单ID) --- 偌设我们使用一个常见的父级ID,或者创建一个新的医保管理顶级菜单 - --- 添加医保管理顶级菜单(如果不存在的话) -INSERT INTO sys_menu -(menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) -SELECT '医保管理', 0, 7, 'ybmanagement', '', NULL, 'YbManagement', '1', '0', 'M', '0', '0', '', 'medication', 'admin', NOW(), 'admin', NOW(), '医保管理菜单' -WHERE NOT EXISTS ( - SELECT 1 FROM sys_menu WHERE menu_name = '医保管理' AND menu_type = 'M' -); - --- 获取医保管理菜单ID -DO $$ -DECLARE - yb_management_menu_id BIGINT; -BEGIN - SELECT menu_id INTO yb_management_menu_id FROM sys_menu WHERE menu_name = '医保管理' LIMIT 1; - - -- 添加日结医保结算子菜单 - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('日结医保结算', yb_management_menu_id, 1, 'dayEndMedicalInsuranceSettlement', 'ybmanagement/dayEndMedicalInsuranceSettlement/index', NULL, 'DayEndMedicalInsuranceSettlement', '1', '0', 'C', '0', '0', 'ybmanage:dayEndMedicalInsuranceSettlement:view', 'document', 'admin', NOW(), 'admin', NOW(), '日结医保结算菜单'); -END $$; \ No newline at end of file diff --git a/openhis-server-new/update_consultation_menu_postgresql.sql b/openhis-server-new/update_consultation_menu_postgresql.sql deleted file mode 100644 index d6c04f1b..00000000 --- a/openhis-server-new/update_consultation_menu_postgresql.sql +++ /dev/null @@ -1,118 +0,0 @@ --- 更新会诊管理模块的菜单项,以满足需求文档要求 (PostgreSQL格式) -DO $$ -DECLARE - consultation_menu_id BIGINT; - consultation_request_menu_id BIGINT; - consultation_confirmation_menu_id BIGINT; - consultation_record_menu_id BIGINT; -BEGIN - -- 获取会诊管理菜单ID,如果不存在则创建 - SELECT menu_id INTO consultation_menu_id FROM sys_menu WHERE menu_name = '会诊管理' LIMIT 1; - IF NOT FOUND THEN - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊管理', 0, 10, 'consultationmanagement', '', '', 'ConsultationManagement', '1', '0', 'M', '0', '0', '', 'operation', 'admin', NOW(), 'admin', NOW(), '会诊管理菜单'); - - SELECT menu_id INTO consultation_menu_id FROM sys_menu WHERE menu_name = '会诊管理' LIMIT 1; - END IF; - - -- 获取会诊申请菜单ID,如果不存在则创建 - SELECT menu_id INTO consultation_request_menu_id FROM sys_menu WHERE menu_name = '会诊申请' AND parent_id = consultation_menu_id LIMIT 1; - IF NOT FOUND THEN - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊申请', consultation_menu_id, 1, 'consultationrequest', 'clinicmanagement/consultationrequest/index', '', 'ConsultationRequest', '1', '0', 'C', '0', '0', 'consultation:request:view', 'form', 'admin', NOW(), 'admin', NOW(), '会诊申请菜单'); - - SELECT menu_id INTO consultation_request_menu_id FROM sys_menu WHERE menu_name = '会诊申请' AND parent_id = consultation_menu_id LIMIT 1; - END IF; - - -- 更新或插入会诊申请按钮权限 - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请查询', consultation_request_menu_id, 1, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:query', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:query' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请新增', consultation_request_menu_id, 2, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:add', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:add' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请修改', consultation_request_menu_id, 3, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:edit', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:edit' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请删除', consultation_request_menu_id, 4, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:remove', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:remove' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请提交', consultation_request_menu_id, 5, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:submit', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:submit' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请结束', consultation_request_menu_id, 6, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:end', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:end' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请作废', consultation_request_menu_id, 7, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:cancel', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:cancel' AND parent_id = consultation_request_menu_id); - - -- 获取会诊确认菜单ID,如果不存在则创建 - SELECT menu_id INTO consultation_confirmation_menu_id FROM sys_menu WHERE menu_name = '会诊确认' AND parent_id = consultation_menu_id LIMIT 1; - IF NOT FOUND THEN - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊确认', consultation_menu_id, 2, 'consultationconfirmation', 'clinicmanagement/consultationconfirmation/index', '', 'ConsultationConfirmation', '1', '0', 'C', '0', '0', 'consultation:confirmation:view', 'form', 'admin', NOW(), 'admin', NOW(), '会诊确认菜单'); - - SELECT menu_id INTO consultation_confirmation_menu_id FROM sys_menu WHERE menu_name = '会诊确认' AND parent_id = consultation_menu_id LIMIT 1; - END IF; - - -- 更新或插入会诊确认按钮权限 - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊确认查询', consultation_confirmation_menu_id, 1, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:confirmation:query', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:confirmation:query' AND parent_id = consultation_confirmation_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊确认新增', consultation_confirmation_menu_id, 2, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:confirmation:add', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:confirmation:add' AND parent_id = consultation_confirmation_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊确认修改', consultation_confirmation_menu_id, 3, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:confirmation:edit', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:confirmation:edit' AND parent_id = consultation_confirmation_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊确认删除', consultation_confirmation_menu_id, 4, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:confirmation:remove', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:confirmation:remove' AND parent_id = consultation_confirmation_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊确认操作', consultation_confirmation_menu_id, 5, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:confirmation:confirm', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:confirmation:confirm' AND parent_id = consultation_confirmation_menu_id); - - -- 获取会诊记录菜单ID,如果不存在则创建 - SELECT menu_id INTO consultation_record_menu_id FROM sys_menu WHERE menu_name = '会诊记录' AND parent_id = consultation_menu_id LIMIT 1; - IF NOT FOUND THEN - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊记录', consultation_menu_id, 3, 'consultationrecord', 'clinicmanagement/consultationrecord/index', '', 'ConsultationRecord', '1', '0', 'C', '0', '0', 'consultation:record:view', 'form', 'admin', NOW(), 'admin', NOW(), '会诊记录菜单'); - END IF; - - -- 更新或插入会诊记录按钮权限 - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊记录查询', consultation_record_menu_id, 1, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:record:query', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:record:query' AND parent_id = consultation_record_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊记录新增', consultation_record_menu_id, 2, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:record:add', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:record:add' AND parent_id = consultation_record_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊记录修改', consultation_record_menu_id, 3, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:record:edit', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:record:edit' AND parent_id = consultation_record_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊记录删除', consultation_record_menu_id, 4, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:record:remove', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:record:remove' AND parent_id = consultation_record_menu_id); - -END $$; \ No newline at end of file diff --git a/openhis-server-new/update_consultation_menu_postgresql_corrected.sql b/openhis-server-new/update_consultation_menu_postgresql_corrected.sql deleted file mode 100644 index d6c04f1b..00000000 --- a/openhis-server-new/update_consultation_menu_postgresql_corrected.sql +++ /dev/null @@ -1,118 +0,0 @@ --- 更新会诊管理模块的菜单项,以满足需求文档要求 (PostgreSQL格式) -DO $$ -DECLARE - consultation_menu_id BIGINT; - consultation_request_menu_id BIGINT; - consultation_confirmation_menu_id BIGINT; - consultation_record_menu_id BIGINT; -BEGIN - -- 获取会诊管理菜单ID,如果不存在则创建 - SELECT menu_id INTO consultation_menu_id FROM sys_menu WHERE menu_name = '会诊管理' LIMIT 1; - IF NOT FOUND THEN - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊管理', 0, 10, 'consultationmanagement', '', '', 'ConsultationManagement', '1', '0', 'M', '0', '0', '', 'operation', 'admin', NOW(), 'admin', NOW(), '会诊管理菜单'); - - SELECT menu_id INTO consultation_menu_id FROM sys_menu WHERE menu_name = '会诊管理' LIMIT 1; - END IF; - - -- 获取会诊申请菜单ID,如果不存在则创建 - SELECT menu_id INTO consultation_request_menu_id FROM sys_menu WHERE menu_name = '会诊申请' AND parent_id = consultation_menu_id LIMIT 1; - IF NOT FOUND THEN - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊申请', consultation_menu_id, 1, 'consultationrequest', 'clinicmanagement/consultationrequest/index', '', 'ConsultationRequest', '1', '0', 'C', '0', '0', 'consultation:request:view', 'form', 'admin', NOW(), 'admin', NOW(), '会诊申请菜单'); - - SELECT menu_id INTO consultation_request_menu_id FROM sys_menu WHERE menu_name = '会诊申请' AND parent_id = consultation_menu_id LIMIT 1; - END IF; - - -- 更新或插入会诊申请按钮权限 - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请查询', consultation_request_menu_id, 1, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:query', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:query' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请新增', consultation_request_menu_id, 2, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:add', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:add' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请修改', consultation_request_menu_id, 3, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:edit', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:edit' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请删除', consultation_request_menu_id, 4, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:remove', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:remove' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请提交', consultation_request_menu_id, 5, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:submit', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:submit' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请结束', consultation_request_menu_id, 6, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:end', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:end' AND parent_id = consultation_request_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊申请作废', consultation_request_menu_id, 7, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:request:cancel', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:request:cancel' AND parent_id = consultation_request_menu_id); - - -- 获取会诊确认菜单ID,如果不存在则创建 - SELECT menu_id INTO consultation_confirmation_menu_id FROM sys_menu WHERE menu_name = '会诊确认' AND parent_id = consultation_menu_id LIMIT 1; - IF NOT FOUND THEN - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊确认', consultation_menu_id, 2, 'consultationconfirmation', 'clinicmanagement/consultationconfirmation/index', '', 'ConsultationConfirmation', '1', '0', 'C', '0', '0', 'consultation:confirmation:view', 'form', 'admin', NOW(), 'admin', NOW(), '会诊确认菜单'); - - SELECT menu_id INTO consultation_confirmation_menu_id FROM sys_menu WHERE menu_name = '会诊确认' AND parent_id = consultation_menu_id LIMIT 1; - END IF; - - -- 更新或插入会诊确认按钮权限 - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊确认查询', consultation_confirmation_menu_id, 1, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:confirmation:query', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:confirmation:query' AND parent_id = consultation_confirmation_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊确认新增', consultation_confirmation_menu_id, 2, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:confirmation:add', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:confirmation:add' AND parent_id = consultation_confirmation_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊确认修改', consultation_confirmation_menu_id, 3, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:confirmation:edit', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:confirmation:edit' AND parent_id = consultation_confirmation_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊确认删除', consultation_confirmation_menu_id, 4, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:confirmation:remove', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:confirmation:remove' AND parent_id = consultation_confirmation_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊确认操作', consultation_confirmation_menu_id, 5, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:confirmation:confirm', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:confirmation:confirm' AND parent_id = consultation_confirmation_menu_id); - - -- 获取会诊记录菜单ID,如果不存在则创建 - SELECT menu_id INTO consultation_record_menu_id FROM sys_menu WHERE menu_name = '会诊记录' AND parent_id = consultation_menu_id LIMIT 1; - IF NOT FOUND THEN - INSERT INTO sys_menu - (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - VALUES - ('会诊记录', consultation_menu_id, 3, 'consultationrecord', 'clinicmanagement/consultationrecord/index', '', 'ConsultationRecord', '1', '0', 'C', '0', '0', 'consultation:record:view', 'form', 'admin', NOW(), 'admin', NOW(), '会诊记录菜单'); - END IF; - - -- 更新或插入会诊记录按钮权限 - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊记录查询', consultation_record_menu_id, 1, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:record:query', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:record:query' AND parent_id = consultation_record_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊记录新增', consultation_record_menu_id, 2, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:record:add', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:record:add' AND parent_id = consultation_record_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊记录修改', consultation_record_menu_id, 3, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:record:edit', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:record:edit' AND parent_id = consultation_record_menu_id); - - INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) - SELECT '会诊记录删除', consultation_record_menu_id, 4, '', '', '', '', '1', '0', 'F', '0', '0', 'consultation:record:remove', '#', 'admin', NOW(), 'admin', NOW(), '' - WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'consultation:record:remove' AND parent_id = consultation_record_menu_id); - -END $$; \ No newline at end of file diff --git a/openhis-ui-vue3/src/layout/components/Sidebar/SidebarItem.vue b/openhis-ui-vue3/src/layout/components/Sidebar/SidebarItem.vue index 9bd3b986..c174a983 100644 --- a/openhis-ui-vue3/src/layout/components/Sidebar/SidebarItem.vue +++ b/openhis-ui-vue3/src/layout/components/Sidebar/SidebarItem.vue @@ -16,12 +16,12 @@ diff --git a/openhis-ui-vue3/vite.config.js.timestamp-1770788344262-2691367b6bac9.mjs b/openhis-ui-vue3/vite.config.js.timestamp-1770788344262-2691367b6bac9.mjs new file mode 100644 index 00000000..fdabf5c5 --- /dev/null +++ b/openhis-ui-vue3/vite.config.js.timestamp-1770788344262-2691367b6bac9.mjs @@ -0,0 +1,143 @@ +// vite.config.js +import { defineConfig, loadEnv } from "file:///F:/Java/Code/jkhl/openhis-ui-vue3/node_modules/vite/dist/node/index.js"; +import path2 from "path"; + +// vite/plugins/index.js +import vue from "file:///F:/Java/Code/jkhl/openhis-ui-vue3/node_modules/@vitejs/plugin-vue/dist/index.mjs"; + +// vite/plugins/auto-import.js +import autoImport from "file:///F:/Java/Code/jkhl/openhis-ui-vue3/node_modules/unplugin-auto-import/dist/vite.js"; +function createAutoImport() { + return autoImport({ + imports: [ + "vue", + "vue-router", + "pinia" + ], + dts: false + }); +} + +// vite/plugins/svg-icon.js +import { createSvgIconsPlugin } from "file:///F:/Java/Code/jkhl/openhis-ui-vue3/node_modules/vite-plugin-svg-icons/dist/index.mjs"; +import path from "path"; +function createSvgIcon(isBuild) { + return createSvgIconsPlugin({ + iconDirs: [path.resolve(process.cwd(), "src/assets/icons/svg")], + symbolId: "icon-[dir]-[name]", + svgoOptions: isBuild + }); +} + +// vite/plugins/compression.js +import compression from "file:///F:/Java/Code/jkhl/openhis-ui-vue3/node_modules/vite-plugin-compression/dist/index.mjs"; +function createCompression(env) { + const { VITE_BUILD_COMPRESS } = env; + const plugin = []; + if (VITE_BUILD_COMPRESS) { + const compressList = VITE_BUILD_COMPRESS.split(","); + if (compressList.includes("gzip")) { + plugin.push( + compression({ + ext: ".gz", + deleteOriginFile: false + }) + ); + } + if (compressList.includes("brotli")) { + plugin.push( + compression({ + ext: ".br", + algorithm: "brotliCompress", + deleteOriginFile: false + }) + ); + } + } + return plugin; +} + +// vite/plugins/setup-extend.js +import setupExtend from "file:///F:/Java/Code/jkhl/openhis-ui-vue3/node_modules/unplugin-vue-setup-extend-plus/dist/vite.js"; +function createSetupExtend() { + return setupExtend({}); +} + +// vite/plugins/index.js +function createVitePlugins(viteEnv, isBuild = false) { + const vitePlugins = [vue()]; + vitePlugins.push(createAutoImport()); + vitePlugins.push(createSetupExtend()); + vitePlugins.push(createSvgIcon(isBuild)); + isBuild && vitePlugins.push(...createCompression(viteEnv)); + return vitePlugins; +} + +// vite.config.js +var __vite_injected_original_dirname = "F:\\Java\\Code\\jkhl\\openhis-ui-vue3"; +var vite_config_default = defineConfig(({ mode, command }) => { + const env = loadEnv(mode, process.cwd()); + const { VITE_APP_ENV } = env; + return { + // define: { + // // enable hydration mismatch details in production build + // __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'true' + // }, + // 部署生产环境和开发环境下的URL。 + // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上 + // 例如 https://www.openHIS.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.openhis.vip/admin/,则设置 baseUrl 为 /admin/。 + base: VITE_APP_ENV === "production" ? "/" : "/", + plugins: createVitePlugins(env, command === "build"), + resolve: { + // https://cn.vitejs.dev/config/#resolve-alias + alias: { + // 设置路径 + "~": path2.resolve(__vite_injected_original_dirname, "./"), + // 设置别名 + "@": path2.resolve(__vite_injected_original_dirname, "./src") + }, + // https://cn.vitejs.dev/config/#resolve-extensions + extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"] + }, + // vite 相关配置 + server: { + port: 81, + host: true, + open: true, + proxy: { + // https://cn.vitejs.dev/config/#server-proxy + "/dev-api": { + target: "http://localhost:18080/openhis", + changeOrigin: true, + rewrite: (p) => p.replace(/^\/dev-api/, "") + }, + "/ybplugin": { + target: "http://localhost:5000", + changeOrigin: true, + rewrite: (p) => p.replace(/^\/ybplugin/, "") + } + } + }, + //fix:error:stdin>:7356:1: warning: "@charset" must be the first rule in the file + css: { + postcss: { + plugins: [ + { + postcssPlugin: "internal:charset-removal", + AtRule: { + charset: (atRule) => { + if (atRule.name === "charset") { + atRule.remove(); + } + } + } + } + ] + } + } + }; +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiLCAidml0ZS9wbHVnaW5zL2luZGV4LmpzIiwgInZpdGUvcGx1Z2lucy9hdXRvLWltcG9ydC5qcyIsICJ2aXRlL3BsdWdpbnMvc3ZnLWljb24uanMiLCAidml0ZS9wbHVnaW5zL2NvbXByZXNzaW9uLmpzIiwgInZpdGUvcGx1Z2lucy9zZXR1cC1leHRlbmQuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJGOlxcXFxKYXZhXFxcXENvZGVcXFxcamtobFxcXFxvcGVuaGlzLXVpLXZ1ZTNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkY6XFxcXEphdmFcXFxcQ29kZVxcXFxqa2hsXFxcXG9wZW5oaXMtdWktdnVlM1xcXFx2aXRlLmNvbmZpZy5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRjovSmF2YS9Db2RlL2praGwvb3Blbmhpcy11aS12dWUzL3ZpdGUuY29uZmlnLmpzXCI7LypcbiAqIEBBdXRob3I6IHNqamhcbiAqIEBEYXRlOiAyMDI1LTA0LTA5IDA5OjMzOjM1XG4gKiBARGVzY3JpcHRpb246XG4gKi9cbmltcG9ydCB7ZGVmaW5lQ29uZmlnLCBsb2FkRW52fSBmcm9tICd2aXRlJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IGNyZWF0ZVZpdGVQbHVnaW5zIGZyb20gJy4vdml0ZS9wbHVnaW5zJztcblxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZygoeyBtb2RlLCBjb21tYW5kIH0pID0+IHtcbiAgY29uc3QgZW52ID0gbG9hZEVudihtb2RlLCBwcm9jZXNzLmN3ZCgpKTtcbiAgY29uc3QgeyBWSVRFX0FQUF9FTlYgfSA9IGVudjtcbiAgcmV0dXJuIHtcbiAgICAvLyBkZWZpbmU6IHtcbiAgICAvLyAgIC8vIGVuYWJsZSBoeWRyYXRpb24gbWlzbWF0Y2ggZGV0YWlscyBpbiBwcm9kdWN0aW9uIGJ1aWxkXG4gICAgLy8gICBfX1ZVRV9QUk9EX0hZRFJBVElPTl9NSVNNQVRDSF9ERVRBSUxTX186ICd0cnVlJ1xuICAgIC8vIH0sXG4gICAgLy8gXHU5MEU4XHU3RjcyXHU3NTFGXHU0RUE3XHU3M0FGXHU1ODgzXHU1NDhDXHU1RjAwXHU1M0QxXHU3M0FGXHU1ODgzXHU0RTBCXHU3Njg0VVJMXHUzMDAyXG4gICAgLy8gXHU5RUQ4XHU4QkE0XHU2MEM1XHU1MUI1XHU0RTBCXHVGRjBDdml0ZSBcdTRGMUFcdTUwNDdcdThCQkVcdTRGNjBcdTc2ODRcdTVFOTRcdTc1MjhcdTY2MkZcdTg4QUJcdTkwRThcdTdGNzJcdTU3MjhcdTRFMDBcdTRFMkFcdTU3REZcdTU0MERcdTc2ODRcdTY4MzlcdThERUZcdTVGODRcdTRFMEFcbiAgICAvLyBcdTRGOEJcdTU5ODIgaHR0cHM6Ly93d3cub3BlbkhJUy52aXAvXHUzMDAyXHU1OTgyXHU2NzlDXHU1RTk0XHU3NTI4XHU4OEFCXHU5MEU4XHU3RjcyXHU1NzI4XHU0RTAwXHU0RTJBXHU1QjUwXHU4REVGXHU1Rjg0XHU0RTBBXHVGRjBDXHU0RjYwXHU1QzMxXHU5NzAwXHU4OTgxXHU3NTI4XHU4RkQ5XHU0RTJBXHU5MDA5XHU5ODc5XHU2MzA3XHU1QjlBXHU4RkQ5XHU0RTJBXHU1QjUwXHU4REVGXHU1Rjg0XHUzMDAyXHU0RjhCXHU1OTgyXHVGRjBDXHU1OTgyXHU2NzlDXHU0RjYwXHU3Njg0XHU1RTk0XHU3NTI4XHU4OEFCXHU5MEU4XHU3RjcyXHU1NzI4IGh0dHBzOi8vd3d3Lm9wZW5oaXMudmlwL2FkbWluL1x1RkYwQ1x1NTIxOVx1OEJCRVx1N0Y2RSBiYXNlVXJsIFx1NEUzQSAvYWRtaW4vXHUzMDAyXG4gICAgYmFzZTogVklURV9BUFBfRU5WID09PSAncHJvZHVjdGlvbicgPyAnLycgOiAnLycsXG4gICAgcGx1Z2luczogY3JlYXRlVml0ZVBsdWdpbnMoZW52LCBjb21tYW5kID09PSAnYnVpbGQnKSxcbiAgICByZXNvbHZlOiB7XG4gICAgICAvLyBodHRwczovL2NuLnZpdGVqcy5kZXYvY29uZmlnLyNyZXNvbHZlLWFsaWFzXG4gICAgICBhbGlhczoge1xuICAgICAgICAvLyBcdThCQkVcdTdGNkVcdThERUZcdTVGODRcbiAgICAgICAgJ34nOiBwYXRoLnJlc29sdmUoX19kaXJuYW1lLCAnLi8nKSxcbiAgICAgICAgLy8gXHU4QkJFXHU3RjZFXHU1MjJCXHU1NDBEXG4gICAgICAgICdAJzogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgJy4vc3JjJyksXG4gICAgICB9LFxuICAgICAgLy8gaHR0cHM6Ly9jbi52aXRlanMuZGV2L2NvbmZpZy8jcmVzb2x2ZS1leHRlbnNpb25zXG4gICAgICBleHRlbnNpb25zOiBbJy5tanMnLCAnLmpzJywgJy50cycsICcuanN4JywgJy50c3gnLCAnLmpzb24nLCAnLnZ1ZSddLFxuICAgIH0sXG4gICAgLy8gdml0ZSBcdTc2RjhcdTUxNzNcdTkxNERcdTdGNkVcbiAgICBzZXJ2ZXI6IHtcbiAgICAgIHBvcnQ6IDgxLFxuICAgICAgaG9zdDogdHJ1ZSxcbiAgICAgIG9wZW46IHRydWUsXG4gICAgICBwcm94eToge1xuICAgICAgICAvLyBodHRwczovL2NuLnZpdGVqcy5kZXYvY29uZmlnLyNzZXJ2ZXItcHJveHlcbiAgICAgICAgJy9kZXYtYXBpJzoge1xuICAgICAgICAgIHRhcmdldDogJ2h0dHA6Ly9sb2NhbGhvc3Q6MTgwODAvb3BlbmhpcycsXG4gICAgICAgICAgY2hhbmdlT3JpZ2luOiB0cnVlLFxuICAgICAgICAgIHJld3JpdGU6IChwKSA9PiBwLnJlcGxhY2UoL15cXC9kZXYtYXBpLywgJycpLFxuICAgICAgICB9LFxuICAgICAgICAnL3licGx1Z2luJzoge1xuICAgICAgICAgIHRhcmdldDogJ2h0dHA6Ly9sb2NhbGhvc3Q6NTAwMCcsXG4gICAgICAgICAgY2hhbmdlT3JpZ2luOiB0cnVlLFxuICAgICAgICAgIHJld3JpdGU6IChwKSA9PiBwLnJlcGxhY2UoL15cXC95YnBsdWdpbi8sICcnKSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfSxcbiAgICAvL2ZpeDplcnJvcjpzdGRpbj46NzM1NjoxOiB3YXJuaW5nOiBcIkBjaGFyc2V0XCIgbXVzdCBiZSB0aGUgZmlyc3QgcnVsZSBpbiB0aGUgZmlsZVxuICAgIGNzczoge1xuICAgICAgcG9zdGNzczoge1xuICAgICAgICBwbHVnaW5zOiBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgcG9zdGNzc1BsdWdpbjogJ2ludGVybmFsOmNoYXJzZXQtcmVtb3ZhbCcsXG4gICAgICAgICAgICBBdFJ1bGU6IHtcbiAgICAgICAgICAgICAgY2hhcnNldDogKGF0UnVsZSkgPT4ge1xuICAgICAgICAgICAgICAgIGlmIChhdFJ1bGUubmFtZSA9PT0gJ2NoYXJzZXQnKSB7XG4gICAgICAgICAgICAgICAgICBhdFJ1bGUucmVtb3ZlKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgfSxcbiAgICB9LFxuICB9O1xufSk7IiwgImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJGOlxcXFxKYXZhXFxcXENvZGVcXFxcamtobFxcXFxvcGVuaGlzLXVpLXZ1ZTNcXFxcdml0ZVxcXFxwbHVnaW5zXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCJGOlxcXFxKYXZhXFxcXENvZGVcXFxcamtobFxcXFxvcGVuaGlzLXVpLXZ1ZTNcXFxcdml0ZVxcXFxwbHVnaW5zXFxcXGluZGV4LmpzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9GOi9KYXZhL0NvZGUvamtobC9vcGVuaGlzLXVpLXZ1ZTMvdml0ZS9wbHVnaW5zL2luZGV4LmpzXCI7aW1wb3J0IHZ1ZSBmcm9tICdAdml0ZWpzL3BsdWdpbi12dWUnXG5cbmltcG9ydCBjcmVhdGVBdXRvSW1wb3J0IGZyb20gJy4vYXV0by1pbXBvcnQnXG5pbXBvcnQgY3JlYXRlU3ZnSWNvbiBmcm9tICcuL3N2Zy1pY29uJ1xuaW1wb3J0IGNyZWF0ZUNvbXByZXNzaW9uIGZyb20gJy4vY29tcHJlc3Npb24nXG5pbXBvcnQgY3JlYXRlU2V0dXBFeHRlbmQgZnJvbSAnLi9zZXR1cC1leHRlbmQnXG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGNyZWF0ZVZpdGVQbHVnaW5zKHZpdGVFbnYsIGlzQnVpbGQgPSBmYWxzZSkge1xuICAgIGNvbnN0IHZpdGVQbHVnaW5zID0gW3Z1ZSgpXVxuICAgIHZpdGVQbHVnaW5zLnB1c2goY3JlYXRlQXV0b0ltcG9ydCgpKVxuXHR2aXRlUGx1Z2lucy5wdXNoKGNyZWF0ZVNldHVwRXh0ZW5kKCkpXG4gICAgdml0ZVBsdWdpbnMucHVzaChjcmVhdGVTdmdJY29uKGlzQnVpbGQpKSAgICBcblx0Ly8gXHU0RjRGXHU5NjYyXHU0RUUzXHU3ODAxXHU1RjcxXHU1NENEXHU2MjUzXHU1MzA1IFx1NjI1M1x1NTMwNVx1NjVGNlx1NjY4Mlx1NjVGNlx1NjZGRlx1NjM2Mlx1NEUzQVx1N0E3QVx1NjU4N1x1NEVGNlxuICAgIC8vIFx1NjVCMFx1NTg5RVx1NjM5Mlx1OTY2NFx1NEY0Rlx1OTY2Mlx1N0JBMVx1NzQwNlx1NkEyMVx1NTc1N1x1NzY4NFx1NjNEMlx1NEVGNlxuICAgIC8vIHZpdGVQbHVnaW5zLnB1c2goe1xuICAgIC8vICAgICBuYW1lOiAnZXhjbHVkZS1pbi1ob3NwaXRhbC1tYW5hZ2VtZW50JyxcbiAgICAvLyAgICAgZW5mb3JjZTogJ3ByZScsXG4gICAgLy8gICAgIGxvYWQoaWQpIHtcbiAgICAvLyAgICAgY29uc3Qgbm9ybWFsaXplZFBhdGggPSBpZC5yZXBsYWNlKC9cXFxcL2csICcvJylcbiAgICAvLyAgICAgaWYgKG5vcm1hbGl6ZWRQYXRoLmluY2x1ZGVzKCcvc3JjL3ZpZXdzL2luSG9zcGl0YWxNYW5hZ2VtZW50LycpIFxuICAgIC8vICAgICAgICAgfHwgbm9ybWFsaXplZFBhdGguaW5jbHVkZXMoJy9zcmMvdmlld3MvaW5wYXRpZW50RG9jdG9yLycpIFxuICAgIC8vICAgICAgICAgfHwgbm9ybWFsaXplZFBhdGguaW5jbHVkZXMoJy9zcmMvdmlld3MvaW5wYXRpZW50TnVyc2UvJykpIHtcbiAgICAvLyAgICAgICAgIHJldHVybiB7XG4gICAgLy8gICAgICAgICBjb2RlOiBgXG4gICAgLy8gICAgICAgICAgICAgPHRlbXBsYXRlPlxuICAgIC8vICAgICAgICAgICAgIDwvdGVtcGxhdGU+XG4gICAgLy8gICAgICAgICAgICAgPHNjcmlwdD5cbiAgICAvLyAgICAgICAgICAgICA8L3NjcmlwdD5cbiAgICAvLyAgICAgICAgIGAsXG4gICAgLy8gICAgICAgICBtYXA6IG51bGxcbiAgICAvLyAgICAgICAgIH1cbiAgICAvLyAgICAgfVxuICAgIC8vICAgICByZXR1cm4gbnVsbFxuICAgIC8vICAgICB9XG4gICAgLy8gfSlcblx0aXNCdWlsZCAmJiB2aXRlUGx1Z2lucy5wdXNoKC4uLmNyZWF0ZUNvbXByZXNzaW9uKHZpdGVFbnYpKVxuICAgIHJldHVybiB2aXRlUGx1Z2luc1xufVxuIiwgImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJGOlxcXFxKYXZhXFxcXENvZGVcXFxcamtobFxcXFxvcGVuaGlzLXVpLXZ1ZTNcXFxcdml0ZVxcXFxwbHVnaW5zXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCJGOlxcXFxKYXZhXFxcXENvZGVcXFxcamtobFxcXFxvcGVuaGlzLXVpLXZ1ZTNcXFxcdml0ZVxcXFxwbHVnaW5zXFxcXGF1dG8taW1wb3J0LmpzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9GOi9KYXZhL0NvZGUvamtobC9vcGVuaGlzLXVpLXZ1ZTMvdml0ZS9wbHVnaW5zL2F1dG8taW1wb3J0LmpzXCI7aW1wb3J0IGF1dG9JbXBvcnQgZnJvbSAndW5wbHVnaW4tYXV0by1pbXBvcnQvdml0ZSdcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gY3JlYXRlQXV0b0ltcG9ydCgpIHtcbiAgICByZXR1cm4gYXV0b0ltcG9ydCh7XG4gICAgICAgIGltcG9ydHM6IFtcbiAgICAgICAgICAgICd2dWUnLFxuICAgICAgICAgICAgJ3Z1ZS1yb3V0ZXInLFxuICAgICAgICAgICAgJ3BpbmlhJ1xuICAgICAgICBdLFxuICAgICAgICBkdHM6IGZhbHNlXG4gICAgfSlcbn1cbiIsICJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiRjpcXFxcSmF2YVxcXFxDb2RlXFxcXGpraGxcXFxcb3Blbmhpcy11aS12dWUzXFxcXHZpdGVcXFxccGx1Z2luc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiRjpcXFxcSmF2YVxcXFxDb2RlXFxcXGpraGxcXFxcb3Blbmhpcy11aS12dWUzXFxcXHZpdGVcXFxccGx1Z2luc1xcXFxzdmctaWNvbi5qc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRjovSmF2YS9Db2RlL2praGwvb3Blbmhpcy11aS12dWUzL3ZpdGUvcGx1Z2lucy9zdmctaWNvbi5qc1wiO2ltcG9ydCB7Y3JlYXRlU3ZnSWNvbnNQbHVnaW59IGZyb20gJ3ZpdGUtcGx1Z2luLXN2Zy1pY29ucydcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnXG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGNyZWF0ZVN2Z0ljb24oaXNCdWlsZCkge1xuICAgIHJldHVybiBjcmVhdGVTdmdJY29uc1BsdWdpbih7XG5cdFx0aWNvbkRpcnM6IFtwYXRoLnJlc29sdmUocHJvY2Vzcy5jd2QoKSwgJ3NyYy9hc3NldHMvaWNvbnMvc3ZnJyldLFxuICAgICAgICBzeW1ib2xJZDogJ2ljb24tW2Rpcl0tW25hbWVdJyxcbiAgICAgICAgc3Znb09wdGlvbnM6IGlzQnVpbGRcbiAgICB9KVxufVxuIiwgImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJGOlxcXFxKYXZhXFxcXENvZGVcXFxcamtobFxcXFxvcGVuaGlzLXVpLXZ1ZTNcXFxcdml0ZVxcXFxwbHVnaW5zXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCJGOlxcXFxKYXZhXFxcXENvZGVcXFxcamtobFxcXFxvcGVuaGlzLXVpLXZ1ZTNcXFxcdml0ZVxcXFxwbHVnaW5zXFxcXGNvbXByZXNzaW9uLmpzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9GOi9KYXZhL0NvZGUvamtobC9vcGVuaGlzLXVpLXZ1ZTMvdml0ZS9wbHVnaW5zL2NvbXByZXNzaW9uLmpzXCI7aW1wb3J0IGNvbXByZXNzaW9uIGZyb20gJ3ZpdGUtcGx1Z2luLWNvbXByZXNzaW9uJ1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBjcmVhdGVDb21wcmVzc2lvbihlbnYpIHtcbiAgICBjb25zdCB7IFZJVEVfQlVJTERfQ09NUFJFU1MgfSA9IGVudlxuICAgIGNvbnN0IHBsdWdpbiA9IFtdXG4gICAgaWYgKFZJVEVfQlVJTERfQ09NUFJFU1MpIHtcbiAgICAgICAgY29uc3QgY29tcHJlc3NMaXN0ID0gVklURV9CVUlMRF9DT01QUkVTUy5zcGxpdCgnLCcpXG4gICAgICAgIGlmIChjb21wcmVzc0xpc3QuaW5jbHVkZXMoJ2d6aXAnKSkge1xuICAgICAgICAgICAgLy8gaHR0cDovL2RvYy5vcGVuaGlzaXMudmlwL29wZW5oaXMtdnVlL290aGVyL2ZhcS5odG1sI1x1NEY3Rlx1NzUyOGd6aXBcdTg5RTNcdTUzOEJcdTdGMjlcdTk3NTlcdTYwMDFcdTY1ODdcdTRFRjZcbiAgICAgICAgICAgIHBsdWdpbi5wdXNoKFxuICAgICAgICAgICAgICAgIGNvbXByZXNzaW9uKHtcbiAgICAgICAgICAgICAgICAgICAgZXh0OiAnLmd6JyxcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlT3JpZ2luRmlsZTogZmFsc2VcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICAgIGlmIChjb21wcmVzc0xpc3QuaW5jbHVkZXMoJ2Jyb3RsaScpKSB7XG4gICAgICAgICAgICBwbHVnaW4ucHVzaChcbiAgICAgICAgICAgICAgICBjb21wcmVzc2lvbih7XG4gICAgICAgICAgICAgICAgICAgIGV4dDogJy5icicsXG4gICAgICAgICAgICAgICAgICAgIGFsZ29yaXRobTogJ2Jyb3RsaUNvbXByZXNzJyxcbiAgICAgICAgICAgICAgICAgICAgZGVsZXRlT3JpZ2luRmlsZTogZmFsc2VcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgKVxuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBwbHVnaW5cbn1cbiIsICJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiRjpcXFxcSmF2YVxcXFxDb2RlXFxcXGpraGxcXFxcb3Blbmhpcy11aS12dWUzXFxcXHZpdGVcXFxccGx1Z2luc1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiRjpcXFxcSmF2YVxcXFxDb2RlXFxcXGpraGxcXFxcb3Blbmhpcy11aS12dWUzXFxcXHZpdGVcXFxccGx1Z2luc1xcXFxzZXR1cC1leHRlbmQuanNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL0Y6L0phdmEvQ29kZS9qa2hsL29wZW5oaXMtdWktdnVlMy92aXRlL3BsdWdpbnMvc2V0dXAtZXh0ZW5kLmpzXCI7aW1wb3J0IHNldHVwRXh0ZW5kIGZyb20gJ3VucGx1Z2luLXZ1ZS1zZXR1cC1leHRlbmQtcGx1cy92aXRlJ1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBjcmVhdGVTZXR1cEV4dGVuZCgpIHtcbiAgICByZXR1cm4gc2V0dXBFeHRlbmQoe30pXG59XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBS0EsU0FBUSxjQUFjLGVBQWM7QUFDcEMsT0FBT0EsV0FBVTs7O0FDTjZTLE9BQU8sU0FBUzs7O0FDQUosT0FBTyxnQkFBZ0I7QUFFbFYsU0FBUixtQkFBb0M7QUFDdkMsU0FBTyxXQUFXO0FBQUEsSUFDZCxTQUFTO0FBQUEsTUFDTDtBQUFBLE1BQ0E7QUFBQSxNQUNBO0FBQUEsSUFDSjtBQUFBLElBQ0EsS0FBSztBQUFBLEVBQ1QsQ0FBQztBQUNMOzs7QUNYb1UsU0FBUSw0QkFBMkI7QUFDdlcsT0FBTyxVQUFVO0FBRUYsU0FBUixjQUErQixTQUFTO0FBQzNDLFNBQU8scUJBQXFCO0FBQUEsSUFDOUIsVUFBVSxDQUFDLEtBQUssUUFBUSxRQUFRLElBQUksR0FBRyxzQkFBc0IsQ0FBQztBQUFBLElBQ3hELFVBQVU7QUFBQSxJQUNWLGFBQWE7QUFBQSxFQUNqQixDQUFDO0FBQ0w7OztBQ1QwVSxPQUFPLGlCQUFpQjtBQUVuVixTQUFSLGtCQUFtQyxLQUFLO0FBQzNDLFFBQU0sRUFBRSxvQkFBb0IsSUFBSTtBQUNoQyxRQUFNLFNBQVMsQ0FBQztBQUNoQixNQUFJLHFCQUFxQjtBQUNyQixVQUFNLGVBQWUsb0JBQW9CLE1BQU0sR0FBRztBQUNsRCxRQUFJLGFBQWEsU0FBUyxNQUFNLEdBQUc7QUFFL0IsYUFBTztBQUFBLFFBQ0gsWUFBWTtBQUFBLFVBQ1IsS0FBSztBQUFBLFVBQ0wsa0JBQWtCO0FBQUEsUUFDdEIsQ0FBQztBQUFBLE1BQ0w7QUFBQSxJQUNKO0FBQ0EsUUFBSSxhQUFhLFNBQVMsUUFBUSxHQUFHO0FBQ2pDLGFBQU87QUFBQSxRQUNILFlBQVk7QUFBQSxVQUNSLEtBQUs7QUFBQSxVQUNMLFdBQVc7QUFBQSxVQUNYLGtCQUFrQjtBQUFBLFFBQ3RCLENBQUM7QUFBQSxNQUNMO0FBQUEsSUFDSjtBQUFBLEVBQ0o7QUFDQSxTQUFPO0FBQ1g7OztBQzNCNFUsT0FBTyxpQkFBaUI7QUFFclYsU0FBUixvQkFBcUM7QUFDeEMsU0FBTyxZQUFZLENBQUMsQ0FBQztBQUN6Qjs7O0FKR2UsU0FBUixrQkFBbUMsU0FBUyxVQUFVLE9BQU87QUFDaEUsUUFBTSxjQUFjLENBQUMsSUFBSSxDQUFDO0FBQzFCLGNBQVksS0FBSyxpQkFBaUIsQ0FBQztBQUN0QyxjQUFZLEtBQUssa0JBQWtCLENBQUM7QUFDakMsY0FBWSxLQUFLLGNBQWMsT0FBTyxDQUFDO0FBd0IxQyxhQUFXLFlBQVksS0FBSyxHQUFHLGtCQUFrQixPQUFPLENBQUM7QUFDdEQsU0FBTztBQUNYOzs7QURyQ0EsSUFBTSxtQ0FBbUM7QUFVekMsSUFBTyxzQkFBUSxhQUFhLENBQUMsRUFBRSxNQUFNLFFBQVEsTUFBTTtBQUNqRCxRQUFNLE1BQU0sUUFBUSxNQUFNLFFBQVEsSUFBSSxDQUFDO0FBQ3ZDLFFBQU0sRUFBRSxhQUFhLElBQUk7QUFDekIsU0FBTztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsSUFRTCxNQUFNLGlCQUFpQixlQUFlLE1BQU07QUFBQSxJQUM1QyxTQUFTLGtCQUFrQixLQUFLLFlBQVksT0FBTztBQUFBLElBQ25ELFNBQVM7QUFBQTtBQUFBLE1BRVAsT0FBTztBQUFBO0FBQUEsUUFFTCxLQUFLQyxNQUFLLFFBQVEsa0NBQVcsSUFBSTtBQUFBO0FBQUEsUUFFakMsS0FBS0EsTUFBSyxRQUFRLGtDQUFXLE9BQU87QUFBQSxNQUN0QztBQUFBO0FBQUEsTUFFQSxZQUFZLENBQUMsUUFBUSxPQUFPLE9BQU8sUUFBUSxRQUFRLFNBQVMsTUFBTTtBQUFBLElBQ3BFO0FBQUE7QUFBQSxJQUVBLFFBQVE7QUFBQSxNQUNOLE1BQU07QUFBQSxNQUNOLE1BQU07QUFBQSxNQUNOLE1BQU07QUFBQSxNQUNOLE9BQU87QUFBQTtBQUFBLFFBRUwsWUFBWTtBQUFBLFVBQ1YsUUFBUTtBQUFBLFVBQ1IsY0FBYztBQUFBLFVBQ2QsU0FBUyxDQUFDLE1BQU0sRUFBRSxRQUFRLGNBQWMsRUFBRTtBQUFBLFFBQzVDO0FBQUEsUUFDQSxhQUFhO0FBQUEsVUFDWCxRQUFRO0FBQUEsVUFDUixjQUFjO0FBQUEsVUFDZCxTQUFTLENBQUMsTUFBTSxFQUFFLFFBQVEsZUFBZSxFQUFFO0FBQUEsUUFDN0M7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBO0FBQUEsSUFFQSxLQUFLO0FBQUEsTUFDSCxTQUFTO0FBQUEsUUFDUCxTQUFTO0FBQUEsVUFDUDtBQUFBLFlBQ0UsZUFBZTtBQUFBLFlBQ2YsUUFBUTtBQUFBLGNBQ04sU0FBUyxDQUFDLFdBQVc7QUFDbkIsb0JBQUksT0FBTyxTQUFTLFdBQVc7QUFDN0IseUJBQU8sT0FBTztBQUFBLGdCQUNoQjtBQUFBLGNBQ0Y7QUFBQSxZQUNGO0FBQUEsVUFDRjtBQUFBLFFBQ0Y7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogWyJwYXRoIiwgInBhdGgiXQp9Cg==