diff --git a/.github/workflows/nuget_pre.yml b/.github/workflows/nuget_pre.yml
index 96303e6..b0b93fc 100644
--- a/.github/workflows/nuget_pre.yml
+++ b/.github/workflows/nuget_pre.yml
@@ -13,11 +13,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Setup .NET
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: 6.0.x
+ dotnet-version: |
+ 8.0.x
+ 10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
diff --git a/.github/workflows/nuget_prod.yml b/.github/workflows/nuget_prod.yml
index 999568a..d158e4a 100644
--- a/.github/workflows/nuget_prod.yml
+++ b/.github/workflows/nuget_prod.yml
@@ -13,11 +13,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Setup .NET
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: 6.0.x
+ dotnet-version: |
+ 8.0.x
+ 10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index 0802805..9a45b93 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -13,11 +13,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Setup .NET
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: 6.0.x
+ dotnet-version: |
+ 8.0.x
+ 10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
diff --git a/.idea/.idea.EasyTool/.idea/.gitignore b/.idea/.idea.EasyTool/.idea/.gitignore
new file mode 100644
index 0000000..2e97252
--- /dev/null
+++ b/.idea/.idea.EasyTool/.idea/.gitignore
@@ -0,0 +1,15 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# Rider 忽略的文件
+/projectSettingsUpdater.xml
+/.idea.EasyTool.iml
+/contentModel.xml
+/modules.xml
+# 已忽略包含查询文件的默认文件夹
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
diff --git a/.idea/.idea.EasyTool/.idea/encodings.xml b/.idea/.idea.EasyTool/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.EasyTool/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.EasyTool/.idea/indexLayout.xml b/.idea/.idea.EasyTool/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.EasyTool/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.EasyTool/.idea/vcs.xml b/.idea/.idea.EasyTool/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/.idea.EasyTool/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.spec-workflow/templates/design-template.md b/.spec-workflow/templates/design-template.md
new file mode 100644
index 0000000..1295d7b
--- /dev/null
+++ b/.spec-workflow/templates/design-template.md
@@ -0,0 +1,96 @@
+# 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]
diff --git a/.spec-workflow/templates/product-template.md b/.spec-workflow/templates/product-template.md
new file mode 100644
index 0000000..82e60de
--- /dev/null
+++ b/.spec-workflow/templates/product-template.md
@@ -0,0 +1,51 @@
+# 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]
diff --git a/.spec-workflow/templates/requirements-template.md b/.spec-workflow/templates/requirements-template.md
new file mode 100644
index 0000000..1c80ca0
--- /dev/null
+++ b/.spec-workflow/templates/requirements-template.md
@@ -0,0 +1,50 @@
+# 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]
diff --git a/.spec-workflow/templates/structure-template.md b/.spec-workflow/templates/structure-template.md
new file mode 100644
index 0000000..1ab1fbc
--- /dev/null
+++ b/.spec-workflow/templates/structure-template.md
@@ -0,0 +1,145 @@
+# 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
diff --git a/.spec-workflow/templates/tasks-template.md b/.spec-workflow/templates/tasks-template.md
new file mode 100644
index 0000000..be461de
--- /dev/null
+++ b/.spec-workflow/templates/tasks-template.md
@@ -0,0 +1,139 @@
+# 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_
diff --git a/.spec-workflow/templates/tech-template.md b/.spec-workflow/templates/tech-template.md
new file mode 100644
index 0000000..57cd538
--- /dev/null
+++ b/.spec-workflow/templates/tech-template.md
@@ -0,0 +1,99 @@
+# 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]
diff --git a/.spec-workflow/user-templates/README.md b/.spec-workflow/user-templates/README.md
new file mode 100644
index 0000000..ad36a48
--- /dev/null
+++ b/.spec-workflow/user-templates/README.md
@@ -0,0 +1,64 @@
+# 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
diff --git a/EasyTool.Core/ToolCategory/CreditCodeUtil.cs b/EasyTool.Core/BusinessCategory/CreditCodeUtil.cs
similarity index 88%
rename from EasyTool.Core/ToolCategory/CreditCodeUtil.cs
rename to EasyTool.Core/BusinessCategory/CreditCodeUtil.cs
index fc4704a..c96ed02 100644
--- a/EasyTool.Core/ToolCategory/CreditCodeUtil.cs
+++ b/EasyTool.Core/BusinessCategory/CreditCodeUtil.cs
@@ -1,13 +1,13 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Text;
-namespace EasyTool
+namespace EasyTool.BusinessCategory
{
///
/// 社会信用代码工具
///
- public class CreditCodeUtil
+ public static class CreditCodeUtil
{
private const string BaseCode = "0123456789ABCDEFGHJKLMNPQRTUWXY"; // 社会信用代码中的基础字符集
private const int Modulo = 31; // 校验码计算中的模数
@@ -68,7 +68,7 @@ public static string GenerateRandomCreditCode()
{
string orgCode = "911101"; // 默认的组织机构代码
string entType = "00"; // 默认的企业类型
- string regNum = RandomUtil.RandomNumberString(10); // 生成随机的注册号
+ string regNum = EasyTool.MathCategory.RandomUtil.RandomNumberString(10); // 生成随机的注册号
string code = orgCode + entType + regNum;
// 计算出校验码并添加到社会信用代码中
@@ -89,20 +89,21 @@ public static string GenerateRandomCreditCode()
///
/// 社会信用代码
/// 组织机构代码
- public static string GetOrgCodeFromCreditCode(string creditCode)
+ public static string? GetOrgCodeFromCreditCode(string? creditCode)
{
if (string.IsNullOrWhiteSpace(creditCode) || creditCode.Length != 18)
{
return null;
}
- return creditCode.Substring(0, 9);
- }
+ return creditCode.Substring(0, 9);
+ }
+
///
/// 从社会信用代码中提取企业类型
///
/// 社会信用代码
/// 企业类型
- public static string GetEntTypeFromCreditCode(string creditCode)
+ public static string? GetEntTypeFromCreditCode(string? creditCode)
{
if (string.IsNullOrWhiteSpace(creditCode) || creditCode.Length != 18)
{
@@ -117,7 +118,7 @@ public static string GetEntTypeFromCreditCode(string creditCode)
///
/// 社会信用代码
/// 注册号
- public static string GetRegNumFromCreditCode(string creditCode)
+ public static string? GetRegNumFromCreditCode(string? creditCode)
{
if (string.IsNullOrWhiteSpace(creditCode) || creditCode.Length != 18)
{
@@ -132,7 +133,7 @@ public static string GetRegNumFromCreditCode(string creditCode)
///
/// 社会信用代码
/// 行政区划码
- public static string GetAreaCodeFromCreditCode(string creditCode)
+ public static string? GetAreaCodeFromCreditCode(string? creditCode)
{
if (string.IsNullOrWhiteSpace(creditCode) || creditCode.Length != 18)
{
@@ -147,7 +148,7 @@ public static string GetAreaCodeFromCreditCode(string creditCode)
///
/// 社会信用代码
/// 机构类型
- public static string GetOrgTypeFromCreditCode(string creditCode)
+ public static string? GetOrgTypeFromCreditCode(string? creditCode)
{
if (string.IsNullOrWhiteSpace(creditCode) || creditCode.Length != 18)
{
diff --git a/EasyTool.Core/CloneCategory/CloneExtension.cs b/EasyTool.Core/CloneCategory/CloneExtension.cs
deleted file mode 100644
index 43f892d..0000000
--- a/EasyTool.Core/CloneCategory/CloneExtension.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace EasyTool.Extension
-{
- public static class CloneExtension
- {
- //定义一个泛型方法,接受一个泛型参数 T,并返回一个 T 类型的对象
- public static T Clone(this T obj)=> CloneUtil.Clone(obj);
- }
-}
diff --git a/EasyTool.Core/CloneCategory/CloneUtil.cs b/EasyTool.Core/CloneCategory/CloneUtil.cs
deleted file mode 100644
index 646e98e..0000000
--- a/EasyTool.Core/CloneCategory/CloneUtil.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using System;
-using System.IO;
-using System.Runtime.Serialization.Formatters.Binary;
-using System.Runtime.Serialization;
-using System.Threading.Tasks;
-
-namespace EasyTool
-{
- ///
- /// 静态工具类 CloneHelper,用于深度克隆对象
- ///
- public static class CloneUtil
- {
- // 定义一个泛型方法,接受一个泛型参数 T,并返回一个 T 类型的对象
- public static T Clone(T obj)
- {
- // 检查类型是否可序列化
- if (!typeof(T).IsSerializable)
- {
- throw new ArgumentException("The type must be serializable.", nameof(obj));
- }
-
- // 如果对象为 null,则返回 null
- if (ReferenceEquals(obj, null))
- {
- return default(T);
- }
-
- // 创建一个二进制序列化器
- IFormatter formatter = new BinaryFormatter();
-
- // 创建一个内存流
- using (var stream = new MemoryStream())
- {
- // 使用二进制序列化将对象写入内存流
- formatter.Serialize(stream, obj);
-
- // 将内存流位置重置为开头
- stream.Seek(0, SeekOrigin.Begin);
-
- // 使用反序列化从内存流中读取并返回克隆的对象
- return (T)formatter.Deserialize(stream);
- }
- }
-
- ///
- /// 静态工具类 CloneHelper,用于异步深度克隆对象
- ///
- ///
- ///
- ///
- ///
- public static async Task CloneAsync(T obj)
- {
- // 检查类型是否可序列化
- if (!typeof(T).IsSerializable)
- {
- throw new ArgumentException("The type must be serializable.", nameof(obj));
- }
-
- // 如果对象为 null,则返回 null
- if (ReferenceEquals(obj, null))
- {
- return default(T);
- }
-
- // 创建一个二进制序列化器
- IFormatter formatter = new BinaryFormatter();
-
- // 创建一个内存流
- using (var stream = new MemoryStream())
- {
- // 使用二进制序列化将对象写入内存流
- formatter.Serialize(stream, obj);
-
- // 将内存流位置重置为开头
- stream.Seek(0, SeekOrigin.Begin);
-
- // 使用反序列化从内存流中读取并返回克隆的对象
- return (T)formatter.Deserialize(stream);
- }
- }
- }
-}
diff --git a/EasyTool.Core/CodeCategory/AesUtil.cs b/EasyTool.Core/CodeCategory/AesUtil.cs
index e2d2bb2..c7c8142 100644
--- a/EasyTool.Core/CodeCategory/AesUtil.cs
+++ b/EasyTool.Core/CodeCategory/AesUtil.cs
@@ -168,5 +168,130 @@ private static bool KeyIsLegalSize(string sk)
return keyLength == 16 || keyLength == 24 || keyLength == 32;
}
private static bool IvIsLegalSize(string iv) => iv.Length == 16;
+
+ ///
+ /// AES 加密(字节数组版本)
+ ///
+ /// 需要加密的数据
+ /// 加密key
+ /// 向量iv
+ /// 默认CBC
+ /// 默认PKCS7
+ /// 加密后的数据
+ ///
+ public static byte[] Encrypt(byte[] data, byte[] key, byte[]? iv = null, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
+ {
+ if (data == null || data.Length == 0)
+ return Array.Empty();
+ if (key == null || key.Length == 0)
+ throw new ArgumentException("Key cannot be null or empty", nameof(key));
+ if (!KeyIsLegalSizeBytes(key))
+ throw new ArgumentException("不合规的秘钥,请确认秘钥为16、24、32位");
+ if (iv != null && iv.Length != 16)
+ throw new ArgumentException("不合规的iv,请确认iv为16位");
+
+ using var symmetricKey = Aes.Create();
+ symmetricKey.Mode = cipher;
+ symmetricKey.Padding = padding;
+ using var encryptor = symmetricKey.CreateEncryptor(key, iv);
+ using var memoryStream = new MemoryStream();
+ using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
+
+ cryptoStream.Write(data, 0, data.Length);
+ cryptoStream.FlushFinalBlock();
+ return memoryStream.ToArray();
+ }
+
+ ///
+ /// AES 解密(字节数组版本)
+ ///
+ /// 需要解密的数据
+ /// 解密key
+ /// 向量iv
+ /// 默认CBC
+ /// 默认PKCS7
+ /// 解密后的数据
+ ///
+ public static byte[] Decrypt(byte[] data, byte[] key, byte[]? iv = null, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
+ {
+ if (data == null || data.Length == 0)
+ return Array.Empty();
+ if (key == null || key.Length == 0)
+ throw new ArgumentException("Key cannot be null or empty", nameof(key));
+ if (!KeyIsLegalSizeBytes(key))
+ throw new ArgumentException("不合规的秘钥,请确认秘钥为16、24、32位");
+ if (iv != null && iv.Length != 16)
+ throw new ArgumentException("不合规的iv,请确认iv为16位");
+
+ using var symmetricKey = Aes.Create();
+ symmetricKey.Mode = cipher;
+ symmetricKey.Padding = padding;
+ using var decryptor = symmetricKey.CreateDecryptor(key, iv);
+ using var memoryStream = new MemoryStream(data);
+ using var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
+ using var resultStream = new MemoryStream();
+ cryptoStream.CopyTo(resultStream);
+ return resultStream.ToArray();
+ }
+
+ private static bool KeyIsLegalSizeBytes(byte[] key)
+ {
+ int keyLength = key.Length;
+ return keyLength == 16 || keyLength == 24 || keyLength == 32;
+ }
+
+ ///
+ /// 创建加密流
+ ///
+ /// 输出流
+ /// 加密key
+ /// 向量iv
+ /// 默认CBC
+ /// 默认PKCS7
+ /// 加密流
+ public static CryptoStream CreateEncryptingStream(Stream outputStream, byte[] key, byte[]? iv = null, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
+ {
+ if (outputStream == null)
+ throw new ArgumentNullException(nameof(outputStream));
+ if (key == null || key.Length == 0)
+ throw new ArgumentException("Key cannot be null or empty", nameof(key));
+ if (!KeyIsLegalSizeBytes(key))
+ throw new ArgumentException("不合规的秘钥,请确认秘钥为16、24、32位");
+ if (iv != null && iv.Length != 16)
+ throw new ArgumentException("不合规的iv,请确认iv为16位");
+
+ var aes = Aes.Create();
+ aes.Mode = cipher;
+ aes.Padding = padding;
+ var encryptor = aes.CreateEncryptor(key, iv);
+ return new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write);
+ }
+
+ ///
+ /// 创建解密流
+ ///
+ /// 输入流
+ /// 解密key
+ /// 向量iv
+ /// 默认CBC
+ /// 默认PKCS7
+ /// 解密流
+ public static CryptoStream CreateDecryptingStream(Stream inputStream, byte[] key, byte[]? iv = null, CipherMode cipher = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
+ {
+ if (inputStream == null)
+ throw new ArgumentNullException(nameof(inputStream));
+ if (key == null || key.Length == 0)
+ throw new ArgumentException("Key cannot be null or empty", nameof(key));
+ if (!KeyIsLegalSizeBytes(key))
+ throw new ArgumentException("不合规的秘钥,请确认秘钥为16、24、32位");
+ if (iv != null && iv.Length != 16)
+ throw new ArgumentException("不合规的iv,请确认iv为16位");
+
+ var aes = Aes.Create();
+ aes.Mode = cipher;
+ aes.Padding = padding;
+ var decryptor = aes.CreateDecryptor(key, iv);
+ return new CryptoStream(inputStream, decryptor, CryptoStreamMode.Read);
+ }
}
}
diff --git a/EasyTool.Core/CodeCategory/Base32Util.cs b/EasyTool.Core/CodeCategory/Base32Util.cs
deleted file mode 100644
index c3c5641..0000000
--- a/EasyTool.Core/CodeCategory/Base32Util.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace EasyTool
-{
- ///
- /// Base32 编码解码工具类
- ///
- public static class Base32Util
- {
- // Base32 字符集,共 32 个字符
- private static readonly char[] BASE32_CHARS =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".ToCharArray();
-
- // Base32 填充字符
- private const char BASE32_PADDING_CHAR = '=';
-
- ///
- /// 将给定的字节数组转换为 Base32 编码字符串。
- ///
- /// 要转换的字节数组
- /// 转换后的 Base32 编码字符串
- public static string Encode(byte[] bytes)
- {
- if (bytes == null)
- {
- throw new ArgumentNullException(nameof(bytes));
- }
-
- int length = bytes.Length;
- if (length == 0)
- {
- return string.Empty;
- }
-
- char[] chars = new char[(length + 4) / 5 * 8];
- int index = 0;
- for (int i = 0; i < length; i += 5)
- {
- int val = (bytes[i] << 24) + ((i + 1 < length ? bytes[i + 1] : 0) << 16) +
- ((i + 2 < length ? bytes[i + 2] : 0) << 8) + ((i + 3 < length ? bytes[i + 3] : 0) << 0);
- chars[index++] = BASE32_CHARS[(val >> 35) & 0x1F];
- chars[index++] = BASE32_CHARS[(val >> 30) & 0x1F];
- chars[index++] = BASE32_CHARS[(val >> 25) & 0x1F];
- chars[index++] = BASE32_CHARS[(val >> 20) & 0x1F];
- chars[index++] = BASE32_CHARS[(val >> 15) & 0x1F];
- chars[index++] = BASE32_CHARS[(val >> 10) & 0x1F];
- chars[index++] = BASE32_CHARS[(val >> 5) & 0x1F];
- chars[index++] = BASE32_CHARS[val & 0x1F];
- }
-
- // 添加填充字符
- int paddingCount = length % 5;
- if (paddingCount > 0)
- {
- chars[chars.Length - 1] = BASE32_PADDING_CHAR;
- if (paddingCount == 1)
- {
- chars[chars.Length - 2] = BASE32_PADDING_CHAR;
- }
- if (paddingCount <= 2)
- {
- chars[chars.Length - 3] = BASE32_PADDING_CHAR;
- }
- if (paddingCount <= 3)
- {
- chars[chars.Length - 4] = BASE32_PADDING_CHAR;
- }
- if (paddingCount <= 4)
- {
- chars[chars.Length - 5] = BASE32_PADDING_CHAR;
- }
- }
-
- return new string(chars);
- }
-
- ///
- /// 将给定的 Base32 编码字符串转换为字节数组。
- ///
- /// 要转换的 Base32 编码字符串
- /// 转换后的字节数组
- public static byte[] Decode(string str)
- {
- if (string.IsNullOrEmpty(str))
- {
- throw new ArgumentException("String is null or empty.", nameof(str));
- }
-
- int length = str.Length;
- if (length % 8 != 0)
- {
- throw new ArgumentException("Invalid length of input string: " + length, nameof(str));
- }
-
- int paddingCount = 0;
- if (length > 0 && str[length - 1] == BASE32_PADDING_CHAR)
- {
- paddingCount++;
- }
- if (length > 1 && str[length - 2] == BASE32_PADDING_CHAR)
- {
- paddingCount++;
- }
- if (length > 3 && str[length - 3] == BASE32_PADDING_CHAR)
- {
- paddingCount++;
- }
- if (length > 4 && str[length - 4] == BASE32_PADDING_CHAR)
- {
- paddingCount++;
- }
- if (length > 6 && str[length - 6] == BASE32_PADDING_CHAR)
- {
- paddingCount++;
- }
-
- byte[] bytes = new byte[length / 8 * 5 - paddingCount];
- int index = 0;
- for (int i = 0; i < length; i += 8)
- {
- int val = (DecodeBase32Char(str[i]) << 35) +
- (DecodeBase32Char(str[i + 1]) << 30) +
- (DecodeBase32Char(str[i + 2]) << 25) +
- (DecodeBase32Char(str[i + 3]) << 20) +
- (DecodeBase32Char(str[i + 4]) << 15) +
- (DecodeBase32Char(str[i + 5]) << 10) +
- (DecodeBase32Char(str[i + 6]) << 5) +
- DecodeBase32Char(str[i + 7]);
- bytes[index++] = (byte)(val >> 24);
- if (index < bytes.Length)
- {
- bytes[index++] = (byte)(val >> 16);
- }
- if (index < bytes.Length)
- {
- bytes[index++] = (byte)(val >> 8);
- }
- if (index < bytes.Length)
- {
- bytes[index++] = (byte)val;
- }
- }
-
- return bytes;
- }
-
- // 解码 Base32 字符
- private static int DecodeBase32Char(char c)
- {
- if (c >= 'A' && c <= 'Z')
- {
- return c - 'A';
- }
- if (c >= '2' && c <= '7')
- {
- return c - '2' + 26;
- }
- throw new ArgumentException("Invalid character in input string: " + c, nameof(c));
- }
- }
-}
diff --git a/EasyTool.Core/CodeCategory/Base62Util.cs b/EasyTool.Core/CodeCategory/Base62Util.cs
deleted file mode 100644
index cac4b89..0000000
--- a/EasyTool.Core/CodeCategory/Base62Util.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace EasyTool
-{
- ///
- /// Base62 编码解码工具类
- ///
- public static class Base62Util
- {
- // Base62 字符集,共 62 个字符
- private static readonly char[] BASE62_CHARS =
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray();
-
- ///
- /// 将给定的整数转换为 Base62 编码字符串。
- ///
- /// 要转换的整数
- /// 转换后的 Base62 编码字符串
- public static string Encode(long number)
- {
- if (number < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(number), "Number must be non-negative.");
- }
-
- if (number == 0)
- {
- return BASE62_CHARS[0].ToString();
- }
-
- List chars = new List();
- int targetBase = BASE62_CHARS.Length;
- while (number > 0)
- {
- int index = (int)(number % targetBase);
- chars.Add(BASE62_CHARS[index]);
- number = number / targetBase;
- }
- chars.Reverse();
- return new string(chars.ToArray());
- }
-
- ///
- /// 将给定的 Base62 编码字符串转换为整数。
- ///
- /// 要转换的 Base62 编码字符串
- /// 转换后的整数
- public static long Decode(string str)
- {
- if (string.IsNullOrEmpty(str))
- {
- throw new ArgumentException("String is null or empty.", nameof(str));
- }
-
- long result = 0;
- int sourceBase = BASE62_CHARS.Length;
- long multiplier = 1;
- for (int i = str.Length - 1; i >= 0; i--)
- {
- int digit = Array.IndexOf(BASE62_CHARS, str[i]);
- if (digit == -1)
- {
- throw new ArgumentException("Invalid character in string: " + str[i], nameof(str));
- }
- result += digit * multiplier;
- multiplier *= sourceBase;
- }
- return result;
- }
- }
-}
diff --git a/EasyTool.Core/CodeCategory/Base64Util.cs b/EasyTool.Core/CodeCategory/Base64Util.cs
deleted file mode 100644
index 7a84f8c..0000000
--- a/EasyTool.Core/CodeCategory/Base64Util.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace EasyTool
-{
- ///
- /// Base64 编码解码工具类
- ///
- public static class Base64Util
- {
- // Base64 字符集,共 64 个字符
- private static readonly char[] BASE64_CHARS =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".ToCharArray();
-
- // Base64 填充字符
- private const char BASE64_PADDING_CHAR = '=';
-
- ///
- /// 将给定的字节数组转换为 Base64 编码字符串。
- ///
- /// 要转换的字节数组
- /// 转换后的 Base64 编码字符串
- public static string Encode(byte[] bytes)
- {
- if (bytes == null)
- {
- throw new ArgumentNullException(nameof(bytes));
- }
-
- int length = bytes.Length;
- if (length == 0)
- {
- return string.Empty;
- }
-
- char[] chars = new char[(length + 2) / 3 * 4];
- int index = 0;
- for (int i = 0; i < length; i += 3)
- {
- int val = (bytes[i] << 16) + ((i + 1 < length ? bytes[i + 1] : 0) << 8) + (i + 2 < length ? bytes[i + 2] : 0);
- chars[index++] = BASE64_CHARS[(val >> 18) & 0x3F];
- chars[index++] = BASE64_CHARS[(val >> 12) & 0x3F];
- chars[index++] = BASE64_CHARS[(val >> 6) & 0x3F];
- chars[index++] = BASE64_CHARS[val & 0x3F];
- }
-
- // 添加填充字符
- int paddingCount = length % 3;
- if (paddingCount > 0)
- {
- chars[chars.Length - 1] = BASE64_PADDING_CHAR;
- if (paddingCount == 1)
- {
- chars[chars.Length - 2] = BASE64_PADDING_CHAR;
- }
- }
-
- return new string(chars);
- }
-
- ///
- /// 将给定的 Base64 编码字符串转换为字节数组。
- ///
- /// 要转换的 Base64 编码字符串
- /// 转换后的字节数组
- public static byte[] Decode(string str)
- {
- if (string.IsNullOrEmpty(str))
- {
- throw new ArgumentException("String is null or empty.", nameof(str));
- }
- int length = str.Length;
- if (length % 4 != 0)
- {
- throw new ArgumentException("Invalid length of input string: " + length, nameof(str));
- }
-
- int paddingCount = 0;
- if (length > 0 && str[length - 1] == BASE64_PADDING_CHAR)
- {
- paddingCount++;
- }
- if (length > 1 && str[length - 2] == BASE64_PADDING_CHAR)
- {
- paddingCount++;
- }
-
- byte[] bytes = new byte[length / 4 * 3 - paddingCount];
- int index = 0;
- for (int i = 0; i < length; i += 4)
- {
- int val = (DecodeBase64Char(str[i]) << 18) +
- (DecodeBase64Char(str[i + 1]) << 12) +
- (DecodeBase64Char(str[i + 2]) << 6) +
- DecodeBase64Char(str[i + 3]);
- bytes[index++] = (byte)(val >> 16);
- if (index < bytes.Length)
- {
- bytes[index++] = (byte)(val >> 8);
- }
- if (index < bytes.Length)
- {
- bytes[index++] = (byte)val;
- }
- }
-
- return bytes;
- }
-
- // 解码 Base64 字符
- private static int DecodeBase64Char(char c)
- {
- if (c >= 'A' && c <= 'Z')
- {
- return c - 'A';
- }
- if (c >= 'a' && c <= 'z')
- {
- return c - 'a' + 26;
- }
- if (c >= '0' && c <= '9')
- {
- return c - '0' + 52;
- }
- if (c == '+')
- {
- return 62;
- }
- if (c == '/')
- {
- return 63;
- }
- throw new ArgumentException("Invalid character in input string: " + c, nameof(c));
- }
- }
-}
diff --git a/EasyTool.Core/CodeCategory/DesUtil.cs b/EasyTool.Core/CodeCategory/DesUtil.cs
index 94e350f..1fad310 100644
--- a/EasyTool.Core/CodeCategory/DesUtil.cs
+++ b/EasyTool.Core/CodeCategory/DesUtil.cs
@@ -85,12 +85,13 @@ public static string Encrypt(string str, string sk,string iv, CipherMode cipher
if (!IsLegalSize(iv)) throw new ArgumentException("不合规的IV,请确认IV为8位的字符");
encoding ??= Encoding.UTF8;
byte[] keyBytes = encoding.GetBytes(sk).ToArray();
+ byte[] ivBytes = encoding.GetBytes(iv).ToArray();
byte[] toEncrypt = encoding.GetBytes(str);
var des = DES.Create();
des.Mode = cipher;
des.Padding = padding;
des.Key = keyBytes;
- des.IV = keyBytes;
+ des.IV = ivBytes;
ICryptoTransform cTransform = des.CreateEncryptor();
var resultArray = cTransform.TransformFinalBlock(toEncrypt, 0, toEncrypt.Length);
@@ -115,12 +116,13 @@ public static string Decrypt(string str, string sk, string iv, CipherMode cipher
if (!IsLegalSize(iv)) throw new ArgumentException("不合规的IV,请确认IV为8位的字符");
encoding ??= Encoding.UTF8;
byte[] keyBytes = encoding.GetBytes(sk).ToArray();
+ byte[] ivBytes = encoding.GetBytes(iv).ToArray();
byte[] toDecrypt = Convert.FromBase64String(str);
var des = DES.Create();
des.Mode = cipher;
des.Padding = padding;
des.Key = keyBytes;
- des.IV = keyBytes;
+ des.IV = ivBytes;
ICryptoTransform cTransform = des.CreateDecryptor();
var resultArray = cTransform.TransformFinalBlock(toDecrypt, 0, toDecrypt.Length);
return encoding.GetString(resultArray);
@@ -134,5 +136,69 @@ private static bool IsLegalSize(string sk)
return false;
}
+ ///
+ /// DES 加密(字节数组版本)
+ ///
+ /// 待加密数据
+ /// 秘钥
+ /// 向量Iv
+ /// 默认ECB
+ /// 默认PKCS7
+ ///
+ ///
+ public static byte[] Encrypt(byte[] data, byte[] keyBytes, byte[]? ivBytes = null, CipherMode cipher = CipherMode.ECB, PaddingMode padding = PaddingMode.PKCS7)
+ {
+ if (data == null || data.Length == 0)
+ return Array.Empty();
+ if (keyBytes == null || keyBytes.Length != 8)
+ throw new ArgumentException("不合规的秘钥,请确认秘钥为8位");
+ if (ivBytes != null && ivBytes.Length != 8)
+ throw new ArgumentException("不合规的IV,请确认IV为8位");
+
+ var des = DES.Create();
+ des.Mode = cipher;
+ des.Padding = padding;
+ des.Key = keyBytes;
+ if (ivBytes != null)
+ des.IV = ivBytes;
+ else
+ des.IV = keyBytes;
+
+ ICryptoTransform cTransform = des.CreateEncryptor();
+ return cTransform.TransformFinalBlock(data, 0, data.Length);
+ }
+
+ ///
+ /// DES 解密(字节数组版本)
+ ///
+ /// 待解密数据
+ /// 秘钥
+ /// 向量Iv
+ /// 默认ECB
+ /// 默认PKCS7
+ ///
+ ///
+ public static byte[] Decrypt(byte[] data, byte[] keyBytes, byte[]? ivBytes = null, CipherMode cipher = CipherMode.ECB, PaddingMode padding = PaddingMode.PKCS7)
+ {
+ if (data == null || data.Length == 0)
+ return Array.Empty();
+ if (keyBytes == null || keyBytes.Length != 8)
+ throw new ArgumentException("不合规的秘钥,请确认秘钥为8位");
+ if (ivBytes != null && ivBytes.Length != 8)
+ throw new ArgumentException("不合规的IV,请确认IV为8位");
+
+ var des = DES.Create();
+ des.Mode = cipher;
+ des.Padding = padding;
+ des.Key = keyBytes;
+ if (ivBytes != null)
+ des.IV = ivBytes;
+ else
+ des.IV = keyBytes;
+
+ ICryptoTransform cTransform = des.CreateDecryptor();
+ return cTransform.TransformFinalBlock(data, 0, data.Length);
+ }
+
}
}
diff --git a/EasyTool.Core/CodeCategory/EncodingUtil.cs b/EasyTool.Core/CodeCategory/EncodingUtil.cs
new file mode 100644
index 0000000..58d7a78
--- /dev/null
+++ b/EasyTool.Core/CodeCategory/EncodingUtil.cs
@@ -0,0 +1,394 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace EasyTool.CodeCategory
+{
+ ///
+ /// 编码工具类,提供各种编码格式的转换功能
+ ///
+ public static class EncodingUtil
+ {
+ #region Base32 编码
+
+ // Base32 字符集,共 32 个字符
+ private static readonly char[] BASE32_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".ToCharArray();
+
+ // Base32 填充字符
+ private const char BASE32_PADDING_CHAR = '=';
+
+ ///
+ /// 将给定的字节数组转换为 Base32 编码字符串
+ ///
+ /// 要转换的字节数组
+ /// 转换后的 Base32 编码字符串
+ public static string Base32Encode(byte[] bytes)
+ {
+ if (bytes == null)
+ {
+ throw new ArgumentNullException(nameof(bytes));
+ }
+
+ int length = bytes.Length;
+ if (length == 0)
+ {
+ return string.Empty;
+ }
+
+ char[] chars = new char[(length + 4) / 5 * 8];
+ int index = 0;
+ for (int i = 0; i < length; i += 5)
+ {
+ int val = (bytes[i] << 32) + ((i + 1 < length ? bytes[i + 1] : 0) << 24) +
+ ((i + 2 < length ? bytes[i + 2] : 0) << 16) + ((i + 3 < length ? bytes[i + 3] : 0) << 8) +
+ ((i + 4 < length ? bytes[i + 4] : 0) << 0);
+ chars[index++] = BASE32_CHARS[(val >> 35) & 0x1F];
+ chars[index++] = BASE32_CHARS[(val >> 30) & 0x1F];
+ chars[index++] = BASE32_CHARS[(val >> 25) & 0x1F];
+ chars[index++] = BASE32_CHARS[(val >> 20) & 0x1F];
+ chars[index++] = BASE32_CHARS[(val >> 15) & 0x1F];
+ chars[index++] = BASE32_CHARS[(val >> 10) & 0x1F];
+ chars[index++] = BASE32_CHARS[(val >> 5) & 0x1F];
+ chars[index++] = BASE32_CHARS[val & 0x1F];
+ }
+
+ // 添加填充字符
+ int paddingCount = length % 5;
+ if (paddingCount > 0)
+ {
+ chars[chars.Length - 1] = BASE32_PADDING_CHAR;
+ if (paddingCount == 1)
+ {
+ chars[chars.Length - 2] = BASE32_PADDING_CHAR;
+ }
+ else if (paddingCount <= 2)
+ {
+ chars[chars.Length - 3] = BASE32_PADDING_CHAR;
+ }
+ else if (paddingCount <= 3)
+ {
+ chars[chars.Length - 4] = BASE32_PADDING_CHAR;
+ }
+ else if (paddingCount <= 4)
+ {
+ chars[chars.Length - 5] = BASE32_PADDING_CHAR;
+ }
+ }
+
+ return new string(chars);
+ }
+
+ ///
+ /// 将给定的 Base32 编码字符串转换为字节数组
+ ///
+ /// 要转换的 Base32 编码字符串
+ /// 转换后的字节数组
+ public static byte[] Base32Decode(string str)
+ {
+ if (string.IsNullOrEmpty(str))
+ {
+ throw new ArgumentException("String is null or empty.", nameof(str));
+ }
+
+ // 移除填充字符
+ str = str.TrimEnd('=');
+
+ int length = str.Length;
+ if (length % 8 != 0)
+ {
+ throw new ArgumentException("Invalid length of input string: " + length, nameof(str));
+ }
+
+ int paddingCount = 0;
+ if (length > 0 && str[length - 1] == BASE32_PADDING_CHAR)
+ {
+ paddingCount++;
+ }
+ if (length > 1 && str[length - 2] == BASE32_PADDING_CHAR)
+ {
+ paddingCount++;
+ }
+ if (length > 3 && str[length - 3] == BASE32_PADDING_CHAR)
+ {
+ paddingCount++;
+ }
+ if (length > 4 && str[length - 4] == BASE32_PADDING_CHAR)
+ {
+ paddingCount++;
+ }
+ if (length > 5 && str[length - 5] == BASE32_PADDING_CHAR)
+ {
+ paddingCount++;
+ }
+ if (length > 6 && str[length - 6] == BASE32_PADDING_CHAR)
+ {
+ paddingCount++;
+ }
+
+ byte[] bytes = new byte[length / 8 * 5 - paddingCount];
+ int index = 0;
+ for (int i = 0; i < length; i += 8)
+ {
+ int val = (DecodeBase32Char(str[i]) << 35) +
+ (DecodeBase32Char(str[i + 1]) << 30) +
+ (DecodeBase32Char(str[i + 2]) << 25) +
+ (DecodeBase32Char(str[i + 3]) << 20) +
+ (DecodeBase32Char(str[i + 4]) << 15) +
+ (DecodeBase32Char(str[i + 5]) << 10) +
+ (DecodeBase32Char(str[i + 6]) << 5) +
+ DecodeBase32Char(str[i + 7]);
+ bytes[index++] = (byte)(val >> 24);
+ if (index < bytes.Length)
+ {
+ bytes[index++] = (byte)(val >> 16);
+ }
+ if (index < bytes.Length)
+ {
+ bytes[index++] = (byte)(val >> 8);
+ }
+ if (index < bytes.Length)
+ {
+ bytes[index++] = (byte)val;
+ }
+ }
+
+ return bytes;
+ }
+
+ // 解码 Base32 字符
+ private static int DecodeBase32Char(char c)
+ {
+ // 支持大小写
+ if (c >= 'A' && c <= 'Z')
+ {
+ return c - 'A';
+ }
+ if (c >= 'a' && c <= 'z')
+ {
+ return c - 'a';
+ }
+ if (c >= '2' && c <= '7')
+ {
+ return c - '2' + 26;
+ }
+ throw new ArgumentException("Invalid character in input string: " + c, nameof(c));
+ }
+
+ #endregion
+
+ #region Base62 编码
+
+ // Base62 字符集,共 62 个字符
+ private static readonly char[] BASE62_CHARS =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray();
+
+ ///
+ /// 将给定的整数转换为 Base62 编码字符串
+ ///
+ /// 要转换的整数
+ /// 转换后的 Base62 编码字符串
+ public static string Base62Encode(long number)
+ {
+ if (number < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(number), "Number must be non-negative.");
+ }
+
+ if (number == 0)
+ {
+ return BASE62_CHARS[0].ToString();
+ }
+
+ List chars = new List();
+ int targetBase = BASE62_CHARS.Length;
+ while (number > 0)
+ {
+ int index = (int)(number % targetBase);
+ chars.Add(BASE62_CHARS[index]);
+ number = number / targetBase;
+ }
+ chars.Reverse();
+ return new string(chars.ToArray());
+ }
+
+ ///
+ /// 将给定的 Base62 编码字符串转换为整数
+ ///
+ /// 要转换的 Base62 编码字符串
+ /// 转换后的整数
+ public static long Base62Decode(string str)
+ {
+ if (string.IsNullOrEmpty(str))
+ {
+ throw new ArgumentException("String is null or empty.", nameof(str));
+ }
+
+ long result = 0;
+ int sourceBase = BASE62_CHARS.Length;
+ long multiplier = 1;
+ for (int i = str.Length - 1; i >= 0; i--)
+ {
+ int digit = Array.IndexOf(BASE62_CHARS, str[i]);
+ if (digit == -1)
+ {
+ throw new ArgumentException("Invalid character in string: " + str[i], nameof(str));
+ }
+ result += digit * multiplier;
+ multiplier *= sourceBase;
+ }
+ return result;
+ }
+
+ #endregion
+
+ #region ROT 加密
+
+ ///
+ /// 将给定的字符串按照 ROT 加密算法进行加密
+ ///
+ /// 要加密的字符串
+ /// 偏移量
+ /// 加密后的字符串
+ public static string RotEncrypt(string text, int n)
+ {
+ if (string.IsNullOrEmpty(text))
+ {
+ return text;
+ }
+
+ string upperCaseText = text.ToUpper();
+ return new string(upperCaseText.Select(c =>
+ {
+ if (!char.IsLetter(c))
+ {
+ return c;
+ }
+ int x = c - 'A';
+ int y = (x + n) % 26;
+ return (char)(y + 'A');
+ }).ToArray());
+ }
+
+ ///
+ /// 将给定的字符串按照 ROT 加密算法进行解密
+ ///
+ /// 要解密的字符串
+ /// 偏移量
+ /// 解密后的字符串
+ public static string RotDecrypt(string text, int n)
+ {
+ return RotEncrypt(text, 26 - n);
+ }
+
+ #endregion
+
+ #region Morse 电码
+
+ // Morse 电码表
+ private static readonly Dictionary MORSE_TABLE = new Dictionary
+ {
+ {'A', ".-"},
+ {'B', "-..."},
+ {'C', "-.-."},
+ {'D', "-.."},
+ {'E', "."},
+ {'F', "..-."},
+ {'G', "--."},
+ {'H', "...."},
+ {'I', ".."},
+ {'J', ".---"},
+ {'K', "-.-"},
+ {'L', ".-.."},
+ {'M', "--"},
+ {'N', "-."},
+ {'O', "---"},
+ {'P', ".--."},
+ {'Q', "--.-"},
+ {'R', ".-."},
+ {'S', "..."},
+ {'T', "-"},
+ {'U', "..-"},
+ {'V', "...-"},
+ {'W', ".--"},
+ {'X', "-.."},
+ {'Y', "-.--"},
+ {'Z', "--.."},
+ {'0', "-----"},
+ {'1', ".----"},
+ {'2', "..---"},
+ {'3', "...--"},
+ {'4', "....-"},
+ {'5', "....."},
+ {'6', "-...."},
+ {'7', "--..."},
+ {'8', "---.."},
+ {'9', "----."},
+ {' ', " "}
+ };
+
+ // Morse 反向电码表,用于解码优化性能
+ private static readonly Dictionary MORSE_REVERSE_TABLE;
+
+ static EncodingUtil()
+ {
+ MORSE_REVERSE_TABLE = new Dictionary();
+ foreach (var kvp in MORSE_TABLE)
+ {
+ if (!string.IsNullOrEmpty(kvp.Value))
+ {
+ MORSE_REVERSE_TABLE[kvp.Value] = kvp.Key;
+ }
+ }
+ }
+
+ ///
+ /// 将给定的字符串转换为 Morse 电码字符串
+ ///
+ /// 要转换的字符串
+ /// 转换后的 Morse 电码字符串
+ public static string MorseEncode(string str)
+ {
+ if (string.IsNullOrEmpty(str))
+ {
+ return string.Empty;
+ }
+
+ List morseCodes = new List();
+ foreach (char c in str.ToUpper())
+ {
+ if (MORSE_TABLE.ContainsKey(c))
+ {
+ morseCodes.Add(MORSE_TABLE[c]);
+ }
+ }
+ return string.Join(" ", morseCodes);
+ }
+
+ ///
+ /// 将给定的 Morse 电码字符串转换为原始字符串
+ ///
+ /// 要转换的 Morse 电码字符串
+ /// 转换后的原始字符串
+ public static string MorseDecode(string morseCode)
+ {
+ if (string.IsNullOrEmpty(morseCode))
+ {
+ return string.Empty;
+ }
+
+ string[] codes = morseCode.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ StringBuilder result = new StringBuilder(codes.Length);
+ foreach (string code in codes)
+ {
+ if (MORSE_REVERSE_TABLE.TryGetValue(code, out char c))
+ {
+ result.Append(c);
+ }
+ }
+ return result.ToString();
+ }
+
+ #endregion
+ }
+}
diff --git a/EasyTool.Core/ToolCategory/HashUtil.cs b/EasyTool.Core/CodeCategory/HashUtil.cs
similarity index 76%
rename from EasyTool.Core/ToolCategory/HashUtil.cs
rename to EasyTool.Core/CodeCategory/HashUtil.cs
index 6913e7a..b6915f4 100644
--- a/EasyTool.Core/ToolCategory/HashUtil.cs
+++ b/EasyTool.Core/CodeCategory/HashUtil.cs
@@ -1,8 +1,8 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Text;
-namespace EasyTool
+namespace EasyTool.CodeCategory
{
///
/// hash算法工具类
@@ -14,13 +14,17 @@ public class HashUtil
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint AdditiveHash(string str)
+ public static uint AdditiveHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = 0;
foreach (char c in str)
{
hash += c;
}
+
return hash;
}
@@ -29,13 +33,17 @@ public static uint AdditiveHash(string str)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint RotatingHash(string str)
+ public static uint RotatingHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = (uint)str.Length;
foreach (char c in str)
{
hash = (hash << 4) ^ (hash >> 28) ^ c;
}
+
return hash;
}
@@ -44,8 +52,11 @@ public static uint RotatingHash(string str)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint OneByOneHash(string str)
+ public static uint OneByOneHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = 0;
foreach (char c in str)
{
@@ -53,6 +64,7 @@ public static uint OneByOneHash(string str)
hash += (hash << 10);
hash ^= (hash >> 6);
}
+
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
@@ -64,13 +76,17 @@ public static uint OneByOneHash(string str)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint Bernstein(string str)
+ public static uint Bernstein(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = 5381;
foreach (char c in str)
{
hash = 33 * hash + c;
}
+
return hash;
}
@@ -81,14 +97,22 @@ public static uint Bernstein(string str)
/// 大质数
/// 哈希桶的数量
/// a的取值范围为[1, prime - 1]
- /// b的取值范围
- public static uint Universal(string str, uint prime, uint num_buckets, uint a, uint b)
+ /// b的取值范围
+ public static uint Universal(string? str, uint prime, uint num_buckets, uint a, uint b)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+ if (prime == 0)
+ throw new ArgumentException("Prime must be greater than 0", nameof(prime));
+ if (num_buckets == 0)
+ throw new ArgumentException("Number of buckets must be greater than 0", nameof(num_buckets));
+
uint hash = a;
foreach (char c in str)
{
hash = hash * prime + c;
}
+
hash = (hash * a + b) % num_buckets;
return hash;
}
@@ -99,13 +123,20 @@ public static uint Universal(string str, uint prime, uint num_buckets, uint a, u
/// 要进行hash的字符串
/// 随机数表
/// 返回hash值
- public static uint Zobrist(string str, uint[] table)
+ public static uint Zobrist(string? str, uint[]? table)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+ if (table == null || table.Length == 0)
+ throw new ArgumentException("Table cannot be null or empty", nameof(table));
+
uint hash = 0;
for (int i = 0; i < str.Length; i++)
{
- hash ^= table[str[i]];
+ int index = Math.Min(str[i], table.Length - 1);
+ hash ^= table[index];
}
+
return hash;
}
@@ -114,8 +145,11 @@ public static uint Zobrist(string str, uint[] table)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint FnvHash(string str)
+ public static uint FnvHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
const uint fnv_prime = 0x811C9DC5;
uint hash = 0;
foreach (char c in str)
@@ -123,6 +157,7 @@ public static uint FnvHash(string str)
hash *= fnv_prime;
hash ^= c;
}
+
return hash;
}
@@ -149,14 +184,20 @@ public static uint IntHash(uint key)
/// b的取值范围为[1, 255]
/// a的取值范围为[1, b-1]
/// 返回hash值
- public static uint RsHash(string str, uint b, uint a)
+ public static uint RsHash(string? str, uint b, uint a)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+ if (b == 0)
+ throw new ArgumentException("b must be greater than 0", nameof(b));
+
uint hash = 0;
foreach (char c in str)
{
hash = hash * a + c;
a = a * b;
}
+
return hash;
}
@@ -165,13 +206,17 @@ public static uint RsHash(string str, uint b, uint a)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint JsHash(string str)
+ public static uint JsHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = 1315423911;
foreach (char c in str)
{
hash ^= ((hash << 5) + c + (hash >> 2));
}
+
return hash;
}
@@ -180,8 +225,11 @@ public static uint JsHash(string str)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint PjwHash(string str)
+ public static uint PjwHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = 0;
const uint BitsInUnsignedInt = (uint)(sizeof(uint) * 8);
const uint ThreeQuarters = (uint)((BitsInUnsignedInt * 3) / 4);
@@ -196,6 +244,7 @@ public static uint PjwHash(string str)
hash = ((hash ^ (test >> (int)ThreeQuarters)) & (~HighBits));
}
}
+
return hash;
}
@@ -204,8 +253,11 @@ public static uint PjwHash(string str)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint ElfHash(string str)
+ public static uint ElfHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = 0;
uint x = 0;
foreach (char c in str)
@@ -216,8 +268,10 @@ public static uint ElfHash(string str)
{
hash ^= (x >> 24);
}
+
hash &= ~x;
}
+
return hash;
}
@@ -227,13 +281,19 @@ public static uint ElfHash(string str)
/// 要进行hash的字符串
/// 种子值
/// 返回hash值
- public static uint BkdrHash(string str, uint seed)
+ public static uint BkdrHash(string? str, uint seed)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+ if (seed == 0)
+ throw new ArgumentException("Seed must be greater than 0", nameof(seed));
+
uint hash = 0;
foreach (char c in str)
{
hash = hash * seed + c;
}
+
return hash;
}
@@ -242,13 +302,17 @@ public static uint BkdrHash(string str, uint seed)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint SdbmHash(string str)
+ public static uint SdbmHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = 0;
foreach (char c in str)
{
hash = c + (hash << 6) + (hash << 16) - hash;
}
+
return hash;
}
@@ -257,13 +321,17 @@ public static uint SdbmHash(string str)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint DjbHash(string str)
+ public static uint DjbHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = 5381;
foreach (char c in str)
{
hash = ((hash << 5) + hash) + c;
}
+
return hash;
}
@@ -272,13 +340,17 @@ public static uint DjbHash(string str)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint DekHash(string str)
+ public static uint DekHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = (uint)str.Length;
foreach (char c in str)
{
hash = ((hash << 5) ^ (hash >> 27)) ^ c;
}
+
return hash;
}
@@ -287,8 +359,11 @@ public static uint DekHash(string str)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint ApHash(string str)
+ public static uint ApHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = 0;
int i;
for (i = 0; i < str.Length; i++)
@@ -302,6 +377,7 @@ public static uint ApHash(string str)
hash ^= (~((hash << 11) ^ str[i] ^ (hash >> 5)));
}
}
+
return hash;
}
@@ -311,14 +387,17 @@ public static uint ApHash(string str)
/// 要进行hash的字符串
/// hash表的长度
/// 返回hash值
- public static uint TianlHash(string str, uint len)
+ public static uint TianlHash(string? str, uint len)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+ if (len == 0)
+ throw new ArgumentException("Length must be greater than 0", nameof(len));
+
uint hash = 0;
uint[] w = new uint[64];
uint[] v = new uint[8];
- if (str.Length == 0) return 0;
-
if (str.Length <= 64)
{
for (int i = 0; i < str.Length; i++)
@@ -327,6 +406,7 @@ public static uint TianlHash(string str, uint len)
hash += (hash << 10);
hash ^= (hash >> 6);
}
+
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
@@ -385,14 +465,18 @@ public static uint TianlHash(string str, uint len)
///
/// 要进行hash的字符串
/// 返回hash值
- public static uint JavaDefaultHash(string str)
+ public static uint JavaDefaultHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint hash = 0;
uint h = hash;
foreach (char c in str)
{
h = 31 * h + c;
}
+
hash = h;
return hash;
}
@@ -402,8 +486,11 @@ public static uint JavaDefaultHash(string str)
///
/// 要进行hash的字符串
/// 返回hash值
- public static ulong MixHash(string str)
+ public static ulong MixHash(string? str)
{
+ if (string.IsNullOrEmpty(str))
+ return 0;
+
uint seed = 131; // 31 131 1313 13131 131313 etc..
ulong hash1 = 0;
ulong hash2 = 0;
@@ -412,8 +499,9 @@ public static ulong MixHash(string str)
hash1 = (hash1 * seed) + c;
hash2 = (hash2 * seed) + c + 1;
}
+
return hash1 + (hash2 * 1566083941);
}
}
- }
+}
diff --git a/EasyTool.Core/ToolCategory/HexUtil.cs b/EasyTool.Core/CodeCategory/HexUtil.cs
similarity index 86%
rename from EasyTool.Core/ToolCategory/HexUtil.cs
rename to EasyTool.Core/CodeCategory/HexUtil.cs
index 1bc1079..e3f9870 100644
--- a/EasyTool.Core/ToolCategory/HexUtil.cs
+++ b/EasyTool.Core/CodeCategory/HexUtil.cs
@@ -2,12 +2,12 @@
using System.Collections.Generic;
using System.Text;
-namespace EasyTool
+namespace EasyTool.CodeCategory
{
///
/// 16进制工具类
///
- public class HexUtil
+ public static class HexUtil
{
///
/// 将16进制字符串转换为字节数组
@@ -32,16 +32,53 @@ public static byte[] HexToBytes(string hex)
/// 将字节数组转换为16进制字符串
///
/// 字节数组
+ /// 是否使用小写(默认大写)
/// 16进制字符串
- public static string BytesToHex(byte[] bytes)
+ public static string BytesToHex(byte[] bytes, bool lowercase = false)
{
- StringBuilder sb = new StringBuilder();
+ if (bytes == null)
+ throw new ArgumentNullException(nameof(bytes));
+
+ StringBuilder sb = new StringBuilder(bytes.Length * 2);
+ string format = lowercase ? "x2" : "X2";
foreach (byte b in bytes)
- sb.Append(b.ToString("X2"));
+ sb.Append(b.ToString(format));
return sb.ToString();
}
+ ///
+ /// 将16进制字符串转换为字节数组(安全版本,不抛出异常)
+ ///
+ /// 16进制字符串
+ /// 转换后的字节数组
+ /// 是否转换成功
+ public static bool TryHexToBytes(string hex, out byte[]? bytes)
+ {
+ bytes = null;
+ if (string.IsNullOrWhiteSpace(hex))
+ return false;
+
+ hex = hex.Replace(" ", "");
+ if (hex.Length % 2 != 0)
+ return false;
+
+ try
+ {
+ bytes = new byte[hex.Length / 2];
+ for (int i = 0; i < hex.Length; i += 2)
+ {
+ if (!byte.TryParse(hex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber, null, out bytes[i / 2]))
+ return false;
+ }
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
///
/// 比较两个16进制字符串是否相等
///
@@ -72,7 +109,25 @@ public static bool HexEquals(string hex1, string hex2)
/// 十进制数值
public static int HexToInt(string hex)
{
- return Convert.ToInt32(hex, 16);
+ try
+ {
+ return Convert.ToInt32(hex, 16);
+ }
+ catch (FormatException ex)
+ {
+ throw new ArgumentException("Invalid hex string: " + hex, nameof(hex), ex);
+ }
+ }
+
+ ///
+ /// 将16进制字符串转换为十进制数值(安全版本)
+ ///
+ /// 16进制字符串
+ /// 转换后的数值
+ /// 是否转换成功
+ public static bool TryHexToInt(string hex, out int result)
+ {
+ return int.TryParse(hex, System.Globalization.NumberStyles.HexNumber, null, out result);
}
///
@@ -86,24 +141,15 @@ public static string IntToHex(int number)
}
///
- /// 将16进制字符串中的所有字符转换为大写
+ /// 将16进制字符串转换为大写形式
///
/// 16进制字符串
- /// 大写16进制字符串
+ /// 大写形式的16进制字符串
public static string HexToUpper(string hex)
{
return hex.ToUpper();
}
- ///
- /// 将16进制字符串中的所有字符转换为小写
- ///
- /// 16进制字符串
- /// 小写16进制字符串
- public static string HexToLower(string hex)
- {
- return hex.ToLower();
- }
///
/// 获取16进制字符串中指定位置的字符
diff --git a/EasyTool.Core/CodeCategory/MorseUtil.cs b/EasyTool.Core/CodeCategory/MorseUtil.cs
deleted file mode 100644
index 4341231..0000000
--- a/EasyTool.Core/CodeCategory/MorseUtil.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace EasyTool
-{
- ///
- /// Morse 电码工具类
- ///
- public static class MorseUtil
- {
- // Morse 电码表
- private static readonly Dictionary MORSE_TABLE = new Dictionary()
- {
- {'A', ".-"},
- {'B', "-..."},
- {'C', "-.-."},
- {'D', "-.."},
- {'E', "."},
- {'F', "..-."},
- {'G', "--."},
- {'H', "...."},
- {'I', ".."},
- {'J', ".---"},
- {'K', "-.-"},
- {'L', ".-.."},
- {'M', "--"},
- {'N', "-."},
- {'O', "---"},
- {'P', ".--."},
- {'Q', "--.-"},
- {'R', ".-."},
- {'S', "..."},
- {'T', "-"},
- {'U', "..-"},
- {'V', "...-"},
- {'W', ".--"},
- {'X', "-..-"},
- {'Y', "-.--"},
- {'Z', "--.."},
- {'0', "-----"},
- {'1', ".----"},
- {'2', "..---"},
- {'3', "...--"},
- {'4', "....-"},
- {'5', "....."},
- {'6', "-...."},
- {'7', "--..."},
- {'8', "---.."},
- {'9', "----."},
- {' ', " "}
- };
-
- ///
- /// 将给定的字符串转换为 Morse 电码字符串。
- ///
- /// 要转换的字符串
- /// 转换后的 Morse 电码字符串
- public static string Encode(string str)
- {
- if (string.IsNullOrEmpty(str))
- {
- return string.Empty;
- }
-
- List morseCodes = new List();
- foreach (char c in str.ToUpper())
- {
- if (MORSE_TABLE.ContainsKey(c))
- {
- morseCodes.Add(MORSE_TABLE[c]);
- }
- }
- return string.Join(" ", morseCodes);
- }
-
-
- ///
- /// 将给定的 Morse 电码字符串转换为原始字符串。
- ///
- /// 要转换的 Morse 电码字符串
- /// 转换后的原始字符串
- public static string Decode(string morseCode)
- {
- if (string.IsNullOrEmpty(morseCode))
- {
- return string.Empty;
- }
-
- string[] codes = morseCode.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
- List chars = new List();
- foreach (string code in codes)
- {
- foreach (KeyValuePair kvp in MORSE_TABLE)
- {
- if (kvp.Value == code)
- {
- chars.Add(kvp.Key);
- break;
- }
- }
- }
- return new string(chars.ToArray());
- }
-
- }
-}
diff --git a/EasyTool.Core/CodeCategory/PunycodeUtil.cs b/EasyTool.Core/CodeCategory/PunycodeUtil.cs
deleted file mode 100644
index cee3389..0000000
--- a/EasyTool.Core/CodeCategory/PunycodeUtil.cs
+++ /dev/null
@@ -1,254 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace EasyTool
-{
- ///
- /// Punycode 工具类
- ///
- public static class PunycodeUtil
- {
- private const int BASE = 36;
- private const int TMIN = 1;
- private const int TMAX = 26;
- private const int SKEW = 38;
- private const int DAMP = 700;
- private const int INITIAL_BIAS = 72;
- private const int INITIAL_N = 128;
-
- private static readonly char[] DELIMITER = { '-' };
-
- ///
- /// 将给定的 Unicode 字符串按照 Punycode 编码规则进行编码。
- ///
- /// 要编码的 Unicode 字符串
- /// 编码后的字符串
- public static string Encode(string input)
- {
- if (string.IsNullOrEmpty(input))
- {
- return input;
- }
-
- List inputChars = input.Select(c => (int)c).ToList();
- List basicChars = inputChars.Where(c => c < 0x80).ToList();
- List extendedChars = inputChars.Except(basicChars).ToList();
-
- List output = new List();
- int n = INITIAL_N;
- int delta = 0;
- int bias = INITIAL_BIAS;
-
- // Encode the basic code points
- foreach (int b in basicChars)
- {
- output.Add(b);
- }
-
- int h = output.Count;
- int bLength = basicChars.Count;
- if (bLength > 0 && extendedChars.Count > 0)
- {
- output.Add('-');
- }
-
- // Main encoding loop
- while (h < inputChars.Count)
- {
- int m = int.MaxValue;
- foreach (int e in extendedChars)
- {
- if (e >= n && e < m)
- {
- m = e;
- }
- }
-
- delta += (m - n) * (h + 1);
- n = m;
- foreach (int e in extendedChars)
- {
- if (e < n)
- {
- delta++;
- }
-
- if (e == n)
- {
- int q = delta;
- int k = BASE;
- while (true)
- {
- int t = k <= bias ? TMIN : (k >= bias + TMAX ? TMAX : k - bias);
- if (q < t)
- {
- break;
- }
-
- output.Add(GetCodePoint(t + (q - t) % (BASE - t)));
- q = (q - t) / (BASE - t);
- k += BASE;
- }
-
- output.Add(GetCodePoint(q));
- bias = Adapt(delta, h + 1, h == bLength);
- delta = 0;
- h++;
- }
- }
-
- delta++;
- n++;
- }
-
- return new string(output.Select(c => (char)c).ToArray());
- }
-
- ///
- /// 将给定的 Punycode 编码字符串进行解码,得到原始的 Unicode 字符串。
- ///
- /// 要解码的 Punycode 编码字符串
- /// 原始的 Unicode 字符串
- public static string Decode(string input)
- {
- if (string.IsNullOrEmpty(input))
- {
- return input;
- }
-
- List output = new List();
- List inputChars = input.Select(c => (int)c).ToList();
- List basicChars = inputChars.Where(c => c < 0x80).ToList();
- int i = 0;
- int n = INITIAL_N;
- int bias = INITIAL_BIAS;
-
- // Find the last delimiter
- int lastDelim = input.LastIndexOf('-');
- if (lastDelim < 0)
- {
- lastDelim = 0;
- }
-
- // Decode the basic code points
- for (int j = 0; j < lastDelim; j++)
- {
- int c = inputChars[j];
- if (!IsBasic(c))
- {
- throw new ArgumentException("Invalid input string.");
- }
-
- output.Add(c);
- }
-
- // Main decoding loop
- int p = lastDelim > 0 ? lastDelim + 1 : 0;
- while (p < inputChars.Count)
- {
- int oldi = i;
- int w = 1;
- int k = BASE;
- while (true)
- {
- if (p >= inputChars.Count)
- {
- throw new ArgumentException("Invalid input string.");
- }
-
- int c = inputChars[p++];
- int digit = GetDigit(c);
- if (digit >= BASE)
- {
- throw new ArgumentException("Invalid input string.");
- }
-
- if (digit > (int.MaxValue - i) / w)
- {
- throw new ArgumentException("Invalid input string.");
- }
-
- i += digit * w;
- int t = k <= bias ? TMIN : (k >= bias + TMAX ? TMAX : k - bias);
- if (digit < t)
- {
- break;
- }
-
- if (w > int.MaxValue / (BASE - t))
- {
- throw new ArgumentException("Invalid input string.");
- }
-
- w *= BASE - t;
- k += BASE;
- }
-
- int delta = i - oldi;
- output.Add(GetCodePoint(delta));
- bias = Adapt(delta, output.Count, oldi == 0);
- n += i / output.Count;
- i %= output.Count;
- }
-
- return new string(output.Select(c => (char)c).ToArray());
- }
-
- private static bool IsBasic(int codePoint)
- {
- return codePoint < 0x80;
- }
-
- private static int GetDigit(int codePoint)
- {
- if (codePoint - '0' < 10)
- {
- return codePoint - '0' + 26;
- }
-
- if (codePoint - 'a' < 26)
- {
- return codePoint - 'a';
- }
-
- if (codePoint - 'A' < 26)
- {
- return codePoint - 'A';
- }
-
- throw new ArgumentException("Invalid input string.");
- }
-
- private static int Adapt(int delta, int numPoints, bool firstTime)
- {
- delta = firstTime ? delta / DAMP : delta >> 1;
- delta += delta / numPoints;
-
- int k = 0;
- while (delta > ((BASE - TMIN) * TMAX) / 2)
- {
- delta /= BASE - TMIN;
- k += BASE;
- }
-
- return k + (((BASE - TMIN + 1) * delta) / (delta + SKEW));
- }
-
- private static int GetCodePoint(int digit)
- {
- if (digit < 26)
- {
- return digit + 'a';
- }
-
- if (digit < 36)
- {
- return digit - 26 + '0';
- }
-
- throw new ArgumentException("Invalid input string.");
- }
- }
-}
diff --git a/EasyTool.Core/CodeCategory/RotUtil.cs b/EasyTool.Core/CodeCategory/RotUtil.cs
deleted file mode 100644
index f2b86c3..0000000
--- a/EasyTool.Core/CodeCategory/RotUtil.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace EasyTool
-{
- ///
- /// ROT 工具类
- ///
- public static class RotUtil
- {
- ///
- /// 将给定的字符串按照 ROT 加密算法进行加密。
- ///
- /// 要加密的字符串
- /// 偏移量
- /// 加密后的字符串
- public static string Encrypt(string text, int n)
- {
- if (string.IsNullOrEmpty(text))
- {
- return text;
- }
-
- string upperCaseText = text.ToUpper();
- return new string(upperCaseText.Select(c =>
- {
- if (!char.IsLetter(c))
- {
- return c;
- }
-
- int x = c - 'A';
- int y = (x + n) % 26;
- return (char)(y + 'A');
- }).ToArray());
- }
-
- ///
- /// 将给定的字符串按照 ROT 加密算法进行解密。
- ///
- /// 要解密的字符串
- /// 偏移量
- /// 解密后的字符串
- public static string Decrypt(string text, int n)
- {
- return Encrypt(text, 26 - n);
- }
- }
-}
diff --git a/EasyTool.Core/CollectionsCategory/ArrayExtension.cs b/EasyTool.Core/CollectionsCategory/ArrayExtension.cs
new file mode 100644
index 0000000..ac3f4a4
--- /dev/null
+++ b/EasyTool.Core/CollectionsCategory/ArrayExtension.cs
@@ -0,0 +1,401 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace EasyTool.CollectionsCategory
+{
+ ///
+ /// 数组扩展方法
+ ///
+ public static class ArrayExtension
+ {
+ #region 空值判断
+
+ ///
+ /// 判断数组是否为空或 null
+ ///
+ public static bool IsEmpty(this T[]? array)
+ {
+ return array == null || array.Length == 0;
+ }
+
+ ///
+ /// 判断数组是否非空
+ ///
+ public static bool IsNotEmpty(this T[]? array)
+ {
+ return array != null && array.Length > 0;
+ }
+
+ #endregion
+
+ #region 数组操作
+
+ ///
+ /// 数组排序
+ ///
+ public static T[] Sort(this T[]? array) where T : IComparable
+ {
+ if (array.IsEmpty())
+ {
+ throw new ArgumentException("Array is empty.");
+ }
+
+ T[] sortedArray = new T[array.Length];
+ array.CopyTo(sortedArray, 0);
+ Array.Sort(sortedArray);
+
+ return sortedArray;
+ }
+
+ ///
+ /// 数组反转
+ ///
+ public static T[] Reverse(this T[]? array)
+ {
+ if (array.IsEmpty())
+ {
+ throw new ArgumentException("Array is empty.");
+ }
+
+ T[] reversedArray = new T[array.Length];
+ array.CopyTo(reversedArray, 0);
+ Array.Reverse(reversedArray);
+
+ return reversedArray;
+ }
+
+ ///
+ /// 判断两个数组是否完全相等
+ ///
+ public static bool EqualsTo(this T[]? array, T[]? other)
+ {
+ if (array.IsEmpty() && other.IsEmpty())
+ {
+ return true;
+ }
+
+ if (array.IsEmpty() || other.IsEmpty())
+ {
+ return false;
+ }
+
+ if (array!.Length != other!.Length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ if (!array[i].Equals(other[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// 合并两个数组
+ ///
+ public static T[] Concat(this T[]? array, T[]? other)
+ {
+ if (array.IsEmpty())
+ {
+ return other ?? Array.Empty();
+ }
+
+ if (other.IsEmpty())
+ {
+ return array;
+ }
+
+ T[] result = new T[array.Length + other.Length];
+ array.CopyTo(result, 0);
+ other.CopyTo(result, array.Length);
+
+ return result;
+ }
+
+ ///
+ /// 随机打乱数组顺序(Fisher-Yates 洗牌算法)
+ ///
+ public static T[]? Shuffle(this T[]? array)
+ {
+ if (array == null || array.Length < 2)
+ return array;
+
+ var random = new Random();
+ var result = (T[])array.Clone();
+
+ for (int i = result.Length - 1; i > 0; i--)
+ {
+ int j = random.Next(i + 1);
+ (result[i], result[j]) = (result[j], result[i]);
+ }
+
+ return result;
+ }
+
+ ///
+ /// 将数组分割成指定大小的块
+ ///
+ /// 原始数组
+ /// 每块的大小
+ public static IEnumerable Chunk(this T[]? array, int chunkSize)
+ {
+ if (array == null)
+ yield break;
+
+ if (chunkSize <= 0)
+ throw new ArgumentException("chunkSize must be greater than 0", nameof(chunkSize));
+
+ for (int i = 0; i < array.Length; i += chunkSize)
+ {
+ int remaining = array.Length - i;
+ int size = Math.Min(chunkSize, remaining);
+ var chunk = new T[size];
+ Array.Copy(array, i, chunk, 0, size);
+ yield return chunk;
+ }
+ }
+
+ public static T[]? Distinct(this T[]? array)
+ {
+ if (array == null)
+ return null;
+
+ return array.Distinct().ToArray();
+ }
+
+ ///
+ /// 按指定键清除数组中的重复元素
+ ///
+ public static T[]? DistinctBy(this T[]? array, Func keySelector)
+ {
+ if (array == null)
+ return null;
+
+ return array.GroupBy(keySelector).Select(g => g.First()).ToArray();
+ }
+
+ ///
+ /// 将数组元素拼接成字符串(支持格式化)
+ ///
+ /// 数组
+ /// 分隔符
+ /// 格式化字符串
+ public static string JoinFormat(this T[]? array, string separator, string format)
+ {
+ if (array == null || array.Length == 0)
+ return string.Empty;
+
+ var formatted = array.Select(item => string.Format(format, item));
+ return string.Join(separator, formatted);
+ }
+
+ #endregion
+
+ #region 数组查找
+
+
+ ///
+ /// 查找数组中满足条件的所有元素的索引
+ ///
+ public static int[] FindAllIndexes(this T[]? array, Func predicate)
+ {
+ if (array == null)
+ return Array.Empty();
+
+ var indexes = new List();
+ for (int i = 0; i < array.Length; i++)
+ {
+ if (predicate(array[i]))
+ {
+ indexes.Add(i);
+ }
+ }
+ return indexes.ToArray();
+ }
+
+ ///
+ /// 判断数组是否包含指定元素(使用自定义比较器)
+ ///
+ public static bool Contains(this T[]? array, T value, IEqualityComparer comparer)
+ {
+ if (array == null)
+ return false;
+
+ return Array.Exists(array, item => comparer.Equals(item, value));
+ }
+
+ #endregion
+
+ #region 数组转换
+
+
+ ///
+ /// 将二维数组展平为一维数组
+ ///
+ public static T[]? Flatten(this T[,]? array)
+ {
+ if (array == null)
+ return null;
+
+ int width = array.GetLength(0);
+ int height = array.GetLength(1);
+ var result = new T[width * height];
+
+ int index = 0;
+ for (int i = 0; i < width; i++)
+ {
+ for (int j = 0; j < height; j++)
+ {
+ result[index++] = array[i, j];
+ }
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ #region 数组切片
+
+ ///
+ /// 获取数组中指定范围的元素
+ ///
+ /// 数组
+ /// 起始索引
+ /// 长度
+ public static T[]? Slice(this T[]? array, int startIndex, int length)
+ {
+ if (array == null)
+ return null;
+
+ if (startIndex < 0 || startIndex >= array.Length)
+ throw new ArgumentOutOfRangeException(nameof(startIndex));
+
+ if (length < 0 || startIndex + length > array.Length)
+ throw new ArgumentOutOfRangeException(nameof(length));
+
+ var result = new T[length];
+ Array.Copy(array, startIndex, result, 0, length);
+ return result;
+ }
+
+ ///
+ /// 获取数组从指定索引开始到末尾的元素
+ ///
+ public static T[]? Slice(this T[]? array, int startIndex)
+ {
+ if (array == null)
+ return null;
+
+ if (startIndex < 0)
+ startIndex = 0;
+
+ if (startIndex >= array.Length)
+ return Array.Empty();
+
+ int length = array.Length - startIndex;
+ return Slice(array, startIndex, length);
+ }
+
+ #endregion
+
+ #region 数组合并
+
+ ///
+ /// 合并多个数组
+ ///
+ public static T[] Merge(params T[][]? arrays)
+ {
+ if (arrays == null || arrays.Length == 0)
+ return Array.Empty();
+
+ int totalLength = 0;
+ foreach (var array in arrays)
+ {
+ if (array != null)
+ totalLength += array.Length;
+ }
+
+ var result = new T[totalLength];
+ int offset = 0;
+
+ foreach (var array in arrays)
+ {
+ if (array != null && array.Length > 0)
+ {
+ Array.Copy(array, 0, result, offset, array.Length);
+ offset += array.Length;
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// 在数组开头添加元素
+ ///
+ public static T[] Prepend(this T[]? array, params T[]? items)
+ {
+ if (array == null)
+ return items ?? Array.Empty();
+
+ if (items == null || items.Length == 0)
+ return array;
+
+ var result = new T[array.Length + items.Length];
+ Array.Copy(items, 0, result, 0, items.Length);
+ Array.Copy(array, 0, result, items.Length, array.Length);
+ return result;
+ }
+
+ ///
+ /// 在数组末尾添加元素
+ ///
+ public static T[] Append(this T[]? array, params T[]? items)
+ {
+ if (array == null)
+ return items ?? Array.Empty();
+
+ if (items == null || items.Length == 0)
+ return array;
+
+ var result = new T[array.Length + items.Length];
+ Array.Copy(array, 0, result, 0, array.Length);
+ Array.Copy(items, 0, result, array.Length, items.Length);
+ return result;
+ }
+
+ #endregion
+
+ #region 数组遍历
+
+
+ ///
+ /// 遍历数组并对每个元素及其索引执行指定操作
+ ///
+ public static void ForEach(this T[]? array, Action action)
+ {
+ if (array == null || action == null)
+ return;
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ action(array[i], i);
+ }
+ }
+
+ #endregion
+
+ #region 数组统计
+
+
+ #endregion
+ }
+}
diff --git a/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs b/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs
index e3f7900..b8620c0 100644
--- a/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs
+++ b/EasyTool.Core/CollectionsCategory/DictionaryExtension.cs
@@ -1,9 +1,9 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
-namespace EasyTool.Extension
+namespace EasyTool.CollectionsCategory
{
public static class DictionaryExtension
{
@@ -14,7 +14,7 @@ public static class DictionaryExtension
/// 要获取值的键
/// 如果字典中不存在该键,则返回的默认值
/// 指定键的值,如果字典中不存在该键,则返回默认值
- public static TValue GetValueOrDefault(this IDictionary dictionary, TKey key, TValue defaultValue = default)
+ public static TValue? GetValueOrDefault(this IDictionary dictionary, TKey key, TValue? defaultValue = default)
{
if (dictionary.TryGetValue(key, out TValue value))
{
@@ -42,25 +42,6 @@ public static void AddRange(this IDictionary destina
}
}
- ///
- /// 返回字典中键的集合
- ///
- /// 要获取键的字典
- /// 字典中所有键的集合
- public static IEnumerable GetKeys(this IDictionary dictionary)
- {
- return dictionary.Keys;
- }
-
- ///
- /// 返回字典中值的集合
- ///
- /// 要获取值的字典
- /// 字典中所有值的集合
- public static IEnumerable GetValues(this IDictionary dictionary)
- {
- return dictionary.Values;
- }
///
/// 从字典中删除指定的键
diff --git a/EasyTool.Core/CollectionsCategory/IEnumerableExtensions.cs b/EasyTool.Core/CollectionsCategory/IEnumerableExtensions.cs
new file mode 100644
index 0000000..34a7875
--- /dev/null
+++ b/EasyTool.Core/CollectionsCategory/IEnumerableExtensions.cs
@@ -0,0 +1,471 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Text;
+
+namespace EasyTool.CollectionsCategory
+{
+ ///
+ /// IEnumerable 通用扩展方法
+ ///
+ public static class IEnumerableExtensions
+ {
+ #region 空值处理
+
+ ///
+ /// 对 List 等集合 Foreach 的时候不用在上层判空,直接加上这个就好
+ ///
+ public static IEnumerable CheckNull(this IEnumerable values)
+ {
+ return values is null ? new List(0) : values;
+ }
+
+ ///
+ /// 判断集合是否为空或 null
+ ///
+ public static bool IsNullOrEmpty(this IEnumerable source)
+ {
+ return source == null || !source.Any();
+ }
+
+ ///
+ /// 判断集合是否非空
+ ///
+ public static bool IsNotEmpty(this IEnumerable source)
+ {
+ return source != null && source.Any();
+ }
+
+ #endregion
+
+ #region 遍历操作
+
+ ///
+ /// 遍历集合并对每个元素执行指定操作
+ ///
+ public static void ForEach(this IEnumerable source, Action action)
+ {
+ if (source == null || action == null)
+ return;
+
+ foreach (var item in source)
+ {
+ action(item);
+ }
+ }
+
+ ///
+ /// 遍历集合并对每个元素及其索引执行指定操作
+ ///
+ public static void ForEach(this IEnumerable source, Action action)
+ {
+ if (source == null || action == null)
+ return;
+
+ int index = 0;
+ foreach (var item in source)
+ {
+ action(item, index++);
+ }
+ }
+
+ #endregion
+
+ #region 集合运算
+
+ ///
+ /// 求集合的笛卡尔积
+ ///
+ public static IEnumerable> Cartesian(this IEnumerable> sequences)
+ {
+ IEnumerable> tempProduct = new[] { Enumerable.Empty() };
+ return sequences.Aggregate(tempProduct,
+ (accumulator, sequence) =>
+ from accseq in accumulator
+ from item in sequence
+ select accseq.Concat(new[] { item
+ }));
+ }
+
+ ///
+ /// 按指定键去重
+ ///
+ public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector)
+ {
+ if (source == null)
+ yield break;
+
+ var seenKeys = new HashSet();
+ foreach (var item in source)
+ {
+ if (seenKeys.Add(keySelector(item)))
+ {
+ yield return item;
+ }
+ }
+ }
+
+ ///
+ /// 批量处理集合
+ ///
+ /// 每批的大小
+ public static IEnumerable> Batch(this IEnumerable source, int batchSize)
+ {
+ if (source == null)
+ yield break;
+
+ if (batchSize <= 0)
+ throw new ArgumentException("batchSize must be greater than 0", nameof(batchSize));
+
+ var batch = new List(batchSize);
+ foreach (var item in source)
+ {
+ batch.Add(item);
+ if (batch.Count == batchSize)
+ {
+ yield return batch;
+ batch = new List(batchSize);
+ }
+ }
+
+ if (batch.Count > 0)
+ {
+ yield return batch;
+ }
+ }
+
+ #endregion
+
+ #region 转换操作
+
+ ///
+ /// 将集合转换为 DataTable
+ ///
+ public static DataTable ToDataTable(this IEnumerable source)
+ {
+ var table = new DataTable(typeof(T).Name);
+
+ var properties = typeof(T).GetProperties();
+ foreach (var prop in properties)
+ {
+ table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
+ }
+
+ foreach (var item in source)
+ {
+ var row = table.NewRow();
+ foreach (var prop in properties)
+ {
+ row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
+ }
+ table.Rows.Add(row);
+ }
+
+ return table;
+ }
+
+ ///
+ /// 将集合转换为 HashSet
+ ///
+ public static HashSet ToHashSet(this IEnumerable source)
+ {
+ if (source == null)
+ return new HashSet();
+
+ return new HashSet(source);
+ }
+
+ ///
+ /// 将集合转换为 Queue
+ ///
+ public static Queue ToQueue(this IEnumerable source)
+ {
+ if (source == null)
+ return new Queue();
+
+ return new Queue(source);
+ }
+
+ ///
+ /// 将集合转换为 Stack
+ ///
+ public static Stack ToStack(this IEnumerable source)
+ {
+ if (source == null)
+ return new Stack();
+
+ return new Stack(source);
+ }
+
+ ///
+ /// 将集合转换为 LinkedList
+ ///
+ public static LinkedList ToLinkedList(this IEnumerable source)
+ {
+ if (source == null)
+ return new LinkedList();
+
+ return new LinkedList(source);
+ }
+
+ #endregion
+
+ #region 连接操作
+
+ ///
+ /// 将集合元素连接成字符串
+ ///
+ /// 分隔符
+ public static string JoinAsString(this IEnumerable source, string separator = ",")
+ {
+ if (source == null)
+ return string.Empty;
+
+ return string.Join(separator, source);
+ }
+
+ ///
+ /// 将集合元素连接成字符串(使用格式化)
+ ///
+ /// 分隔符
+ /// 格式化字符串
+ public static string JoinAsString(this IEnumerable source, string separator, string format)
+ {
+ if (source == null)
+ return string.Empty;
+
+ return string.Join(separator, source.Select(item => string.Format(format, item)));
+ }
+
+ #endregion
+
+ #region 分页操作
+
+ ///
+ /// 分页
+ ///
+ /// 页码(从1开始)
+ /// 每页大小
+ public static IEnumerable Page(this IEnumerable source, int pageIndex, int pageSize)
+ {
+ if (source == null || pageIndex < 1 || pageSize < 1)
+ yield break;
+
+ int skip = (pageIndex - 1) * pageSize;
+ foreach (var item in source.Skip(skip).Take(pageSize))
+ {
+ yield return item;
+ }
+ }
+
+ #endregion
+
+ #region 随机操作
+
+ ///
+ /// 随机排序
+ ///
+ public static IEnumerable Shuffle(this IEnumerable source)
+ {
+ if (source == null)
+ yield break;
+
+ var random = new Random();
+ var list = source.ToList();
+
+ for (int i = list.Count - 1; i > 0; i--)
+ {
+ int j = random.Next(i + 1);
+ (list[i], list[j]) = (list[j], list[i]);
+ }
+
+ foreach (var item in list)
+ {
+ yield return item;
+ }
+ }
+
+ ///
+ /// 随机获取一个元素
+ ///
+ public static T Random(this IEnumerable source)
+ {
+ if (source == null)
+ return default;
+
+ var list = source.ToList();
+ if (list.Count == 0)
+ return default;
+
+ var random = new Random();
+ return list[random.Next(list.Count)];
+ }
+
+ ///
+ /// 随机获取指定数量的元素
+ ///
+ public static IEnumerable RandomTake(this IEnumerable source, int count)
+ {
+ if (source == null || count <= 0)
+ yield break;
+
+ var list = source.ToList();
+ if (list.Count == 0)
+ yield break;
+
+ count = Math.Min(count, list.Count);
+ var random = new Random();
+ var selected = new HashSet();
+
+ while (selected.Count < count)
+ {
+ selected.Add(random.Next(list.Count));
+ }
+
+ foreach (var index in selected)
+ {
+ yield return list[index];
+ }
+ }
+
+ #endregion
+
+ #region 条件操作
+
+ ///
+ /// 根据条件执行不同的操作
+ ///
+ public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate)
+ {
+ if (source == null)
+ yield break;
+
+ if (condition)
+ {
+ foreach (var item in source.Where(predicate))
+ {
+ yield return item;
+ }
+ }
+ else
+ {
+ foreach (var item in source)
+ {
+ yield return item;
+ }
+ }
+ }
+
+ ///
+ /// 如果集合为空则返回默认集合
+ ///
+ public static IEnumerable IfEmpty(this IEnumerable source, IEnumerable defaultValue)
+ {
+ if (source == null || !source.Any())
+ return defaultValue ?? Enumerable.Empty();
+
+ return source;
+ }
+
+ #endregion
+
+ #region 统计操作
+
+ ///
+ /// 统计满足条件的元素数量
+ ///
+ public static int CountEx(this IEnumerable source, Func predicate)
+ {
+ if (source == null)
+ return 0;
+
+ return source.Count(predicate);
+ }
+
+ #endregion
+
+ #region 索引操作
+
+ ///
+ /// 获取指定索引处的元素
+ ///
+ public static T ElementAtOrDefault(this IEnumerable source, int index, T defaultValue)
+ {
+ if (source == null || index < 0)
+ return defaultValue;
+
+ int i = 0;
+ foreach (var item in source)
+ {
+ if (i == index)
+ return item;
+ i++;
+ }
+
+ return defaultValue;
+ }
+
+ ///
+ /// 获取第一个元素,如果集合为空则返回默认值
+ ///
+ public static T FirstOrValue(this IEnumerable source, T defaultValue)
+ {
+ if (source == null)
+ return defaultValue;
+
+ foreach (var item in source)
+ {
+ return item;
+ }
+
+ return defaultValue;
+ }
+
+ ///
+ /// 获取最后一个元素,如果集合为空则返回默认值
+ ///
+ public static T LastOrValue(this IEnumerable source, T defaultValue)
+ {
+ if (source == null)
+ return defaultValue;
+
+ var last = defaultValue;
+ var hasElement = false;
+
+ foreach (var item in source)
+ {
+ last = item;
+ hasElement = true;
+ }
+
+ return hasElement ? last : defaultValue;
+ }
+
+ #endregion
+
+ #region 集合合并
+
+ ///
+ /// 合并多个集合
+ ///
+ public static IEnumerable Merge(params IEnumerable[] sources)
+ {
+ if (sources == null || sources.Length == 0)
+ yield break;
+
+ foreach (var source in sources)
+ {
+ if (source != null)
+ {
+ foreach (var item in source)
+ {
+ yield return item;
+ }
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/EasyTool.Core/CollectionsCategory/IteratorUtil.cs b/EasyTool.Core/CollectionsCategory/IteratorUtil.cs
deleted file mode 100644
index b707296..0000000
--- a/EasyTool.Core/CollectionsCategory/IteratorUtil.cs
+++ /dev/null
@@ -1,186 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace EasyTool
-{
- //TODO:疑问,这些功能Linq支持吗?
- ///
- /// 迭代器工具类
- ///
- public static class IteratorUtil
- {
- ///
- /// 将一个数组转换为一个迭代器
- ///
- public static IEnumerable AsIterator(this T[] array)
- {
- foreach (var item in array)
- {
- yield return item;
- }
- }
-
- ///
- /// 过滤掉一个迭代器中不符合条件的元素
- ///
- public static IEnumerable Filter(this IEnumerable source, Func predicate)
- {
- foreach (var item in source)
- {
- if (predicate(item))
- {
- yield return item;
- }
- }
- }
-
- ///
- /// 对一个迭代器中的每个元素进行转换
- ///
- public static IEnumerable Map(this IEnumerable source, Func selector)
- {
- foreach (var item in source)
- {
- yield return selector(item);
- }
- }
-
- ///
- /// 从一个迭代器中取出前 n 个元素
- ///
- public static IEnumerable Take(this IEnumerable source, int count)
- {
- foreach (var item in source)
- {
- if (count-- > 0)
- {
- yield return item;
- }
- else
- {
- break;
- }
- }
- }
-
- ///
- /// 跳过一个迭代器中的前 n 个元素
- ///
- public static IEnumerable Skip(this IEnumerable source, int count)
- {
- foreach (var item in source)
- {
- if (count-- > 0)
- {
- continue;
- }
- else
- {
- yield return item;
- }
- }
- }
-
- ///
- /// 将一个迭代器的元素分组
- ///
- public static IEnumerable> GroupBy(this IEnumerable source, Func keySelector)
- {
- return source.GroupBy(keySelector, x => x);
- }
-
- ///
- /// 将一个迭代器的元素按照指定的方式分组
- ///
- public static IEnumerable> GroupBy(this IEnumerable source, Func keySelector, Func elementSelector)
- {
- var dictionary = new Dictionary>();
- foreach (var item in source)
- {
- var key = keySelector(item);
- var element = elementSelector(item);
- if (!dictionary.ContainsKey(key))
- {
- dictionary[key] = new List();
- }
- dictionary[key].Add(element);
- }
- foreach (var group in dictionary)
- {
- yield return new Grouping(group.Key, group.Value);
- }
- }
-
- private class Grouping : IGrouping
- {
- private readonly List _elements;
-
- public Grouping(TKey key, List elements)
- {
- Key = key;
- _elements = elements;
- }
- public TKey Key { get; }
-
- public IEnumerator GetEnumerator()
- {
- return _elements.GetEnumerator();
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
-
- ///
- /// 对一个迭代器中的元素进行排序
- ///
- public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector)
- {
- return source.OrderBy(keySelector, Comparer.Default);
- }
-
- ///
- /// 对一个迭代器中的元素按照指定的方式进行排序
- ///
- public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, IComparer comparer)
- {
- return source.OrderBy(keySelector, comparer, false);
- }
-
- ///
- /// 对一个迭代器中的元素按照指定的方式进行排序,并指定排序方向
- ///
- public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, IComparer comparer, bool descending)
- {
- return descending ? source.OrderByDescending(keySelector, comparer) : source.OrderBy(keySelector, comparer);
- }
-
- ///
- /// 对一个迭代器中的元素进行倒序排序
- ///
- public static IOrderedEnumerable OrderByDescending(this IEnumerable source, Func keySelector)
- {
- return source.OrderByDescending(keySelector, Comparer.Default);
- }
-
- ///
- /// 对一个迭代器中的元素按照指定的方式进行倒序排序
- ///
- public static IOrderedEnumerable OrderByDescending(this IEnumerable source, Func keySelector, IComparer comparer)
- {
- return source.OrderByDescending(keySelector, comparer, false);
- }
-
- ///
- /// 对一个迭代器中的元素按照指定的方式进行倒序排序,并指定排序方向
- ///
- public static IOrderedEnumerable OrderByDescending(this IEnumerable source, Func keySelector, IComparer comparer, bool descending)
- {
- return descending ? source.OrderBy(keySelector, comparer) : source.OrderByDescending(keySelector, comparer);
- }
- }
-}
diff --git a/EasyTool.Core/CollectionsCategory/LinkedListExtension.cs b/EasyTool.Core/CollectionsCategory/LinkedListExtension.cs
deleted file mode 100644
index 3a1487a..0000000
--- a/EasyTool.Core/CollectionsCategory/LinkedListExtension.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace EasyTool.Extension
-{
-
- ///
- /// 双向链表工具类
- ///
- public static class LinkedListExtension
- {
- ///
- /// 将双向链表中的某个节点移动到链表的结尾处。
- ///
- /// 双向链表元素类型
- /// 双向链表
- /// 要移动的节点
- public static void MoveLast(this LinkedList list, LinkedListNode node) => LinkedListUtil.MoveLast(list,node);
-
- ///
- /// 将双向链表中移动到最前方
- ///
- /// 双向链表元素类型
- /// 双向链表
- /// 要移动的节点
- public static void MoveFirst(this LinkedList list, LinkedListNode node) => LinkedListUtil.MoveFirst(list,node);
-
- }
-}
diff --git a/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs b/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs
index d3893db..5729fea 100644
--- a/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs
+++ b/EasyTool.Core/CollectionsCategory/LinkedListUtil.cs
@@ -2,19 +2,21 @@
using System.Collections.Generic;
using System.Text;
-namespace EasyTool
+namespace EasyTool.CollectionsCategory
{
///
/// 双向链表工具类
///
- public class LinkedListUtil
+ public static class LinkedListUtil
{
///
/// 将指定元素添加到双向链表的结尾处。
+ /// [Obsolete("请直接使用 list.AddLast(item)")]
///
/// 双向链表元素类型
/// 双向链表
/// 要添加的元素
+ [Obsolete("请直接使用 list.AddLast(item)", false)]
public static void AddLast(LinkedList list, T item)
{
list.AddLast(item);
@@ -22,10 +24,12 @@ public static void AddLast(LinkedList list, T item)
///
/// 将指定元素添加到双向链表的开头处。
+ /// [Obsolete("请直接使用 list.AddFirst(item)")]
///
/// 双向链表元素类型
/// 双向链表
/// 要添加的元素
+ [Obsolete("请直接使用 list.AddFirst(item)", false)]
public static void AddFirst(LinkedList list, T item)
{
list.AddFirst(item);
@@ -33,12 +37,14 @@ public static void AddFirst(LinkedList list, T item)
///
/// 将指定元素插入到双向链表中的指定位置之前。
+ /// [Obsolete("请直接使用 list.AddBefore(node, item)")]
///
/// 双向链表元素类型
/// 双向链表
/// 要在其前面插入新元素的节点
/// 要添加的元素
/// 新节点
+ [Obsolete("请直接使用 list.AddBefore(node, item)", false)]
public static LinkedListNode AddBefore(LinkedList list, LinkedListNode node, T item)
{
return list.AddBefore(node, item);
@@ -46,12 +52,14 @@ public static LinkedListNode AddBefore(LinkedList list, LinkedListNode<
///