| New file |
| | |
| | | # Design Document |
| | | |
| | | ## Overview |
| | | |
| | | [High-level description of the feature and its place in the overall system] |
| | | |
| | | ## Steering Document Alignment |
| | | |
| | | ### Technical Standards (tech.md) |
| | | [How the design follows documented technical patterns and standards] |
| | | |
| | | ### Project Structure (structure.md) |
| | | [How the implementation will follow project organization conventions] |
| | | |
| | | ## Code Reuse Analysis |
| | | [What existing code will be leveraged, extended, or integrated with this feature] |
| | | |
| | | ### Existing Components to Leverage |
| | | - **[Component/Utility Name]**: [How it will be used] |
| | | - **[Service/Helper Name]**: [How it will be extended] |
| | | |
| | | ### Integration Points |
| | | - **[Existing System/API]**: [How the new feature will integrate] |
| | | - **[Database/Storage]**: [How data will connect to existing schemas] |
| | | |
| | | ## Architecture |
| | | |
| | | [Describe the overall architecture and design patterns used] |
| | | |
| | | ### Modular Design Principles |
| | | - **Single File Responsibility**: Each file should handle one specific concern or domain |
| | | - **Component Isolation**: Create small, focused components rather than large monolithic files |
| | | - **Service Layer Separation**: Separate data access, business logic, and presentation layers |
| | | - **Utility Modularity**: Break utilities into focused, single-purpose modules |
| | | |
| | | ```mermaid |
| | | graph TD |
| | | A[Component A] --> B[Component B] |
| | | B --> C[Component C] |
| | | ``` |
| | | |
| | | ## Components and Interfaces |
| | | |
| | | ### Component 1 |
| | | - **Purpose:** [What this component does] |
| | | - **Interfaces:** [Public methods/APIs] |
| | | - **Dependencies:** [What it depends on] |
| | | - **Reuses:** [Existing components/utilities it builds upon] |
| | | |
| | | ### Component 2 |
| | | - **Purpose:** [What this component does] |
| | | - **Interfaces:** [Public methods/APIs] |
| | | - **Dependencies:** [What it depends on] |
| | | - **Reuses:** [Existing components/utilities it builds upon] |
| | | |
| | | ## Data Models |
| | | |
| | | ### Model 1 |
| | | ``` |
| | | [Define the structure of Model1 in your language] |
| | | - id: [unique identifier type] |
| | | - name: [string/text type] |
| | | - [Additional properties as needed] |
| | | ``` |
| | | |
| | | ### Model 2 |
| | | ``` |
| | | [Define the structure of Model2 in your language] |
| | | - id: [unique identifier type] |
| | | - [Additional properties as needed] |
| | | ``` |
| | | |
| | | ## Error Handling |
| | | |
| | | ### Error Scenarios |
| | | 1. **Scenario 1:** [Description] |
| | | - **Handling:** [How to handle] |
| | | - **User Impact:** [What user sees] |
| | | |
| | | 2. **Scenario 2:** [Description] |
| | | - **Handling:** [How to handle] |
| | | - **User Impact:** [What user sees] |
| | | |
| | | ## Testing Strategy |
| | | |
| | | ### Unit Testing |
| | | - [Unit testing approach] |
| | | - [Key components to test] |
| | | |
| | | ### Integration Testing |
| | | - [Integration testing approach] |
| | | - [Key flows to test] |
| | | |
| | | ### End-to-End Testing |
| | | - [E2E testing approach] |
| | | - [User scenarios to test] |
| New file |
| | |
| | | # Product Overview |
| | | |
| | | ## Product Purpose |
| | | [Describe the core purpose of this product/project. What problem does it solve?] |
| | | |
| | | ## Target Users |
| | | [Who are the primary users of this product? What are their needs and pain points?] |
| | | |
| | | ## Key Features |
| | | [List the main features that deliver value to users] |
| | | |
| | | 1. **Feature 1**: [Description] |
| | | 2. **Feature 2**: [Description] |
| | | 3. **Feature 3**: [Description] |
| | | |
| | | ## Business Objectives |
| | | [What are the business goals this product aims to achieve?] |
| | | |
| | | - [Objective 1] |
| | | - [Objective 2] |
| | | - [Objective 3] |
| | | |
| | | ## Success Metrics |
| | | [How will we measure the success of this product?] |
| | | |
| | | - [Metric 1]: [Target] |
| | | - [Metric 2]: [Target] |
| | | - [Metric 3]: [Target] |
| | | |
| | | ## Product Principles |
| | | [Core principles that guide product decisions] |
| | | |
| | | 1. **[Principle 1]**: [Explanation] |
| | | 2. **[Principle 2]**: [Explanation] |
| | | 3. **[Principle 3]**: [Explanation] |
| | | |
| | | ## Monitoring & Visibility (if applicable) |
| | | [How do users track progress and monitor the system?] |
| | | |
| | | - **Dashboard Type**: [e.g., Web-based, CLI, Desktop app] |
| | | - **Real-time Updates**: [e.g., WebSocket, polling, push notifications] |
| | | - **Key Metrics Displayed**: [What information is most important to surface] |
| | | - **Sharing Capabilities**: [e.g., read-only links, exports, reports] |
| | | |
| | | ## Future Vision |
| | | [Where do we see this product evolving in the future?] |
| | | |
| | | ### Potential Enhancements |
| | | - **Remote Access**: [e.g., Tunnel features for sharing dashboards with stakeholders] |
| | | - **Analytics**: [e.g., Historical trends, performance metrics] |
| | | - **Collaboration**: [e.g., Multi-user support, commenting] |
| New file |
| | |
| | | # Requirements Document |
| | | |
| | | ## Introduction |
| | | |
| | | [Provide a brief overview of the feature, its purpose, and its value to users] |
| | | |
| | | ## Alignment with Product Vision |
| | | |
| | | [Explain how this feature supports the goals outlined in product.md] |
| | | |
| | | ## Requirements |
| | | |
| | | ### Requirement 1 |
| | | |
| | | **User Story:** As a [role], I want [feature], so that [benefit] |
| | | |
| | | #### Acceptance Criteria |
| | | |
| | | 1. WHEN [event] THEN [system] SHALL [response] |
| | | 2. IF [precondition] THEN [system] SHALL [response] |
| | | 3. WHEN [event] AND [condition] THEN [system] SHALL [response] |
| | | |
| | | ### Requirement 2 |
| | | |
| | | **User Story:** As a [role], I want [feature], so that [benefit] |
| | | |
| | | #### Acceptance Criteria |
| | | |
| | | 1. WHEN [event] THEN [system] SHALL [response] |
| | | 2. IF [precondition] THEN [system] SHALL [response] |
| | | |
| | | ## Non-Functional Requirements |
| | | |
| | | ### Code Architecture and Modularity |
| | | - **Single Responsibility Principle**: Each file should have a single, well-defined purpose |
| | | - **Modular Design**: Components, utilities, and services should be isolated and reusable |
| | | - **Dependency Management**: Minimize interdependencies between modules |
| | | - **Clear Interfaces**: Define clean contracts between components and layers |
| | | |
| | | ### Performance |
| | | - [Performance requirements] |
| | | |
| | | ### Security |
| | | - [Security requirements] |
| | | |
| | | ### Reliability |
| | | - [Reliability requirements] |
| | | |
| | | ### Usability |
| | | - [Usability requirements] |
| New file |
| | |
| | | # Project Structure |
| | | |
| | | ## Directory Organization |
| | | |
| | | ``` |
| | | [Define your project's directory structure. Examples below - adapt to your project type] |
| | | |
| | | Example for a library/package: |
| | | project-root/ |
| | | ├── src/ # Source code |
| | | ├── tests/ # Test files |
| | | ├── docs/ # Documentation |
| | | ├── examples/ # Usage examples |
| | | └── [build/dist/out] # Build output |
| | | |
| | | Example for an application: |
| | | project-root/ |
| | | ├── [src/app/lib] # Main source code |
| | | ├── [assets/resources] # Static resources |
| | | ├── [config/settings] # Configuration |
| | | ├── [scripts/tools] # Build/utility scripts |
| | | └── [tests/spec] # Test files |
| | | |
| | | Common patterns: |
| | | - Group by feature/module |
| | | - Group by layer (UI, business logic, data) |
| | | - Group by type (models, controllers, views) |
| | | - Flat structure for simple projects |
| | | ``` |
| | | |
| | | ## Naming Conventions |
| | | |
| | | ### Files |
| | | - **Components/Modules**: [e.g., `PascalCase`, `snake_case`, `kebab-case`] |
| | | - **Services/Handlers**: [e.g., `UserService`, `user_service`, `user-service`] |
| | | - **Utilities/Helpers**: [e.g., `dateUtils`, `date_utils`, `date-utils`] |
| | | - **Tests**: [e.g., `[filename]_test`, `[filename].test`, `[filename]Test`] |
| | | |
| | | ### Code |
| | | - **Classes/Types**: [e.g., `PascalCase`, `CamelCase`, `snake_case`] |
| | | - **Functions/Methods**: [e.g., `camelCase`, `snake_case`, `PascalCase`] |
| | | - **Constants**: [e.g., `UPPER_SNAKE_CASE`, `SCREAMING_CASE`, `PascalCase`] |
| | | - **Variables**: [e.g., `camelCase`, `snake_case`, `lowercase`] |
| | | |
| | | ## Import Patterns |
| | | |
| | | ### Import Order |
| | | 1. External dependencies |
| | | 2. Internal modules |
| | | 3. Relative imports |
| | | 4. Style imports |
| | | |
| | | ### Module/Package Organization |
| | | ``` |
| | | [Describe your project's import/include patterns] |
| | | Examples: |
| | | - Absolute imports from project root |
| | | - Relative imports within modules |
| | | - Package/namespace organization |
| | | - Dependency management approach |
| | | ``` |
| | | |
| | | ## Code Structure Patterns |
| | | |
| | | [Define common patterns for organizing code within files. Below are examples - choose what applies to your project] |
| | | |
| | | ### Module/Class Organization |
| | | ``` |
| | | Example patterns: |
| | | 1. Imports/includes/dependencies |
| | | 2. Constants and configuration |
| | | 3. Type/interface definitions |
| | | 4. Main implementation |
| | | 5. Helper/utility functions |
| | | 6. Exports/public API |
| | | ``` |
| | | |
| | | ### Function/Method Organization |
| | | ``` |
| | | Example patterns: |
| | | - Input validation first |
| | | - Core logic in the middle |
| | | - Error handling throughout |
| | | - Clear return points |
| | | ``` |
| | | |
| | | ### File Organization Principles |
| | | ``` |
| | | Choose what works for your project: |
| | | - One class/module per file |
| | | - Related functionality grouped together |
| | | - Public API at the top/bottom |
| | | - Implementation details hidden |
| | | ``` |
| | | |
| | | ## Code Organization Principles |
| | | |
| | | 1. **Single Responsibility**: Each file should have one clear purpose |
| | | 2. **Modularity**: Code should be organized into reusable modules |
| | | 3. **Testability**: Structure code to be easily testable |
| | | 4. **Consistency**: Follow patterns established in the codebase |
| | | |
| | | ## Module Boundaries |
| | | [Define how different parts of your project interact and maintain separation of concerns] |
| | | |
| | | Examples of boundary patterns: |
| | | - **Core vs Plugins**: Core functionality vs extensible plugins |
| | | - **Public API vs Internal**: What's exposed vs implementation details |
| | | - **Platform-specific vs Cross-platform**: OS-specific code isolation |
| | | - **Stable vs Experimental**: Production code vs experimental features |
| | | - **Dependencies direction**: Which modules can depend on which |
| | | |
| | | ## Code Size Guidelines |
| | | [Define your project's guidelines for file and function sizes] |
| | | |
| | | Suggested guidelines: |
| | | - **File size**: [Define maximum lines per file] |
| | | - **Function/Method size**: [Define maximum lines per function] |
| | | - **Class/Module complexity**: [Define complexity limits] |
| | | - **Nesting depth**: [Maximum nesting levels] |
| | | |
| | | ## Dashboard/Monitoring Structure (if applicable) |
| | | [How dashboard or monitoring components are organized] |
| | | |
| | | ### Example Structure: |
| | | ``` |
| | | src/ |
| | | └── dashboard/ # Self-contained dashboard subsystem |
| | | ├── server/ # Backend server components |
| | | ├── client/ # Frontend assets |
| | | ├── shared/ # Shared types/utilities |
| | | └── public/ # Static assets |
| | | ``` |
| | | |
| | | ### Separation of Concerns |
| | | - Dashboard isolated from core business logic |
| | | - Own CLI entry point for independent operation |
| | | - Minimal dependencies on main application |
| | | - Can be disabled without affecting core functionality |
| | | |
| | | ## Documentation Standards |
| | | - All public APIs must have documentation |
| | | - Complex logic should include inline comments |
| | | - README files for major modules |
| | | - Follow language-specific documentation conventions |
| New file |
| | |
| | | # Tasks Document |
| | | |
| | | - [ ] 1. Create core interfaces in src/types/feature.ts |
| | | - File: src/types/feature.ts |
| | | - Define TypeScript interfaces for feature data structures |
| | | - Extend existing base interfaces from base.ts |
| | | - Purpose: Establish type safety for feature implementation |
| | | - _Leverage: src/types/base.ts_ |
| | | - _Requirements: 1.1_ |
| | | - _Prompt: Role: TypeScript Developer specializing in type systems and interfaces | Task: Create comprehensive TypeScript interfaces for the feature data structures following requirements 1.1, extending existing base interfaces from src/types/base.ts | Restrictions: Do not modify existing base interfaces, maintain backward compatibility, follow project naming conventions | Success: All interfaces compile without errors, proper inheritance from base types, full type coverage for feature requirements_ |
| | | |
| | | - [ ] 2. Create base model class in src/models/FeatureModel.ts |
| | | - File: src/models/FeatureModel.ts |
| | | - Implement base model extending BaseModel class |
| | | - Add validation methods using existing validation utilities |
| | | - Purpose: Provide data layer foundation for feature |
| | | - _Leverage: src/models/BaseModel.ts, src/utils/validation.ts_ |
| | | - _Requirements: 2.1_ |
| | | - _Prompt: Role: Backend Developer with expertise in Node.js and data modeling | Task: Create a base model class extending BaseModel and implementing validation following requirement 2.1, leveraging existing patterns from src/models/BaseModel.ts and src/utils/validation.ts | Restrictions: Must follow existing model patterns, do not bypass validation utilities, maintain consistent error handling | Success: Model extends BaseModel correctly, validation methods implemented and tested, follows project architecture patterns_ |
| | | |
| | | - [ ] 3. Add specific model methods to FeatureModel.ts |
| | | - File: src/models/FeatureModel.ts (continue from task 2) |
| | | - Implement create, update, delete methods |
| | | - Add relationship handling for foreign keys |
| | | - Purpose: Complete model functionality for CRUD operations |
| | | - _Leverage: src/models/BaseModel.ts_ |
| | | - _Requirements: 2.2, 2.3_ |
| | | - _Prompt: Role: Backend Developer with expertise in ORM and database operations | Task: Implement CRUD methods and relationship handling in FeatureModel.ts following requirements 2.2 and 2.3, extending patterns from src/models/BaseModel.ts | Restrictions: Must maintain transaction integrity, follow existing relationship patterns, do not duplicate base model functionality | Success: All CRUD operations work correctly, relationships are properly handled, database operations are atomic and efficient_ |
| | | |
| | | - [ ] 4. Create model unit tests in tests/models/FeatureModel.test.ts |
| | | - File: tests/models/FeatureModel.test.ts |
| | | - Write tests for model validation and CRUD methods |
| | | - Use existing test utilities and fixtures |
| | | - Purpose: Ensure model reliability and catch regressions |
| | | - _Leverage: tests/helpers/testUtils.ts, tests/fixtures/data.ts_ |
| | | - _Requirements: 2.1, 2.2_ |
| | | - _Prompt: Role: QA Engineer with expertise in unit testing and Jest/Mocha frameworks | Task: Create comprehensive unit tests for FeatureModel validation and CRUD methods covering requirements 2.1 and 2.2, using existing test utilities from tests/helpers/testUtils.ts and fixtures from tests/fixtures/data.ts | Restrictions: Must test both success and failure scenarios, do not test external dependencies directly, maintain test isolation | Success: All model methods are tested with good coverage, edge cases covered, tests run independently and consistently_ |
| | | |
| | | - [ ] 5. Create service interface in src/services/IFeatureService.ts |
| | | - File: src/services/IFeatureService.ts |
| | | - Define service contract with method signatures |
| | | - Extend base service interface patterns |
| | | - Purpose: Establish service layer contract for dependency injection |
| | | - _Leverage: src/services/IBaseService.ts_ |
| | | - _Requirements: 3.1_ |
| | | - _Prompt: Role: Software Architect specializing in service-oriented architecture and TypeScript interfaces | Task: Design service interface contract following requirement 3.1, extending base service patterns from src/services/IBaseService.ts for dependency injection | Restrictions: Must maintain interface segregation principle, do not expose internal implementation details, ensure contract compatibility with DI container | Success: Interface is well-defined with clear method signatures, extends base service appropriately, supports all required service operations_ |
| | | |
| | | - [ ] 6. Implement feature service in src/services/FeatureService.ts |
| | | - File: src/services/FeatureService.ts |
| | | - Create concrete service implementation using FeatureModel |
| | | - Add error handling with existing error utilities |
| | | - Purpose: Provide business logic layer for feature operations |
| | | - _Leverage: src/services/BaseService.ts, src/utils/errorHandler.ts, src/models/FeatureModel.ts_ |
| | | - _Requirements: 3.2_ |
| | | - _Prompt: Role: Backend Developer with expertise in service layer architecture and business logic | Task: Implement concrete FeatureService following requirement 3.2, using FeatureModel and extending BaseService patterns with proper error handling from src/utils/errorHandler.ts | Restrictions: Must implement interface contract exactly, do not bypass model validation, maintain separation of concerns from data layer | Success: Service implements all interface methods correctly, robust error handling implemented, business logic is well-encapsulated and testable_ |
| | | |
| | | - [ ] 7. Add service dependency injection in src/utils/di.ts |
| | | - File: src/utils/di.ts (modify existing) |
| | | - Register FeatureService in dependency injection container |
| | | - Configure service lifetime and dependencies |
| | | - Purpose: Enable service injection throughout application |
| | | - _Leverage: existing DI configuration in src/utils/di.ts_ |
| | | - _Requirements: 3.1_ |
| | | - _Prompt: Role: DevOps Engineer with expertise in dependency injection and IoC containers | Task: Register FeatureService in DI container following requirement 3.1, configuring appropriate lifetime and dependencies using existing patterns from src/utils/di.ts | Restrictions: Must follow existing DI container patterns, do not create circular dependencies, maintain service resolution efficiency | Success: FeatureService is properly registered and resolvable, dependencies are correctly configured, service lifetime is appropriate for use case_ |
| | | |
| | | - [ ] 8. Create service unit tests in tests/services/FeatureService.test.ts |
| | | - File: tests/services/FeatureService.test.ts |
| | | - Write tests for service methods with mocked dependencies |
| | | - Test error handling scenarios |
| | | - Purpose: Ensure service reliability and proper error handling |
| | | - _Leverage: tests/helpers/testUtils.ts, tests/mocks/modelMocks.ts_ |
| | | - _Requirements: 3.2, 3.3_ |
| | | - _Prompt: Role: QA Engineer with expertise in service testing and mocking frameworks | Task: Create comprehensive unit tests for FeatureService methods covering requirements 3.2 and 3.3, using mocked dependencies from tests/mocks/modelMocks.ts and test utilities | Restrictions: Must mock all external dependencies, test business logic in isolation, do not test framework code | Success: All service methods tested with proper mocking, error scenarios covered, tests verify business logic correctness and error handling_ |
| | | |
| | | - [ ] 4. Create API endpoints |
| | | - Design API structure |
| | | - _Leverage: src/api/baseApi.ts, src/utils/apiUtils.ts_ |
| | | - _Requirements: 4.0_ |
| | | - _Prompt: Role: API Architect specializing in RESTful design and Express.js | Task: Design comprehensive API structure following requirement 4.0, leveraging existing patterns from src/api/baseApi.ts and utilities from src/utils/apiUtils.ts | Restrictions: Must follow REST conventions, maintain API versioning compatibility, do not expose internal data structures directly | Success: API structure is well-designed and documented, follows existing patterns, supports all required operations with proper HTTP methods and status codes_ |
| | | |
| | | - [ ] 4.1 Set up routing and middleware |
| | | - Configure application routes |
| | | - Add authentication middleware |
| | | - Set up error handling middleware |
| | | - _Leverage: src/middleware/auth.ts, src/middleware/errorHandler.ts_ |
| | | - _Requirements: 4.1_ |
| | | - _Prompt: Role: Backend Developer with expertise in Express.js middleware and routing | Task: Configure application routes and middleware following requirement 4.1, integrating authentication from src/middleware/auth.ts and error handling from src/middleware/errorHandler.ts | Restrictions: Must maintain middleware order, do not bypass security middleware, ensure proper error propagation | Success: Routes are properly configured with correct middleware chain, authentication works correctly, errors are handled gracefully throughout the request lifecycle_ |
| | | |
| | | - [ ] 4.2 Implement CRUD endpoints |
| | | - Create API endpoints |
| | | - Add request validation |
| | | - Write API integration tests |
| | | - _Leverage: src/controllers/BaseController.ts, src/utils/validation.ts_ |
| | | - _Requirements: 4.2, 4.3_ |
| | | - _Prompt: Role: Full-stack Developer with expertise in API development and validation | Task: Implement CRUD endpoints following requirements 4.2 and 4.3, extending BaseController patterns and using validation utilities from src/utils/validation.ts | Restrictions: Must validate all inputs, follow existing controller patterns, ensure proper HTTP status codes and responses | Success: All CRUD operations work correctly, request validation prevents invalid data, integration tests pass and cover all endpoints_ |
| | | |
| | | - [ ] 5. Add frontend components |
| | | - Plan component architecture |
| | | - _Leverage: src/components/BaseComponent.tsx, src/styles/theme.ts_ |
| | | - _Requirements: 5.0_ |
| | | - _Prompt: Role: Frontend Architect with expertise in React component design and architecture | Task: Plan comprehensive component architecture following requirement 5.0, leveraging base patterns from src/components/BaseComponent.tsx and theme system from src/styles/theme.ts | Restrictions: Must follow existing component patterns, maintain design system consistency, ensure component reusability | Success: Architecture is well-planned and documented, components are properly organized, follows existing patterns and theme system_ |
| | | |
| | | - [ ] 5.1 Create base UI components |
| | | - Set up component structure |
| | | - Implement reusable components |
| | | - Add styling and theming |
| | | - _Leverage: src/components/BaseComponent.tsx, src/styles/theme.ts_ |
| | | - _Requirements: 5.1_ |
| | | - _Prompt: Role: Frontend Developer specializing in React and component architecture | Task: Create reusable UI components following requirement 5.1, extending BaseComponent patterns and using existing theme system from src/styles/theme.ts | Restrictions: Must use existing theme variables, follow component composition patterns, ensure accessibility compliance | Success: Components are reusable and properly themed, follow existing architecture, accessible and responsive_ |
| | | |
| | | - [ ] 5.2 Implement feature-specific components |
| | | - Create feature components |
| | | - Add state management |
| | | - Connect to API endpoints |
| | | - _Leverage: src/hooks/useApi.ts, src/components/BaseComponent.tsx_ |
| | | - _Requirements: 5.2, 5.3_ |
| | | - _Prompt: Role: React Developer with expertise in state management and API integration | Task: Implement feature-specific components following requirements 5.2 and 5.3, using API hooks from src/hooks/useApi.ts and extending BaseComponent patterns | Restrictions: Must use existing state management patterns, handle loading and error states properly, maintain component performance | Success: Components are fully functional with proper state management, API integration works smoothly, user experience is responsive and intuitive_ |
| | | |
| | | - [ ] 6. Integration and testing |
| | | - Plan integration approach |
| | | - _Leverage: src/utils/integrationUtils.ts, tests/helpers/testUtils.ts_ |
| | | - _Requirements: 6.0_ |
| | | - _Prompt: Role: Integration Engineer with expertise in system integration and testing strategies | Task: Plan comprehensive integration approach following requirement 6.0, leveraging integration utilities from src/utils/integrationUtils.ts and test helpers | Restrictions: Must consider all system components, ensure proper test coverage, maintain integration test reliability | Success: Integration plan is comprehensive and feasible, all system components work together correctly, integration points are well-tested_ |
| | | |
| | | - [ ] 6.1 Write end-to-end tests |
| | | - Set up E2E testing framework |
| | | - Write user journey tests |
| | | - Add test automation |
| | | - _Leverage: tests/helpers/testUtils.ts, tests/fixtures/data.ts_ |
| | | - _Requirements: All_ |
| | | - _Prompt: Role: QA Automation Engineer with expertise in E2E testing and test frameworks like Cypress or Playwright | Task: Implement comprehensive end-to-end tests covering all requirements, setting up testing framework and user journey tests using test utilities and fixtures | Restrictions: Must test real user workflows, ensure tests are maintainable and reliable, do not test implementation details | Success: E2E tests cover all critical user journeys, tests run reliably in CI/CD pipeline, user experience is validated from end-to-end_ |
| | | |
| | | - [ ] 6.2 Final integration and cleanup |
| | | - Integrate all components |
| | | - Fix any integration issues |
| | | - Clean up code and documentation |
| | | - _Leverage: src/utils/cleanup.ts, docs/templates/_ |
| | | - _Requirements: All_ |
| | | - _Prompt: Role: Senior Developer with expertise in code quality and system integration | Task: Complete final integration of all components and perform comprehensive cleanup covering all requirements, using cleanup utilities and documentation templates | Restrictions: Must not break existing functionality, ensure code quality standards are met, maintain documentation consistency | Success: All components are fully integrated and working together, code is clean and well-documented, system meets all requirements and quality standards_ |
| New file |
| | |
| | | # Technology Stack |
| | | |
| | | ## Project Type |
| | | [Describe what kind of project this is: web application, CLI tool, desktop application, mobile app, library, API service, embedded system, game, etc.] |
| | | |
| | | ## Core Technologies |
| | | |
| | | ### Primary Language(s) |
| | | - **Language**: [e.g., Python 3.11, Go 1.21, TypeScript, Rust, C++] |
| | | - **Runtime/Compiler**: [if applicable] |
| | | - **Language-specific tools**: [package managers, build tools, etc.] |
| | | |
| | | ### Key Dependencies/Libraries |
| | | [List the main libraries and frameworks your project depends on] |
| | | - **[Library/Framework name]**: [Purpose and version] |
| | | - **[Library/Framework name]**: [Purpose and version] |
| | | |
| | | ### Application Architecture |
| | | [Describe how your application is structured - this could be MVC, event-driven, plugin-based, client-server, standalone, microservices, monolithic, etc.] |
| | | |
| | | ### Data Storage (if applicable) |
| | | - **Primary storage**: [e.g., PostgreSQL, files, in-memory, cloud storage] |
| | | - **Caching**: [e.g., Redis, in-memory, disk cache] |
| | | - **Data formats**: [e.g., JSON, Protocol Buffers, XML, binary] |
| | | |
| | | ### External Integrations (if applicable) |
| | | - **APIs**: [External services you integrate with] |
| | | - **Protocols**: [e.g., HTTP/REST, gRPC, WebSocket, TCP/IP] |
| | | - **Authentication**: [e.g., OAuth, API keys, certificates] |
| | | |
| | | ### Monitoring & Dashboard Technologies (if applicable) |
| | | - **Dashboard Framework**: [e.g., React, Vue, vanilla JS, terminal UI] |
| | | - **Real-time Communication**: [e.g., WebSocket, Server-Sent Events, polling] |
| | | - **Visualization Libraries**: [e.g., Chart.js, D3, terminal graphs] |
| | | - **State Management**: [e.g., Redux, Vuex, file system as source of truth] |
| | | |
| | | ## Development Environment |
| | | |
| | | ### Build & Development Tools |
| | | - **Build System**: [e.g., Make, CMake, Gradle, npm scripts, cargo] |
| | | - **Package Management**: [e.g., pip, npm, cargo, go mod, apt, brew] |
| | | - **Development workflow**: [e.g., hot reload, watch mode, REPL] |
| | | |
| | | ### Code Quality Tools |
| | | - **Static Analysis**: [Tools for code quality and correctness] |
| | | - **Formatting**: [Code style enforcement tools] |
| | | - **Testing Framework**: [Unit, integration, and/or end-to-end testing tools] |
| | | - **Documentation**: [Documentation generation tools] |
| | | |
| | | ### Version Control & Collaboration |
| | | - **VCS**: [e.g., Git, Mercurial, SVN] |
| | | - **Branching Strategy**: [e.g., Git Flow, GitHub Flow, trunk-based] |
| | | - **Code Review Process**: [How code reviews are conducted] |
| | | |
| | | ### Dashboard Development (if applicable) |
| | | - **Live Reload**: [e.g., Hot module replacement, file watchers] |
| | | - **Port Management**: [e.g., Dynamic allocation, configurable ports] |
| | | - **Multi-Instance Support**: [e.g., Running multiple dashboards simultaneously] |
| | | |
| | | ## Deployment & Distribution (if applicable) |
| | | - **Target Platform(s)**: [Where/how the project runs: cloud, on-premise, desktop, mobile, embedded] |
| | | - **Distribution Method**: [How users get your software: download, package manager, app store, SaaS] |
| | | - **Installation Requirements**: [Prerequisites, system requirements] |
| | | - **Update Mechanism**: [How updates are delivered] |
| | | |
| | | ## Technical Requirements & Constraints |
| | | |
| | | ### Performance Requirements |
| | | - [e.g., response time, throughput, memory usage, startup time] |
| | | - [Specific benchmarks or targets] |
| | | |
| | | ### Compatibility Requirements |
| | | - **Platform Support**: [Operating systems, architectures, versions] |
| | | - **Dependency Versions**: [Minimum/maximum versions of dependencies] |
| | | - **Standards Compliance**: [Industry standards, protocols, specifications] |
| | | |
| | | ### Security & Compliance |
| | | - **Security Requirements**: [Authentication, encryption, data protection] |
| | | - **Compliance Standards**: [GDPR, HIPAA, SOC2, etc. if applicable] |
| | | - **Threat Model**: [Key security considerations] |
| | | |
| | | ### Scalability & Reliability |
| | | - **Expected Load**: [Users, requests, data volume] |
| | | - **Availability Requirements**: [Uptime targets, disaster recovery] |
| | | - **Growth Projections**: [How the system needs to scale] |
| | | |
| | | ## Technical Decisions & Rationale |
| | | [Document key architectural and technology choices] |
| | | |
| | | ### Decision Log |
| | | 1. **[Technology/Pattern Choice]**: [Why this was chosen, alternatives considered] |
| | | 2. **[Architecture Decision]**: [Rationale, trade-offs accepted] |
| | | 3. **[Tool/Library Selection]**: [Reasoning, evaluation criteria] |
| | | |
| | | ## Known Limitations |
| | | [Document any technical debt, limitations, or areas for improvement] |
| | | |
| | | - [Limitation 1]: [Impact and potential future solutions] |
| | | - [Limitation 2]: [Why it exists and when it might be addressed] |
| New file |
| | |
| | | # User Templates |
| | | |
| | | This directory allows you to create custom templates that override the default Spec Workflow templates. |
| | | |
| | | ## How to Use Custom Templates |
| | | |
| | | 1. **Create your custom template file** in this directory with the exact same name as the default template you want to override: |
| | | - `requirements-template.md` - Override requirements document template |
| | | - `design-template.md` - Override design document template |
| | | - `tasks-template.md` - Override tasks document template |
| | | - `product-template.md` - Override product steering template |
| | | - `tech-template.md` - Override tech steering template |
| | | - `structure-template.md` - Override structure steering template |
| | | |
| | | 2. **Template Loading Priority**: |
| | | - The system first checks this `user-templates/` directory |
| | | - If a matching template is found here, it will be used |
| | | - Otherwise, the default template from `templates/` will be used |
| | | |
| | | ## Example Custom Template |
| | | |
| | | To create a custom requirements template: |
| | | |
| | | 1. Create a file named `requirements-template.md` in this directory |
| | | 2. Add your custom structure, for example: |
| | | |
| | | ```markdown |
| | | # Requirements Document |
| | | |
| | | ## Executive Summary |
| | | [Your custom section] |
| | | |
| | | ## Business Requirements |
| | | [Your custom structure] |
| | | |
| | | ## Technical Requirements |
| | | [Your custom fields] |
| | | |
| | | ## Custom Sections |
| | | [Add any sections specific to your workflow] |
| | | ``` |
| | | |
| | | ## Template Variables |
| | | |
| | | Templates can include placeholders that will be replaced when documents are created: |
| | | - `{{projectName}}` - The name of your project |
| | | - `{{featureName}}` - The name of the feature being specified |
| | | - `{{date}}` - The current date |
| | | - `{{author}}` - The document author |
| | | |
| | | ## Best Practices |
| | | |
| | | 1. **Start from defaults**: Copy a default template from `../templates/` as a starting point |
| | | 2. **Keep structure consistent**: Maintain similar section headers for tool compatibility |
| | | 3. **Document changes**: Add comments explaining why sections were added/modified |
| | | 4. **Version control**: Track your custom templates in version control |
| | | 5. **Test thoroughly**: Ensure custom templates work with the spec workflow tools |
| | | |
| | | ## Notes |
| | | |
| | | - Custom templates are project-specific and not included in the package distribution |
| | | - The `templates/` directory contains the default templates which are updated with each version |
| | | - Your custom templates in this directory are preserved during updates |
| | | - If a custom template has errors, the system will fall back to the default template |
| | |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.WebUtils; |
| | | import com.ruoyi.framework.web.service.TokenService; |
| | | import com.ruoyi.system.constant.GoodsTypeConstants; |
| | | import com.ruoyi.system.dto.*; |
| | | import com.ruoyi.system.export.TErpGoodsDetailExport; |
| | | import com.ruoyi.system.importExcel.TErpCloudGoodsExcel; |
| | | import com.ruoyi.system.importExcel.TErpFixedGoodsExcel; |
| | | import com.ruoyi.system.importExcel.TErpGoodsExcel; |
| | | import com.ruoyi.system.model.*; |
| | | import com.ruoyi.system.query.PageAddListQuery; |
| | |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | import java.util.stream.Stream; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | @ApiOperation(value = "平台设置商品排序") |
| | | @PostMapping(value = "/setGoodsSort") |
| | | public R<?> setGoodsSort(@RequestBody GoodsSortUpdateDto dto) { |
| | | SysUser user = tokenService.getLoginUser().getUser(); |
| | | Integer roleType = user.getRoleType(); |
| | | if (roleType == 1) { |
| | | // 平台 |
| | | TErpGoods goods = erpGoodsService.getById(dto.getId()); |
| | | if(goods!=null){ |
| | | goods.setSort(dto.getSort()); |
| | | erpGoodsService.updateById(goods); |
| | | } |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | |
| | | |
| | | @ApiOperation(value = "平台审核商品内容") |
| | |
| | | } |
| | | Integer roleType = user.getRoleType(); |
| | | TErpGoods goods = new TErpGoods(); |
| | | BeanUtils.copyProperties(dto, goods); |
| | | if (roleType == 4) { |
| | | // 供应商 |
| | | TCrmSupplier crmSupplier = crmSupplierService.getOne(Wrappers.lambdaQuery(TCrmSupplier.class) |
| | |
| | | goods.setFormulationSpec(dto.getFormulationSpec()); |
| | | goods.setPackingSpec(dto.getPackingSpec()); |
| | | goods.setGoodsName(dto.getGoodsName()); |
| | | goods.setIsPrescriptionDrug(null); |
| | | goods.setIsPrescriptionDrug(dto.getIsPrescriptionDrug()); |
| | | goods.setTypeId(dto.getTypeId()); |
| | | goods.setGoodsSpec(dto.getFormulationSpec() + "*" + dto.getPackingSpec()); |
| | | goods.setGoodsSpec(buildGoodsSpec(dto.getFormulationSpec(), dto.getPackingSpec())); |
| | | return R.ok(erpGoodsService.save(goods)); |
| | | } |
| | | |
| | |
| | | if (erpGoodsService.isExit(dto.getGoodsIdCode(), dto.getQuasiNumber(), null)) { |
| | | return R.fail("erp商品已存在,商品条码重复"); |
| | | } |
| | | if (erpGoodsService.isExitOne(dto.getGoodsIdCode(), dto.getQuasiNumber(), null)) { |
| | | if (StringUtils.isNotBlank(dto.getQuasiNumber()) |
| | | && erpGoodsService.isExitOne(dto.getGoodsIdCode(), dto.getQuasiNumber(), null)) { |
| | | return R.fail("erp商品已存在,国药准字号重复"); |
| | | } |
| | | Integer roleType = user.getRoleType(); |
| | | TErpGoods goods = new TErpGoods(); |
| | | BeanUtils.copyProperties(dto, goods); |
| | | goods.setGoodsSpec(dto.getFormulationSpec() + "*" + dto.getPackingSpec()); |
| | | goods.setGoodsSpec(buildGoodsSpec(dto.getFormulationSpec(), dto.getPackingSpec())); |
| | | if (roleType == 5) { |
| | | // 诊所 |
| | | TCrmClinic crmClinic = crmClinicService.getOne(Wrappers.lambdaQuery(TCrmClinic.class) |
| | |
| | | } |
| | | goods.setCreateId(user.getUserId().toString()); |
| | | goods.setGoodsIdCode(dto.getGoodsIdCode()); |
| | | goods.setGoodsSpec(dto.getFormulationSpec() + "*" + dto.getPackingSpec()); |
| | | goods.setGoodsSpec(buildGoodsSpec(dto.getFormulationSpec(), dto.getPackingSpec())); |
| | | // goods.setClinicPurchasePrice(dto.getSalesAmount()); |
| | | if (goods.getIsPrescriptionDrug() != null && goods.getIsPrescriptionDrug() == 3) { |
| | | goods.setIsPrescriptionDrug(null); |
| | |
| | | goods.setCreateId(user.getUserId().toString()); |
| | | goods.setGoodsIdCode(dto.getGoodsIdCode()); |
| | | goods.setId(dto.getId()); |
| | | goods.setGoodsSpec(dto.getFormulationSpec() + "*" + dto.getPackingSpec()); |
| | | goods.setGoodsSpec(buildGoodsSpec(dto.getFormulationSpec(), dto.getPackingSpec())); |
| | | if (goods.getIsPrescriptionDrug() != null && goods.getIsPrescriptionDrug() == 3) { |
| | | goods.setIsPrescriptionDrug(null); |
| | | } |
| | |
| | | return R.ok(goods); |
| | | } |
| | | |
| | | private String buildGoodsSpec(String formulationSpec, String packingSpec) { |
| | | if (StringUtils.isNotBlank(formulationSpec) && StringUtils.isNotBlank(packingSpec)) { |
| | | return formulationSpec + "*" + packingSpec; |
| | | } |
| | | return StringUtils.isNotBlank(packingSpec) ? packingSpec : formulationSpec; |
| | | } |
| | | |
| | | @ApiOperation(value = "商品管理信息导出--导出") |
| | | @PostMapping("/export") |
| | | @Log(title = "商品管理-导出", businessType = BusinessType.EXPORT) |
| | |
| | | } |
| | | |
| | | List<TErpGoodsVO> pageList = erpGoodsService.listExport(query, user); |
| | | Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), TErpGoodsVO.class, pageList); |
| | | List<TErpGoodsDetailExport> exportList = pageList.stream().map(goods -> { |
| | | TErpGoodsDetailExport exportItem = new TErpGoodsDetailExport(); |
| | | BeanUtils.copyProperties(goods, exportItem); |
| | | return exportItem; |
| | | }).collect(Collectors.toList()); |
| | | Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), TErpGoodsDetailExport.class, exportList); |
| | | HttpServletResponse response = WebUtils.response(); |
| | | assert response != null; |
| | | response.setContentType("application/vnd.ms-excel"); |
| | |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 供应商固定商品类型导入。旧商品导入接口保留给非固定类型模板使用。 |
| | | */ |
| | | @ApiOperation(value = "供应商固定商品类型导入") |
| | | @PostMapping("/importFixedGoodsFile") |
| | | @Log(title = "商品管理-固定类型导入", businessType = BusinessType.IMPORT) |
| | | public AjaxResult importFixedGoodsFile( |
| | | @RequestParam(value = "file") MultipartFile file, |
| | | @RequestParam(value = "typeId") String typeId) { |
| | | SysUser user = tokenService.getLoginUser().getUser(); |
| | | if (user == null || user.getRoleType() == null || user.getRoleType() != 4) { |
| | | return AjaxResult.error("仅供应商可以导入固定商品类型"); |
| | | } |
| | | int sheetIndex = getFixedGoodsSheetIndex(typeId); |
| | | if (sheetIndex < 0) { |
| | | return AjaxResult.error("商品类型不支持固定模板导入"); |
| | | } |
| | | if (file == null || file.isEmpty()) { |
| | | return AjaxResult.error("请选择要导入的文件"); |
| | | } |
| | | |
| | | TCrmSupplier supplier = getCurrentSupplier(user); |
| | | if (supplier == null) { |
| | | return AjaxResult.error("未找到当前供应商信息"); |
| | | } |
| | | |
| | | List<String> errors = new ArrayList<>(); |
| | | List<TErpGoods> goodsList = new ArrayList<>(); |
| | | Set<String> barcodeSet = new HashSet<>(); |
| | | ImportParams params = new ImportParams(); |
| | | params.setHeadRows(1); |
| | | params.setStartSheetIndex(sheetIndex); |
| | | params.setSheetNum(1); |
| | | |
| | | try (InputStream inputStream = file.getInputStream()) { |
| | | List<TErpFixedGoodsExcel> rows = ExcelImportUtil.importExcel( |
| | | inputStream, TErpFixedGoodsExcel.class, params); |
| | | if (rows == null || rows.isEmpty()) { |
| | | return AjaxResult.error("所选商品类型工作表没有可导入的数据"); |
| | | } |
| | | |
| | | for (int index = 0; index < rows.size(); index++) { |
| | | TErpFixedGoodsExcel row = rows.get(index); |
| | | if (isBlankFixedGoodsRow(row)) { |
| | | continue; |
| | | } |
| | | String rowName = StringUtils.isBlank(row.getGoodsName()) |
| | | ? "第" + (index + 2) + "行" |
| | | : row.getGoodsName().trim(); |
| | | String validationError = validateFixedGoodsRow(row, typeId); |
| | | if (validationError != null) { |
| | | errors.add(rowName + ":" + validationError); |
| | | continue; |
| | | } |
| | | |
| | | String goodsIdCode = row.getGoodsIdCode().trim(); |
| | | if (!barcodeSet.add(goodsIdCode)) { |
| | | errors.add(rowName + ":商品条码在导入文件中重复"); |
| | | continue; |
| | | } |
| | | if (erpGoodsService.count(new QueryWrapper<TErpGoods>() |
| | | .eq("goods_id_code", goodsIdCode)) > 0) { |
| | | errors.add(rowName + ":商品条码已存在"); |
| | | continue; |
| | | } |
| | | |
| | | try { |
| | | TErpGoods goods = buildFixedGoods(row, typeId, supplier, user); |
| | | goodsList.add(goods); |
| | | } catch (IllegalArgumentException e) { |
| | | errors.add(rowName + ":" + e.getMessage()); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return AjaxResult.error("导入文件解析失败,请确认使用固定商品类型模板"); |
| | | } |
| | | |
| | | if (goodsList.isEmpty() && errors.isEmpty()) { |
| | | return AjaxResult.error("所选商品类型工作表没有可导入的数据"); |
| | | } |
| | | if (!errors.isEmpty()) { |
| | | return AjaxResult.error("导入失败记录如下:", errors); |
| | | } |
| | | erpGoodsService.saveBatch(goodsList); |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | private int getFixedGoodsSheetIndex(String typeId) { |
| | | if (GoodsTypeConstants.DRUG_ID.equals(typeId)) { |
| | | return 0; |
| | | } |
| | | if (GoodsTypeConstants.MEDICAL_DEVICE_ID.equals(typeId)) { |
| | | return 1; |
| | | } |
| | | if (GoodsTypeConstants.FOOD_ID.equals(typeId)) { |
| | | return 2; |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | private TCrmSupplier getCurrentSupplier(SysUser user) { |
| | | TCrmSupplier supplier = crmSupplierService.getOne( |
| | | Wrappers.lambdaQuery(TCrmSupplier.class) |
| | | .eq(TCrmSupplier::getUserId, user.getUserId()) |
| | | .last("LIMIT 1")); |
| | | if (supplier == null) { |
| | | SysUser sysUser = sysUserService.selectUserById(user.getUserId()); |
| | | if (sysUser != null && sysUser.getObjectId() != null) { |
| | | supplier = crmSupplierService.getById(sysUser.getObjectId()); |
| | | } |
| | | } |
| | | return supplier; |
| | | } |
| | | |
| | | private String validateFixedGoodsRow(TErpFixedGoodsExcel row, String typeId) { |
| | | if (StringUtils.isBlank(row.getGoodsName())) { |
| | | return "商品名称不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getGoodsIdCode())) { |
| | | return "商品条形码不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getManufacturer())) { |
| | | return "生产厂家不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getWarningInventory())) { |
| | | return "预警库存不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getPackingUnitName())) { |
| | | return "包装单位不能为空"; |
| | | } |
| | | if (GoodsTypeConstants.DRUG_ID.equals(typeId)) { |
| | | if (StringUtils.isBlank(row.getQuasiNumber())) { |
| | | return "批准文号或国药准字号不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getProductType())) { |
| | | return "商品类型不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getFormulationSpec())) { |
| | | return "剂型规格不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getPackingSpec())) { |
| | | return "包装规格不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getEffectivePeriod())) { |
| | | return "有效时间不能为空"; |
| | | } |
| | | } else if (GoodsTypeConstants.MEDICAL_DEVICE_ID.equals(typeId)) { |
| | | if (StringUtils.isBlank(row.getDeviceCategory())) { |
| | | return "器械类别不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getProductType())) { |
| | | return "商品类型不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getProductTechnicalRequirementNumber())) { |
| | | return "产品技术要求编号不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getProductionLicenseOrFilingNumber())) { |
| | | return "生产许可证或备案凭证编号不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getRegistrantOrFiler())) { |
| | | return "注册人或备案人不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getEffectivePeriod())) { |
| | | return "有效时间不能为空"; |
| | | } |
| | | } else if (GoodsTypeConstants.FOOD_ID.equals(typeId)) { |
| | | if (StringUtils.isBlank(row.getProductionLicenseNumber())) { |
| | | return "生产许可证号不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getProductType())) { |
| | | return "商品类型不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getPackingSpec())) { |
| | | return "包装规格不能为空"; |
| | | } |
| | | if (StringUtils.isBlank(row.getProductCategory())) { |
| | | return "产品类别不能为空"; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private boolean isBlankFixedGoodsRow(TErpFixedGoodsExcel row) { |
| | | return Stream.of( |
| | | row.getGoodsName(), row.getCommonName(), row.getGoodsIdCode(), row.getQuasiNumber(), |
| | | row.getProductType(), row.getManufacturer(), row.getFormulationSpec(), row.getPackingSpec(), |
| | | row.getDeviceCategory(), row.getProductTechnicalRequirementNumber(), |
| | | row.getProductionLicenseOrFilingNumber(), row.getRegistrantOrFiler(), row.getStructureComposition(), |
| | | row.getApplicableScope(), row.getOriginType(), row.getProductionLicenseNumber(), |
| | | row.getProductCategory(), row.getProductExecutionStandard(), row.getSpecialFoodFlag(), |
| | | row.getSpecialFood(), row.getGoodsYards(), row.getMaintenanceInterval(), row.getLowPurchaseQuantity(), |
| | | row.getLowUnitName(), row.getIsPrescriptionDrug(), row.getGoodsSpec(), row.getWarningInventory(), |
| | | row.getPackingUnitName(), row.getInstructionsUse(), row.getSideEffect(), row.getSimplifiedCode(), |
| | | row.getDosageForm(), row.getUsageClassification(), row.getProductBrand(), |
| | | row.getProductBrandAndPrecautions(), row.getPlaceOfOrigin(), row.getIngredient(), row.getTrait(), |
| | | row.getIndication(), row.getTaboo(), row.getPrecautions(), row.getInteraction(), |
| | | row.getAdverseReaction(), row.getStorage(), row.getEffectivePeriod(), row.getOtherDescription()) |
| | | .allMatch(StringUtils::isBlank); |
| | | } |
| | | |
| | | private TErpGoods buildFixedGoods( |
| | | TErpFixedGoodsExcel row, |
| | | String typeId, |
| | | TCrmSupplier supplier, |
| | | SysUser user) { |
| | | TErpGoods goods = new TErpGoods(); |
| | | goods.setSupplierClinicId(supplier.getId()); |
| | | goods.setGoodsSource(1); |
| | | goods.setCreateId(user.getUserId().toString()); |
| | | goods.setTypeId(typeId); |
| | | goods.setGoodsName(row.getGoodsName().trim()); |
| | | goods.setGoodsIdCode(row.getGoodsIdCode().trim()); |
| | | goods.setManufacturer(row.getManufacturer().trim()); |
| | | goods.setQuasiNumber(trim(row.getQuasiNumber())); |
| | | goods.setFormulationSpec(trim(row.getFormulationSpec())); |
| | | goods.setPackingSpec(trim(row.getPackingSpec())); |
| | | goods.setCommonName(trim(row.getCommonName())); |
| | | goods.setProductType(trim(row.getProductType())); |
| | | goods.setDeviceCategory(trim(row.getDeviceCategory())); |
| | | goods.setProductTechnicalRequirementNumber(trim(row.getProductTechnicalRequirementNumber())); |
| | | goods.setProductionLicenseOrFilingNumber(trim(row.getProductionLicenseOrFilingNumber())); |
| | | goods.setRegistrantOrFiler(trim(row.getRegistrantOrFiler())); |
| | | goods.setStructureComposition(trim(row.getStructureComposition())); |
| | | goods.setApplicableScope(trim(row.getApplicableScope())); |
| | | goods.setOriginType(trim(row.getOriginType())); |
| | | goods.setProductionLicenseNumber(trim(row.getProductionLicenseNumber())); |
| | | goods.setProductCategory(trim(row.getProductCategory())); |
| | | goods.setProductExecutionStandard(trim(row.getProductExecutionStandard())); |
| | | goods.setSpecialFoodFlag(parseFlag(row.getSpecialFoodFlag(), "是否特殊食品")); |
| | | goods.setSpecialFood(trim(row.getSpecialFood())); |
| | | goods.setGoodsYards(trim(row.getGoodsYards())); |
| | | parseInteger(row.getMaintenanceInterval(), "保养周期"); |
| | | goods.setMaintenanceInterval(trim(row.getMaintenanceInterval())); |
| | | goods.setLowPurchaseQuantity(parseInteger(row.getLowPurchaseQuantity(), "最低采购数量")); |
| | | goods.setIsPrescriptionDrug(parseFlag(row.getIsPrescriptionDrug(), "是否为处方药")); |
| | | goods.setGoodsSpec(StringUtils.isBlank(row.getGoodsSpec()) |
| | | ? buildGoodsSpec(row.getFormulationSpec(), row.getPackingSpec()) |
| | | : row.getGoodsSpec().trim()); |
| | | goods.setWarningInventory(parseInteger(row.getWarningInventory(), "预警库存")); |
| | | goods.setLowUnitId(findUnitId(row.getLowUnitName(), "最低采购单位")); |
| | | goods.setPackingUnitId(findUnitId(row.getPackingUnitName(), "包装单位")); |
| | | goods.setInstructionsUse(trim(row.getInstructionsUse())); |
| | | goods.setSideEffect(StringUtils.isBlank(row.getSideEffect()) |
| | | ? trim(row.getAdverseReaction()) : row.getSideEffect().trim()); |
| | | goods.setSimplifiedCode(trim(row.getSimplifiedCode())); |
| | | goods.setDosageForm(trim(row.getDosageForm())); |
| | | goods.setUsageClassification(trim(row.getUsageClassification())); |
| | | goods.setProductBrand(StringUtils.isBlank(row.getProductBrand()) |
| | | ? trim(row.getProductBrandAndPrecautions()) |
| | | : row.getProductBrand().trim()); |
| | | goods.setPlaceOfOrigin(trim(row.getPlaceOfOrigin())); |
| | | goods.setIngredient(trim(row.getIngredient())); |
| | | goods.setTrait(trim(row.getTrait())); |
| | | goods.setIndication(trim(row.getIndication())); |
| | | goods.setTaboo(trim(row.getTaboo())); |
| | | goods.setPrecautions(trim(row.getPrecautions())); |
| | | goods.setInteraction(trim(row.getInteraction())); |
| | | goods.setStorage(trim(row.getStorage())); |
| | | goods.setEffectivePeriod(trim(row.getEffectivePeriod())); |
| | | goods.setOtherDescription(trim(row.getOtherDescription())); |
| | | return goods; |
| | | } |
| | | |
| | | private String findUnitId(String unitName, String fieldName) { |
| | | if (StringUtils.isBlank(unitName)) { |
| | | return null; |
| | | } |
| | | TErpGoodsUnit unit = erpGoodsUnitService.getOne( |
| | | new LambdaQueryWrapper<TErpGoodsUnit>().eq(TErpGoodsUnit::getUnitName, unitName.trim())); |
| | | if (unit == null) { |
| | | throw new IllegalArgumentException(fieldName + "有误"); |
| | | } |
| | | return unit.getId(); |
| | | } |
| | | |
| | | private Integer parseInteger(String value, String fieldName) { |
| | | if (StringUtils.isBlank(value)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return new BigDecimal(value.trim()).intValueExact(); |
| | | } catch (Exception e) { |
| | | throw new IllegalArgumentException(fieldName + "有误"); |
| | | } |
| | | } |
| | | |
| | | private Integer parseFlag(String value, String fieldName) { |
| | | if (StringUtils.isBlank(value)) { |
| | | return null; |
| | | } |
| | | String normalized = value.trim(); |
| | | if ("是".equals(normalized) || "1".equals(normalized)) { |
| | | return 1; |
| | | } |
| | | if ("否".equals(normalized) || "2".equals(normalized)) { |
| | | return 2; |
| | | } |
| | | throw new IllegalArgumentException(fieldName + "有误"); |
| | | } |
| | | |
| | | private String trim(String value) { |
| | | return StringUtils.isBlank(value) ? null : value.trim(); |
| | | } |
| | | |
| | | |
| | | @ApiOperation(value = "平台设置分佣") |
| | | @PostMapping(value = "/setCommission") |
| | |
| | | import com.ruoyi.common.basic.PageInfo; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.enums.BusinessType; |
| | | import com.ruoyi.system.constant.GoodsTypeConstants; |
| | | import com.ruoyi.system.model.TErpGoods; |
| | | import com.ruoyi.system.model.TErpGoodsType; |
| | | import com.ruoyi.system.query.TErpGoodsTypeQuery; |
| | |
| | | @ApiOperation(value = "获取erp商品类型列表") |
| | | @PostMapping(value = "/list") |
| | | public R<List<TErpGoodsType>> list() { |
| | | erpGoodsTypeService.ensureBuiltinTypes(); |
| | | return R.ok(erpGoodsTypeService.list(Wrappers.lambdaQuery(TErpGoodsType.class).orderByDesc(TErpGoodsType::getCreateTime))); |
| | | } |
| | | |
| | |
| | | if (erpGoodsTypeService.isExit(dto)) { |
| | | return R.fail("erp商品类型名称已存在"); |
| | | } |
| | | dto.setId(null); |
| | | return R.ok(erpGoodsTypeService.save(dto)); |
| | | } |
| | | |
| | |
| | | if (erpGoodsTypeService.isExit(dto)) { |
| | | return R.fail("erp商品类型名称已存在"); |
| | | } |
| | | return R.ok(erpGoodsTypeService.updateById(dto)); |
| | | TErpGoodsType existing = erpGoodsTypeService.getById(dto.getId()); |
| | | if (existing == null) { |
| | | return R.fail("商品类型不存在"); |
| | | } |
| | | existing.setTypeName(dto.getTypeName()); |
| | | return R.ok(erpGoodsTypeService.updateById(existing)); |
| | | } |
| | | |
| | | /** |
| | |
| | | @ApiOperation(value = "删除erp商品类型") |
| | | @DeleteMapping(value = "/deleteById") |
| | | public R<Boolean> deleteById(@RequestParam String id) { |
| | | if (GoodsTypeConstants.isBuiltin(id)) { |
| | | return R.fail("内置商品类型不可删除"); |
| | | } |
| | | long count = erpGoodsService.count(Wrappers.lambdaQuery(TErpGoods.class).eq(TErpGoods::getTypeId, id)); |
| | | if (count>0) { |
| | | return R.fail("该商品分类下有商品,无法删除"); |
| | |
| | | @ApiOperation(value = "批量删除erp商品类型") |
| | | @DeleteMapping(value = "/deleteByIds") |
| | | public R<Boolean> deleteByIds(@RequestBody List<String> ids) { |
| | | if (ids.stream().anyMatch(GoodsTypeConstants::isBuiltin)) { |
| | | return R.fail("内置商品类型不可删除"); |
| | | } |
| | | List<TErpGoods> items = erpGoodsService.list(Wrappers.lambdaQuery(TErpGoods.class).in(TErpGoods::getTypeId, ids)); |
| | | for (String id : ids) { |
| | | if (items.stream().anyMatch(t -> t.getTypeId().equals(id))) { |
| | |
| | | |
| | | |
| | | String apiPath = "api/v3/labs/trans/preorder"; |
| | | String body = "{\n" + |
| | | "\t\"req_time\": \"" + time + "\",\n" + |
| | | "\t\"version\": \"3.0\",\n" + |
| | | "\t\"req_data\": {\n" + |
| | | "\t\t\"out_trade_no\": \"" + outTradeNo + "\",\n" + |
| | | "\t\t\"merchant_no\": \""+supplier.getRecvMerchantNo()+"\",\n" + |
| | | "\t\t\"term_no\": \""+supplier.getTermNo()+"\",\n" + |
| | | String body=""; |
| | | if("822641048160MMJ".equals(supplier.getRecvMerchantNo())){ |
| | | body= "{\n" + |
| | | "\t\"req_time\": \"" + time + "\",\n" + |
| | | "\t\"version\": \"3.0\",\n" + |
| | | "\t\"req_data\": {\n" + |
| | | "\t\t\"out_trade_no\": \"" + outTradeNo + "\",\n" + |
| | | "\t\t\"merchant_no\": \""+supplier.getRecvMerchantNo()+"\",\n" + |
| | | "\t\t\"term_no\": \""+supplier.getTermNo()+"\",\n" + |
| | | // "\t\t\"notify_url\": \"http://221.182.45.100:8089/t-sys-order/messageHandle\",\n" + |
| | | "\t\t\"notify_url\": \"https://haizhentong.com/manage/t-sys-order/messageHandle\",\n" + |
| | | "\t\t\"location_info\": {\n" + |
| | | "\t\t\t\"request_ip\": \"" + ipAddr + "\"\n" + |
| | | "\t\t},\n" + |
| | | "\t\t\"subject\": \"商品采购\",\n" + |
| | | "\t\t\"settle_type\": \"1\",\n" + |
| | | "\t\t\"total_amount\": " + amount + ",\n" + |
| | | "\t\t\"account_type\": \"" + type + "\",\n" + |
| | | "\t\t\"trans_type\": \""+trans_type+"\"\n" + |
| | | "\t}\n" + |
| | | "}"; |
| | | "\t\t\"notify_url\": \"https://haizhentong.com/manage/t-sys-order/messageHandle\",\n" + |
| | | "\t\t\"location_info\": {\n" + |
| | | "\t\t\t\"request_ip\": \"" + ipAddr + "\"\n" + |
| | | "\t\t},\n" + |
| | | "\t\t\"subject\": \"商品采购\",\n" + |
| | | "\t\t\"settle_type\": \"0\",\n" + |
| | | "\t\t\"total_amount\": " + amount + ",\n" + |
| | | "\t\t\"account_type\": \"" + type + "\",\n" + |
| | | "\t\t\"trans_type\": \""+trans_type+"\"\n" + |
| | | "\t}\n" + |
| | | "}"; |
| | | }else { |
| | | body = "{\n" + |
| | | "\t\"req_time\": \"" + time + "\",\n" + |
| | | "\t\"version\": \"3.0\",\n" + |
| | | "\t\"req_data\": {\n" + |
| | | "\t\t\"out_trade_no\": \"" + outTradeNo + "\",\n" + |
| | | "\t\t\"merchant_no\": \""+supplier.getRecvMerchantNo()+"\",\n" + |
| | | "\t\t\"term_no\": \""+supplier.getTermNo()+"\",\n" + |
| | | // "\t\t\"notify_url\": \"http://221.182.45.100:8089/t-sys-order/messageHandle\",\n" + |
| | | "\t\t\"notify_url\": \"https://haizhentong.com/manage/t-sys-order/messageHandle\",\n" + |
| | | "\t\t\"location_info\": {\n" + |
| | | "\t\t\t\"request_ip\": \"" + ipAddr + "\"\n" + |
| | | "\t\t},\n" + |
| | | "\t\t\"subject\": \"商品采购\",\n" + |
| | | "\t\t\"settle_type\": \"1\",\n" + |
| | | "\t\t\"total_amount\": " + amount + ",\n" + |
| | | "\t\t\"account_type\": \"" + type + "\",\n" + |
| | | "\t\t\"trans_type\": \""+trans_type+"\"\n" + |
| | | "\t}\n" + |
| | | "}"; |
| | | } |
| | | |
| | | |
| | | |
| | | String authorization = getAuthorization(body); |
| | | System.err.println("支付请求"+body); |
| | |
| | | erpProcurement.setMoney(payMoney); |
| | | erpProcurement.setOutSeparateNo(outTradeNo); |
| | | erpProcurementService.updateById(erpProcurement); |
| | | // 添加平台的 |
| | | map.put("recv_datas", objects); |
| | | |
| | | reqMap.put("req_data", map); |
| | | TCrmSupplier supplier = crmSupplierService.getById(erpProcurement.getSupplierId()); |
| | | if("822641048160MMJ".equals(supplier.getRecvMerchantNo())){ |
| | | list.forEach(e->e.setStatus(1)); |
| | | erpProcurementGoodsService.updateBatchById(list); |
| | | }else { |
| | | // 添加平台的 |
| | | map.put("recv_datas", objects); |
| | | |
| | | reqMap.put("req_data", map); |
| | | |
| | | |
| | | |
| | | String jsonString = JSON.toJSONString(reqMap); |
| | | String jsonString = JSON.toJSONString(reqMap); |
| | | // String authorization1 = getAuthorization(jsonString); |
| | | // post.header("Authorization", SCHEMA + " " + authorization); |
| | | // |
| | | // post.body(jsonString); |
| | | // HttpResponse response = post.execute(); |
| | | // String responseStr = response.body(); |
| | | String authorization1 = getAuthorization(jsonString); |
| | | String authorization1 = getAuthorization(jsonString); |
| | | |
| | | Map<String, ? extends Object> maps = |
| | | new ImmutableMap.Builder<String, String>(). |
| | | put("id", erpProcurement.getId()).put("authorization", authorization1).put("body", jsonString) |
| | | .build(); |
| | | QuartzManager.addJob( |
| | | RevenueSharingJob.class, |
| | | (RevenueSharingJob.name+erpProcurement.getId()).toUpperCase(), |
| | | TimeJobType.REVENUE_SHARING, |
| | | DateUtils.localDateTimeToDate(LocalDateTime.now().plusSeconds(30)), |
| | | maps |
| | | ); |
| | | Map<String, ? extends Object> maps = |
| | | new ImmutableMap.Builder<String, String>(). |
| | | put("id", erpProcurement.getId()).put("authorization", authorization1).put("body", jsonString) |
| | | .build(); |
| | | QuartzManager.addJob( |
| | | RevenueSharingJob.class, |
| | | (RevenueSharingJob.name+erpProcurement.getId()).toUpperCase(), |
| | | TimeJobType.REVENUE_SHARING, |
| | | DateUtils.localDateTimeToDate(LocalDateTime.now().plusSeconds(30)), |
| | | maps |
| | | ); |
| | | } |
| | | |
| | | |
| | | |
| | | // org.apache.http.HttpResponse response1 = post(LakalaConfig.getServerUrl() + "api/v3/sacs/separate", jsonString, authorization1); |
| | | // String responseStr = IOUtils.toString(response1.getEntity().getContent(), ENCODING); |
| | |
| | | # url: jdbc:mysql://124.71.107.223:10633/haizhentong?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true |
| | | # username: haizhentong |
| | | # password: hzt@2025Mysql! |
| | | url: jdbc:mysql://192.168.110.34:3306/haizhentong?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true |
| | | url: jdbc:mysql://192.168.110.111:3306/haizhentong?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true |
| | | username: root |
| | | password: 123456 |
| | | # 从库数据源 |
| | |
| | | # 项目相关配置 |
| | | spring: |
| | | profiles: |
| | | active: prod |
| | | active: test |
| | |
| | | sysAppUserVO.setSysOrders(sysOrders); |
| | | // 查询诊所账号 |
| | | SysUser sysUser = sysUserService.selectByPhone(sysAppUserVO.getPhone()); |
| | | if(Objects.nonNull(sysUser)){ |
| | | if(Objects.nonNull(sysUser) && sysUser.getRoleType() == 5){ |
| | | sysAppUserVO.setIsClinicUser(1); |
| | | // 查询诊所信息 |
| | | TCrmClinic crmClinic = crmClinicService.getOne(Wrappers.lambdaQuery(TCrmClinic.class) |
| | |
| | | import com.ruoyi.system.service.TSysInspectionService; |
| | | import com.ruoyi.system.service.TSysOtherConfigService; |
| | | import com.ruoyi.system.service.TSysPayRecordService; |
| | | import com.ruoyi.system.utils.xiMaUtil.AesUtil; |
| | | import com.ruoyi.system.wxPay.enums.TradeStateEnum; |
| | | import com.ruoyi.system.wxPay.model.WeixinPayProperties; |
| | | import com.ruoyi.system.wxPay.utils.WxTimeUtils; |
| | | import com.ruoyi.system.wxPay.utils.WxV3Pay; |
| | | import com.wechat.pay.contrib.apache.httpclient.util.AesUtil; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | JSONObject jsonObject = JSONObject.parseObject(requestBody.toString()); |
| | | JSONObject resource = jsonObject.getJSONObject("resource"); |
| | | |
| | | AesUtil aesUtil = new AesUtil(weixinPayProperties.getV3().getApiKey().getBytes(StandardCharsets.UTF_8)); |
| | | String decryptedData = aesUtil.decryptToString(resource.getString("associated_data").getBytes(StandardCharsets.UTF_8), resource.getString("nonce").getBytes(StandardCharsets.UTF_8), |
| | | resource.getString("ciphertext")); |
| | | System.err.println("微信解密的字符串信息" + decryptedData); |
| | | JSONObject jsonInfo = JSONObject.parse(decryptedData); |
| | | // AesUtil aesUtil = new AesUtil(weixinPayProperties.getV3().getApiKey().getBytes(StandardCharsets.UTF_8)); |
| | | // String decryptedData = aesUtil.decryptToString(resource.getString("associated_data").getBytes(StandardCharsets.UTF_8), resource.getString("nonce").getBytes(StandardCharsets.UTF_8), |
| | | // resource.getString("ciphertext")); |
| | | // System.err.println("微信解密的字符串信息" + decryptedData); |
| | | JSONObject jsonInfo = JSONObject.parse(""); |
| | | String out_trade_no = jsonInfo.getString("out_trade_no"); |
| | | String transaction_id = jsonInfo.getString("transaction_id"); |
| | | String trade_state = jsonInfo.getString("trade_state"); |
| | |
| | | # 项目相关配置 |
| | | spring: |
| | | profiles: |
| | | active: prod |
| | | active: test |
| New file |
| | |
| | | package com.ruoyi.system.constant; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | /** |
| | | * 内置商品类型。名称允许修改,业务判断必须使用固定 ID。 |
| | | */ |
| | | public final class GoodsTypeConstants { |
| | | |
| | | public static final String DRUG_ID = "00000000000000000000000000000001"; |
| | | public static final String MEDICAL_DEVICE_ID = "00000000000000000000000000000002"; |
| | | public static final String FOOD_ID = "00000000000000000000000000000003"; |
| | | |
| | | public static final Map<String, String> BUILTIN_TYPES; |
| | | |
| | | static { |
| | | Map<String, String> types = new LinkedHashMap<>(); |
| | | types.put(DRUG_ID, "药品类"); |
| | | types.put(MEDICAL_DEVICE_ID, "医疗器械类"); |
| | | types.put(FOOD_ID, "食品类"); |
| | | BUILTIN_TYPES = Collections.unmodifiableMap(types); |
| | | } |
| | | |
| | | private GoodsTypeConstants() { |
| | | } |
| | | |
| | | public static boolean isBuiltin(String id) { |
| | | return id != null && BUILTIN_TYPES.containsKey(id); |
| | | } |
| | | |
| | | public static Set<String> ids() { |
| | | return BUILTIN_TYPES.keySet(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class GoodsSortUpdateDto { |
| | | private String id; |
| | | |
| | | private Integer sort; |
| | | } |
| | |
| | | |
| | | |
| | | @ApiModelProperty(value = "供应商名称") |
| | | @NotBlank ( message = "供应商名称不能为空") |
| | | private String supplierName; |
| | | |
| | | |
| | |
| | | private String manufacturer; |
| | | |
| | | @ApiModelProperty(value = "制剂规格") |
| | | @NotBlank ( message = "制剂规格不能为空") |
| | | private String formulationSpec; |
| | | |
| | | @ApiModelProperty(value = "包装规格") |
| | | @NotBlank ( message = "包装规格不能为空") |
| | | private String packingSpec; |
| | | |
| | | |
| | |
| | | @ApiModelProperty(value = "商品条码") |
| | | @NotBlank ( message = "商品条码不能为空") |
| | | private String goodsIdCode; |
| | | |
| | | @ApiModelProperty(value = "商品标识码") |
| | | private String goodsYards; |
| | | |
| | | |
| | | @ApiModelProperty(value = "保养周期") |
| | |
| | | @ApiModelProperty(value = "是否为处方药 1=是 2=否") |
| | | private Integer isPrescriptionDrug; |
| | | |
| | | @ApiModelProperty(value = "最低采购数量") |
| | | private Integer lowPurchaseQuantity; |
| | | |
| | | @ApiModelProperty(value = "最低采购单位,单位id") |
| | | private String lowUnitId; |
| | | |
| | | |
| | | @ApiModelProperty(value = "商品规格") |
| | | @NotBlank ( message = "商品规格不能为空") |
| | | private String goodsSpec; |
| | | |
| | | |
| | |
| | | |
| | | @ApiModelProperty(value = "副作用") |
| | | private String sideEffect; |
| | | |
| | | @ApiModelProperty(value = "封面图") |
| | | private String cover; |
| | | |
| | | @ApiModelProperty(value = "详情图") |
| | | private String detailPictures; |
| | | |
| | | |
| | | |
| | |
| | | @ApiModelProperty(value = "相互作用") |
| | | private String interaction; |
| | | |
| | | @ApiModelProperty(value = "贮藏") |
| | | private String storage; |
| | | |
| | | private String commonName; |
| | | private String productType; |
| | | private String deviceCategory; |
| | | private String productTechnicalRequirementNumber; |
| | | private String productionLicenseOrFilingNumber; |
| | | private String registrantOrFiler; |
| | | private String structureComposition; |
| | | private String applicableScope; |
| | | private String originType; |
| | | private String productionLicenseNumber; |
| | | private String productCategory; |
| | | private String productExecutionStandard; |
| | | private Integer specialFoodFlag; |
| | | private String specialFood; |
| | | private String effectivePeriod; |
| | | private String otherDescription; |
| | | |
| | | |
| | | |
| | | |
| | |
| | | |
| | | |
| | | @ApiModelProperty(value = "供应商名称") |
| | | @NotBlank ( message = "供应商名称不能为空") |
| | | private String supplierName; |
| | | |
| | | |
| | |
| | | private String manufacturer; |
| | | |
| | | @ApiModelProperty(value = "制剂规格") |
| | | @NotBlank ( message = "制剂规格不能为空") |
| | | private String formulationSpec; |
| | | |
| | | @ApiModelProperty(value = "包装规格") |
| | | @NotBlank ( message = "包装规格不能为空") |
| | | private String packingSpec; |
| | | |
| | | |
| | |
| | | @ApiModelProperty(value = "商品条码") |
| | | @NotBlank ( message = "商品条码不能为空") |
| | | private String goodsIdCode; |
| | | |
| | | @ApiModelProperty(value = "商品标识码") |
| | | private String goodsYards; |
| | | |
| | | |
| | | @ApiModelProperty(value = "保养周期") |
| | |
| | | @ApiModelProperty(value = "是否为处方药 1=是 2=否") |
| | | private Integer isPrescriptionDrug; |
| | | |
| | | @ApiModelProperty(value = "最低采购数量") |
| | | private Integer lowPurchaseQuantity; |
| | | |
| | | @ApiModelProperty(value = "最低采购单位,单位id") |
| | | private String lowUnitId; |
| | | |
| | | |
| | | @ApiModelProperty(value = "商品规格") |
| | | @NotBlank ( message = "商品规格不能为空") |
| | | private String goodsSpec; |
| | | |
| | | |
| | |
| | | |
| | | @ApiModelProperty(value = "副作用") |
| | | private String sideEffect; |
| | | |
| | | @ApiModelProperty(value = "封面图") |
| | | private String cover; |
| | | |
| | | @ApiModelProperty(value = "详情图") |
| | | private String detailPictures; |
| | | |
| | | |
| | | |
| | |
| | | @ApiModelProperty(value = "相互作用") |
| | | private String interaction; |
| | | |
| | | @ApiModelProperty(value = "贮藏") |
| | | private String storage; |
| | | |
| | | private String commonName; |
| | | private String productType; |
| | | private String deviceCategory; |
| | | private String productTechnicalRequirementNumber; |
| | | private String productionLicenseOrFilingNumber; |
| | | private String registrantOrFiler; |
| | | private String structureComposition; |
| | | private String applicableScope; |
| | | private String originType; |
| | | private String productionLicenseNumber; |
| | | private String productCategory; |
| | | private String productExecutionStandard; |
| | | private Integer specialFoodFlag; |
| | | private String specialFood; |
| | | private String effectivePeriod; |
| | | private String otherDescription; |
| | | |
| | | |
| | | |
| | | |
| | |
| | | |
| | | |
| | | @ApiModelProperty(value = "制剂规格") |
| | | @NotBlank(message = "制剂规格不能为空") |
| | | private String formulationSpec; |
| | | |
| | | @ApiModelProperty(value = "包装规格") |
| | | @NotBlank(message = "包装规格不能为空") |
| | | private String packingSpec; |
| | | |
| | | @ApiModelProperty(value = "封面图") |
| | | private String cover; |
| | | |
| | | @ApiModelProperty(value = "详情图") |
| | | private String detailPictures; |
| | | |
| | | @ApiModelProperty(value = "商品标识码") |
| | | private String goodsYards; |
| | | |
| | | @ApiModelProperty(value = "保养周期") |
| | | private String maintenanceInterval; |
| | | |
| | | @ApiModelProperty(value = "保养周期单位") |
| | | private String maintenanceIntervalUnit; |
| | | |
| | | @ApiModelProperty(value = "最低采购数量") |
| | | private Integer lowPurchaseQuantity; |
| | | |
| | | @ApiModelProperty(value = "最低采购单位") |
| | | private String lowUnitId; |
| | | |
| | | @ApiModelProperty(value = "是否为处方药 1=是 2=否") |
| | | private Integer isPrescriptionDrug; |
| | | |
| | | @ApiModelProperty(value = "商品规格") |
| | | private String goodsSpec; |
| | | |
| | | @ApiModelProperty(value = "预警库存") |
| | | private Integer warningInventory; |
| | | |
| | | @ApiModelProperty(value = "包装单位") |
| | | private String packingUnitId; |
| | | |
| | | @ApiModelProperty(value = "使用说明") |
| | | private String instructionsUse; |
| | | |
| | | @ApiModelProperty(value = "副作用") |
| | | private String sideEffect; |
| | | |
| | | @ApiModelProperty(value = "拼音简码") |
| | | private String simplifiedCode; |
| | | |
| | | @ApiModelProperty(value = "剂型") |
| | | private String dosageForm; |
| | | |
| | | @ApiModelProperty(value = "用途分类") |
| | | private String usageClassification; |
| | | |
| | | @ApiModelProperty(value = "商品品牌名") |
| | | private String productBrand; |
| | | |
| | | @ApiModelProperty(value = "产地") |
| | | private String placeOfOrigin; |
| | | |
| | | @ApiModelProperty(value = "成分") |
| | | private String ingredient; |
| | | |
| | | @ApiModelProperty(value = "性状") |
| | | private String trait; |
| | | |
| | | @ApiModelProperty(value = "适应症") |
| | | private String indication; |
| | | |
| | | @ApiModelProperty(value = "禁忌") |
| | | private String taboo; |
| | | |
| | | @ApiModelProperty(value = "注意事项") |
| | | private String precautions; |
| | | |
| | | @ApiModelProperty(value = "相互作用") |
| | | private String interaction; |
| | | |
| | | @ApiModelProperty(value = "贮藏") |
| | | private String storage; |
| | | |
| | | private String commonName; |
| | | private String productType; |
| | | private String deviceCategory; |
| | | private String productTechnicalRequirementNumber; |
| | | private String productionLicenseOrFilingNumber; |
| | | private String registrantOrFiler; |
| | | private String structureComposition; |
| | | private String applicableScope; |
| | | private String originType; |
| | | private String productionLicenseNumber; |
| | | private String productCategory; |
| | | private String productExecutionStandard; |
| | | private Integer specialFoodFlag; |
| | | private String specialFood; |
| | | private String effectivePeriod; |
| | | private String otherDescription; |
| | | |
| | | } |
| | |
| | | @ApiModelProperty(value = "生产厂家") |
| | | private String manufacturer; |
| | | |
| | | private String commonName; |
| | | private String productType; |
| | | private String deviceCategory; |
| | | private String productTechnicalRequirementNumber; |
| | | private String productionLicenseOrFilingNumber; |
| | | private String registrantOrFiler; |
| | | private String structureComposition; |
| | | private String applicableScope; |
| | | private String originType; |
| | | private String productionLicenseNumber; |
| | | private String productCategory; |
| | | private String productExecutionStandard; |
| | | private Integer specialFoodFlag; |
| | | private String specialFood; |
| | | private String effectivePeriod; |
| | | private String otherDescription; |
| | | |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.export; |
| | | |
| | | import cn.afterturn.easypoi.excel.annotation.Excel; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * 商品详情导出。 |
| | | */ |
| | | @Data |
| | | public class TErpGoodsDetailExport implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Excel(name = "所属供应商", width = 20) |
| | | private String supplierName; |
| | | |
| | | @Excel(name = "商品名称", width = 25) |
| | | private String goodsName; |
| | | |
| | | @Excel(name = "商品分类", width = 15) |
| | | private String typeName; |
| | | |
| | | @Excel(name = "商品条形码", width = 20) |
| | | private String goodsIdCode; |
| | | |
| | | @Excel(name = "通用名", width = 20) |
| | | private String commonName; |
| | | |
| | | @Excel(name = "批准文号或国药准字号", width = 25) |
| | | private String quasiNumber; |
| | | |
| | | @Excel(name = "商品细分类型", width = 20) |
| | | private String productType; |
| | | |
| | | @Excel(name = "器械类别", width = 20) |
| | | private String deviceCategory; |
| | | |
| | | @Excel(name = "产品技术要求编号", width = 25) |
| | | private String productTechnicalRequirementNumber; |
| | | |
| | | @Excel(name = "生产许可证或备案凭证编号", width = 30) |
| | | private String productionLicenseOrFilingNumber; |
| | | |
| | | @Excel(name = "注册人或备案人", width = 25) |
| | | private String registrantOrFiler; |
| | | |
| | | @Excel(name = "结构及组成", width = 35) |
| | | private String structureComposition; |
| | | |
| | | @Excel(name = "适用范围", width = 35) |
| | | private String applicableScope; |
| | | |
| | | @Excel(name = "国产/进口", width = 15) |
| | | private String originType; |
| | | |
| | | @Excel(name = "生产许可证号", width = 25) |
| | | private String productionLicenseNumber; |
| | | |
| | | @Excel(name = "产品类别", width = 20) |
| | | private String productCategory; |
| | | |
| | | @Excel(name = "商品执行标准", width = 25) |
| | | private String productExecutionStandard; |
| | | |
| | | @Excel(name = "是否特殊食品", width = 15, replace = {"是_1", "否_2"}) |
| | | private Integer specialFoodFlag; |
| | | |
| | | @Excel(name = "特殊食品", width = 20) |
| | | private String specialFood; |
| | | |
| | | @Excel(name = "生产厂家", width = 25) |
| | | private String manufacturer; |
| | | |
| | | @Excel(name = "制剂规格", width = 20) |
| | | private String formulationSpec; |
| | | |
| | | @Excel(name = "包装规格", width = 20) |
| | | private String packingSpec; |
| | | |
| | | @Excel(name = "商品标识码", width = 20) |
| | | private String goodsYards; |
| | | |
| | | @Excel(name = "保养周期", width = 15) |
| | | private String maintenanceInterval; |
| | | |
| | | @Excel(name = "保养周期单位", width = 15) |
| | | private String maintenanceIntervalUnit; |
| | | |
| | | @Excel(name = "诊所采购价格", width = 15) |
| | | private BigDecimal clinicPurchasePrice; |
| | | |
| | | @Excel(name = "平台分佣金额", width = 15) |
| | | private BigDecimal platformCommissionPrice; |
| | | |
| | | @Excel(name = "是否为处方药", width = 15, replace = {"是_1", "否_2", "_3"}) |
| | | private Integer isPrescriptionDrug; |
| | | |
| | | @Excel(name = "最低采购数量", width = 15) |
| | | private Integer lowPurchaseQuantity; |
| | | |
| | | @Excel(name = "最低采购单位", width = 15) |
| | | private String lowUnitName; |
| | | |
| | | @Excel(name = "商品规格", width = 20) |
| | | private String goodsSpec; |
| | | |
| | | @Excel(name = "预警库存", width = 15) |
| | | private Integer warningInventory; |
| | | |
| | | @Excel(name = "包装单位", width = 15) |
| | | private String packingUnitName; |
| | | |
| | | @Excel(name = "商品售价", width = 15) |
| | | private BigDecimal salesAmount; |
| | | |
| | | @Excel(name = "使用说明", width = 35) |
| | | private String instructionsUse; |
| | | |
| | | @Excel(name = "副作用", width = 35) |
| | | private String sideEffect; |
| | | |
| | | @Excel(name = "封面图", width = 40) |
| | | private String cover; |
| | | |
| | | @Excel(name = "详情图", width = 40) |
| | | private String detailPictures; |
| | | |
| | | @Excel(name = "拼音简码", width = 15) |
| | | private String simplifiedCode; |
| | | |
| | | @Excel(name = "剂型", width = 15) |
| | | private String dosageForm; |
| | | |
| | | @Excel(name = "用途分类", width = 20) |
| | | private String usageClassification; |
| | | |
| | | @Excel(name = "商品名(品牌)", width = 20) |
| | | private String productBrand; |
| | | |
| | | @Excel(name = "产地", width = 20) |
| | | private String placeOfOrigin; |
| | | |
| | | @Excel(name = "主要成分", width = 35) |
| | | private String ingredient; |
| | | |
| | | @Excel(name = "性状", width = 35) |
| | | private String trait; |
| | | |
| | | @Excel(name = "适应症", width = 35) |
| | | private String indication; |
| | | |
| | | @Excel(name = "禁忌", width = 35) |
| | | private String taboo; |
| | | |
| | | @Excel(name = "注意事项", width = 35) |
| | | private String precautions; |
| | | |
| | | @Excel(name = "相互作用", width = 35) |
| | | private String interaction; |
| | | |
| | | @Excel(name = "贮藏", width = 25) |
| | | private String storage; |
| | | |
| | | @Excel(name = "有效时间", width = 20) |
| | | private String effectivePeriod; |
| | | |
| | | @Excel(name = "其他说明", width = 35) |
| | | private String otherDescription; |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.importExcel; |
| | | |
| | | import cn.afterturn.easypoi.excel.annotation.Excel; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 供应商固定商品类型导入模板。 |
| | | * 三个工作表共用一套字段模型,按所选商品类型读取对应工作表。 |
| | | */ |
| | | @Data |
| | | public class TErpFixedGoodsExcel { |
| | | |
| | | @Excel(name = "商品名称") |
| | | private String goodsName; |
| | | |
| | | @Excel(name = "通用名") |
| | | private String commonName; |
| | | |
| | | @Excel(name = "商品条形码") |
| | | private String goodsIdCode; |
| | | |
| | | @Excel(name = "批准文号或国药准字") |
| | | private String quasiNumber; |
| | | |
| | | @Excel(name = "商品类型") |
| | | private String productType; |
| | | |
| | | @Excel(name = "生产厂家") |
| | | private String manufacturer; |
| | | |
| | | @Excel(name = "剂型规格") |
| | | private String formulationSpec; |
| | | |
| | | @Excel(name = "包装规格") |
| | | private String packingSpec; |
| | | |
| | | @Excel(name = "器械类别") |
| | | private String deviceCategory; |
| | | |
| | | @Excel(name = "产品技术要求编号") |
| | | private String productTechnicalRequirementNumber; |
| | | |
| | | @Excel(name = "生产许可证或备案凭证编号") |
| | | private String productionLicenseOrFilingNumber; |
| | | |
| | | @Excel(name = "注册人或备案人") |
| | | private String registrantOrFiler; |
| | | |
| | | @Excel(name = "结构及组成") |
| | | private String structureComposition; |
| | | |
| | | @Excel(name = "适用范围") |
| | | private String applicableScope; |
| | | |
| | | @Excel(name = "国产/进口") |
| | | private String originType; |
| | | |
| | | @Excel(name = "生产许可证号") |
| | | private String productionLicenseNumber; |
| | | |
| | | @Excel(name = "产品类别") |
| | | private String productCategory; |
| | | |
| | | @Excel(name = "商品执行标准") |
| | | private String productExecutionStandard; |
| | | |
| | | @Excel(name = "是否特殊食品") |
| | | private String specialFoodFlag; |
| | | |
| | | @Excel(name = "特殊食品") |
| | | private String specialFood; |
| | | |
| | | @Excel(name = "商品标识码") |
| | | private String goodsYards; |
| | | |
| | | @Excel(name = "保养周期") |
| | | private String maintenanceInterval; |
| | | |
| | | @Excel(name = "最低采购数量") |
| | | private String lowPurchaseQuantity; |
| | | |
| | | @Excel(name = "最低采购单位") |
| | | private String lowUnitName; |
| | | |
| | | @Excel(name = "是否为处方药") |
| | | private String isPrescriptionDrug; |
| | | |
| | | @Excel(name = "商品规格") |
| | | private String goodsSpec; |
| | | |
| | | @Excel(name = "预警库存") |
| | | private String warningInventory; |
| | | |
| | | @Excel(name = "包装单位") |
| | | private String packingUnitName; |
| | | |
| | | @Excel(name = "使用说明") |
| | | private String instructionsUse; |
| | | |
| | | @Excel(name = "副作用") |
| | | private String sideEffect; |
| | | |
| | | @Excel(name = "拼音简码") |
| | | private String simplifiedCode; |
| | | |
| | | @Excel(name = "剂型") |
| | | private String dosageForm; |
| | | |
| | | @Excel(name = "用途分类") |
| | | private String usageClassification; |
| | | |
| | | @Excel(name = "商品名(品牌)") |
| | | private String productBrand; |
| | | |
| | | @Excel(name = "商品名(品牌),注意事项") |
| | | private String productBrandAndPrecautions; |
| | | |
| | | @Excel(name = "产地") |
| | | private String placeOfOrigin; |
| | | |
| | | @Excel(name = "主要成份") |
| | | private String ingredient; |
| | | |
| | | @Excel(name = "性状") |
| | | private String trait; |
| | | |
| | | @Excel(name = "适应症") |
| | | private String indication; |
| | | |
| | | @Excel(name = "禁忌") |
| | | private String taboo; |
| | | |
| | | @Excel(name = "注意事项") |
| | | private String precautions; |
| | | |
| | | @Excel(name = "相互作用") |
| | | private String interaction; |
| | | |
| | | @Excel(name = "不良反应") |
| | | private String adverseReaction; |
| | | |
| | | @Excel(name = "贮藏") |
| | | private String storage; |
| | | |
| | | @Excel(name = "有效时间") |
| | | private String effectivePeriod; |
| | | |
| | | @Excel(name = "其他说明") |
| | | private String otherDescription; |
| | | } |
| | |
| | | @Excel(name = "*包装规格") |
| | | private String packingSpec; |
| | | |
| | | @ApiModelProperty(value = "通用名") |
| | | @TableField("common_name") |
| | | private String commonName; |
| | | |
| | | @ApiModelProperty(value = "商品细分类型") |
| | | @TableField("product_type") |
| | | private String productType; |
| | | |
| | | @ApiModelProperty(value = "器械类别") |
| | | @TableField("device_category") |
| | | private String deviceCategory; |
| | | |
| | | @ApiModelProperty(value = "产品技术要求编号") |
| | | @TableField("product_technical_requirement_number") |
| | | private String productTechnicalRequirementNumber; |
| | | |
| | | @ApiModelProperty(value = "生产许可证或备案凭证编号") |
| | | @TableField("production_license_or_filing_number") |
| | | private String productionLicenseOrFilingNumber; |
| | | |
| | | @ApiModelProperty(value = "注册人或备案人") |
| | | @TableField("registrant_or_filer") |
| | | private String registrantOrFiler; |
| | | |
| | | @ApiModelProperty(value = "结构及组成") |
| | | @TableField("structure_composition") |
| | | private String structureComposition; |
| | | |
| | | @ApiModelProperty(value = "适用范围") |
| | | @TableField("applicable_scope") |
| | | private String applicableScope; |
| | | |
| | | @ApiModelProperty(value = "国产/进口") |
| | | @TableField("origin_type") |
| | | private String originType; |
| | | |
| | | @ApiModelProperty(value = "生产许可证号") |
| | | @TableField("production_license_number") |
| | | private String productionLicenseNumber; |
| | | |
| | | @ApiModelProperty(value = "产品类别") |
| | | @TableField("product_category") |
| | | private String productCategory; |
| | | |
| | | @ApiModelProperty(value = "商品执行标准") |
| | | @TableField("product_execution_standard") |
| | | private String productExecutionStandard; |
| | | |
| | | @ApiModelProperty(value = "是否特殊食品") |
| | | @TableField("special_food_flag") |
| | | private Integer specialFoodFlag; |
| | | |
| | | @ApiModelProperty(value = "特殊食品") |
| | | @TableField("special_food") |
| | | private String specialFood; |
| | | |
| | | @ApiModelProperty(value = "有效时间") |
| | | @TableField("effective_period") |
| | | private String effectivePeriod; |
| | | |
| | | @ApiModelProperty(value = "其他说明") |
| | | @TableField("other_description") |
| | | private String otherDescription; |
| | | |
| | | @ApiModelProperty(value = "商品类型名称") |
| | | @TableField(exist = false) |
| | | @Excel(name = "*商品类型") |
| | |
| | | @ApiModelProperty(value = "记录创建人id,前端忽略") |
| | | @TableField(value = "create_id", fill = FieldFill.INSERT) |
| | | private String createId; |
| | | |
| | | /** |
| | | * 排序 |
| | | */ |
| | | private Integer sort; |
| | | } |
| | |
| | | @ApiModelProperty(value = "国药准字号") |
| | | private String quasiNumber; |
| | | |
| | | @ApiModelProperty(value = "关键字号") |
| | | private String keyword; |
| | | |
| | | @ApiModelProperty(value = "是否处方药 1是 2否") |
| | | private Integer isPrescriptionDrug; |
| | | |
| | | |
| | | @ApiModelProperty(value = "1启用 2禁用") |
| | | private Integer state; |
| | |
| | | @ApiModelProperty(value = "国药准字号") |
| | | private String quasiNumber; |
| | | |
| | | @ApiModelProperty(value = "关键字号") |
| | | private String keyword; |
| | | |
| | | @ApiModelProperty(value = "是否处方药 1是 2否") |
| | | private Integer isPrescriptionDrug; |
| | | |
| | | |
| | | @ApiModelProperty(value = "1启用 2禁用") |
| | | private Integer state; |
| | |
| | | * @return |
| | | */ |
| | | PageInfo<TErpGoodsType> pageList(TErpGoodsTypeQuery query); |
| | | |
| | | /** |
| | | * 幂等补齐系统内置商品类型。 |
| | | */ |
| | | void ensureBuiltinTypes(); |
| | | } |
| | |
| | | crmSupplierCloudShopService.remove(Wrappers.lambdaQuery(TCrmSupplierCloudShop.class).eq(TCrmSupplierCloudShop::getSupplierId, dto.getId())); |
| | | // 添加供应商云仓店铺信息 |
| | | List<TCrmSupplierCloudShop> crmSupplierCloudShops = dto.getCrmSupplierCloudShops(); |
| | | for (TCrmSupplierCloudShop crmSupplierCloudShop : crmSupplierCloudShops) { |
| | | crmSupplierCloudShop.setSupplierId(dto.getId()); |
| | | if(crmSupplierCloudShops!=null){ |
| | | for (TCrmSupplierCloudShop crmSupplierCloudShop : crmSupplierCloudShops) { |
| | | crmSupplierCloudShop.setSupplierId(dto.getId()); |
| | | } |
| | | crmSupplierCloudShopService.saveBatch(crmSupplierCloudShops); |
| | | } |
| | | crmSupplierCloudShopService.saveBatch(crmSupplierCloudShops); |
| | | |
| | | if(Objects.nonNull(user)){ |
| | | // 修改账号 |
| | | user.setPhonenumber(dto.getPhone()); |
| | |
| | | List<TErpGoodsType> typeList = erpGoodsTypeMapper.selectBatchIds(typeIds); |
| | | for (TErpGoodsVO tErpGoodsVO : list) { |
| | | typeList.stream().filter(t -> t.getId().equals(tErpGoodsVO.getTypeId())).findFirst().ifPresent(t -> tErpGoodsVO.setTypeName(t.getTypeName())); |
| | | tErpGoodsVO.setTypeName(tErpGoodsVO.getTypeName()); |
| | | } |
| | | } |
| | | List<String> supplierIds = list.stream() |
| | | .filter(goods -> Objects.equals(goods.getGoodsSource(), 1)) |
| | | .map(TErpGoods::getSupplierClinicId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | if (!supplierIds.isEmpty()) { |
| | | Map<String, String> supplierNames = crmSupplierMapper.selectBatchIds(supplierIds).stream() |
| | | .collect(Collectors.toMap(TCrmSupplier::getId, TCrmSupplier::getSupplierName)); |
| | | list.forEach(goods -> { |
| | | if (Objects.equals(goods.getGoodsSource(), 1)) { |
| | | goods.setSupplierName(supplierNames.get(goods.getSupplierClinicId())); |
| | | } |
| | | }); |
| | | } |
| | | List<String> lowUnitIds = list.stream().map(TErpGoods::getLowUnitId).collect(Collectors.toList()); |
| | | if (!lowUnitIds.isEmpty()) { |
| | | List<TErpGoodsUnit> tErpGoodsUnits = erpGoodsUnitMapper.selectBatchIds(lowUnitIds); |
| | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.ruoyi.common.basic.PageInfo; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.system.constant.GoodsTypeConstants; |
| | | import com.ruoyi.system.model.TErpGoodsType; |
| | | import com.ruoyi.system.mapper.TErpGoodsTypeMapper; |
| | | import com.ruoyi.system.query.TErpGoodsTypeQuery; |
| | |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | |
| | | @Override |
| | | public PageInfo<TErpGoodsType> pageList(TErpGoodsTypeQuery query) { |
| | | ensureBuiltinTypes(); |
| | | PageInfo<TErpGoodsType> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); |
| | | List<TErpGoodsType> list = this.baseMapper.pageList(query,pageInfo); |
| | | pageInfo.setRecords(list); |
| | | return pageInfo; |
| | | } |
| | | |
| | | @Override |
| | | public void ensureBuiltinTypes() { |
| | | for (Map.Entry<String, String> entry : GoodsTypeConstants.BUILTIN_TYPES.entrySet()) { |
| | | if (this.getById(entry.getKey()) == null) { |
| | | TErpGoodsType type = new TErpGoodsType(); |
| | | type.setId(entry.getKey()); |
| | | type.setTypeName(entry.getValue()); |
| | | type.setDisabled(false); |
| | | this.save(type); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | tErpProcurement.setMoney(add.subtract(bigDecimal)); |
| | | if (tErpProcurement.getMoney().doubleValue() < 0) { |
| | | throw new RuntimeException("金额设置错误,请联系平台管理员"); |
| | | throw new RuntimeException("最低平台分账不低于"+bigDecimal+"元!"); |
| | | } |
| | | tErpProcurement.setSupplierMoney(supplierCommissionPrice); |
| | | |
| | |
| | | obsClient.putObject(bucketName,"file/"+fileName,content,meta);// 上传Object. |
| | | if(fileName != null && !"".equals(fileName)){ |
| | | System.out.println(fileName); |
| | | fileName = oss_domain_cdn+"file/"+fileName; |
| | | fileName = oss_domain+"file/"+fileName; |
| | | } |
| | | } |
| | | return fileName; |
| | |
| | | obsClient.putObject(bucketName,"file/"+fileName,content,meta);// 上传Object. |
| | | if(fileName != null && !"".equals(fileName)){ |
| | | System.out.println(fileName); |
| | | fileName = oss_domain_cdn+"file/"+fileName; |
| | | fileName = oss_domain+"file/"+fileName; |
| | | } |
| | | } |
| | | return fileName; |
| | |
| | | * |
| | | * @author lihen |
| | | */ |
| | | @Configuration |
| | | //@Configuration |
| | | public class WxConfig { |
| | | |
| | | private final WeixinPayProperties weixinPayProperties; |
| | |
| | | <result column="manufacturer" property="manufacturer"/> |
| | | <result column="formulation_spec" property="formulationSpec"/> |
| | | <result column="packing_spec" property="packingSpec"/> |
| | | <result column="common_name" property="commonName"/> |
| | | <result column="product_type" property="productType"/> |
| | | <result column="device_category" property="deviceCategory"/> |
| | | <result column="product_technical_requirement_number" property="productTechnicalRequirementNumber"/> |
| | | <result column="production_license_or_filing_number" property="productionLicenseOrFilingNumber"/> |
| | | <result column="registrant_or_filer" property="registrantOrFiler"/> |
| | | <result column="structure_composition" property="structureComposition"/> |
| | | <result column="applicable_scope" property="applicableScope"/> |
| | | <result column="origin_type" property="originType"/> |
| | | <result column="production_license_number" property="productionLicenseNumber"/> |
| | | <result column="product_category" property="productCategory"/> |
| | | <result column="product_execution_standard" property="productExecutionStandard"/> |
| | | <result column="special_food_flag" property="specialFoodFlag"/> |
| | | <result column="special_food" property="specialFood"/> |
| | | <result column="effective_period" property="effectivePeriod"/> |
| | | <result column="other_description" property="otherDescription"/> |
| | | <result column="type_id" property="typeId"/> |
| | | <result column="goods_yards" property="goodsYards"/> |
| | | <result column="maintenance_interval" property="maintenanceInterval"/> |
| | |
| | | <!-- 通用查询结果列 --> |
| | | <sql id="Base_Column_List"> |
| | | id, supplier_clinic_id, goods_source, goods_name, goods_id_code, quasi_number, manufacturer, formulation_spec, |
| | | packing_spec, type_id, goods_yards, maintenance_interval,maintenance_interval_unit, low_purchase_quantity, |
| | | packing_spec, common_name, product_type, device_category, product_technical_requirement_number, |
| | | production_license_or_filing_number, registrant_or_filer, structure_composition, applicable_scope, |
| | | origin_type, production_license_number, product_category, product_execution_standard, special_food_flag, |
| | | special_food, effective_period, other_description, type_id, goods_yards, maintenance_interval,maintenance_interval_unit, low_purchase_quantity, |
| | | low_unit_id, is_prescription_drug, goods_spec, warning_inventory, sales_amount, packing_unit_id, instructions_use, |
| | | side_effect, clinic_purchase_price, platform_commission_price, create_time, update_time, create_by,`state`, |
| | | update_by, disabled,simplified_code,dosage_form,usage_classification,product_brand,place_of_origin,ingredient, |
| | |
| | | |
| | | <select id="pageList" resultType="com.ruoyi.system.vo.TErpGoodsVO"> |
| | | select |
| | | t1.id |
| | | , t1.supplier_clinic_id, t1.goods_source, t1.goods_name, t1.goods_id_code, t1.quasi_number, t1.manufacturer, t1.formulation_spec, t1.packing_spec, t1.type_id, t1.goods_yards, t1.maintenance_interval, t1.maintenance_interval_unit, t1.low_purchase_quantity, t1.low_unit_id, t1.is_prescription_drug, t1.goods_spec, t1.warning_inventory, t1.sales_amount, t1.packing_unit_id, t1.instructions_use, t1.side_effect, t1.clinic_purchase_price, t1.platform_commission_price, t1.create_time, t1.update_time, t1.create_by, t1.`state`, t1.update_by, t1.disabled, |
| | | t1.simplified_code, |
| | | t1.dosage_form, |
| | | t1.usage_classification, |
| | | t1.product_brand, |
| | | t1.place_of_origin, |
| | | t1.ingredient, |
| | | t1.trait, |
| | | t1.indication, |
| | | t1.taboo, |
| | | t1.precautions, |
| | | t1.interaction, |
| | | t1.storage, |
| | | t1.content, |
| | | t1.content_state, |
| | | t1.content_remark, |
| | | t1.state_remark, |
| | | t1.cover,t1.detail_pictures,t1.retail_price,t1.cloud_warehouse,t1.cloud_goods_code,t1.cloud_spec_code,t1.cloud_goods_id,t1.cloud_spec_id, |
| | | t1.id, |
| | | t1.supplier_clinic_id, t1.goods_source, t1.goods_name, t1.goods_id_code, t1.quasi_number, |
| | | t1.manufacturer, t1.formulation_spec, t1.packing_spec, |
| | | t1.common_name, t1.product_type, t1.device_category, t1.product_technical_requirement_number, |
| | | t1.production_license_or_filing_number, t1.registrant_or_filer, t1.structure_composition, |
| | | t1.applicable_scope, t1.origin_type, t1.production_license_number, t1.product_category, |
| | | t1.product_execution_standard, t1.special_food_flag, t1.special_food, t1.effective_period, |
| | | t1.other_description, |
| | | t1.type_id, t1.goods_yards, t1.maintenance_interval, t1.maintenance_interval_unit, |
| | | t1.low_purchase_quantity, t1.low_unit_id, t1.is_prescription_drug, t1.goods_spec, |
| | | t1.warning_inventory, t1.sales_amount, t1.packing_unit_id, t1.instructions_use, t1.side_effect, |
| | | t1.clinic_purchase_price, t1.platform_commission_price, t1.create_time, t1.update_time, |
| | | t1.create_by, t1.`state`, t1.update_by, t1.disabled, t1.simplified_code, t1.dosage_form, |
| | | t1.usage_classification, t1.product_brand, t1.place_of_origin, t1.ingredient, t1.trait, |
| | | t1.indication, t1.taboo, t1.precautions, t1.interaction, t1.storage, t1.content, |
| | | t1.content_state, t1.content_remark, t1.state_remark, t1.cover, t1.detail_pictures, |
| | | t1.retail_price, t1.cloud_warehouse, t1.cloud_goods_code, t1.cloud_spec_code, |
| | | t1.cloud_goods_id, t1.cloud_spec_id, |
| | | case t1.goods_source |
| | | when 1 then t2.supplier_name |
| | | when 2 then t1.supplier_name |
| | |
| | | </if> |
| | | <if test="query.quasiNumber != null and query.quasiNumber != ''"> |
| | | and t1.quasi_number like concat('%',#{query.quasiNumber},'%') |
| | | </if> |
| | | <if test="query.keyword != null and query.keyword != ''"> |
| | | and ( |
| | | t1.quasi_number like concat('%',#{query.keyword},'%') |
| | | or t1.product_technical_requirement_number like concat('%',#{query.keyword},'%') |
| | | or t1.production_license_or_filing_number like concat('%',#{query.keyword},'%') |
| | | or t1.production_license_number like concat('%',#{query.keyword},'%') |
| | | ) |
| | | </if> |
| | | <if test="query.isPrescriptionDrug != null"> |
| | | and t1.is_prescription_drug = #{query.isPrescriptionDrug} |
| | | </if> |
| | | <if test="query.state != null "> |
| | | and t1.state = #{query.state} |
| | |
| | | </if> |
| | | <if test="query.quasiNumber != null and query.quasiNumber != ''"> |
| | | and quasi_number like concat('%',#{query.quasiNumber},'%') |
| | | </if> |
| | | <if test="query.keyword != null and query.keyword != ''"> |
| | | and ( |
| | | quasi_number like concat('%',#{query.keyword},'%') |
| | | or product_technical_requirement_number like concat('%',#{query.keyword},'%') |
| | | or production_license_or_filing_number like concat('%',#{query.keyword},'%') |
| | | or production_license_number like concat('%',#{query.keyword},'%') |
| | | ) |
| | | </if> |
| | | <if test="query.isPrescriptionDrug != null"> |
| | | and is_prescription_drug = #{query.isPrescriptionDrug} |
| | | </if> |
| | | <if test="query.supplierName != null and query.supplierName != ''"> |
| | | and ( |
| | | (goods_source = 1 and exists ( |
| | | select 1 from t_crm_supplier supplier |
| | | where supplier.id = t_erp_goods.supplier_clinic_id |
| | | and supplier.supplier_name like concat('%',#{query.supplierName},'%') |
| | | )) |
| | | or (goods_source = 2 and supplier_name like concat('%',#{query.supplierName},'%')) |
| | | ) |
| | | </if> |
| | | <if test="query.state != null "> |
| | | and state = #{query.state} |
| | |
| | | |
| | | <select id="pageAdminList" resultType="com.ruoyi.system.vo.TErpGoodsVO"> |
| | | select |
| | | t1.id |
| | | , t1.supplier_clinic_id, t1.goods_source, t1.goods_name, t1.goods_id_code, t1.quasi_number,t1.manufacturer, t1.formulation_spec, t1.packing_spec, t1.type_id, t1.goods_yards, |
| | | t1.maintenance_interval,t1.maintenance_interval_unit, t1.low_purchase_quantity, t1.low_unit_id, t1.is_prescription_drug, t1.goods_spec, t1.warning_inventory, |
| | | t1.sales_amount, t1.packing_unit_id, t1.instructions_use, t1.side_effect, t1.clinic_purchase_price, t1.platform_commission_price, t1.create_time, t1.update_time, t1.create_by, |
| | | t1.`state`, t1.update_by, t1.disabled, |
| | | t1.simplified_code, |
| | | t1.dosage_form, |
| | | t1.usage_classification, |
| | | t1.product_brand, |
| | | t1.place_of_origin, |
| | | t1.ingredient, |
| | | t1.trait, |
| | | t1.indication, |
| | | t1.taboo, |
| | | t1.precautions, |
| | | t1.interaction, |
| | | t1.content,t1.content_state, |
| | | t1.storage,t1.state_remark, |
| | | t1.cover,t1.detail_pictures,t1.retail_price,t1.cloud_warehouse,t1.cloud_goods_code,t1.cloud_spec_code,t1.cloud_goods_id,t1.cloud_spec_id, |
| | | t1.id, |
| | | t1.supplier_clinic_id, t1.goods_source, t1.goods_name, t1.goods_id_code, t1.quasi_number, |
| | | t1.manufacturer, t1.formulation_spec, t1.packing_spec, |
| | | t1.common_name, t1.product_type, t1.device_category, t1.product_technical_requirement_number, |
| | | t1.production_license_or_filing_number, t1.registrant_or_filer, t1.structure_composition, |
| | | t1.applicable_scope, t1.origin_type, t1.production_license_number, t1.product_category, |
| | | t1.product_execution_standard, t1.special_food_flag, t1.special_food, t1.effective_period, |
| | | t1.other_description, |
| | | t1.type_id, t1.goods_yards, t1.maintenance_interval, t1.maintenance_interval_unit, |
| | | t1.low_purchase_quantity, t1.low_unit_id, t1.is_prescription_drug, t1.goods_spec, |
| | | t1.warning_inventory, t1.sales_amount, t1.packing_unit_id, t1.instructions_use, t1.side_effect, |
| | | t1.clinic_purchase_price, t1.platform_commission_price, t1.create_time, t1.update_time, |
| | | t1.create_by, t1.`state`, t1.update_by, t1.disabled, t1.simplified_code, t1.dosage_form, |
| | | t1.usage_classification, t1.product_brand, t1.place_of_origin, t1.ingredient, t1.trait, |
| | | t1.indication, t1.taboo, t1.precautions, t1.interaction, t1.content, t1.content_state, |
| | | t1.storage, t1.state_remark, t1.cover, t1.detail_pictures, t1.retail_price, |
| | | t1.cloud_warehouse, t1.cloud_goods_code, t1.cloud_spec_code, t1.cloud_goods_id, |
| | | t1.cloud_spec_id, |
| | | t2.supplier_name |
| | | from t_erp_goods t1 left join t_crm_supplier t2 on t1.supplier_clinic_id = t2.id and t1.goods_source =1 |
| | | where t1.disabled = 0 and t1.goods_source =1 |
| | |
| | | <if test="query.quasiNumber != null and query.quasiNumber != ''"> |
| | | and t1.quasi_number like concat('%',#{query.quasiNumber},'%') |
| | | </if> |
| | | <if test="query.keyword != null and query.keyword != ''"> |
| | | and ( |
| | | t1.quasi_number like concat('%',#{query.keyword},'%') |
| | | or t1.product_technical_requirement_number like concat('%',#{query.keyword},'%') |
| | | or t1.production_license_or_filing_number like concat('%',#{query.keyword},'%') |
| | | or t1.production_license_number like concat('%',#{query.keyword},'%') |
| | | ) |
| | | </if> |
| | | <if test="query.isPrescriptionDrug != null"> |
| | | and t1.is_prescription_drug = #{query.isPrescriptionDrug} |
| | | </if> |
| | | <if test="query.state != null "> |
| | | and t1.state = #{query.state} |
| | | </if> |
| | |
| | | <if test="query.category != null "> |
| | | and t1.content is not null |
| | | </if> |
| | | order by t1.create_time desc |
| | | order by t1.sort desc,t1.create_time desc |
| | | </select> |
| | | <select id="pageAddList" resultType="com.ruoyi.system.model.TErpGoods"> |
| | | SELECT t1.id, |
| New file |
| | |
| | | -- 商品内置分类表单扩展字段。部署后端代码前执行一次。 |
| | | ALTER TABLE t_erp_goods |
| | | ADD COLUMN common_name VARCHAR(100) NULL COMMENT '通用名', |
| | | ADD COLUMN product_type VARCHAR(100) NULL COMMENT '商品细分类型', |
| | | ADD COLUMN device_category VARCHAR(100) NULL COMMENT '器械类别', |
| | | ADD COLUMN product_technical_requirement_number VARCHAR(100) NULL COMMENT '产品技术要求编号', |
| | | ADD COLUMN production_license_or_filing_number VARCHAR(100) NULL COMMENT '生产许可证或备案凭证编号', |
| | | ADD COLUMN registrant_or_filer VARCHAR(100) NULL COMMENT '注册人或备案人', |
| | | ADD COLUMN structure_composition TEXT NULL COMMENT '结构及组成', |
| | | ADD COLUMN applicable_scope TEXT NULL COMMENT '适用范围', |
| | | ADD COLUMN origin_type VARCHAR(20) NULL COMMENT '国产/进口', |
| | | ADD COLUMN production_license_number VARCHAR(100) NULL COMMENT '生产许可证号', |
| | | ADD COLUMN product_category VARCHAR(100) NULL COMMENT '产品类别', |
| | | ADD COLUMN product_execution_standard VARCHAR(100) NULL COMMENT '商品执行标准', |
| | | ADD COLUMN special_food_flag TINYINT NULL COMMENT '是否特殊食品 1是 2否', |
| | | ADD COLUMN special_food VARCHAR(100) NULL COMMENT '特殊食品', |
| | | ADD COLUMN effective_period VARCHAR(100) NULL COMMENT '有效时间', |
| | | ADD COLUMN other_description TEXT NULL COMMENT '其他说明'; |