From 46894bd787a2a468bcd056f102ec4eb7112d94e2 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Mon, 25 Nov 2024 13:39:48 +0400 Subject: [PATCH 01/25] init: back --- back/.gitignore | 484 ++++++++++++++++++ back/Api.sln | 40 ++ back/Contracts/Contracts.csproj | 9 + back/Controllers/Controllers.csproj | 13 + back/Controllers/Controllers.http | 6 + back/Controllers/Program.cs | 25 + .../Properties/launchSettings.json | 41 ++ back/Controllers/appsettings.Development.json | 8 + back/Controllers/appsettings.json | 9 + back/Infrastructure/Infrastructure.csproj | 9 + back/Services/Services.csproj | 9 + 11 files changed, 653 insertions(+) create mode 100644 back/.gitignore create mode 100644 back/Api.sln create mode 100644 back/Contracts/Contracts.csproj create mode 100644 back/Controllers/Controllers.csproj create mode 100644 back/Controllers/Controllers.http create mode 100644 back/Controllers/Program.cs create mode 100644 back/Controllers/Properties/launchSettings.json create mode 100644 back/Controllers/appsettings.Development.json create mode 100644 back/Controllers/appsettings.json create mode 100644 back/Infrastructure/Infrastructure.csproj create mode 100644 back/Services/Services.csproj diff --git a/back/.gitignore b/back/.gitignore new file mode 100644 index 0000000..104b544 --- /dev/null +++ b/back/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/back/Api.sln b/back/Api.sln new file mode 100644 index 0000000..c0cdbbb --- /dev/null +++ b/back/Api.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Controllers", "Controllers\Controllers.csproj", "{BEA05282-6DE5-4D67-983A-EF13AAF8EECE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services", "Services\Services.csproj", "{B937E273-1D6E-42DF-BD34-14DA5E71FC71}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contracts", "Contracts\Contracts.csproj", "{B9246DBE-67B0-4B0F-8648-E9ED2EB7172C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{A35121D4-7D41-4266-8DA4-87135E8ABF89}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BEA05282-6DE5-4D67-983A-EF13AAF8EECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BEA05282-6DE5-4D67-983A-EF13AAF8EECE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BEA05282-6DE5-4D67-983A-EF13AAF8EECE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BEA05282-6DE5-4D67-983A-EF13AAF8EECE}.Release|Any CPU.Build.0 = Release|Any CPU + {B937E273-1D6E-42DF-BD34-14DA5E71FC71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B937E273-1D6E-42DF-BD34-14DA5E71FC71}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B937E273-1D6E-42DF-BD34-14DA5E71FC71}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B937E273-1D6E-42DF-BD34-14DA5E71FC71}.Release|Any CPU.Build.0 = Release|Any CPU + {B9246DBE-67B0-4B0F-8648-E9ED2EB7172C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9246DBE-67B0-4B0F-8648-E9ED2EB7172C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9246DBE-67B0-4B0F-8648-E9ED2EB7172C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9246DBE-67B0-4B0F-8648-E9ED2EB7172C}.Release|Any CPU.Build.0 = Release|Any CPU + {A35121D4-7D41-4266-8DA4-87135E8ABF89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A35121D4-7D41-4266-8DA4-87135E8ABF89}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A35121D4-7D41-4266-8DA4-87135E8ABF89}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A35121D4-7D41-4266-8DA4-87135E8ABF89}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/back/Contracts/Contracts.csproj b/back/Contracts/Contracts.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/back/Contracts/Contracts.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/back/Controllers/Controllers.csproj b/back/Controllers/Controllers.csproj new file mode 100644 index 0000000..9daa180 --- /dev/null +++ b/back/Controllers/Controllers.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/back/Controllers/Controllers.http b/back/Controllers/Controllers.http new file mode 100644 index 0000000..01f7fc0 --- /dev/null +++ b/back/Controllers/Controllers.http @@ -0,0 +1,6 @@ +@Controllers_HostAddress = http://localhost:5125 + +GET {{Controllers_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/back/Controllers/Program.cs b/back/Controllers/Program.cs new file mode 100644 index 0000000..48863a6 --- /dev/null +++ b/back/Controllers/Program.cs @@ -0,0 +1,25 @@ +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/back/Controllers/Properties/launchSettings.json b/back/Controllers/Properties/launchSettings.json new file mode 100644 index 0000000..3683563 --- /dev/null +++ b/back/Controllers/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:47535", + "sslPort": 44340 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5125", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7189;http://localhost:5125", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/back/Controllers/appsettings.Development.json b/back/Controllers/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/back/Controllers/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/back/Controllers/appsettings.json b/back/Controllers/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/back/Controllers/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/back/Infrastructure/Infrastructure.csproj b/back/Infrastructure/Infrastructure.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/back/Infrastructure/Infrastructure.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/back/Services/Services.csproj b/back/Services/Services.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/back/Services/Services.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + -- 2.25.1 From a7529c4e5eff6f7d51fc0b94100b702a74f2ac9e Mon Sep 17 00:00:00 2001 From: mfnefd Date: Mon, 25 Nov 2024 13:56:05 +0400 Subject: [PATCH 02/25] init: front --- front/.gitignore | 24 + front/index.html | 12 + front/package-lock.json | 1292 +++++++++++++++++ front/package.json | 20 + front/src/App.vue | 9 + front/src/main.ts | 5 + front/src/style.css | 0 front/src/vite-env.d.ts | 1 + front/tsconfig.app.json | 26 + front/tsconfig.json | 7 + front/tsconfig.node.json | 24 + front/vite.config.ts | 7 + ....timestamp-1732528329064-33d75fbae33f5.mjs | 10 + 13 files changed, 1437 insertions(+) create mode 100644 front/.gitignore create mode 100644 front/index.html create mode 100644 front/package-lock.json create mode 100644 front/package.json create mode 100644 front/src/App.vue create mode 100644 front/src/main.ts create mode 100644 front/src/style.css create mode 100644 front/src/vite-env.d.ts create mode 100644 front/tsconfig.app.json create mode 100644 front/tsconfig.json create mode 100644 front/tsconfig.node.json create mode 100644 front/vite.config.ts create mode 100644 front/vite.config.ts.timestamp-1732528329064-33d75fbae33f5.mjs diff --git a/front/.gitignore b/front/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/front/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/front/index.html b/front/index.html new file mode 100644 index 0000000..28e68e6 --- /dev/null +++ b/front/index.html @@ -0,0 +1,12 @@ + + + + + + ДомБюдж + + +
+ + + diff --git a/front/package-lock.json b/front/package-lock.json new file mode 100644 index 0000000..7fb419c --- /dev/null +++ b/front/package-lock.json @@ -0,0 +1,1292 @@ +{ + "name": "dombudg", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dombudg", + "version": "0.0.0", + "dependencies": { + "vue": "^3.5.12" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.1.4", + "typescript": "~5.6.2", + "vite": "^5.4.10", + "vue-tsc": "^2.1.8" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz", + "integrity": "sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz", + "integrity": "sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz", + "integrity": "sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz", + "integrity": "sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz", + "integrity": "sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz", + "integrity": "sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz", + "integrity": "sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz", + "integrity": "sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz", + "integrity": "sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz", + "integrity": "sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz", + "integrity": "sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz", + "integrity": "sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz", + "integrity": "sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz", + "integrity": "sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz", + "integrity": "sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz", + "integrity": "sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz", + "integrity": "sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz", + "integrity": "sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.0.tgz", + "integrity": "sha512-7n7KdUEtx/7Yl7I/WVAMZ1bEb0eVvXF3ummWTeLcs/9gvo9pJhuLdouSXGjdZ/MKD1acf1I272+X0RMua4/R3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.10.tgz", + "integrity": "sha512-hG3Z13+nJmGaT+fnQzAkS0hjJRa2FCeqZt6Bd+oGNhUkQ+mTFsDETg5rqUTxyzIh5pSOGY7FHCWUS8G82AzLCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.10" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.10.tgz", + "integrity": "sha512-OCV+b5ihV0RF3A7vEvNyHPi4G4kFa6ukPmyVocmqm5QzOd8r5yAtiNvaPEjl8dNvgC/lj4JPryeeHLdXd62rWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.10.tgz", + "integrity": "sha512-F8ZtBMhSXyYKuBfGpYwqA5rsONnOwAVvjyE7KPYJ7wgZqo2roASqNWUnianOomJX5u1cxeRooHV59N0PhvEOgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.10", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/language-core": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.10.tgz", + "integrity": "sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~2.4.8", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^0.2.0", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "vue": "3.5.13" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", + "license": "MIT" + }, + "node_modules/alien-signals": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.2.tgz", + "integrity": "sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/magic-string": { + "version": "0.30.13", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.13.tgz", + "integrity": "sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.4.tgz", + "integrity": "sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.27.4", + "@rollup/rollup-android-arm64": "4.27.4", + "@rollup/rollup-darwin-arm64": "4.27.4", + "@rollup/rollup-darwin-x64": "4.27.4", + "@rollup/rollup-freebsd-arm64": "4.27.4", + "@rollup/rollup-freebsd-x64": "4.27.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.27.4", + "@rollup/rollup-linux-arm-musleabihf": "4.27.4", + "@rollup/rollup-linux-arm64-gnu": "4.27.4", + "@rollup/rollup-linux-arm64-musl": "4.27.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.27.4", + "@rollup/rollup-linux-riscv64-gnu": "4.27.4", + "@rollup/rollup-linux-s390x-gnu": "4.27.4", + "@rollup/rollup-linux-x64-gnu": "4.27.4", + "@rollup/rollup-linux-x64-musl": "4.27.4", + "@rollup/rollup-win32-arm64-msvc": "4.27.4", + "@rollup/rollup-win32-ia32-msvc": "4.27.4", + "@rollup/rollup-win32-x64-msvc": "4.27.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-tsc": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.10.tgz", + "integrity": "sha512-RBNSfaaRHcN5uqVqJSZh++Gy/YUzryuv9u1aFWhsammDJXNtUiJMNoJ747lZcQ68wUQFx6E73y4FY3D8E7FGMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "~2.4.8", + "@vue/language-core": "2.1.10", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + } + } +} diff --git a/front/package.json b/front/package.json new file mode 100644 index 0000000..a3857a9 --- /dev/null +++ b/front/package.json @@ -0,0 +1,20 @@ +{ + "name": "dombudg", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.5.12" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.1.4", + "typescript": "~5.6.2", + "vite": "^5.4.10", + "vue-tsc": "^2.1.8" + } +} diff --git a/front/src/App.vue b/front/src/App.vue new file mode 100644 index 0000000..bbb0db4 --- /dev/null +++ b/front/src/App.vue @@ -0,0 +1,9 @@ + + + + + diff --git a/front/src/main.ts b/front/src/main.ts new file mode 100644 index 0000000..2425c0f --- /dev/null +++ b/front/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import './style.css' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/front/src/style.css b/front/src/style.css new file mode 100644 index 0000000..e69de29 diff --git a/front/src/vite-env.d.ts b/front/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/front/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/front/tsconfig.app.json b/front/tsconfig.app.json new file mode 100644 index 0000000..cb88a5a --- /dev/null +++ b/front/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "preserve", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/front/tsconfig.json b/front/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/front/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/front/tsconfig.node.json b/front/tsconfig.node.json new file mode 100644 index 0000000..abcd7f0 --- /dev/null +++ b/front/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/front/vite.config.ts b/front/vite.config.ts new file mode 100644 index 0000000..bbcf80c --- /dev/null +++ b/front/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue()], +}) diff --git a/front/vite.config.ts.timestamp-1732528329064-33d75fbae33f5.mjs b/front/vite.config.ts.timestamp-1732528329064-33d75fbae33f5.mjs new file mode 100644 index 0000000..88c3d34 --- /dev/null +++ b/front/vite.config.ts.timestamp-1732528329064-33d75fbae33f5.mjs @@ -0,0 +1,10 @@ +// vite.config.ts +import { defineConfig } from "file:///C:/Users/MM-NKA/source/repos/domBudg/front/node_modules/vite/dist/node/index.js"; +import vue from "file:///C:/Users/MM-NKA/source/repos/domBudg/front/node_modules/@vitejs/plugin-vue/dist/index.mjs"; +var vite_config_default = defineConfig({ + plugins: [vue()] +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJDOlxcXFxVc2Vyc1xcXFxNTS1OS0FcXFxcc291cmNlXFxcXHJlcG9zXFxcXGRvbUJ1ZGdcXFxcZnJvbnRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkM6XFxcXFVzZXJzXFxcXE1NLU5LQVxcXFxzb3VyY2VcXFxccmVwb3NcXFxcZG9tQnVkZ1xcXFxmcm9udFxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vQzovVXNlcnMvTU0tTktBL3NvdXJjZS9yZXBvcy9kb21CdWRnL2Zyb250L3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcbmltcG9ydCB2dWUgZnJvbSAnQHZpdGVqcy9wbHVnaW4tdnVlJ1xuXG4vLyBodHRwczovL3ZpdGUuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHBsdWdpbnM6IFt2dWUoKV0sXG59KVxuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUE4VCxTQUFTLG9CQUFvQjtBQUMzVixPQUFPLFNBQVM7QUFHaEIsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDMUIsU0FBUyxDQUFDLElBQUksQ0FBQztBQUNqQixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo= -- 2.25.1 From 1e107f86ec81ecccc5bdd45dd7f0ec7edb2439e4 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Mon, 25 Nov 2024 16:05:52 +0400 Subject: [PATCH 03/25] =?UTF-8?q?add:=20=D0=9A=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BA=D1=82=D1=8B=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/DTOs/UserDTO.cs | 9 +++++++++ back/Contracts/Repositories/IUserRepo.cs | 12 ++++++++++++ back/Contracts/SearchModels/UserSearch.cs | 6 ++++++ back/Contracts/Services/IAuthService.cs | 12 ++++++++++++ back/Contracts/ViewModels/UserViewModel.cs | 8 ++++++++ 5 files changed, 47 insertions(+) create mode 100644 back/Contracts/DTOs/UserDTO.cs create mode 100644 back/Contracts/Repositories/IUserRepo.cs create mode 100644 back/Contracts/SearchModels/UserSearch.cs create mode 100644 back/Contracts/Services/IAuthService.cs create mode 100644 back/Contracts/ViewModels/UserViewModel.cs diff --git a/back/Contracts/DTOs/UserDTO.cs b/back/Contracts/DTOs/UserDTO.cs new file mode 100644 index 0000000..abf5f99 --- /dev/null +++ b/back/Contracts/DTOs/UserDTO.cs @@ -0,0 +1,9 @@ +namespace Contracts.DTO; + +public class UserDto +{ + public Guid? Id { get; set; } + public string? Username { get; set; } + public string? Password { get; set; } + public decimal? Balance { get; set; } +} \ No newline at end of file diff --git a/back/Contracts/Repositories/IUserRepo.cs b/back/Contracts/Repositories/IUserRepo.cs new file mode 100644 index 0000000..3785e5e --- /dev/null +++ b/back/Contracts/Repositories/IUserRepo.cs @@ -0,0 +1,12 @@ +using Contracts.DTO; +using Contracts.SearchModels; + +namespace Contracts.Repositories; + +public interface IUserRepo +{ + public Task Get(UserSearch search); + public Task Create(UserDto user); + public Task Update(UserDto user); + public Task Delete(UserSearch search); +} \ No newline at end of file diff --git a/back/Contracts/SearchModels/UserSearch.cs b/back/Contracts/SearchModels/UserSearch.cs new file mode 100644 index 0000000..7165750 --- /dev/null +++ b/back/Contracts/SearchModels/UserSearch.cs @@ -0,0 +1,6 @@ +namespace Contracts.SearchModels; + +public class UserSearch +{ + public Guid? Id { get; set; } +} \ No newline at end of file diff --git a/back/Contracts/Services/IAuthService.cs b/back/Contracts/Services/IAuthService.cs new file mode 100644 index 0000000..d6e5983 --- /dev/null +++ b/back/Contracts/Services/IAuthService.cs @@ -0,0 +1,12 @@ +using Contracts.DTO; +using Contracts.ViewModels; + +namespace Contracts.Services; + +public interface IAuthService +{ + public Task Login(); + public Task Register(UserDto user); + public Task UpdateUserData(UserDto user); + public Task Delete(Guid id); +} \ No newline at end of file diff --git a/back/Contracts/ViewModels/UserViewModel.cs b/back/Contracts/ViewModels/UserViewModel.cs new file mode 100644 index 0000000..9bf54dc --- /dev/null +++ b/back/Contracts/ViewModels/UserViewModel.cs @@ -0,0 +1,8 @@ +namespace Contracts.ViewModels; + +public class UserViewModel +{ + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public decimal Balance { get; set; } +} \ No newline at end of file -- 2.25.1 From bc7d0ff1568812d738beb2cc5bfde33c5dcdfc6e Mon Sep 17 00:00:00 2001 From: mfnefd Date: Mon, 25 Nov 2024 16:59:13 +0400 Subject: [PATCH 04/25] =?UTF-8?q?add:=20=D0=A1=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=D1=8B=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/DTOs/UserDTO.cs | 8 +-- back/Contracts/DTOs/UserLoginDTO.cs | 7 +++ back/Contracts/Mappers/UserMapper.cs | 16 ++++++ back/Contracts/Repositories/IUserRepo.cs | 2 +- back/Contracts/SearchModels/UserSearch.cs | 1 + back/Contracts/Services/IAuthService.cs | 5 +- back/Contracts/Services/IUserService.cs | 12 +++++ back/Services/Domain/AuthService.cs | 49 +++++++++++++++++++ back/Services/Domain/UserService.cs | 47 ++++++++++++++++++ back/Services/Services.csproj | 4 ++ .../Exceptions/AlreadyExistsException.cs | 9 ++++ .../Exceptions/UserNotFoundException.cs | 11 +++++ 12 files changed, 163 insertions(+), 8 deletions(-) create mode 100644 back/Contracts/DTOs/UserLoginDTO.cs create mode 100644 back/Contracts/Mappers/UserMapper.cs create mode 100644 back/Contracts/Services/IUserService.cs create mode 100644 back/Services/Domain/AuthService.cs create mode 100644 back/Services/Domain/UserService.cs create mode 100644 back/Services/Support/Exceptions/AlreadyExistsException.cs create mode 100644 back/Services/Support/Exceptions/UserNotFoundException.cs diff --git a/back/Contracts/DTOs/UserDTO.cs b/back/Contracts/DTOs/UserDTO.cs index abf5f99..e312313 100644 --- a/back/Contracts/DTOs/UserDTO.cs +++ b/back/Contracts/DTOs/UserDTO.cs @@ -2,8 +2,8 @@ namespace Contracts.DTO; public class UserDto { - public Guid? Id { get; set; } - public string? Username { get; set; } - public string? Password { get; set; } - public decimal? Balance { get; set; } + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + public decimal Balance { get; set; } } \ No newline at end of file diff --git a/back/Contracts/DTOs/UserLoginDTO.cs b/back/Contracts/DTOs/UserLoginDTO.cs new file mode 100644 index 0000000..73210e9 --- /dev/null +++ b/back/Contracts/DTOs/UserLoginDTO.cs @@ -0,0 +1,7 @@ +namespace Contracts.DTO; + +public class UserLoginDTO +{ + public string Name { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/back/Contracts/Mappers/UserMapper.cs b/back/Contracts/Mappers/UserMapper.cs new file mode 100644 index 0000000..a32afed --- /dev/null +++ b/back/Contracts/Mappers/UserMapper.cs @@ -0,0 +1,16 @@ +using Contracts.DTO; +using Contracts.ViewModels; + +namespace Contracts.Mappers; + +public static class UserMapper +{ + public static UserViewModel ToView(this UserDto user) + => new() + { + Id = user.Id, + Name = user.Name, + Balance = user.Balance + }; + +} \ No newline at end of file diff --git a/back/Contracts/Repositories/IUserRepo.cs b/back/Contracts/Repositories/IUserRepo.cs index 3785e5e..926450c 100644 --- a/back/Contracts/Repositories/IUserRepo.cs +++ b/back/Contracts/Repositories/IUserRepo.cs @@ -5,7 +5,7 @@ namespace Contracts.Repositories; public interface IUserRepo { - public Task Get(UserSearch search); + public Task Get(UserSearch search); public Task Create(UserDto user); public Task Update(UserDto user); public Task Delete(UserSearch search); diff --git a/back/Contracts/SearchModels/UserSearch.cs b/back/Contracts/SearchModels/UserSearch.cs index 7165750..7ea4178 100644 --- a/back/Contracts/SearchModels/UserSearch.cs +++ b/back/Contracts/SearchModels/UserSearch.cs @@ -3,4 +3,5 @@ namespace Contracts.SearchModels; public class UserSearch { public Guid? Id { get; set; } + public string? Name { get; set; } } \ No newline at end of file diff --git a/back/Contracts/Services/IAuthService.cs b/back/Contracts/Services/IAuthService.cs index d6e5983..03548f7 100644 --- a/back/Contracts/Services/IAuthService.cs +++ b/back/Contracts/Services/IAuthService.cs @@ -1,12 +1,11 @@ using Contracts.DTO; +using Contracts.SearchModels; using Contracts.ViewModels; namespace Contracts.Services; public interface IAuthService { - public Task Login(); + public Task Login(UserLoginDTO loginData); public Task Register(UserDto user); - public Task UpdateUserData(UserDto user); - public Task Delete(Guid id); } \ No newline at end of file diff --git a/back/Contracts/Services/IUserService.cs b/back/Contracts/Services/IUserService.cs new file mode 100644 index 0000000..736d3bf --- /dev/null +++ b/back/Contracts/Services/IUserService.cs @@ -0,0 +1,12 @@ +using Contracts.DTO; +using Contracts.SearchModels; +using Contracts.ViewModels; + +namespace Contracts.Services; + +public interface IUserService +{ + public Task GetDetails(UserSearch search); + public Task UpdateUserData(UserDto user); + public Task Delete(UserSearch search); +} \ No newline at end of file diff --git a/back/Services/Domain/AuthService.cs b/back/Services/Domain/AuthService.cs new file mode 100644 index 0000000..4d59a79 --- /dev/null +++ b/back/Services/Domain/AuthService.cs @@ -0,0 +1,49 @@ +using Contracts.DTO; +using Contracts.Mappers; +using Contracts.Repositories; +using Contracts.SearchModels; +using Contracts.Services; +using Contracts.ViewModels; +using Services.Support.Exceptions; + +namespace Services.Domain; + +public class AuthService : IAuthService +{ + private readonly IUserRepo _userRepo; + + public AuthService(IUserRepo userRepo) + { + _userRepo = userRepo; + } + + public async Task Login(UserLoginDTO loginData) + { + if (loginData == null || string.IsNullOrWhiteSpace(loginData.Name) + || string.IsNullOrWhiteSpace(loginData.Password)) + { + throw new ArgumentException("Неверные данные для входа"); + } + + var user = await _userRepo.Get(new UserSearch() { Name = loginData.Name }); + if (user == null) + { + throw new UserNotFoundException($"Пользователь {loginData.Name} не найден"); + } + + return user.ToView(); + } + + public async Task Register(UserDto user) + { + var existingUser = await _userRepo.Get(new UserSearch() { Name = user.Name }); + if (existingUser != null) + { + throw new AlreadyExistsException("Такой пользователь уже существует"); + } + + var createdUser = await _userRepo.Create(user); + + return createdUser.ToView(); + } +} diff --git a/back/Services/Domain/UserService.cs b/back/Services/Domain/UserService.cs new file mode 100644 index 0000000..e54904d --- /dev/null +++ b/back/Services/Domain/UserService.cs @@ -0,0 +1,47 @@ +using Contracts.DTO; +using Contracts.Mappers; +using Contracts.Repositories; +using Contracts.SearchModels; +using Contracts.Services; +using Contracts.ViewModels; +using Services.Support.Exceptions; + +namespace Services.Domain; + +public class UserService : IUserService +{ + private readonly IUserRepo _userRepo; + + public UserService(IUserRepo userRepo) + { + _userRepo = userRepo; + } + + public async Task Delete(UserSearch search) + { + var user = await _userRepo.Delete(search); + return user.ToView(); + } + + public async Task GetDetails(UserSearch search) + { + var user = await _userRepo.Get(search); + if (user == null) + { + throw new UserNotFoundException($"Пользователь {search.Name} не найден"); + } + return user.ToView(); + } + + public async Task UpdateUserData(UserDto user) + { + var existingUser = await _userRepo.Get(new UserSearch() { Name = user.Name }); + if (existingUser == null) + { + throw new UserNotFoundException($"Пользователь {user.Name} не найден"); + } + + var updatedUser = await _userRepo.Update(user); + return updatedUser.ToView(); + } +} diff --git a/back/Services/Services.csproj b/back/Services/Services.csproj index fa71b7a..3c22415 100644 --- a/back/Services/Services.csproj +++ b/back/Services/Services.csproj @@ -1,5 +1,9 @@  + + + + net8.0 enable diff --git a/back/Services/Support/Exceptions/AlreadyExistsException.cs b/back/Services/Support/Exceptions/AlreadyExistsException.cs new file mode 100644 index 0000000..8577218 --- /dev/null +++ b/back/Services/Support/Exceptions/AlreadyExistsException.cs @@ -0,0 +1,9 @@ +namespace Services.Support.Exceptions; + +public class AlreadyExistsException : Exception +{ + public AlreadyExistsException(string message) : base(message) { } + + public AlreadyExistsException(string message, Exception innerException) + : base(message, innerException) { } +} \ No newline at end of file diff --git a/back/Services/Support/Exceptions/UserNotFoundException.cs b/back/Services/Support/Exceptions/UserNotFoundException.cs new file mode 100644 index 0000000..df152a1 --- /dev/null +++ b/back/Services/Support/Exceptions/UserNotFoundException.cs @@ -0,0 +1,11 @@ +using Contracts.SearchModels; + +namespace Services.Support.Exceptions; + +public class UserNotFoundException : Exception +{ + public UserNotFoundException(string message) + : base(message) { } + public UserNotFoundException(string message, Exception innerException) + : base(message, innerException) { } +} \ No newline at end of file -- 2.25.1 From f7de7ab197491330dd6a9b03497735153ef4f63c Mon Sep 17 00:00:00 2001 From: mfnefd Date: Mon, 25 Nov 2024 17:51:16 +0400 Subject: [PATCH 05/25] =?UTF-8?q?add:=20=D0=A0=D0=B5=D0=BF=D0=BE=D0=B7?= =?UTF-8?q?=D0=B8=D1=82=D0=BE=D1=80=D0=B8=D0=B9=20=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/Repositories/IUserRepo.cs | 4 +- back/Infrastructure/DatabaseContext.cs | 15 ++++ back/Infrastructure/DbContextFactory.cs | 20 ++++++ back/Infrastructure/Infrastructure.csproj | 12 ++++ back/Infrastructure/Models/User.cs | 25 +++++++ back/Infrastructure/Repositories/UserRepo.cs | 72 +++++++++++++++++++ .../Support/Mappers/UserMapper.cs | 25 +++++++ 7 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 back/Infrastructure/DatabaseContext.cs create mode 100644 back/Infrastructure/DbContextFactory.cs create mode 100644 back/Infrastructure/Models/User.cs create mode 100644 back/Infrastructure/Repositories/UserRepo.cs create mode 100644 back/Infrastructure/Support/Mappers/UserMapper.cs diff --git a/back/Contracts/Repositories/IUserRepo.cs b/back/Contracts/Repositories/IUserRepo.cs index 926450c..ff32390 100644 --- a/back/Contracts/Repositories/IUserRepo.cs +++ b/back/Contracts/Repositories/IUserRepo.cs @@ -7,6 +7,6 @@ public interface IUserRepo { public Task Get(UserSearch search); public Task Create(UserDto user); - public Task Update(UserDto user); - public Task Delete(UserSearch search); + public Task Update(UserDto user); + public Task Delete(UserSearch search); } \ No newline at end of file diff --git a/back/Infrastructure/DatabaseContext.cs b/back/Infrastructure/DatabaseContext.cs new file mode 100644 index 0000000..603765e --- /dev/null +++ b/back/Infrastructure/DatabaseContext.cs @@ -0,0 +1,15 @@ +using Infrastructure.Models; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure; + +public class DatabaseContext : DbContext +{ + public DatabaseContext(DbContextOptions options) + : base(options) + { + Database.EnsureCreated(); + } + + public DbSet Users { get; set; } = null!; +} \ No newline at end of file diff --git a/back/Infrastructure/DbContextFactory.cs b/back/Infrastructure/DbContextFactory.cs new file mode 100644 index 0000000..00e1443 --- /dev/null +++ b/back/Infrastructure/DbContextFactory.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Infrastructure; + +public class DbContextFactory : IDbContextFactory +{ + private readonly IServiceProvider _serviceProvider; + + public DbContextFactory(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public DatabaseContext CreateDbContext() + { + var scope = _serviceProvider.CreateScope(); + return scope.ServiceProvider.GetRequiredService(); + } +} \ No newline at end of file diff --git a/back/Infrastructure/Infrastructure.csproj b/back/Infrastructure/Infrastructure.csproj index fa71b7a..28669eb 100644 --- a/back/Infrastructure/Infrastructure.csproj +++ b/back/Infrastructure/Infrastructure.csproj @@ -1,5 +1,17 @@  + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + net8.0 enable diff --git a/back/Infrastructure/Models/User.cs b/back/Infrastructure/Models/User.cs new file mode 100644 index 0000000..f33eda9 --- /dev/null +++ b/back/Infrastructure/Models/User.cs @@ -0,0 +1,25 @@ +using System.Reflection.Metadata.Ecma335; +using Contracts.DTO; + +namespace Infrastructure.Models; + +public class User +{ + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public string Password { get; set; } = null!; + public decimal Balance { get; set; } + + public void Update(UserDto userDto) + { + Id = userDto.Id; + if (!string.IsNullOrWhiteSpace(userDto.Name)) + { + Name = userDto.Name; + } + if (!string.IsNullOrWhiteSpace(userDto.Password)) + { + Password = userDto.Password; + } + } +} \ No newline at end of file diff --git a/back/Infrastructure/Repositories/UserRepo.cs b/back/Infrastructure/Repositories/UserRepo.cs new file mode 100644 index 0000000..d6f6cb5 --- /dev/null +++ b/back/Infrastructure/Repositories/UserRepo.cs @@ -0,0 +1,72 @@ +using Contracts.DTO; +using Contracts.Repositories; +using Contracts.SearchModels; +using Infrastructure.Support.Mappers; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure.Repositories; + +public class UserRepo : IUserRepo +{ + public readonly DbContextFactory _factory; + + public UserRepo(DbContextFactory factory) + { + _factory = factory; + } + + public async Task Create(UserDto user) + { + using var context = _factory.CreateDbContext(); + + var createdUser = await context.Users.AddAsync(user.ToModel()); + + await context.SaveChangesAsync(); + return createdUser.Entity.ToDto(); + } + + public async Task Delete(UserSearch search) + { + using var context = _factory.CreateDbContext(); + + var user = await context.Users + .FirstOrDefaultAsync(x => x.Id == search.Id + || x.Name == search.Name); + if (user == null) + { + return null; + } + + context.Users.Remove(user); + await context.SaveChangesAsync(); + return user.ToDto(); + } + + public async Task Get(UserSearch search) + { + using var context = _factory.CreateDbContext(); + + var user = await context.Users + .FirstOrDefaultAsync(x => x.Id == search.Id + || x.Name == search.Name); + + return user?.ToDto(); + } + + public async Task Update(UserDto user) + { + using var context = _factory.CreateDbContext(); + + var existingUser = await context.Users.FirstOrDefaultAsync(x => x.Id == user.Id); + + if (existingUser == null) + { + return null; + } + + existingUser.Update(user); + context.Users.Update(existingUser); + await context.SaveChangesAsync(); + return existingUser.ToDto(); + } +} \ No newline at end of file diff --git a/back/Infrastructure/Support/Mappers/UserMapper.cs b/back/Infrastructure/Support/Mappers/UserMapper.cs new file mode 100644 index 0000000..02e47fc --- /dev/null +++ b/back/Infrastructure/Support/Mappers/UserMapper.cs @@ -0,0 +1,25 @@ +using Contracts.DTO; +using Infrastructure.Models; + +namespace Infrastructure.Support.Mappers; + +public static class UserMapper +{ + public static UserDto ToDto(this User user) + => new() + { + Id = user.Id, + Name = user.Name, + Balance = user.Balance, + Password = user.Password + }; + + public static User ToModel(this UserDto user) + => new() + { + Id = user.Id, + Name = user.Name, + Balance = user.Balance, + Password = user.Password + }; +} \ No newline at end of file -- 2.25.1 From e8a6cc2b178bbdf78afe88c781a6fb6a441c2913 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Mon, 25 Nov 2024 18:25:04 +0400 Subject: [PATCH 06/25] =?UTF-8?q?add:=20=D0=9A=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=D1=8B=20=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Controllers/Controllers.csproj | 6 ++ .../Controllers/Controllers/AuthController.cs | 64 +++++++++++++++++++ .../Controllers/Controllers/UserController.cs | 44 +++++++++++++ .../Extensions/AddDbConnectionService.cs | 19 ++++++ .../Extensions/AddDomainServicesExt.cs | 13 ++++ back/Controllers/Extensions/AddReposExt.cs | 14 ++++ back/Controllers/Program.cs | 5 ++ 7 files changed, 165 insertions(+) create mode 100644 back/Controllers/Controllers/AuthController.cs create mode 100644 back/Controllers/Controllers/UserController.cs create mode 100644 back/Controllers/Extensions/AddDbConnectionService.cs create mode 100644 back/Controllers/Extensions/AddDomainServicesExt.cs create mode 100644 back/Controllers/Extensions/AddReposExt.cs diff --git a/back/Controllers/Controllers.csproj b/back/Controllers/Controllers.csproj index 9daa180..34f05b7 100644 --- a/back/Controllers/Controllers.csproj +++ b/back/Controllers/Controllers.csproj @@ -10,4 +10,10 @@ + + + + + + diff --git a/back/Controllers/Controllers/AuthController.cs b/back/Controllers/Controllers/AuthController.cs new file mode 100644 index 0000000..5184f89 --- /dev/null +++ b/back/Controllers/Controllers/AuthController.cs @@ -0,0 +1,64 @@ +using Contracts.DTO; +using Contracts.Services; +using Contracts.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Services.Support.Exceptions; + +namespace Controllers.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class AuthController : ControllerBase + { + private readonly IAuthService _authService; + + public AuthController(IAuthService authService) + { + _authService = authService; + } + + [HttpPost] + public async Task> Login([FromBody] UserLoginDTO loginData) + { + try + { + var user = await _authService.Login(loginData); + return Ok(user); + } + catch (ArgumentException ex) + { + return BadRequest(ex.Message); + } + catch (UserNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpPost("register")] + public async Task> Register([FromBody] UserDto user) + { + try + { + var createdUser = await _authService.Register(user); + return CreatedAtAction(nameof(Login), new { name = createdUser.Name }, createdUser); + } + catch (ArgumentException ex) + { + return BadRequest(ex.Message); + } + catch (AlreadyExistsException ex) + { + return Conflict(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + } +} diff --git a/back/Controllers/Controllers/UserController.cs b/back/Controllers/Controllers/UserController.cs new file mode 100644 index 0000000..bfca307 --- /dev/null +++ b/back/Controllers/Controllers/UserController.cs @@ -0,0 +1,44 @@ +using Contracts.DTO; +using Contracts.SearchModels; +using Contracts.Services; +using Contracts.ViewModels; +using Microsoft.AspNetCore.Mvc; + +namespace Controllers.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class UserController : ControllerBase + { + private readonly IUserService _userService; + + public UserController(IUserService userService) + { + _userService = userService; + } + + [HttpGet] + public async Task> GetUser([FromQuery] UserSearch search) + { + var user = await _userService.GetDetails(search); + + return Ok(user); + } + + [HttpPut] + public async Task> UpdateUser([FromBody] UserDto user) + { + var updatedUser = await _userService.UpdateUserData(user); + + return Ok(updatedUser); + } + + [HttpDelete] + public async Task> DeleteUser([FromQuery] UserSearch search) + { + var deletedUser = await _userService.Delete(search); + + return Ok(deletedUser); + } + } +} diff --git a/back/Controllers/Extensions/AddDbConnectionService.cs b/back/Controllers/Extensions/AddDbConnectionService.cs new file mode 100644 index 0000000..e78595e --- /dev/null +++ b/back/Controllers/Extensions/AddDbConnectionService.cs @@ -0,0 +1,19 @@ +using Infrastructure; +using Microsoft.EntityFrameworkCore; + +namespace Controllers.Extensions; + +public static class DbConnectionServiceExtension +{ + public static void AddDbConnectionService(this IServiceCollection services) + { + var host = Environment.GetEnvironmentVariable("DB_HOST"); + var database = Environment.GetEnvironmentVariable("DB_NAME"); + var username = Environment.GetEnvironmentVariable("DB_USER"); + var password = Environment.GetEnvironmentVariable("DB_PASSWORD"); + var connectionString = $"Host={host};Database={database};Username={username};Password={password}"; + + services.AddDbContext(options => options.UseNpgsql(connectionString)); + services.AddSingleton, DbContextFactory>(); + } +} \ No newline at end of file diff --git a/back/Controllers/Extensions/AddDomainServicesExt.cs b/back/Controllers/Extensions/AddDomainServicesExt.cs new file mode 100644 index 0000000..f68caa1 --- /dev/null +++ b/back/Controllers/Extensions/AddDomainServicesExt.cs @@ -0,0 +1,13 @@ +using Contracts.Services; +using Services.Domain; + +namespace Controllers.Extensions; + +public static class AddDomainServicesExtension +{ + public static void AddDomainServices(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/back/Controllers/Extensions/AddReposExt.cs b/back/Controllers/Extensions/AddReposExt.cs new file mode 100644 index 0000000..27d5b68 --- /dev/null +++ b/back/Controllers/Extensions/AddReposExt.cs @@ -0,0 +1,14 @@ +using Contracts.Repositories; +using Contracts.Services; +using Infrastructure.Repositories; +using Services.Domain; + +namespace Controllers.Extensions; + +public static class AddReposExtension +{ + public static void AddRepos(this IServiceCollection services) + { + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/back/Controllers/Program.cs b/back/Controllers/Program.cs index 48863a6..1bb1553 100644 --- a/back/Controllers/Program.cs +++ b/back/Controllers/Program.cs @@ -1,6 +1,11 @@ +using Controllers.Extensions; + var builder = WebApplication.CreateBuilder(args); // Add services to the container. +builder.Services.AddRepos(); +builder.Services.AddDomainServices(); +builder.Services.AddDbConnectionService(); builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -- 2.25.1 From 7f30d20c73cae93501a74ed3beb028a4f4d679a5 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Mon, 25 Nov 2024 20:56:39 +0400 Subject: [PATCH 07/25] =?UTF-8?q?fix:=20=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB?= =?UTF-8?q?=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=20=D0=B1=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Controllers/Controllers.http | 1 - .../Extensions/AddDbConnectionService.cs | 19 ------- .../Extensions/AddDomainServicesExt.cs | 4 +- back/Controllers/Extensions/AddReposExt.cs | 2 +- .../Extensions/DatabaseSetupExt.cs | 29 +++++++++++ back/Controllers/Program.cs | 6 ++- .../20241125164748_User.Designer.cs | 52 +++++++++++++++++++ .../Migrations/20241125164748_User.cs | 36 +++++++++++++ .../DatabaseContextModelSnapshot.cs | 49 +++++++++++++++++ back/Infrastructure/Repositories/UserRepo.cs | 4 +- 10 files changed, 175 insertions(+), 27 deletions(-) delete mode 100644 back/Controllers/Extensions/AddDbConnectionService.cs create mode 100644 back/Controllers/Extensions/DatabaseSetupExt.cs create mode 100644 back/Infrastructure/Migrations/20241125164748_User.Designer.cs create mode 100644 back/Infrastructure/Migrations/20241125164748_User.cs create mode 100644 back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs diff --git a/back/Controllers/Controllers.http b/back/Controllers/Controllers.http index 01f7fc0..f5c450d 100644 --- a/back/Controllers/Controllers.http +++ b/back/Controllers/Controllers.http @@ -1,6 +1,5 @@ @Controllers_HostAddress = http://localhost:5125 -GET {{Controllers_HostAddress}}/weatherforecast/ Accept: application/json ### diff --git a/back/Controllers/Extensions/AddDbConnectionService.cs b/back/Controllers/Extensions/AddDbConnectionService.cs deleted file mode 100644 index e78595e..0000000 --- a/back/Controllers/Extensions/AddDbConnectionService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Infrastructure; -using Microsoft.EntityFrameworkCore; - -namespace Controllers.Extensions; - -public static class DbConnectionServiceExtension -{ - public static void AddDbConnectionService(this IServiceCollection services) - { - var host = Environment.GetEnvironmentVariable("DB_HOST"); - var database = Environment.GetEnvironmentVariable("DB_NAME"); - var username = Environment.GetEnvironmentVariable("DB_USER"); - var password = Environment.GetEnvironmentVariable("DB_PASSWORD"); - var connectionString = $"Host={host};Database={database};Username={username};Password={password}"; - - services.AddDbContext(options => options.UseNpgsql(connectionString)); - services.AddSingleton, DbContextFactory>(); - } -} \ No newline at end of file diff --git a/back/Controllers/Extensions/AddDomainServicesExt.cs b/back/Controllers/Extensions/AddDomainServicesExt.cs index f68caa1..f28a69d 100644 --- a/back/Controllers/Extensions/AddDomainServicesExt.cs +++ b/back/Controllers/Extensions/AddDomainServicesExt.cs @@ -7,7 +7,7 @@ public static class AddDomainServicesExtension { public static void AddDomainServices(this IServiceCollection services) { - services.AddSingleton(); - services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); } } \ No newline at end of file diff --git a/back/Controllers/Extensions/AddReposExt.cs b/back/Controllers/Extensions/AddReposExt.cs index 27d5b68..9207a8b 100644 --- a/back/Controllers/Extensions/AddReposExt.cs +++ b/back/Controllers/Extensions/AddReposExt.cs @@ -9,6 +9,6 @@ public static class AddReposExtension { public static void AddRepos(this IServiceCollection services) { - services.AddSingleton(); + services.AddTransient(); } } \ No newline at end of file diff --git a/back/Controllers/Extensions/DatabaseSetupExt.cs b/back/Controllers/Extensions/DatabaseSetupExt.cs new file mode 100644 index 0000000..35752d1 --- /dev/null +++ b/back/Controllers/Extensions/DatabaseSetupExt.cs @@ -0,0 +1,29 @@ +using Infrastructure; +using Microsoft.EntityFrameworkCore; + +namespace Controllers.Extensions; + +public static class DatabaseSetupExtension +{ + public static void AddDbConnectionService(this IServiceCollection services, IConfiguration config) + { + var connectionString = config.GetConnectionString("DefaultConnection") + ?? throw new ArgumentException("Нет строки подключения"); + services.AddDbContext(options => options.UseNpgsql(connectionString)); + services.AddSingleton, DbContextFactory>(); + } + + public static void MigrateDb(this IApplicationBuilder app) + { + try + { + using var scope = app.ApplicationServices.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + context.Database.Migrate(); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } +} \ No newline at end of file diff --git a/back/Controllers/Program.cs b/back/Controllers/Program.cs index 1bb1553..2e20712 100644 --- a/back/Controllers/Program.cs +++ b/back/Controllers/Program.cs @@ -3,9 +3,9 @@ using Controllers.Extensions; var builder = WebApplication.CreateBuilder(args); // Add services to the container. +builder.Services.AddDbConnectionService(builder.Configuration); builder.Services.AddRepos(); builder.Services.AddDomainServices(); -builder.Services.AddDbConnectionService(); builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle @@ -21,10 +21,12 @@ if (app.Environment.IsDevelopment()) app.UseSwaggerUI(); } +app.MigrateDb(); + app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); -app.Run(); +await app.RunAsync(); diff --git a/back/Infrastructure/Migrations/20241125164748_User.Designer.cs b/back/Infrastructure/Migrations/20241125164748_User.Designer.cs new file mode 100644 index 0000000..9872477 --- /dev/null +++ b/back/Infrastructure/Migrations/20241125164748_User.Designer.cs @@ -0,0 +1,52 @@ +// +using System; +using Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20241125164748_User")] + partial class User + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Balance") + .HasColumnType("numeric"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/back/Infrastructure/Migrations/20241125164748_User.cs b/back/Infrastructure/Migrations/20241125164748_User.cs new file mode 100644 index 0000000..f35370d --- /dev/null +++ b/back/Infrastructure/Migrations/20241125164748_User.cs @@ -0,0 +1,36 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class User : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + Password = table.Column(type: "text", nullable: false), + Balance = table.Column(type: "numeric", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Users"); + } + } +} diff --git a/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs b/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs new file mode 100644 index 0000000..1e66849 --- /dev/null +++ b/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs @@ -0,0 +1,49 @@ +// +using System; +using Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(DatabaseContext))] + partial class DatabaseContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Balance") + .HasColumnType("numeric"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/back/Infrastructure/Repositories/UserRepo.cs b/back/Infrastructure/Repositories/UserRepo.cs index d6f6cb5..a075d6d 100644 --- a/back/Infrastructure/Repositories/UserRepo.cs +++ b/back/Infrastructure/Repositories/UserRepo.cs @@ -8,9 +8,9 @@ namespace Infrastructure.Repositories; public class UserRepo : IUserRepo { - public readonly DbContextFactory _factory; + public readonly IDbContextFactory _factory; - public UserRepo(DbContextFactory factory) + public UserRepo(IDbContextFactory factory) { _factory = factory; } -- 2.25.1 From 08a3b74d587e6dc48ed9d22911246244668654fe Mon Sep 17 00:00:00 2001 From: mfnefd Date: Mon, 25 Nov 2024 23:46:53 +0400 Subject: [PATCH 08/25] =?UTF-8?q?add:=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BA=D1=82=D1=8B=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=20?= =?UTF-8?q?=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/DTOs/SpendingGroupDto.cs | 8 ++++++++ back/Contracts/Mappers/SpendingGroupMapper.cs | 14 ++++++++++++++ back/Contracts/Repositories/ISpendingGroupRepo.cs | 13 +++++++++++++ back/Contracts/SearchModels/SpendingGroupSearch.cs | 7 +++++++ back/Contracts/Services/ISpendingGroupService.cs | 14 ++++++++++++++ .../Contracts/ViewModels/SpendingGroupViewModel.cs | 7 +++++++ 6 files changed, 63 insertions(+) create mode 100644 back/Contracts/DTOs/SpendingGroupDto.cs create mode 100644 back/Contracts/Mappers/SpendingGroupMapper.cs create mode 100644 back/Contracts/Repositories/ISpendingGroupRepo.cs create mode 100644 back/Contracts/SearchModels/SpendingGroupSearch.cs create mode 100644 back/Contracts/Services/ISpendingGroupService.cs create mode 100644 back/Contracts/ViewModels/SpendingGroupViewModel.cs diff --git a/back/Contracts/DTOs/SpendingGroupDto.cs b/back/Contracts/DTOs/SpendingGroupDto.cs new file mode 100644 index 0000000..cf03704 --- /dev/null +++ b/back/Contracts/DTOs/SpendingGroupDto.cs @@ -0,0 +1,8 @@ +namespace Contracts.DTO; + +public class SpendingGroupDto +{ + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public Guid UserId { get; set; } +} \ No newline at end of file diff --git a/back/Contracts/Mappers/SpendingGroupMapper.cs b/back/Contracts/Mappers/SpendingGroupMapper.cs new file mode 100644 index 0000000..7eca2d3 --- /dev/null +++ b/back/Contracts/Mappers/SpendingGroupMapper.cs @@ -0,0 +1,14 @@ +using Contracts.DTO; +using Contracts.ViewModels; + +namespace Contracts.Mappers; + +public static class SpendingGroupMapper +{ + public static SpendingGroupViewModel ToView(this SpendingGroupDto spendingGroup) + => new() + { + Id = spendingGroup.Id, + Name = spendingGroup.Name + }; +} \ No newline at end of file diff --git a/back/Contracts/Repositories/ISpendingGroupRepo.cs b/back/Contracts/Repositories/ISpendingGroupRepo.cs new file mode 100644 index 0000000..f8f7999 --- /dev/null +++ b/back/Contracts/Repositories/ISpendingGroupRepo.cs @@ -0,0 +1,13 @@ +using Contracts.DTO; +using Contracts.SearchModels; + +namespace Contracts.Repositories; + +public interface ISpendingGroupRepo +{ + Task Get(SpendingGroupSearch search); + Task> GetList(SpendingGroupSearch? search = null); + Task Create(SpendingGroupDto spendingGroup); + Task Delete(SpendingGroupSearch search); + Task Update(SpendingGroupDto spendingGroup); +} diff --git a/back/Contracts/SearchModels/SpendingGroupSearch.cs b/back/Contracts/SearchModels/SpendingGroupSearch.cs new file mode 100644 index 0000000..927a206 --- /dev/null +++ b/back/Contracts/SearchModels/SpendingGroupSearch.cs @@ -0,0 +1,7 @@ +namespace Contracts.SearchModels; + +public class SpendingGroupSearch +{ + public Guid? Id { get; set; } + public string? Name { get; set; } +} \ No newline at end of file diff --git a/back/Contracts/Services/ISpendingGroupService.cs b/back/Contracts/Services/ISpendingGroupService.cs new file mode 100644 index 0000000..e6664ee --- /dev/null +++ b/back/Contracts/Services/ISpendingGroupService.cs @@ -0,0 +1,14 @@ +using Contracts.DTO; +using Contracts.SearchModels; +using Contracts.ViewModels; + +namespace Contracts.Services; + +public interface ISpendingGroupService +{ + Task GetDetails(SpendingGroupSearch search); + Task> GetList(SpendingGroupSearch search); + Task Create(SpendingGroupDto model); + Task Update(SpendingGroupDto model); + Task Delete(SpendingGroupSearch search); +} \ No newline at end of file diff --git a/back/Contracts/ViewModels/SpendingGroupViewModel.cs b/back/Contracts/ViewModels/SpendingGroupViewModel.cs new file mode 100644 index 0000000..12cba2d --- /dev/null +++ b/back/Contracts/ViewModels/SpendingGroupViewModel.cs @@ -0,0 +1,7 @@ +namespace Contracts.ViewModels; + +public class SpendingGroupViewModel +{ + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; +} \ No newline at end of file -- 2.25.1 From 3f7687454705836dea181ec167d606b6176d9d41 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 26 Nov 2024 00:00:31 +0400 Subject: [PATCH 09/25] =?UTF-8?q?add:=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=20=D1=80=D0=B0=D1=81?= =?UTF-8?q?=D1=85=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Services/Domain/SpendingGroupService.cs | 63 +++++++++++++++++++ .../Exceptions/EntityNotFoundException.cs | 10 +++ .../Exceptions/UserNotFoundException.cs | 3 +- 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 back/Services/Domain/SpendingGroupService.cs create mode 100644 back/Services/Support/Exceptions/EntityNotFoundException.cs diff --git a/back/Services/Domain/SpendingGroupService.cs b/back/Services/Domain/SpendingGroupService.cs new file mode 100644 index 0000000..005ee40 --- /dev/null +++ b/back/Services/Domain/SpendingGroupService.cs @@ -0,0 +1,63 @@ +using Contracts.DTO; +using Contracts.Mappers; +using Contracts.Repositories; +using Contracts.SearchModels; +using Contracts.ViewModels; +using Services.Support.Exceptions; + +namespace Contracts.Services; + +public class SpendingGroupService : ISpendingGroupService +{ + private readonly ISpendingGroupRepo _spendingGroupRepo; + + public SpendingGroupService(ISpendingGroupRepo spendingGroupRepo) + { + _spendingGroupRepo = spendingGroupRepo; + } + + public async Task Create(SpendingGroupDto model) + { + var group = await _spendingGroupRepo.Create(model); + return group.ToView(); + } + + public async Task Delete(SpendingGroupSearch search) + { + var group = await _spendingGroupRepo.Delete(search); + if (group == null) + { + throw new EntityNotFoundException("При удалении не получилось найти группу"); + } + + return group.ToView(); + } + + public async Task GetDetails(SpendingGroupSearch search) + { + var group = await _spendingGroupRepo.Get(search); + if (group == null) + { + throw new EntityNotFoundException("Не удалось найти группу по таким параметрам"); + } + + return group.ToView(); + } + + public async Task> GetList(SpendingGroupSearch search) + { + var groups = await _spendingGroupRepo.GetList(search); + return groups.Select(x => x.ToView()); + } + + public async Task Update(SpendingGroupDto model) + { + var group = await _spendingGroupRepo.Update(model); + if (group == null) + { + throw new EntityNotFoundException("При обновлении не получилось найти группу"); + } + + return group.ToView(); + } +} diff --git a/back/Services/Support/Exceptions/EntityNotFoundException.cs b/back/Services/Support/Exceptions/EntityNotFoundException.cs new file mode 100644 index 0000000..957b0b3 --- /dev/null +++ b/back/Services/Support/Exceptions/EntityNotFoundException.cs @@ -0,0 +1,10 @@ +namespace Services.Support.Exceptions; + +public class EntityNotFoundException : Exception +{ + public EntityNotFoundException(string message) + : base(message) { } + public EntityNotFoundException(string message, Exception innerException) + : base(message, innerException) { } + +} \ No newline at end of file diff --git a/back/Services/Support/Exceptions/UserNotFoundException.cs b/back/Services/Support/Exceptions/UserNotFoundException.cs index df152a1..77b2297 100644 --- a/back/Services/Support/Exceptions/UserNotFoundException.cs +++ b/back/Services/Support/Exceptions/UserNotFoundException.cs @@ -2,10 +2,11 @@ using Contracts.SearchModels; namespace Services.Support.Exceptions; -public class UserNotFoundException : Exception +public class UserNotFoundException : EntityNotFoundException { public UserNotFoundException(string message) : base(message) { } public UserNotFoundException(string message, Exception innerException) : base(message, innerException) { } + } \ No newline at end of file -- 2.25.1 From 6e4e47dc595688c31fac9a8be0a388622702996f Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 26 Nov 2024 00:15:31 +0400 Subject: [PATCH 10/25] =?UTF-8?q?add:=20=D1=80=D0=B5=D0=BF=D0=BE=D0=B7?= =?UTF-8?q?=D0=B8=D1=82=D0=BE=D1=80=D0=B8=D0=B9=20=D0=B3=D1=80=D1=83=D0=BF?= =?UTF-8?q?=D0=BF=20=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Infrastructure/DatabaseContext.cs | 2 + back/Infrastructure/Models/SpendingGroup.cs | 10 ++ .../Repositories/SpendingGroupRepo.cs | 94 +++++++++++++++++++ .../Support/Mappers/SpendingGroupMapper.cs | 22 +++++ 4 files changed, 128 insertions(+) create mode 100644 back/Infrastructure/Models/SpendingGroup.cs create mode 100644 back/Infrastructure/Repositories/SpendingGroupRepo.cs create mode 100644 back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs diff --git a/back/Infrastructure/DatabaseContext.cs b/back/Infrastructure/DatabaseContext.cs index 603765e..051dcb7 100644 --- a/back/Infrastructure/DatabaseContext.cs +++ b/back/Infrastructure/DatabaseContext.cs @@ -1,3 +1,4 @@ +using Contracts.DTO; using Infrastructure.Models; using Microsoft.EntityFrameworkCore; @@ -12,4 +13,5 @@ public class DatabaseContext : DbContext } public DbSet Users { get; set; } = null!; + public DbSet SpendingGroups { get; set; } = null!; } \ No newline at end of file diff --git a/back/Infrastructure/Models/SpendingGroup.cs b/back/Infrastructure/Models/SpendingGroup.cs new file mode 100644 index 0000000..b3d5d20 --- /dev/null +++ b/back/Infrastructure/Models/SpendingGroup.cs @@ -0,0 +1,10 @@ +namespace Infrastructure.Models; + +public class SpendingGroup +{ + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + + public Guid UserId { get; set; } + public User User { get; set; } = null!; +} \ No newline at end of file diff --git a/back/Infrastructure/Repositories/SpendingGroupRepo.cs b/back/Infrastructure/Repositories/SpendingGroupRepo.cs new file mode 100644 index 0000000..38a69d0 --- /dev/null +++ b/back/Infrastructure/Repositories/SpendingGroupRepo.cs @@ -0,0 +1,94 @@ +using Contracts.DTO; +using Contracts.Repositories; +using Contracts.SearchModels; +using Infrastructure.Support.Mappers; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure.Repositories; + +public class SpendingGroupRepo : ISpendingGroupRepo +{ + public readonly IDbContextFactory _factory; + + public SpendingGroupRepo(IDbContextFactory factory) + { + _factory = factory; + } + + public async Task Create(SpendingGroupDto spendingGroup) + { + using var context = _factory.CreateDbContext(); + + var createdGroup = await context.SpendingGroups.AddAsync(spendingGroup.ToModel()); + await context.SaveChangesAsync(); + return createdGroup.Entity.ToDto(); + } + + public async Task Delete(SpendingGroupSearch search) + { + using var context = _factory.CreateDbContext(); + + var group = await context.SpendingGroups + .FirstOrDefaultAsync(x => x.Id == search.Id + || x.Name == search.Name); + if (group == null) + { + return null; + } + + context.SpendingGroups.Remove(group); + await context.SaveChangesAsync(); + return group.ToDto(); + } + + public async Task Get(SpendingGroupSearch search) + { + using var context = _factory.CreateDbContext(); + + var group = await context.SpendingGroups + .FirstOrDefaultAsync(x => x.Id == search.Id + || x.Name == search.Name); + + return group?.ToDto(); + } + + public async Task> GetList(SpendingGroupSearch? search = null) + { + using var context = _factory.CreateDbContext(); + + var query = context.SpendingGroups.AsQueryable(); + + if (search != null) + { + if (search.Id != null) + { + query = query.Where(x => x.Id == search.Id); + } + + if (!string.IsNullOrWhiteSpace(search.Name)) + { + query = query.Where(x => x.Name.Contains(search.Name, StringComparison.OrdinalIgnoreCase)); + } + } + + return await query.Select(x => x.ToDto()).ToListAsync(); + } + + public async Task Update(SpendingGroupDto spendingGroup) + { + using var context = _factory.CreateDbContext(); + + var existingGroup = await context.SpendingGroups + .FirstOrDefaultAsync(x => x.Id == spendingGroup.Id); + + if (existingGroup == null) + { + return null; + } + + existingGroup.Name = spendingGroup.Name; + context.SpendingGroups.Update(existingGroup); + await context.SaveChangesAsync(); + return existingGroup.ToDto(); + } +} diff --git a/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs b/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs new file mode 100644 index 0000000..94a39b6 --- /dev/null +++ b/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs @@ -0,0 +1,22 @@ +using Contracts.DTO; +using Infrastructure.Models; + +namespace Infrastructure.Support.Mappers; + +public static class SpendingGroupMapper +{ + public static SpendingGroupDto ToDto(this SpendingGroup group) + => new() + { + Id = group.Id, + Name = group.Name, + UserId = group.UserId + }; + public static SpendingGroup ToModel(this SpendingGroupDto group) + => new() + { + Id = group.Id, + Name = group.Name, + UserId = group.UserId + }; +} \ No newline at end of file -- 2.25.1 From 078b46975249f8a787ae7ae4893e2b0f6813b7a2 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 26 Nov 2024 00:33:48 +0400 Subject: [PATCH 11/25] =?UTF-8?q?add:=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF?= =?UTF-8?q?=20=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/ISpendingGroupService.cs | 2 +- .../Controllers/SpendingGroupController.cs | 118 ++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 back/Controllers/Controllers/SpendingGroupController.cs diff --git a/back/Contracts/Services/ISpendingGroupService.cs b/back/Contracts/Services/ISpendingGroupService.cs index e6664ee..938806f 100644 --- a/back/Contracts/Services/ISpendingGroupService.cs +++ b/back/Contracts/Services/ISpendingGroupService.cs @@ -7,7 +7,7 @@ namespace Contracts.Services; public interface ISpendingGroupService { Task GetDetails(SpendingGroupSearch search); - Task> GetList(SpendingGroupSearch search); + Task> GetList(SpendingGroupSearch? search = null); Task Create(SpendingGroupDto model); Task Update(SpendingGroupDto model); Task Delete(SpendingGroupSearch search); diff --git a/back/Controllers/Controllers/SpendingGroupController.cs b/back/Controllers/Controllers/SpendingGroupController.cs new file mode 100644 index 0000000..36ad45b --- /dev/null +++ b/back/Controllers/Controllers/SpendingGroupController.cs @@ -0,0 +1,118 @@ +namespace Controllers.Extensions; + +using Contracts.DTO; +using Contracts.SearchModels; +using Contracts.Services; +using Contracts.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Services.Support.Exceptions; + +[ApiController] +[Route("api/[controller]")] +public class SpendingGroupController : ControllerBase +{ + private readonly ISpendingGroupService _spendingGroupService; + + public SpendingGroupController(ISpendingGroupService spendingGroupService) + { + _spendingGroupService = spendingGroupService; + } + [HttpGet("details")] + public async Task> GetSpendingGroup( + [FromQuery] SpendingGroupSearch search) + { + try + { + var group = await _spendingGroupService.GetDetails(search); + return Ok(group); + } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpGet] + public async Task>> GetSpendingGroups() + { + try + { + var groups = await _spendingGroupService.GetList(); + return Ok(groups); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpGet("filter")] + public async Task>> GetSpendingGroups( + [FromQuery] SpendingGroupSearch search) + { + try + { + var groups = await _spendingGroupService.GetList(search); + return Ok(groups); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpPost] + public async Task> CreateSpendingGroup( + [FromBody] SpendingGroupDto dto) + { + try + { + var group = await _spendingGroupService.Create(dto); + return CreatedAtAction(nameof(GetSpendingGroup), new { id = group.Id }, group); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpPatch] + public async Task> UpdateSpendingGroup([FromBody] SpendingGroupDto dto) + { + try + { + var group = await _spendingGroupService.Update(dto); + return Ok(group); + } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpDelete] + public async Task DeleteSpendingGroup([FromQuery] SpendingGroupSearch search) + { + try + { + var group = await _spendingGroupService.Delete(search); + return Ok(group); + } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } +} \ No newline at end of file -- 2.25.1 From 43b00bfc2fc8e1d9b1dc72c9045425e8a3dd2328 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 26 Nov 2024 00:34:57 +0400 Subject: [PATCH 12/25] =?UTF-8?q?fix:=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=D1=8B=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20try=20catch=20=D0=B2=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BD=D1=82=D1=80=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F,?= =?UTF-8?q?=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=82=D1=80=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=20=D0=B0?= =?UTF-8?q?=D0=B2=D1=82=D0=BE=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Controllers/AuthController.cs | 95 +++++++++---------- .../Controllers/Controllers/UserController.cs | 69 ++++++++++---- 2 files changed, 96 insertions(+), 68 deletions(-) diff --git a/back/Controllers/Controllers/AuthController.cs b/back/Controllers/Controllers/AuthController.cs index 5184f89..15eb928 100644 --- a/back/Controllers/Controllers/AuthController.cs +++ b/back/Controllers/Controllers/AuthController.cs @@ -4,61 +4,60 @@ using Contracts.ViewModels; using Microsoft.AspNetCore.Mvc; using Services.Support.Exceptions; -namespace Controllers.Controllers +namespace Controllers.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class AuthController : ControllerBase { - [ApiController] - [Route("api/[controller]")] - public class AuthController : ControllerBase + private readonly IAuthService _authService; + + public AuthController(IAuthService authService) { - private readonly IAuthService _authService; + _authService = authService; + } - public AuthController(IAuthService authService) + [HttpPost] + public async Task> Login([FromBody] UserLoginDTO loginData) + { + try { - _authService = authService; + var user = await _authService.Login(loginData); + return Ok(user); } - - [HttpPost] - public async Task> Login([FromBody] UserLoginDTO loginData) + catch (ArgumentException ex) { - try - { - var user = await _authService.Login(loginData); - return Ok(user); - } - catch (ArgumentException ex) - { - return BadRequest(ex.Message); - } - catch (UserNotFoundException ex) - { - return NotFound(ex.Message); - } - catch (Exception ex) - { - return StatusCode(500, ex.Message); - } + return BadRequest(ex.Message); } - - [HttpPost("register")] - public async Task> Register([FromBody] UserDto user) + catch (UserNotFoundException ex) { - try - { - var createdUser = await _authService.Register(user); - return CreatedAtAction(nameof(Login), new { name = createdUser.Name }, createdUser); - } - catch (ArgumentException ex) - { - return BadRequest(ex.Message); - } - catch (AlreadyExistsException ex) - { - return Conflict(ex.Message); - } - catch (Exception ex) - { - return StatusCode(500, ex.Message); - } + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); } } -} + + [HttpPost("register")] + public async Task> Register([FromBody] UserDto user) + { + try + { + var createdUser = await _authService.Register(user); + return CreatedAtAction(nameof(Login), new { name = createdUser.Name }, createdUser); + } + catch (ArgumentException ex) + { + return BadRequest(ex.Message); + } + catch (AlreadyExistsException ex) + { + return Conflict(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } +} \ No newline at end of file diff --git a/back/Controllers/Controllers/UserController.cs b/back/Controllers/Controllers/UserController.cs index bfca307..c93c276 100644 --- a/back/Controllers/Controllers/UserController.cs +++ b/back/Controllers/Controllers/UserController.cs @@ -3,42 +3,71 @@ using Contracts.SearchModels; using Contracts.Services; using Contracts.ViewModels; using Microsoft.AspNetCore.Mvc; +using Services.Support.Exceptions; -namespace Controllers.Controllers +namespace Controllers.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class UserController : ControllerBase { - [ApiController] - [Route("api/[controller]")] - public class UserController : ControllerBase + private readonly IUserService _userService; + + public UserController(IUserService userService) { - private readonly IUserService _userService; - - public UserController(IUserService userService) - { - _userService = userService; - } - - [HttpGet] - public async Task> GetUser([FromQuery] UserSearch search) + _userService = userService; + } + [HttpGet] + public async Task> GetUser([FromQuery] UserSearch search) + { + try { var user = await _userService.GetDetails(search); - return Ok(user); } + catch (UserNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } - [HttpPut] - public async Task> UpdateUser([FromBody] UserDto user) + [HttpPatch] + public async Task> UpdateUser([FromBody] UserDto user) + { + try { var updatedUser = await _userService.UpdateUserData(user); - return Ok(updatedUser); } + catch (UserNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } - [HttpDelete] - public async Task> DeleteUser([FromQuery] UserSearch search) + [HttpDelete] + public async Task> DeleteUser([FromQuery] UserSearch search) + { + try { var deletedUser = await _userService.Delete(search); - return Ok(deletedUser); } + catch (UserNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } } } -- 2.25.1 From f0bdffeb86f56d0136e05d494e3910c77502a92d Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 26 Nov 2024 20:23:59 +0400 Subject: [PATCH 13/25] =?UTF-8?q?fix:=20=D0=BA=D0=BE=D1=80=D1=80=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D0=BD=D0=BE=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D0=BA=D1=80=D1=83=D0=B4=20=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=BF=D0=BF=20=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Controllers/Controllers.csproj | 4 + .../Extensions/AddDomainServicesExt.cs | 2 + back/Controllers/Extensions/AddReposExt.cs | 1 + .../Extensions/DatabaseSetupExt.cs | 5 +- back/Infrastructure/DatabaseContext.cs | 1 - back/Infrastructure/Infrastructure.csproj | 4 - .../20241126140310_SpendingGroup.Designer.cs | 88 +++++++++++++++++++ .../20241126140310_SpendingGroup.cs | 46 ++++++++++ .../DatabaseContextModelSnapshot.cs | 36 ++++++++ back/Infrastructure/Models/User.cs | 1 + back/Services/Domain/SpendingGroupService.cs | 2 +- back/Services/Domain/UserService.cs | 14 +-- 12 files changed, 190 insertions(+), 14 deletions(-) create mode 100644 back/Infrastructure/Migrations/20241126140310_SpendingGroup.Designer.cs create mode 100644 back/Infrastructure/Migrations/20241126140310_SpendingGroup.cs diff --git a/back/Controllers/Controllers.csproj b/back/Controllers/Controllers.csproj index 34f05b7..03a20aa 100644 --- a/back/Controllers/Controllers.csproj +++ b/back/Controllers/Controllers.csproj @@ -7,6 +7,10 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/back/Controllers/Extensions/AddDomainServicesExt.cs b/back/Controllers/Extensions/AddDomainServicesExt.cs index f28a69d..575d50a 100644 --- a/back/Controllers/Extensions/AddDomainServicesExt.cs +++ b/back/Controllers/Extensions/AddDomainServicesExt.cs @@ -9,5 +9,7 @@ public static class AddDomainServicesExtension { services.AddTransient(); services.AddTransient(); + + services.AddTransient(); } } \ No newline at end of file diff --git a/back/Controllers/Extensions/AddReposExt.cs b/back/Controllers/Extensions/AddReposExt.cs index 9207a8b..d019ce0 100644 --- a/back/Controllers/Extensions/AddReposExt.cs +++ b/back/Controllers/Extensions/AddReposExt.cs @@ -10,5 +10,6 @@ public static class AddReposExtension public static void AddRepos(this IServiceCollection services) { services.AddTransient(); + services.AddTransient(); } } \ No newline at end of file diff --git a/back/Controllers/Extensions/DatabaseSetupExt.cs b/back/Controllers/Extensions/DatabaseSetupExt.cs index 35752d1..0f72055 100644 --- a/back/Controllers/Extensions/DatabaseSetupExt.cs +++ b/back/Controllers/Extensions/DatabaseSetupExt.cs @@ -18,8 +18,9 @@ public static class DatabaseSetupExtension try { using var scope = app.ApplicationServices.CreateScope(); - var context = scope.ServiceProvider.GetRequiredService(); - context.Database.Migrate(); + var context = scope.ServiceProvider.GetRequiredService>(); + using var db = context.CreateDbContext(); + db.Database.Migrate(); } catch (Exception ex) { diff --git a/back/Infrastructure/DatabaseContext.cs b/back/Infrastructure/DatabaseContext.cs index 051dcb7..6901ea8 100644 --- a/back/Infrastructure/DatabaseContext.cs +++ b/back/Infrastructure/DatabaseContext.cs @@ -9,7 +9,6 @@ public class DatabaseContext : DbContext public DatabaseContext(DbContextOptions options) : base(options) { - Database.EnsureCreated(); } public DbSet Users { get; set; } = null!; diff --git a/back/Infrastructure/Infrastructure.csproj b/back/Infrastructure/Infrastructure.csproj index 28669eb..0d0625a 100644 --- a/back/Infrastructure/Infrastructure.csproj +++ b/back/Infrastructure/Infrastructure.csproj @@ -5,10 +5,6 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - diff --git a/back/Infrastructure/Migrations/20241126140310_SpendingGroup.Designer.cs b/back/Infrastructure/Migrations/20241126140310_SpendingGroup.Designer.cs new file mode 100644 index 0000000..0ae4d47 --- /dev/null +++ b/back/Infrastructure/Migrations/20241126140310_SpendingGroup.Designer.cs @@ -0,0 +1,88 @@ +// +using System; +using Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20241126140310_SpendingGroup")] + partial class SpendingGroup + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("SpendingGroups"); + }); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Balance") + .HasColumnType("numeric"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.HasOne("Infrastructure.Models.User", "User") + .WithMany("SpendingGroups") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Navigation("SpendingGroups"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/back/Infrastructure/Migrations/20241126140310_SpendingGroup.cs b/back/Infrastructure/Migrations/20241126140310_SpendingGroup.cs new file mode 100644 index 0000000..1632633 --- /dev/null +++ b/back/Infrastructure/Migrations/20241126140310_SpendingGroup.cs @@ -0,0 +1,46 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class SpendingGroup : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SpendingGroups", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + UserId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SpendingGroups", x => x.Id); + table.ForeignKey( + name: "FK_SpendingGroups_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_SpendingGroups_UserId", + table: "SpendingGroups", + column: "UserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SpendingGroups"); + } + } +} diff --git a/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs b/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs index 1e66849..201a211 100644 --- a/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs +++ b/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs @@ -22,6 +22,26 @@ namespace Infrastructure.Migrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("SpendingGroups"); + }); + modelBuilder.Entity("Infrastructure.Models.User", b => { b.Property("Id") @@ -43,6 +63,22 @@ namespace Infrastructure.Migrations b.ToTable("Users"); }); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.HasOne("Infrastructure.Models.User", "User") + .WithMany("SpendingGroups") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Navigation("SpendingGroups"); + }); #pragma warning restore 612, 618 } } diff --git a/back/Infrastructure/Models/User.cs b/back/Infrastructure/Models/User.cs index f33eda9..add673c 100644 --- a/back/Infrastructure/Models/User.cs +++ b/back/Infrastructure/Models/User.cs @@ -9,6 +9,7 @@ public class User public string Name { get; set; } = null!; public string Password { get; set; } = null!; public decimal Balance { get; set; } + public List? SpendingGroups { get; set; } public void Update(UserDto userDto) { diff --git a/back/Services/Domain/SpendingGroupService.cs b/back/Services/Domain/SpendingGroupService.cs index 005ee40..b0afed1 100644 --- a/back/Services/Domain/SpendingGroupService.cs +++ b/back/Services/Domain/SpendingGroupService.cs @@ -47,7 +47,7 @@ public class SpendingGroupService : ISpendingGroupService public async Task> GetList(SpendingGroupSearch search) { var groups = await _spendingGroupRepo.GetList(search); - return groups.Select(x => x.ToView()); + return groups.Select(x => x.ToView()).ToList(); } public async Task Update(SpendingGroupDto model) diff --git a/back/Services/Domain/UserService.cs b/back/Services/Domain/UserService.cs index e54904d..5f6f2c5 100644 --- a/back/Services/Domain/UserService.cs +++ b/back/Services/Domain/UserService.cs @@ -20,6 +20,10 @@ public class UserService : IUserService public async Task Delete(UserSearch search) { var user = await _userRepo.Delete(search); + if (user == null) + { + throw new UserNotFoundException($"Пользователь для удаления не найден"); + } return user.ToView(); } @@ -35,13 +39,11 @@ public class UserService : IUserService public async Task UpdateUserData(UserDto user) { - var existingUser = await _userRepo.Get(new UserSearch() { Name = user.Name }); - if (existingUser == null) - { - throw new UserNotFoundException($"Пользователь {user.Name} не найден"); - } - var updatedUser = await _userRepo.Update(user); + if (updatedUser == null) + { + throw new EntityNotFoundException("При обновлении не получилось найти пользователя с id = " + user.Id); + } return updatedUser.ToView(); } } -- 2.25.1 From 27df19201ae13a4843980386bc9f9a0c1eea5315 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 26 Nov 2024 20:56:22 +0400 Subject: [PATCH 14/25] =?UTF-8?q?add:=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BA=D1=82=D1=8B=20=D0=B7=D0=B0=D0=BF=D0=B8=D1=81=D0=B8?= =?UTF-8?q?=20=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=BE=D0=B2=20=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BE=D1=85=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/DTOs/ChangeRecordDto.cs | 10 ++++++++++ back/Contracts/Mappers/ChangeRecordMapper.cs | 15 +++++++++++++++ back/Contracts/Repositories/IChangeRecordRepo.cs | 13 +++++++++++++ back/Contracts/SearchModels/ChangeRecordSearch.cs | 9 +++++++++ back/Contracts/Services/IChangeRecordService.cs | 13 +++++++++++++ .../Contracts/ViewModels/ChangeRecordViewModel.cs | 8 ++++++++ 6 files changed, 68 insertions(+) create mode 100644 back/Contracts/DTOs/ChangeRecordDto.cs create mode 100644 back/Contracts/Mappers/ChangeRecordMapper.cs create mode 100644 back/Contracts/Repositories/IChangeRecordRepo.cs create mode 100644 back/Contracts/SearchModels/ChangeRecordSearch.cs create mode 100644 back/Contracts/Services/IChangeRecordService.cs create mode 100644 back/Contracts/ViewModels/ChangeRecordViewModel.cs diff --git a/back/Contracts/DTOs/ChangeRecordDto.cs b/back/Contracts/DTOs/ChangeRecordDto.cs new file mode 100644 index 0000000..9950fb9 --- /dev/null +++ b/back/Contracts/DTOs/ChangeRecordDto.cs @@ -0,0 +1,10 @@ +namespace Contracts.DTO; + +public interface ChangeRecordDto +{ + public Guid Id { get; set; } + public Guid UserId { get; set; } + public Guid SpendingGroupId { get; set; } + public decimal Sum { get; set; } + public DateTime ChangedAt { get; set; } +} \ No newline at end of file diff --git a/back/Contracts/Mappers/ChangeRecordMapper.cs b/back/Contracts/Mappers/ChangeRecordMapper.cs new file mode 100644 index 0000000..a6075c4 --- /dev/null +++ b/back/Contracts/Mappers/ChangeRecordMapper.cs @@ -0,0 +1,15 @@ +using Contracts.DTO; +using Contracts.ViewModels; + +namespace Contracts.Mappers; + +public static class ChangeRecordMapper +{ + public static ChangeRecordViewModel ToView(this ChangeRecordDto changeRecord) + => new() + { + Id = changeRecord.Id, + Sum = changeRecord.Sum, + ChangedAt = changeRecord.ChangedAt + }; +} \ No newline at end of file diff --git a/back/Contracts/Repositories/IChangeRecordRepo.cs b/back/Contracts/Repositories/IChangeRecordRepo.cs new file mode 100644 index 0000000..d8e835e --- /dev/null +++ b/back/Contracts/Repositories/IChangeRecordRepo.cs @@ -0,0 +1,13 @@ +using Contracts.DTO; +using Contracts.SearchModels; + +namespace Contracts.Repositories; + +public interface IChangeRecordRepo +{ + Task Create(ChangeRecordDto changeRecord); + Task Get(ChangeRecordSearch search); + Task> GetList(ChangeRecordSearch? search = null); + Task Update(ChangeRecordDto changeRecord); + Task Delete(ChangeRecordSearch search); +} \ No newline at end of file diff --git a/back/Contracts/SearchModels/ChangeRecordSearch.cs b/back/Contracts/SearchModels/ChangeRecordSearch.cs new file mode 100644 index 0000000..65a5094 --- /dev/null +++ b/back/Contracts/SearchModels/ChangeRecordSearch.cs @@ -0,0 +1,9 @@ +namespace Contracts.SearchModels; + +public class ChangeRecordSearch +{ + public Guid? Id { get; set; } + public Guid? SpendingGroupId { get; set; } + public DateTime? From { get; set; } + public DateTime? To { get; set; } +} \ No newline at end of file diff --git a/back/Contracts/Services/IChangeRecordService.cs b/back/Contracts/Services/IChangeRecordService.cs new file mode 100644 index 0000000..9dd0832 --- /dev/null +++ b/back/Contracts/Services/IChangeRecordService.cs @@ -0,0 +1,13 @@ +using Contracts.DTO; +using Contracts.SearchModels; +using Contracts.ViewModels; + +namespace Contracts.Services; + +public interface IChangeRecordService +{ + Task Create(ChangeRecordDto model); + Task Update(ChangeRecordDto model); + Task Delete(ChangeRecordSearch search); + Task> GetList(ChangeRecordSearch search); +} \ No newline at end of file diff --git a/back/Contracts/ViewModels/ChangeRecordViewModel.cs b/back/Contracts/ViewModels/ChangeRecordViewModel.cs new file mode 100644 index 0000000..3a75eb3 --- /dev/null +++ b/back/Contracts/ViewModels/ChangeRecordViewModel.cs @@ -0,0 +1,8 @@ +namespace Contracts.ViewModels; + +public class ChangeRecordViewModel +{ + public Guid Id { get; set; } + public decimal Sum { get; set; } + public DateTime ChangedAt { get; set; } +} \ No newline at end of file -- 2.25.1 From f94d4ed246e696096acf209abbffd114659fa9eb Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 26 Nov 2024 22:11:17 +0400 Subject: [PATCH 15/25] =?UTF-8?q?add:=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81,=20=D1=80=D0=B5=D0=BF=D0=BE=D0=B7=D0=B8=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=B9=20=D0=B8=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=20=D0=B7=D0=B0=D0=BF=D0=B8=D1=81=D0=B8=20=D0=B8=D0=B7?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B1=D0=B0=D0=BB?= =?UTF-8?q?=D0=B0=D0=BD=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/DTOs/ChangeRecordDto.cs | 2 +- back/Infrastructure/DatabaseContext.cs | 1 + back/Infrastructure/Models/Changerecord.cs | 24 +++++ .../Repositories/ChangeRecordRepo.cs | 94 +++++++++++++++++++ .../Support/Mappers/ChangeRecordMapper.cs | 27 ++++++ back/Services/Domain/ChangeRecordService.cs | 52 ++++++++++ 6 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 back/Infrastructure/Models/Changerecord.cs create mode 100644 back/Infrastructure/Repositories/ChangeRecordRepo.cs create mode 100644 back/Infrastructure/Support/Mappers/ChangeRecordMapper.cs create mode 100644 back/Services/Domain/ChangeRecordService.cs diff --git a/back/Contracts/DTOs/ChangeRecordDto.cs b/back/Contracts/DTOs/ChangeRecordDto.cs index 9950fb9..f95da9c 100644 --- a/back/Contracts/DTOs/ChangeRecordDto.cs +++ b/back/Contracts/DTOs/ChangeRecordDto.cs @@ -1,6 +1,6 @@ namespace Contracts.DTO; -public interface ChangeRecordDto +public class ChangeRecordDto { public Guid Id { get; set; } public Guid UserId { get; set; } diff --git a/back/Infrastructure/DatabaseContext.cs b/back/Infrastructure/DatabaseContext.cs index 6901ea8..02afad7 100644 --- a/back/Infrastructure/DatabaseContext.cs +++ b/back/Infrastructure/DatabaseContext.cs @@ -13,4 +13,5 @@ public class DatabaseContext : DbContext public DbSet Users { get; set; } = null!; public DbSet SpendingGroups { get; set; } = null!; + public DbSet ChangeRecords { get; set; } = null!; } \ No newline at end of file diff --git a/back/Infrastructure/Models/Changerecord.cs b/back/Infrastructure/Models/Changerecord.cs new file mode 100644 index 0000000..4f9a5e9 --- /dev/null +++ b/back/Infrastructure/Models/Changerecord.cs @@ -0,0 +1,24 @@ +using Contracts.DTO; + +namespace Infrastructure.Models; + +public class ChangeRecord +{ + public Guid Id { get; set; } + public decimal Sum { get; set; } + public DateTime ChangedAt { get; set; } + + public Guid UserId { get; set; } + public User User { get; set; } = null!; + + public Guid SpendingGroupId { get; set; } + public SpendingGroup SpendingGroup { get; set; } = null!; + + public void Update(ChangeRecordDto changeRecordDto) + { + Id = changeRecordDto.Id; + Sum = changeRecordDto.Sum; + ChangedAt = changeRecordDto.ChangedAt; + SpendingGroupId = changeRecordDto.SpendingGroupId; + } +} \ No newline at end of file diff --git a/back/Infrastructure/Repositories/ChangeRecordRepo.cs b/back/Infrastructure/Repositories/ChangeRecordRepo.cs new file mode 100644 index 0000000..cafeb2d --- /dev/null +++ b/back/Infrastructure/Repositories/ChangeRecordRepo.cs @@ -0,0 +1,94 @@ +using Contracts.DTO; +using Contracts.Repositories; +using Contracts.SearchModels; +using Infrastructure.Support.Mappers; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure.Repositories; + +public class ChangeRecordRepo : IChangeRecordRepo +{ + public readonly IDbContextFactory _factory; + + public ChangeRecordRepo(IDbContextFactory factory) + { + _factory = factory; + } + + public async Task Create(ChangeRecordDto changeRecord) + { + using var context = _factory.CreateDbContext(); + + var createdRecord = await context.ChangeRecords.AddAsync(changeRecord.ToModel()); + + await context.SaveChangesAsync(); + return createdRecord.Entity.ToDto(); + } + + public async Task Delete(ChangeRecordSearch search) + { + using var context = _factory.CreateDbContext(); + + var record = await context.ChangeRecords + .FirstOrDefaultAsync(x => x.Id == search.Id); + if (record == null) + { + return null; + } + + context.ChangeRecords.Remove(record); + await context.SaveChangesAsync(); + return record.ToDto(); + } + + public async Task Get(ChangeRecordSearch search) + { + using var context = _factory.CreateDbContext(); + + var record = await context.ChangeRecords + .FirstOrDefaultAsync(x => x.Id == search.Id); + if (record == null) + { + return null; + } + return record.ToDto(); + } + + public async Task> GetList(ChangeRecordSearch? search = null) + { + using var context = _factory.CreateDbContext(); + + var query = context.ChangeRecords.AsQueryable(); + + if (search != null) + { + if (search.SpendingGroupId.HasValue) + { + query = query.Where(x => x.SpendingGroupId == search.SpendingGroupId); + } + if (search.From.HasValue && search.To.HasValue) + { + query = query.Where(x => x.ChangedAt >= search.From && x.ChangedAt <= search.To); + } + } + return query.Select(x => x.ToDto()); + } + + public async Task Update(ChangeRecordDto changeRecord) + { + using var context = _factory.CreateDbContext(); + + var existingRecord = await context.ChangeRecords + .FirstOrDefaultAsync(x => x.Id == changeRecord.Id); + + if (existingRecord == null) + { + return null; + } + + existingRecord.Update(changeRecord); + context.ChangeRecords.Update(existingRecord); + await context.SaveChangesAsync(); + return existingRecord.ToDto(); + } +} diff --git a/back/Infrastructure/Support/Mappers/ChangeRecordMapper.cs b/back/Infrastructure/Support/Mappers/ChangeRecordMapper.cs new file mode 100644 index 0000000..cd5d76e --- /dev/null +++ b/back/Infrastructure/Support/Mappers/ChangeRecordMapper.cs @@ -0,0 +1,27 @@ +using Contracts.DTO; +using Infrastructure.Models; + +namespace Infrastructure.Support.Mappers; + +public static class ChangeRecordMapper +{ + public static ChangeRecordDto ToDto(this ChangeRecord changeRecord) + => new() + { + Id = changeRecord.Id, + Sum = changeRecord.Sum, + ChangedAt = changeRecord.ChangedAt, + SpendingGroupId = changeRecord.SpendingGroupId, + UserId = changeRecord.UserId + }; + + public static ChangeRecord ToModel(this ChangeRecordDto changeRecord) + => new() + { + Id = changeRecord.Id, + Sum = changeRecord.Sum, + ChangedAt = changeRecord.ChangedAt, + SpendingGroupId = changeRecord.SpendingGroupId, + UserId = changeRecord.UserId + }; +} \ No newline at end of file diff --git a/back/Services/Domain/ChangeRecordService.cs b/back/Services/Domain/ChangeRecordService.cs new file mode 100644 index 0000000..b90bafa --- /dev/null +++ b/back/Services/Domain/ChangeRecordService.cs @@ -0,0 +1,52 @@ +using Contracts.DTO; +using Contracts.Mappers; +using Contracts.Repositories; +using Contracts.SearchModels; +using Contracts.Services; +using Contracts.ViewModels; + +namespace Services.Domain; + +public class ChangeRecordService : IChangeRecordService +{ + private readonly IChangeRecordRepo _changeRecordRepo; + + public ChangeRecordService(IChangeRecordRepo changeRecordRepo) + { + _changeRecordRepo = changeRecordRepo; + } + + public async Task Create(ChangeRecordDto model) + { + var record = await _changeRecordRepo.Create(model); + + return record.ToView(); + } + + public async Task Delete(ChangeRecordSearch search) + { + var record = await _changeRecordRepo.Delete(search); + if (record == null) + { + throw new EntryPointNotFoundException("При удалении не получилось найти запись измнения баланса"); + } + return record.ToView(); + } + + public async Task> GetList(ChangeRecordSearch search) + { + var records = await _changeRecordRepo.GetList(search); + + return records.Select(x => x.ToView()).ToList(); + } + + public async Task Update(ChangeRecordDto model) + { + var record = await _changeRecordRepo.Update(model); + if (record == null) + { + throw new EntryPointNotFoundException("При изменении не получилось найти запись измнения баланса"); + } + return record.ToView(); + } +} -- 2.25.1 From 3684f1479cc8a70a776204b5ed73c49ee8e1e3ee Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 26 Nov 2024 22:46:38 +0400 Subject: [PATCH 16/25] =?UTF-8?q?add:=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=D1=8B=20=D0=B7=D0=B0=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B5=D0=B9=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B1=D0=B0=D0=BB=D0=B0=D0=BD=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ChangeRecordController.cs | 86 +++++++++++++++++++ .../Controllers/SpendingGroupController.cs | 10 ++- .../Extensions/AddDomainServicesExt.cs | 2 + back/Controllers/Extensions/AddReposExt.cs | 1 + 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 back/Controllers/Controllers/ChangeRecordController.cs diff --git a/back/Controllers/Controllers/ChangeRecordController.cs b/back/Controllers/Controllers/ChangeRecordController.cs new file mode 100644 index 0000000..8ae218a --- /dev/null +++ b/back/Controllers/Controllers/ChangeRecordController.cs @@ -0,0 +1,86 @@ +using Contracts.DTO; +using Contracts.SearchModels; +using Contracts.Services; +using Contracts.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Services.Support.Exceptions; + +namespace Controllers.Controllers; + +[Route("api/[controller]")] +[ApiController] +public class ChangeRecordController : ControllerBase +{ + private readonly IChangeRecordService _changeRecordService; + + public ChangeRecordController(IChangeRecordService changeRecordService) + { + _changeRecordService = changeRecordService; + } + + [HttpPost] + public async Task> CreateChangeRecord( + [FromBody] ChangeRecordDto dto) + { + try + { + var record = await _changeRecordService.Create(dto); + return CreatedAtAction(nameof(GetChangeRecords), record); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpGet] + public async Task>> GetChangeRecords( + [FromQuery] ChangeRecordSearch search) + { + try + { + var records = await _changeRecordService.GetList(search); + return Ok(records); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpPatch] + public async Task> UpdateChangeRecord([FromBody] ChangeRecordDto dto) + { + try + { + var record = await _changeRecordService.Update(dto); + return Ok(record); + } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpDelete] + public async Task DeleteChangeRecord([FromQuery] ChangeRecordSearch search) + { + try + { + var record = await _changeRecordService.Delete(search); + return Ok(record); + } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } +} \ No newline at end of file diff --git a/back/Controllers/Controllers/SpendingGroupController.cs b/back/Controllers/Controllers/SpendingGroupController.cs index 36ad45b..635c06a 100644 --- a/back/Controllers/Controllers/SpendingGroupController.cs +++ b/back/Controllers/Controllers/SpendingGroupController.cs @@ -17,12 +17,16 @@ public class SpendingGroupController : ControllerBase { _spendingGroupService = spendingGroupService; } - [HttpGet("details")] + [HttpGet("{id}")] public async Task> GetSpendingGroup( + Guid id, [FromQuery] SpendingGroupSearch search) { try { + search ??= new(); + search.Id = id; + var group = await _spendingGroupService.GetDetails(search); return Ok(group); } @@ -74,6 +78,10 @@ public class SpendingGroupController : ControllerBase var group = await _spendingGroupService.Create(dto); return CreatedAtAction(nameof(GetSpendingGroup), new { id = group.Id }, group); } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } catch (Exception ex) { return StatusCode(500, ex.Message); diff --git a/back/Controllers/Extensions/AddDomainServicesExt.cs b/back/Controllers/Extensions/AddDomainServicesExt.cs index 575d50a..97d9065 100644 --- a/back/Controllers/Extensions/AddDomainServicesExt.cs +++ b/back/Controllers/Extensions/AddDomainServicesExt.cs @@ -11,5 +11,7 @@ public static class AddDomainServicesExtension services.AddTransient(); services.AddTransient(); + + services.AddTransient(); } } \ No newline at end of file diff --git a/back/Controllers/Extensions/AddReposExt.cs b/back/Controllers/Extensions/AddReposExt.cs index d019ce0..9793b6e 100644 --- a/back/Controllers/Extensions/AddReposExt.cs +++ b/back/Controllers/Extensions/AddReposExt.cs @@ -11,5 +11,6 @@ public static class AddReposExtension { services.AddTransient(); services.AddTransient(); + services.AddTransient(); } } \ No newline at end of file -- 2.25.1 From af936e519c95b2cb394394c7f7ae6e142c49d5d5 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 27 Nov 2024 00:06:15 +0400 Subject: [PATCH 17/25] =?UTF-8?q?add:=20=D0=BC=D0=B8=D0=B3=D1=80=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8=20=D0=B7=D0=B0=D0=BF=D0=B8=D1=81=D0=B8=20=D0=B8=D0=B7?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B1=D0=B0=D0=BB?= =?UTF-8?q?=D0=B0=D0=BD=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...20241126185002_AddChangeRecord.Designer.cs | 134 ++++++++++++++++++ .../20241126185002_AddChangeRecord.cs | 59 ++++++++ .../DatabaseContextModelSnapshot.cs | 46 ++++++ 3 files changed, 239 insertions(+) create mode 100644 back/Infrastructure/Migrations/20241126185002_AddChangeRecord.Designer.cs create mode 100644 back/Infrastructure/Migrations/20241126185002_AddChangeRecord.cs diff --git a/back/Infrastructure/Migrations/20241126185002_AddChangeRecord.Designer.cs b/back/Infrastructure/Migrations/20241126185002_AddChangeRecord.Designer.cs new file mode 100644 index 0000000..fc60907 --- /dev/null +++ b/back/Infrastructure/Migrations/20241126185002_AddChangeRecord.Designer.cs @@ -0,0 +1,134 @@ +// +using System; +using Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20241126185002_AddChangeRecord")] + partial class AddChangeRecord + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Models.ChangeRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChangedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SpendingGroupId") + .HasColumnType("uuid"); + + b.Property("Sum") + .HasColumnType("numeric"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SpendingGroupId"); + + b.HasIndex("UserId"); + + b.ToTable("ChangeRecords"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("SpendingGroups"); + }); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Balance") + .HasColumnType("numeric"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Infrastructure.Models.ChangeRecord", b => + { + b.HasOne("Infrastructure.Models.SpendingGroup", "SpendingGroup") + .WithMany() + .HasForeignKey("SpendingGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpendingGroup"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.HasOne("Infrastructure.Models.User", "User") + .WithMany("SpendingGroups") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Navigation("SpendingGroups"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/back/Infrastructure/Migrations/20241126185002_AddChangeRecord.cs b/back/Infrastructure/Migrations/20241126185002_AddChangeRecord.cs new file mode 100644 index 0000000..0f8a61d --- /dev/null +++ b/back/Infrastructure/Migrations/20241126185002_AddChangeRecord.cs @@ -0,0 +1,59 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddChangeRecord : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ChangeRecords", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Sum = table.Column(type: "numeric", nullable: false), + ChangedAt = table.Column(type: "timestamp with time zone", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + SpendingGroupId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ChangeRecords", x => x.Id); + table.ForeignKey( + name: "FK_ChangeRecords_SpendingGroups_SpendingGroupId", + column: x => x.SpendingGroupId, + principalTable: "SpendingGroups", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ChangeRecords_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ChangeRecords_SpendingGroupId", + table: "ChangeRecords", + column: "SpendingGroupId"); + + migrationBuilder.CreateIndex( + name: "IX_ChangeRecords_UserId", + table: "ChangeRecords", + column: "UserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ChangeRecords"); + } + } +} diff --git a/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs b/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs index 201a211..82c6874 100644 --- a/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs +++ b/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs @@ -22,6 +22,33 @@ namespace Infrastructure.Migrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("Infrastructure.Models.ChangeRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChangedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SpendingGroupId") + .HasColumnType("uuid"); + + b.Property("Sum") + .HasColumnType("numeric"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SpendingGroupId"); + + b.HasIndex("UserId"); + + b.ToTable("ChangeRecords"); + }); + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => { b.Property("Id") @@ -64,6 +91,25 @@ namespace Infrastructure.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("Infrastructure.Models.ChangeRecord", b => + { + b.HasOne("Infrastructure.Models.SpendingGroup", "SpendingGroup") + .WithMany() + .HasForeignKey("SpendingGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Infrastructure.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpendingGroup"); + + b.Navigation("User"); + }); + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => { b.HasOne("Infrastructure.Models.User", "User") -- 2.25.1 From cbc45bbecbd97c17690f417227613c068343b487 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 27 Nov 2024 00:19:35 +0400 Subject: [PATCH 18/25] =?UTF-8?q?fix:=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D0=B7=D0=B0=D0=BF=D0=B8=D1=81=D1=8C=20=D0=B8=D0=B7?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B1=D0=B0=D0=BB?= =?UTF-8?q?=D0=B0=D0=BD=D1=81=D0=B0=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D1=8F?= =?UTF-8?q?=D0=B5=D1=82=20=D0=B1=D0=B0=D0=BB=D0=B0=D0=BD=D1=81=20=D0=9B?= =?UTF-8?q?=D0=9E=D0=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/Repositories/IUserRepo.cs | 1 + back/Infrastructure/Repositories/UserRepo.cs | 19 +++++++++++++++++++ back/Services/Domain/ChangeRecordService.cs | 9 ++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/back/Contracts/Repositories/IUserRepo.cs b/back/Contracts/Repositories/IUserRepo.cs index ff32390..dab5cd0 100644 --- a/back/Contracts/Repositories/IUserRepo.cs +++ b/back/Contracts/Repositories/IUserRepo.cs @@ -8,5 +8,6 @@ public interface IUserRepo public Task Get(UserSearch search); public Task Create(UserDto user); public Task Update(UserDto user); + public Task ChangeBalance(UserSearch search, decimal amount); public Task Delete(UserSearch search); } \ No newline at end of file diff --git a/back/Infrastructure/Repositories/UserRepo.cs b/back/Infrastructure/Repositories/UserRepo.cs index a075d6d..e3a450d 100644 --- a/back/Infrastructure/Repositories/UserRepo.cs +++ b/back/Infrastructure/Repositories/UserRepo.cs @@ -15,6 +15,25 @@ public class UserRepo : IUserRepo _factory = factory; } + public async Task ChangeBalance(UserSearch search, decimal amount) + { + using var context = _factory.CreateDbContext(); + + var user = await context.Users + .FirstOrDefaultAsync(x => x.Id == search.Id + || x.Name == search.Name); + + if (user == null) + { + return false; + } + + user.Balance += amount; + context.Users.Update(user); + await context.SaveChangesAsync(); + return true; + } + public async Task Create(UserDto user) { using var context = _factory.CreateDbContext(); diff --git a/back/Services/Domain/ChangeRecordService.cs b/back/Services/Domain/ChangeRecordService.cs index b90bafa..4df6138 100644 --- a/back/Services/Domain/ChangeRecordService.cs +++ b/back/Services/Domain/ChangeRecordService.cs @@ -10,16 +10,20 @@ namespace Services.Domain; public class ChangeRecordService : IChangeRecordService { private readonly IChangeRecordRepo _changeRecordRepo; + private readonly IUserRepo _userRepo; - public ChangeRecordService(IChangeRecordRepo changeRecordRepo) + public ChangeRecordService(IChangeRecordRepo changeRecordRepo, IUserRepo userRepo) { _changeRecordRepo = changeRecordRepo; + _userRepo = userRepo; } public async Task Create(ChangeRecordDto model) { var record = await _changeRecordRepo.Create(model); + await _userRepo.ChangeBalance(new() { Id = model.UserId }, model.Sum); + return record.ToView(); } @@ -30,6 +34,8 @@ public class ChangeRecordService : IChangeRecordService { throw new EntryPointNotFoundException("При удалении не получилось найти запись измнения баланса"); } + // Возвращает баланс обратно + await _userRepo.ChangeBalance(new() { Id = record.UserId }, -record.Sum); return record.ToView(); } @@ -47,6 +53,7 @@ public class ChangeRecordService : IChangeRecordService { throw new EntryPointNotFoundException("При изменении не получилось найти запись измнения баланса"); } + await _userRepo.ChangeBalance(new() { Id = model.UserId }, model.Sum - record.Sum); return record.ToView(); } } -- 2.25.1 From 6d734bbf798f0950197343df9e97708edd378779 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 27 Nov 2024 00:26:18 +0400 Subject: [PATCH 19/25] =?UTF-8?q?fix:=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D0=BF=D1=80=D0=B8=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B8=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B?= =?UTF-8?q?=20=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=BE=D0=B2=20=D1=82?= =?UTF-8?q?=D0=B0=D0=BA=D0=B6=D0=B5=20=D0=B2=D0=BE=D0=B7=D0=B2=D1=80=D0=B0?= =?UTF-8?q?=D1=89=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D1=81=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=BE=D0=BA=20=D0=B7=D0=B0=D0=BF=D0=B8=D1=81=D0=B5=D0=B9=20?= =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B1?= =?UTF-8?q?=D0=B0=D0=BB=D0=B0=D0=BD=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/DTOs/SpendingGroupDto.cs | 1 + back/Infrastructure/Models/Changerecord.cs | 2 +- back/Infrastructure/Models/SpendingGroup.cs | 1 + back/Infrastructure/Models/User.cs | 1 + back/Infrastructure/Repositories/SpendingGroupRepo.cs | 3 ++- back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs | 3 ++- 6 files changed, 8 insertions(+), 3 deletions(-) diff --git a/back/Contracts/DTOs/SpendingGroupDto.cs b/back/Contracts/DTOs/SpendingGroupDto.cs index cf03704..2adf558 100644 --- a/back/Contracts/DTOs/SpendingGroupDto.cs +++ b/back/Contracts/DTOs/SpendingGroupDto.cs @@ -5,4 +5,5 @@ public class SpendingGroupDto public Guid Id { get; set; } public string Name { get; set; } = string.Empty; public Guid UserId { get; set; } + public List ChangeRecords { get; set; } = new(); } \ No newline at end of file diff --git a/back/Infrastructure/Models/Changerecord.cs b/back/Infrastructure/Models/Changerecord.cs index 4f9a5e9..64b06c4 100644 --- a/back/Infrastructure/Models/Changerecord.cs +++ b/back/Infrastructure/Models/Changerecord.cs @@ -12,7 +12,7 @@ public class ChangeRecord public User User { get; set; } = null!; public Guid SpendingGroupId { get; set; } - public SpendingGroup SpendingGroup { get; set; } = null!; + public SpendingGroup? SpendingGroup { get; set; } public void Update(ChangeRecordDto changeRecordDto) { diff --git a/back/Infrastructure/Models/SpendingGroup.cs b/back/Infrastructure/Models/SpendingGroup.cs index b3d5d20..ec9d48e 100644 --- a/back/Infrastructure/Models/SpendingGroup.cs +++ b/back/Infrastructure/Models/SpendingGroup.cs @@ -7,4 +7,5 @@ public class SpendingGroup public Guid UserId { get; set; } public User User { get; set; } = null!; + public List ChangeRecords { get; set; } = new(); } \ No newline at end of file diff --git a/back/Infrastructure/Models/User.cs b/back/Infrastructure/Models/User.cs index add673c..469e15e 100644 --- a/back/Infrastructure/Models/User.cs +++ b/back/Infrastructure/Models/User.cs @@ -10,6 +10,7 @@ public class User public string Password { get; set; } = null!; public decimal Balance { get; set; } public List? SpendingGroups { get; set; } + public List? ChangeRecords { get; set; } public void Update(UserDto userDto) { diff --git a/back/Infrastructure/Repositories/SpendingGroupRepo.cs b/back/Infrastructure/Repositories/SpendingGroupRepo.cs index 38a69d0..d7e879e 100644 --- a/back/Infrastructure/Repositories/SpendingGroupRepo.cs +++ b/back/Infrastructure/Repositories/SpendingGroupRepo.cs @@ -46,6 +46,7 @@ public class SpendingGroupRepo : ISpendingGroupRepo using var context = _factory.CreateDbContext(); var group = await context.SpendingGroups + .Include(x => x.ChangeRecords) .FirstOrDefaultAsync(x => x.Id == search.Id || x.Name == search.Name); @@ -71,7 +72,7 @@ public class SpendingGroupRepo : ISpendingGroupRepo } } - return await query.Select(x => x.ToDto()).ToListAsync(); + return await query.Include(x => x.ChangeRecords).Select(x => x.ToDto()).ToListAsync(); } public async Task Update(SpendingGroupDto spendingGroup) diff --git a/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs b/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs index 94a39b6..1cb0585 100644 --- a/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs +++ b/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs @@ -10,7 +10,8 @@ public static class SpendingGroupMapper { Id = group.Id, Name = group.Name, - UserId = group.UserId + UserId = group.UserId, + ChangeRecords = group.ChangeRecords.Select(x => x.ToDto()).ToList() }; public static SpendingGroup ToModel(this SpendingGroupDto group) => new() -- 2.25.1 From ba8c4a76d873f13fc2fc589e87e5ec98b184511c Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 27 Nov 2024 01:16:08 +0400 Subject: [PATCH 20/25] =?UTF-8?q?fix:=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D0=BD=D0=B5=D1=82=20=D0=BA=D0=B0=D1=81=D0=BA=D0=B0?= =?UTF-8?q?=D0=B4=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B7=D0=B0=D0=BF=D0=B8=D1=81=D0=B5=D0=B9?= =?UTF-8?q?=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=B1=D0=B0=D0=BB=D0=B0=D0=BD=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/DTOs/ChangeRecordDto.cs | 2 +- back/Contracts/Mappers/SpendingGroupMapper.cs | 3 +- .../Services/IChangeRecordService.cs | 2 +- .../ViewModels/SpendingGroupViewModel.cs | 1 + .../Controllers/ChangeRecordController.cs | 14 ++ ...20241126210947_fixChangeRecord.Designer.cs | 139 ++++++++++++++++++ .../20241126210947_fixChangeRecord.cs | 60 ++++++++ .../DatabaseContextModelSnapshot.cs | 17 ++- back/Infrastructure/Models/Changerecord.cs | 2 +- back/Infrastructure/Models/SpendingGroup.cs | 2 +- .../Repositories/ChangeRecordRepo.cs | 2 +- .../Support/Mappers/SpendingGroupMapper.cs | 2 +- back/Services/Domain/ChangeRecordService.cs | 2 +- back/Services/Domain/SpendingGroupService.cs | 2 +- 14 files changed, 235 insertions(+), 15 deletions(-) create mode 100644 back/Infrastructure/Migrations/20241126210947_fixChangeRecord.Designer.cs create mode 100644 back/Infrastructure/Migrations/20241126210947_fixChangeRecord.cs diff --git a/back/Contracts/DTOs/ChangeRecordDto.cs b/back/Contracts/DTOs/ChangeRecordDto.cs index f95da9c..21d140c 100644 --- a/back/Contracts/DTOs/ChangeRecordDto.cs +++ b/back/Contracts/DTOs/ChangeRecordDto.cs @@ -4,7 +4,7 @@ public class ChangeRecordDto { public Guid Id { get; set; } public Guid UserId { get; set; } - public Guid SpendingGroupId { get; set; } + public Guid? SpendingGroupId { get; set; } public decimal Sum { get; set; } public DateTime ChangedAt { get; set; } } \ No newline at end of file diff --git a/back/Contracts/Mappers/SpendingGroupMapper.cs b/back/Contracts/Mappers/SpendingGroupMapper.cs index 7eca2d3..92877a5 100644 --- a/back/Contracts/Mappers/SpendingGroupMapper.cs +++ b/back/Contracts/Mappers/SpendingGroupMapper.cs @@ -9,6 +9,7 @@ public static class SpendingGroupMapper => new() { Id = spendingGroup.Id, - Name = spendingGroup.Name + Name = spendingGroup.Name, + ChangeRecords = spendingGroup.ChangeRecords.Select(x => x.ToView()).ToList() }; } \ No newline at end of file diff --git a/back/Contracts/Services/IChangeRecordService.cs b/back/Contracts/Services/IChangeRecordService.cs index 9dd0832..b9939e0 100644 --- a/back/Contracts/Services/IChangeRecordService.cs +++ b/back/Contracts/Services/IChangeRecordService.cs @@ -9,5 +9,5 @@ public interface IChangeRecordService Task Create(ChangeRecordDto model); Task Update(ChangeRecordDto model); Task Delete(ChangeRecordSearch search); - Task> GetList(ChangeRecordSearch search); + Task> GetList(ChangeRecordSearch? search = null); } \ No newline at end of file diff --git a/back/Contracts/ViewModels/SpendingGroupViewModel.cs b/back/Contracts/ViewModels/SpendingGroupViewModel.cs index 12cba2d..c22ae62 100644 --- a/back/Contracts/ViewModels/SpendingGroupViewModel.cs +++ b/back/Contracts/ViewModels/SpendingGroupViewModel.cs @@ -4,4 +4,5 @@ public class SpendingGroupViewModel { public Guid Id { get; set; } public string Name { get; set; } = string.Empty; + public List ChangeRecords { get; set; } = new(); } \ No newline at end of file diff --git a/back/Controllers/Controllers/ChangeRecordController.cs b/back/Controllers/Controllers/ChangeRecordController.cs index 8ae218a..c823a99 100644 --- a/back/Controllers/Controllers/ChangeRecordController.cs +++ b/back/Controllers/Controllers/ChangeRecordController.cs @@ -34,6 +34,20 @@ public class ChangeRecordController : ControllerBase } [HttpGet] + public async Task>> GetChangeRecords() + { + try + { + var records = await _changeRecordService.GetList(); + return Ok(records); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpGet("filter")] public async Task>> GetChangeRecords( [FromQuery] ChangeRecordSearch search) { diff --git a/back/Infrastructure/Migrations/20241126210947_fixChangeRecord.Designer.cs b/back/Infrastructure/Migrations/20241126210947_fixChangeRecord.Designer.cs new file mode 100644 index 0000000..937117a --- /dev/null +++ b/back/Infrastructure/Migrations/20241126210947_fixChangeRecord.Designer.cs @@ -0,0 +1,139 @@ +// +using System; +using Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20241126210947_fixChangeRecord")] + partial class fixChangeRecord + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Models.ChangeRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChangedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SpendingGroupId") + .HasColumnType("uuid"); + + b.Property("Sum") + .HasColumnType("numeric"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SpendingGroupId"); + + b.HasIndex("UserId"); + + b.ToTable("ChangeRecords"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("SpendingGroups"); + }); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Balance") + .HasColumnType("numeric"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Infrastructure.Models.ChangeRecord", b => + { + b.HasOne("Infrastructure.Models.SpendingGroup", "SpendingGroup") + .WithMany("ChangeRecords") + .HasForeignKey("SpendingGroupId"); + + b.HasOne("Infrastructure.Models.User", "User") + .WithMany("ChangeRecords") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpendingGroup"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.HasOne("Infrastructure.Models.User", "User") + .WithMany("SpendingGroups") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.Navigation("ChangeRecords"); + }); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Navigation("ChangeRecords"); + + b.Navigation("SpendingGroups"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/back/Infrastructure/Migrations/20241126210947_fixChangeRecord.cs b/back/Infrastructure/Migrations/20241126210947_fixChangeRecord.cs new file mode 100644 index 0000000..68c6484 --- /dev/null +++ b/back/Infrastructure/Migrations/20241126210947_fixChangeRecord.cs @@ -0,0 +1,60 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class fixChangeRecord : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ChangeRecords_SpendingGroups_SpendingGroupId", + table: "ChangeRecords"); + + migrationBuilder.AlterColumn( + name: "SpendingGroupId", + table: "ChangeRecords", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AddForeignKey( + name: "FK_ChangeRecords_SpendingGroups_SpendingGroupId", + table: "ChangeRecords", + column: "SpendingGroupId", + principalTable: "SpendingGroups", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ChangeRecords_SpendingGroups_SpendingGroupId", + table: "ChangeRecords"); + + migrationBuilder.AlterColumn( + name: "SpendingGroupId", + table: "ChangeRecords", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_ChangeRecords_SpendingGroups_SpendingGroupId", + table: "ChangeRecords", + column: "SpendingGroupId", + principalTable: "SpendingGroups", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs b/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs index 82c6874..2c7a5ef 100644 --- a/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs +++ b/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs @@ -31,7 +31,7 @@ namespace Infrastructure.Migrations b.Property("ChangedAt") .HasColumnType("timestamp with time zone"); - b.Property("SpendingGroupId") + b.Property("SpendingGroupId") .HasColumnType("uuid"); b.Property("Sum") @@ -94,13 +94,11 @@ namespace Infrastructure.Migrations modelBuilder.Entity("Infrastructure.Models.ChangeRecord", b => { b.HasOne("Infrastructure.Models.SpendingGroup", "SpendingGroup") - .WithMany() - .HasForeignKey("SpendingGroupId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .WithMany("ChangeRecords") + .HasForeignKey("SpendingGroupId"); b.HasOne("Infrastructure.Models.User", "User") - .WithMany() + .WithMany("ChangeRecords") .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -121,8 +119,15 @@ namespace Infrastructure.Migrations b.Navigation("User"); }); + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.Navigation("ChangeRecords"); + }); + modelBuilder.Entity("Infrastructure.Models.User", b => { + b.Navigation("ChangeRecords"); + b.Navigation("SpendingGroups"); }); #pragma warning restore 612, 618 diff --git a/back/Infrastructure/Models/Changerecord.cs b/back/Infrastructure/Models/Changerecord.cs index 64b06c4..60cb92d 100644 --- a/back/Infrastructure/Models/Changerecord.cs +++ b/back/Infrastructure/Models/Changerecord.cs @@ -11,7 +11,7 @@ public class ChangeRecord public Guid UserId { get; set; } public User User { get; set; } = null!; - public Guid SpendingGroupId { get; set; } + public Guid? SpendingGroupId { get; set; } public SpendingGroup? SpendingGroup { get; set; } public void Update(ChangeRecordDto changeRecordDto) diff --git a/back/Infrastructure/Models/SpendingGroup.cs b/back/Infrastructure/Models/SpendingGroup.cs index ec9d48e..889bdb6 100644 --- a/back/Infrastructure/Models/SpendingGroup.cs +++ b/back/Infrastructure/Models/SpendingGroup.cs @@ -7,5 +7,5 @@ public class SpendingGroup public Guid UserId { get; set; } public User User { get; set; } = null!; - public List ChangeRecords { get; set; } = new(); + public List? ChangeRecords { get; set; } } \ No newline at end of file diff --git a/back/Infrastructure/Repositories/ChangeRecordRepo.cs b/back/Infrastructure/Repositories/ChangeRecordRepo.cs index cafeb2d..6da1839 100644 --- a/back/Infrastructure/Repositories/ChangeRecordRepo.cs +++ b/back/Infrastructure/Repositories/ChangeRecordRepo.cs @@ -71,7 +71,7 @@ public class ChangeRecordRepo : IChangeRecordRepo query = query.Where(x => x.ChangedAt >= search.From && x.ChangedAt <= search.To); } } - return query.Select(x => x.ToDto()); + return await query.Select(x => x.ToDto()).ToListAsync(); } public async Task Update(ChangeRecordDto changeRecord) diff --git a/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs b/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs index 1cb0585..d74490f 100644 --- a/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs +++ b/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs @@ -11,7 +11,7 @@ public static class SpendingGroupMapper Id = group.Id, Name = group.Name, UserId = group.UserId, - ChangeRecords = group.ChangeRecords.Select(x => x.ToDto()).ToList() + ChangeRecords = group.ChangeRecords?.Select(x => x.ToDto()).ToList() ?? [] }; public static SpendingGroup ToModel(this SpendingGroupDto group) => new() diff --git a/back/Services/Domain/ChangeRecordService.cs b/back/Services/Domain/ChangeRecordService.cs index 4df6138..45ed858 100644 --- a/back/Services/Domain/ChangeRecordService.cs +++ b/back/Services/Domain/ChangeRecordService.cs @@ -39,7 +39,7 @@ public class ChangeRecordService : IChangeRecordService return record.ToView(); } - public async Task> GetList(ChangeRecordSearch search) + public async Task> GetList(ChangeRecordSearch? search = null) { var records = await _changeRecordRepo.GetList(search); diff --git a/back/Services/Domain/SpendingGroupService.cs b/back/Services/Domain/SpendingGroupService.cs index b0afed1..e4ff348 100644 --- a/back/Services/Domain/SpendingGroupService.cs +++ b/back/Services/Domain/SpendingGroupService.cs @@ -44,7 +44,7 @@ public class SpendingGroupService : ISpendingGroupService return group.ToView(); } - public async Task> GetList(SpendingGroupSearch search) + public async Task> GetList(SpendingGroupSearch? search = null) { var groups = await _spendingGroupRepo.GetList(search); return groups.Select(x => x.ToView()).ToList(); -- 2.25.1 From b35cd64e80cc0e292469a476782399c7a84cb446 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 27 Nov 2024 01:46:33 +0400 Subject: [PATCH 21/25] =?UTF-8?q?add:=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BA=D1=82=D1=8B=20=D0=BF=D0=BB=D0=B0=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/DTOs/SpendingPlanDto.cs | 10 ++++++++++ back/Contracts/Mappers/SpendingPlanMapper.cs | 16 ++++++++++++++++ back/Contracts/Repositories/ISpendingPlanRepo.cs | 13 +++++++++++++ .../Contracts/SearchModels/SpendingPlanSearch.cs | 6 ++++++ back/Contracts/Services/ISpendingPlan.cs | 14 ++++++++++++++ back/Contracts/ViewModels/SpendingPlanView.cs | 9 +++++++++ 6 files changed, 68 insertions(+) create mode 100644 back/Contracts/DTOs/SpendingPlanDto.cs create mode 100644 back/Contracts/Mappers/SpendingPlanMapper.cs create mode 100644 back/Contracts/Repositories/ISpendingPlanRepo.cs create mode 100644 back/Contracts/SearchModels/SpendingPlanSearch.cs create mode 100644 back/Contracts/Services/ISpendingPlan.cs create mode 100644 back/Contracts/ViewModels/SpendingPlanView.cs diff --git a/back/Contracts/DTOs/SpendingPlanDto.cs b/back/Contracts/DTOs/SpendingPlanDto.cs new file mode 100644 index 0000000..fc88578 --- /dev/null +++ b/back/Contracts/DTOs/SpendingPlanDto.cs @@ -0,0 +1,10 @@ +namespace Contracts.DTO; + +public class SpendingPlanDto +{ + public Guid Id { get; set; } + public Guid SpendingGroupId { get; set; } + public decimal Sum { get; set; } + public DateTime StartAt { get; set; } + public DateTime EndAt { get; set; } +} \ No newline at end of file diff --git a/back/Contracts/Mappers/SpendingPlanMapper.cs b/back/Contracts/Mappers/SpendingPlanMapper.cs new file mode 100644 index 0000000..4e4e144 --- /dev/null +++ b/back/Contracts/Mappers/SpendingPlanMapper.cs @@ -0,0 +1,16 @@ +using Contracts.DTO; +using Contracts.ViewModels; + +namespace Contracts.Mappers; + +public static class SpendingPlanMapper +{ + public static SpendingPlanView ToView(this SpendingPlanDto dto) + => new() + { + Id = dto.Id, + StartAt = dto.StartAt, + EndAt = dto.EndAt, + Sum = dto.Sum + }; +} \ No newline at end of file diff --git a/back/Contracts/Repositories/ISpendingPlanRepo.cs b/back/Contracts/Repositories/ISpendingPlanRepo.cs new file mode 100644 index 0000000..229bb7f --- /dev/null +++ b/back/Contracts/Repositories/ISpendingPlanRepo.cs @@ -0,0 +1,13 @@ +using Contracts.DTO; +using Contracts.SearchModels; + +namespace Contracts.Repositories; + +public interface ISpendingPlanRepo +{ + Task Create(SpendingPlanDto dto); + Task Update(SpendingPlanDto dto); + Task Delete(SpendingPlanSearch search); + Task GetDetails(SpendingPlanSearch search); + Task> GetList(SpendingGroupSearch? search = null); +} \ No newline at end of file diff --git a/back/Contracts/SearchModels/SpendingPlanSearch.cs b/back/Contracts/SearchModels/SpendingPlanSearch.cs new file mode 100644 index 0000000..0b53198 --- /dev/null +++ b/back/Contracts/SearchModels/SpendingPlanSearch.cs @@ -0,0 +1,6 @@ +namespace Contracts.SearchModels; + +public class SpendingPlanSearch +{ + public Guid? Id { get; set; } +} \ No newline at end of file diff --git a/back/Contracts/Services/ISpendingPlan.cs b/back/Contracts/Services/ISpendingPlan.cs new file mode 100644 index 0000000..932c4ca --- /dev/null +++ b/back/Contracts/Services/ISpendingPlan.cs @@ -0,0 +1,14 @@ +using Contracts.DTO; +using Contracts.SearchModels; +using Contracts.ViewModels; + +namespace Contracts.Services; + +public interface ISpendingPlan +{ + Task GetDetails(SpendingPlanSearch search); + Task> GetList(SpendingPlanSearch? search = null); + Task Create(SpendingPlanDto spendingPlan); + Task Delete(SpendingPlanSearch search); + Task Update(SpendingPlanDto spendingPlan); +} \ No newline at end of file diff --git a/back/Contracts/ViewModels/SpendingPlanView.cs b/back/Contracts/ViewModels/SpendingPlanView.cs new file mode 100644 index 0000000..74ed725 --- /dev/null +++ b/back/Contracts/ViewModels/SpendingPlanView.cs @@ -0,0 +1,9 @@ +namespace Contracts.ViewModels; + +public class SpendingPlanView +{ + public Guid Id { get; set; } + public DateTime StartAt { get; set; } + public DateTime EndAt { get; set; } + public decimal Sum { get; set; } +} \ No newline at end of file -- 2.25.1 From 64566065fcb0a878bc41de178dde29120e32bf41 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 27 Nov 2024 01:47:00 +0400 Subject: [PATCH 22/25] =?UTF-8?q?fix:=20=D0=BD=D0=B8=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=B0=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=B5=D1=82=D1=80=D0=B0=20=D0=B2=20=D0=BC=D0=B0=D0=BF?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/Mappers/ChangeRecordMapper.cs | 8 ++++---- back/Contracts/Mappers/SpendingGroupMapper.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/back/Contracts/Mappers/ChangeRecordMapper.cs b/back/Contracts/Mappers/ChangeRecordMapper.cs index a6075c4..606344d 100644 --- a/back/Contracts/Mappers/ChangeRecordMapper.cs +++ b/back/Contracts/Mappers/ChangeRecordMapper.cs @@ -5,11 +5,11 @@ namespace Contracts.Mappers; public static class ChangeRecordMapper { - public static ChangeRecordViewModel ToView(this ChangeRecordDto changeRecord) + public static ChangeRecordViewModel ToView(this ChangeRecordDto dto) => new() { - Id = changeRecord.Id, - Sum = changeRecord.Sum, - ChangedAt = changeRecord.ChangedAt + Id = dto.Id, + Sum = dto.Sum, + ChangedAt = dto.ChangedAt }; } \ No newline at end of file diff --git a/back/Contracts/Mappers/SpendingGroupMapper.cs b/back/Contracts/Mappers/SpendingGroupMapper.cs index 92877a5..f9d4cac 100644 --- a/back/Contracts/Mappers/SpendingGroupMapper.cs +++ b/back/Contracts/Mappers/SpendingGroupMapper.cs @@ -5,11 +5,11 @@ namespace Contracts.Mappers; public static class SpendingGroupMapper { - public static SpendingGroupViewModel ToView(this SpendingGroupDto spendingGroup) + public static SpendingGroupViewModel ToView(this SpendingGroupDto dto) => new() { - Id = spendingGroup.Id, - Name = spendingGroup.Name, - ChangeRecords = spendingGroup.ChangeRecords.Select(x => x.ToView()).ToList() + Id = dto.Id, + Name = dto.Name, + ChangeRecords = dto.ChangeRecords.Select(x => x.ToView()).ToList() }; } \ No newline at end of file -- 2.25.1 From 0bd6261e21319d6001a898bc14fd4506f5ba92fd Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 27 Nov 2024 02:13:01 +0400 Subject: [PATCH 23/25] =?UTF-8?q?add:=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=81=20=D0=B8=20=D1=80=D0=B5=D0=BF=D0=BE=D0=B7=D0=B8=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=B9=20=D0=BF=D0=BB=D0=B0=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repositories/ISpendingPlanRepo.cs | 4 +- ...pendingPlan.cs => ISpendingPlanService.cs} | 6 +- back/Infrastructure/DatabaseContext.cs | 1 + back/Infrastructure/Models/SpendingPlan.cs | 22 +++++ .../Repositories/SpendingPlanRepo.cs | 86 +++++++++++++++++++ .../Support/Mappers/SpendingPlanMapper.cs | 27 ++++++ back/Services/Domain/SpendingPlanService.cs | 61 +++++++++++++ 7 files changed, 202 insertions(+), 5 deletions(-) rename back/Contracts/Services/{ISpendingPlan.cs => ISpendingPlanService.cs} (66%) create mode 100644 back/Infrastructure/Models/SpendingPlan.cs create mode 100644 back/Infrastructure/Repositories/SpendingPlanRepo.cs create mode 100644 back/Infrastructure/Support/Mappers/SpendingPlanMapper.cs create mode 100644 back/Services/Domain/SpendingPlanService.cs diff --git a/back/Contracts/Repositories/ISpendingPlanRepo.cs b/back/Contracts/Repositories/ISpendingPlanRepo.cs index 229bb7f..8dcfd4b 100644 --- a/back/Contracts/Repositories/ISpendingPlanRepo.cs +++ b/back/Contracts/Repositories/ISpendingPlanRepo.cs @@ -8,6 +8,6 @@ public interface ISpendingPlanRepo Task Create(SpendingPlanDto dto); Task Update(SpendingPlanDto dto); Task Delete(SpendingPlanSearch search); - Task GetDetails(SpendingPlanSearch search); - Task> GetList(SpendingGroupSearch? search = null); + Task Get(SpendingPlanSearch search); + Task> GetList(SpendingPlanSearch? search = null); } \ No newline at end of file diff --git a/back/Contracts/Services/ISpendingPlan.cs b/back/Contracts/Services/ISpendingPlanService.cs similarity index 66% rename from back/Contracts/Services/ISpendingPlan.cs rename to back/Contracts/Services/ISpendingPlanService.cs index 932c4ca..b2ee0aa 100644 --- a/back/Contracts/Services/ISpendingPlan.cs +++ b/back/Contracts/Services/ISpendingPlanService.cs @@ -4,11 +4,11 @@ using Contracts.ViewModels; namespace Contracts.Services; -public interface ISpendingPlan +public interface ISpendingPlanService { - Task GetDetails(SpendingPlanSearch search); + Task GetDetails(SpendingPlanSearch search); Task> GetList(SpendingPlanSearch? search = null); Task Create(SpendingPlanDto spendingPlan); - Task Delete(SpendingPlanSearch search); + Task Delete(SpendingPlanSearch search); Task Update(SpendingPlanDto spendingPlan); } \ No newline at end of file diff --git a/back/Infrastructure/DatabaseContext.cs b/back/Infrastructure/DatabaseContext.cs index 02afad7..d787750 100644 --- a/back/Infrastructure/DatabaseContext.cs +++ b/back/Infrastructure/DatabaseContext.cs @@ -14,4 +14,5 @@ public class DatabaseContext : DbContext public DbSet Users { get; set; } = null!; public DbSet SpendingGroups { get; set; } = null!; public DbSet ChangeRecords { get; set; } = null!; + public DbSet SpendingPlans { get; set; } = null!; } \ No newline at end of file diff --git a/back/Infrastructure/Models/SpendingPlan.cs b/back/Infrastructure/Models/SpendingPlan.cs new file mode 100644 index 0000000..8162016 --- /dev/null +++ b/back/Infrastructure/Models/SpendingPlan.cs @@ -0,0 +1,22 @@ +using Contracts.DTO; + +namespace Infrastructure.Models; + +public class SpendingPlan +{ + public Guid Id { get; set; } + public DateTime StartAt { get; set; } + public DateTime EndAt { get; set; } + public decimal Sum { get; set; } + + public Guid SpendingGroupId { get; set; } + public SpendingGroup SpendingGroup { get; set; } = null!; + + public void Update(SpendingPlanDto spendingPlan) + { + StartAt = spendingPlan.StartAt; + EndAt = spendingPlan.EndAt; + Sum = spendingPlan.Sum; + SpendingGroupId = spendingPlan.SpendingGroupId; + } +} \ No newline at end of file diff --git a/back/Infrastructure/Repositories/SpendingPlanRepo.cs b/back/Infrastructure/Repositories/SpendingPlanRepo.cs new file mode 100644 index 0000000..05c03f3 --- /dev/null +++ b/back/Infrastructure/Repositories/SpendingPlanRepo.cs @@ -0,0 +1,86 @@ +using Contracts.DTO; +using Contracts.Repositories; +using Contracts.SearchModels; +using Infrastructure.Support.Mappers; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure.Repositories; + +public class SpendingPlanRepo : ISpendingPlanRepo +{ + public readonly IDbContextFactory _factory; + + public SpendingPlanRepo(IDbContextFactory factory) + { + _factory = factory; + } + + public async Task Create(SpendingPlanDto dto) + { + using var context = _factory.CreateDbContext(); + + var plan = await context.SpendingPlans.AddAsync(dto.ToModel()); + + await context.SaveChangesAsync(); + return plan.Entity.ToDto(); + } + + public async Task Delete(SpendingPlanSearch search) + { + using var context = _factory.CreateDbContext(); + + var plan = await context.SpendingPlans.FirstOrDefaultAsync(x => x.Id == search.Id); + + if (plan == null) + { + return null; + } + + context.SpendingPlans.Remove(plan); + await context.SaveChangesAsync(); + return plan.ToDto(); + } + + public async Task Get(SpendingPlanSearch search) + { + using var context = _factory.CreateDbContext(); + + var plan = await context.SpendingPlans.FirstOrDefaultAsync(x => x.Id == search.Id); + + return plan?.ToDto(); + } + + public async Task> GetList(SpendingPlanSearch? search = null) + { + using var context = _factory.CreateDbContext(); + + var query = context.SpendingPlans.AsQueryable(); + + if (search != null) + { + if (search.Id != null) + { + query = query.Where(x => x.Id == search.Id); + } + } + + return await query.Select(x => x.ToDto()).ToListAsync(); + } + + public async Task Update(SpendingPlanDto dto) + { + using var context = _factory.CreateDbContext(); + + var plan = await context.SpendingPlans.FirstOrDefaultAsync(x => x.Id == dto.Id); + + if (plan == null) + { + return null; + } + + plan.Update(dto); + context.SpendingPlans.Update(plan); + await context.SaveChangesAsync(); + return plan.ToDto(); + } +} diff --git a/back/Infrastructure/Support/Mappers/SpendingPlanMapper.cs b/back/Infrastructure/Support/Mappers/SpendingPlanMapper.cs new file mode 100644 index 0000000..22fe884 --- /dev/null +++ b/back/Infrastructure/Support/Mappers/SpendingPlanMapper.cs @@ -0,0 +1,27 @@ +using Contracts.DTO; +using Infrastructure.Models; + +namespace Infrastructure.Support.Mappers; + +public static class SpendingPlanMapper +{ + public static SpendingPlan ToModel(this SpendingPlanDto plan) + => new() + { + Id = plan.Id, + StartAt = plan.StartAt, + EndAt = plan.EndAt, + Sum = plan.Sum, + SpendingGroupId = plan.SpendingGroupId + }; + + public static SpendingPlanDto ToDto(this SpendingPlan plan) + => new() + { + Id = plan.Id, + StartAt = plan.StartAt, + EndAt = plan.EndAt, + Sum = plan.Sum, + SpendingGroupId = plan.SpendingGroupId + }; +} \ No newline at end of file diff --git a/back/Services/Domain/SpendingPlanService.cs b/back/Services/Domain/SpendingPlanService.cs new file mode 100644 index 0000000..d565869 --- /dev/null +++ b/back/Services/Domain/SpendingPlanService.cs @@ -0,0 +1,61 @@ +using Contracts.DTO; +using Contracts.Mappers; +using Contracts.Repositories; +using Contracts.SearchModels; +using Contracts.Services; +using Contracts.ViewModels; +using Services.Support.Exceptions; + +namespace Services.Domain; + +public class SpendingPlanService : ISpendingPlanService +{ + private readonly ISpendingPlanRepo _spendingPlanRepo; + + public SpendingPlanService(ISpendingPlanRepo spendingPlanRepo) + { + _spendingPlanRepo = spendingPlanRepo; + } + + public async Task Create(SpendingPlanDto spendingPlan) + { + var plan = await _spendingPlanRepo.Create(spendingPlan); + return plan.ToView(); + } + + public async Task Delete(SpendingPlanSearch search) + { + var plan = await _spendingPlanRepo.Delete(search); + if (plan == null) + { + throw new EntityNotFoundException("При удалении не получилось найти план"); + } + return plan.ToView(); + } + + public async Task GetDetails(SpendingPlanSearch search) + { + var plan = await _spendingPlanRepo.Get(search); + if (plan == null) + { + throw new EntityNotFoundException("Не удалось найти план по таким параметрам"); + } + return plan.ToView(); + } + + public async Task> GetList(SpendingPlanSearch? search = null) + { + var plans = await _spendingPlanRepo.GetList(search); + return plans.Select(x => x.ToView()).ToList(); + } + + public async Task Update(SpendingPlanDto spendingPlan) + { + var plan = await _spendingPlanRepo.Update(spendingPlan); + if (plan == null) + { + throw new EntityNotFoundException("При обновлении не получилось найти план"); + } + return plan.ToView(); + } +} -- 2.25.1 From 6ca4576a6bf75dc80e1277b352aa9b44be3e3164 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 27 Nov 2024 02:20:17 +0400 Subject: [PATCH 24/25] =?UTF-8?q?add:=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=20=D0=BF=D0=BB=D0=B0=D0=BD=D0=B0?= =?UTF-8?q?=20=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/Mappers/SpendingPlanMapper.cs | 2 +- .../Services/ISpendingPlanService.cs | 10 +- ...ngPlanView.cs => SpendingPlanViewModel.cs} | 2 +- .../Controllers/SpendingPlanController.cs | 128 ++++++++++++++++++ .../Extensions/AddDomainServicesExt.cs | 2 + back/Controllers/Extensions/AddReposExt.cs | 1 + back/Services/Domain/SpendingPlanService.cs | 10 +- 7 files changed, 143 insertions(+), 12 deletions(-) rename back/Contracts/ViewModels/{SpendingPlanView.cs => SpendingPlanViewModel.cs} (83%) create mode 100644 back/Controllers/Controllers/SpendingPlanController.cs diff --git a/back/Contracts/Mappers/SpendingPlanMapper.cs b/back/Contracts/Mappers/SpendingPlanMapper.cs index 4e4e144..84504b9 100644 --- a/back/Contracts/Mappers/SpendingPlanMapper.cs +++ b/back/Contracts/Mappers/SpendingPlanMapper.cs @@ -5,7 +5,7 @@ namespace Contracts.Mappers; public static class SpendingPlanMapper { - public static SpendingPlanView ToView(this SpendingPlanDto dto) + public static SpendingPlanViewModel ToView(this SpendingPlanDto dto) => new() { Id = dto.Id, diff --git a/back/Contracts/Services/ISpendingPlanService.cs b/back/Contracts/Services/ISpendingPlanService.cs index b2ee0aa..086ec5b 100644 --- a/back/Contracts/Services/ISpendingPlanService.cs +++ b/back/Contracts/Services/ISpendingPlanService.cs @@ -6,9 +6,9 @@ namespace Contracts.Services; public interface ISpendingPlanService { - Task GetDetails(SpendingPlanSearch search); - Task> GetList(SpendingPlanSearch? search = null); - Task Create(SpendingPlanDto spendingPlan); - Task Delete(SpendingPlanSearch search); - Task Update(SpendingPlanDto spendingPlan); + Task GetDetails(SpendingPlanSearch search); + Task> GetList(SpendingPlanSearch? search = null); + Task Create(SpendingPlanDto spendingPlan); + Task Delete(SpendingPlanSearch search); + Task Update(SpendingPlanDto spendingPlan); } \ No newline at end of file diff --git a/back/Contracts/ViewModels/SpendingPlanView.cs b/back/Contracts/ViewModels/SpendingPlanViewModel.cs similarity index 83% rename from back/Contracts/ViewModels/SpendingPlanView.cs rename to back/Contracts/ViewModels/SpendingPlanViewModel.cs index 74ed725..285cdd8 100644 --- a/back/Contracts/ViewModels/SpendingPlanView.cs +++ b/back/Contracts/ViewModels/SpendingPlanViewModel.cs @@ -1,6 +1,6 @@ namespace Contracts.ViewModels; -public class SpendingPlanView +public class SpendingPlanViewModel { public Guid Id { get; set; } public DateTime StartAt { get; set; } diff --git a/back/Controllers/Controllers/SpendingPlanController.cs b/back/Controllers/Controllers/SpendingPlanController.cs new file mode 100644 index 0000000..5f457b1 --- /dev/null +++ b/back/Controllers/Controllers/SpendingPlanController.cs @@ -0,0 +1,128 @@ +using Contracts.DTO; +using Contracts.SearchModels; +using Contracts.Services; +using Contracts.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Services.Support.Exceptions; + +namespace Controllers.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class SpendingPlanController : ControllerBase +{ + private readonly ISpendingPlanService _spendingPlanService; + + public SpendingPlanController(ISpendingPlanService spendingPlanService) + { + _spendingPlanService = spendingPlanService; + } + + [HttpGet("{id}")] + public async Task> GetSpendingPlan( + Guid id, + [FromQuery] SpendingPlanSearch search) + { + try + { + search ??= new(); + search.Id = id; + + var plan = await _spendingPlanService.GetDetails(search); + return Ok(plan); + } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpGet] + public async Task>> GetSpendingPlans() + { + try + { + var plans = await _spendingPlanService.GetList(); + return Ok(plans); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpGet("filter")] + public async Task>> GetSpendingPlans( + [FromQuery] SpendingPlanSearch search) + { + try + { + var plans = await _spendingPlanService.GetList(search); + return Ok(plans); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpPost] + public async Task> CreateSpendingPlan( + [FromBody] SpendingPlanDto dto) + { + try + { + var plan = await _spendingPlanService.Create(dto); + return CreatedAtAction(nameof(GetSpendingPlan), new { id = plan.Id }, plan); + } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpPatch] + public async Task> UpdateSpendingPlan( + [FromBody] SpendingPlanDto dto) + { + try + { + var plan = await _spendingPlanService.Update(dto); + return Ok(plan); + } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } + + [HttpDelete] + public async Task DeleteSpendingPlan([FromQuery] SpendingPlanSearch search) + { + try + { + var plan = await _spendingPlanService.Delete(search); + return Ok(plan); + } + catch (EntityNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + return StatusCode(500, ex.Message); + } + } +} \ No newline at end of file diff --git a/back/Controllers/Extensions/AddDomainServicesExt.cs b/back/Controllers/Extensions/AddDomainServicesExt.cs index 97d9065..7d6cc0a 100644 --- a/back/Controllers/Extensions/AddDomainServicesExt.cs +++ b/back/Controllers/Extensions/AddDomainServicesExt.cs @@ -13,5 +13,7 @@ public static class AddDomainServicesExtension services.AddTransient(); services.AddTransient(); + + services.AddTransient(); } } \ No newline at end of file diff --git a/back/Controllers/Extensions/AddReposExt.cs b/back/Controllers/Extensions/AddReposExt.cs index 9793b6e..5da333a 100644 --- a/back/Controllers/Extensions/AddReposExt.cs +++ b/back/Controllers/Extensions/AddReposExt.cs @@ -12,5 +12,6 @@ public static class AddReposExtension services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } } \ No newline at end of file diff --git a/back/Services/Domain/SpendingPlanService.cs b/back/Services/Domain/SpendingPlanService.cs index d565869..ff655d0 100644 --- a/back/Services/Domain/SpendingPlanService.cs +++ b/back/Services/Domain/SpendingPlanService.cs @@ -17,13 +17,13 @@ public class SpendingPlanService : ISpendingPlanService _spendingPlanRepo = spendingPlanRepo; } - public async Task Create(SpendingPlanDto spendingPlan) + public async Task Create(SpendingPlanDto spendingPlan) { var plan = await _spendingPlanRepo.Create(spendingPlan); return plan.ToView(); } - public async Task Delete(SpendingPlanSearch search) + public async Task Delete(SpendingPlanSearch search) { var plan = await _spendingPlanRepo.Delete(search); if (plan == null) @@ -33,7 +33,7 @@ public class SpendingPlanService : ISpendingPlanService return plan.ToView(); } - public async Task GetDetails(SpendingPlanSearch search) + public async Task GetDetails(SpendingPlanSearch search) { var plan = await _spendingPlanRepo.Get(search); if (plan == null) @@ -43,13 +43,13 @@ public class SpendingPlanService : ISpendingPlanService return plan.ToView(); } - public async Task> GetList(SpendingPlanSearch? search = null) + public async Task> GetList(SpendingPlanSearch? search = null) { var plans = await _spendingPlanRepo.GetList(search); return plans.Select(x => x.ToView()).ToList(); } - public async Task Update(SpendingPlanDto spendingPlan) + public async Task Update(SpendingPlanDto spendingPlan) { var plan = await _spendingPlanRepo.Update(spendingPlan); if (plan == null) -- 2.25.1 From 83b75bed07c2f3e311aa6aca05425f9f5cccfbf0 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 27 Nov 2024 02:40:02 +0400 Subject: [PATCH 25/25] =?UTF-8?q?add:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F.=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=B3?= =?UTF-8?q?=D1=80=D1=83=D0=BF=D0=BF=D1=8B=20=D1=80=D0=B0=D1=81=D1=85=D0=BE?= =?UTF-8?q?=D0=B4=D0=BE=D0=B2=20=D0=B2=D0=BE=D0=B7=D0=B2=D1=80=D0=B0=D1=89?= =?UTF-8?q?=D0=B0=D1=8E=D1=82=D1=81=D1=8F=20=D1=81=20=D0=BF=D0=BB=D0=B0?= =?UTF-8?q?=D0=BD=D0=B0=D0=BC=D0=B8=20=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/Contracts/DTOs/SpendingGroupDto.cs | 1 + back/Contracts/Mappers/SpendingGroupMapper.cs | 3 +- .../ViewModels/SpendingGroupViewModel.cs | 1 + ...20241126222847_AddSpendingPlan.Designer.cs | 177 ++++++++++++++++++ .../20241126222847_AddSpendingPlan.cs | 48 +++++ .../DatabaseContextModelSnapshot.cs | 38 ++++ back/Infrastructure/Models/SpendingGroup.cs | 1 + .../Repositories/SpendingGroupRepo.cs | 7 +- .../Support/Mappers/SpendingGroupMapper.cs | 3 +- 9 files changed, 276 insertions(+), 3 deletions(-) create mode 100644 back/Infrastructure/Migrations/20241126222847_AddSpendingPlan.Designer.cs create mode 100644 back/Infrastructure/Migrations/20241126222847_AddSpendingPlan.cs diff --git a/back/Contracts/DTOs/SpendingGroupDto.cs b/back/Contracts/DTOs/SpendingGroupDto.cs index 2adf558..dd001fd 100644 --- a/back/Contracts/DTOs/SpendingGroupDto.cs +++ b/back/Contracts/DTOs/SpendingGroupDto.cs @@ -6,4 +6,5 @@ public class SpendingGroupDto public string Name { get; set; } = string.Empty; public Guid UserId { get; set; } public List ChangeRecords { get; set; } = new(); + public List SpendingPlans { get; set; } = new(); } \ No newline at end of file diff --git a/back/Contracts/Mappers/SpendingGroupMapper.cs b/back/Contracts/Mappers/SpendingGroupMapper.cs index f9d4cac..fd6a987 100644 --- a/back/Contracts/Mappers/SpendingGroupMapper.cs +++ b/back/Contracts/Mappers/SpendingGroupMapper.cs @@ -10,6 +10,7 @@ public static class SpendingGroupMapper { Id = dto.Id, Name = dto.Name, - ChangeRecords = dto.ChangeRecords.Select(x => x.ToView()).ToList() + ChangeRecords = dto.ChangeRecords.Select(x => x.ToView()).ToList(), + SpendingPlans = dto.SpendingPlans.Select(x => x.ToView()).ToList() }; } \ No newline at end of file diff --git a/back/Contracts/ViewModels/SpendingGroupViewModel.cs b/back/Contracts/ViewModels/SpendingGroupViewModel.cs index c22ae62..1a94317 100644 --- a/back/Contracts/ViewModels/SpendingGroupViewModel.cs +++ b/back/Contracts/ViewModels/SpendingGroupViewModel.cs @@ -5,4 +5,5 @@ public class SpendingGroupViewModel public Guid Id { get; set; } public string Name { get; set; } = string.Empty; public List ChangeRecords { get; set; } = new(); + public List SpendingPlans { get; set; } = new(); } \ No newline at end of file diff --git a/back/Infrastructure/Migrations/20241126222847_AddSpendingPlan.Designer.cs b/back/Infrastructure/Migrations/20241126222847_AddSpendingPlan.Designer.cs new file mode 100644 index 0000000..e43fe93 --- /dev/null +++ b/back/Infrastructure/Migrations/20241126222847_AddSpendingPlan.Designer.cs @@ -0,0 +1,177 @@ +// +using System; +using Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20241126222847_AddSpendingPlan")] + partial class AddSpendingPlan + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Infrastructure.Models.ChangeRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChangedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SpendingGroupId") + .HasColumnType("uuid"); + + b.Property("Sum") + .HasColumnType("numeric"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SpendingGroupId"); + + b.HasIndex("UserId"); + + b.ToTable("ChangeRecords"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("SpendingGroups"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EndAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SpendingGroupId") + .HasColumnType("uuid"); + + b.Property("StartAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Sum") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("SpendingGroupId"); + + b.ToTable("SpendingPlans"); + }); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Balance") + .HasColumnType("numeric"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Infrastructure.Models.ChangeRecord", b => + { + b.HasOne("Infrastructure.Models.SpendingGroup", "SpendingGroup") + .WithMany("ChangeRecords") + .HasForeignKey("SpendingGroupId"); + + b.HasOne("Infrastructure.Models.User", "User") + .WithMany("ChangeRecords") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpendingGroup"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.HasOne("Infrastructure.Models.User", "User") + .WithMany("SpendingGroups") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingPlan", b => + { + b.HasOne("Infrastructure.Models.SpendingGroup", "SpendingGroup") + .WithMany("SpendingPlans") + .HasForeignKey("SpendingGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpendingGroup"); + }); + + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => + { + b.Navigation("ChangeRecords"); + + b.Navigation("SpendingPlans"); + }); + + modelBuilder.Entity("Infrastructure.Models.User", b => + { + b.Navigation("ChangeRecords"); + + b.Navigation("SpendingGroups"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/back/Infrastructure/Migrations/20241126222847_AddSpendingPlan.cs b/back/Infrastructure/Migrations/20241126222847_AddSpendingPlan.cs new file mode 100644 index 0000000..fcc2f3e --- /dev/null +++ b/back/Infrastructure/Migrations/20241126222847_AddSpendingPlan.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddSpendingPlan : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SpendingPlans", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + StartAt = table.Column(type: "timestamp with time zone", nullable: false), + EndAt = table.Column(type: "timestamp with time zone", nullable: false), + Sum = table.Column(type: "numeric", nullable: false), + SpendingGroupId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SpendingPlans", x => x.Id); + table.ForeignKey( + name: "FK_SpendingPlans_SpendingGroups_SpendingGroupId", + column: x => x.SpendingGroupId, + principalTable: "SpendingGroups", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_SpendingPlans_SpendingGroupId", + table: "SpendingPlans", + column: "SpendingGroupId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SpendingPlans"); + } + } +} diff --git a/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs b/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs index 2c7a5ef..93d0295 100644 --- a/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs +++ b/back/Infrastructure/Migrations/DatabaseContextModelSnapshot.cs @@ -69,6 +69,31 @@ namespace Infrastructure.Migrations b.ToTable("SpendingGroups"); }); + modelBuilder.Entity("Infrastructure.Models.SpendingPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EndAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SpendingGroupId") + .HasColumnType("uuid"); + + b.Property("StartAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Sum") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("SpendingGroupId"); + + b.ToTable("SpendingPlans"); + }); + modelBuilder.Entity("Infrastructure.Models.User", b => { b.Property("Id") @@ -119,9 +144,22 @@ namespace Infrastructure.Migrations b.Navigation("User"); }); + modelBuilder.Entity("Infrastructure.Models.SpendingPlan", b => + { + b.HasOne("Infrastructure.Models.SpendingGroup", "SpendingGroup") + .WithMany("SpendingPlans") + .HasForeignKey("SpendingGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SpendingGroup"); + }); + modelBuilder.Entity("Infrastructure.Models.SpendingGroup", b => { b.Navigation("ChangeRecords"); + + b.Navigation("SpendingPlans"); }); modelBuilder.Entity("Infrastructure.Models.User", b => diff --git a/back/Infrastructure/Models/SpendingGroup.cs b/back/Infrastructure/Models/SpendingGroup.cs index 889bdb6..23758eb 100644 --- a/back/Infrastructure/Models/SpendingGroup.cs +++ b/back/Infrastructure/Models/SpendingGroup.cs @@ -8,4 +8,5 @@ public class SpendingGroup public Guid UserId { get; set; } public User User { get; set; } = null!; public List? ChangeRecords { get; set; } + public List? SpendingPlans { get; set; } } \ No newline at end of file diff --git a/back/Infrastructure/Repositories/SpendingGroupRepo.cs b/back/Infrastructure/Repositories/SpendingGroupRepo.cs index d7e879e..6c8382a 100644 --- a/back/Infrastructure/Repositories/SpendingGroupRepo.cs +++ b/back/Infrastructure/Repositories/SpendingGroupRepo.cs @@ -47,6 +47,7 @@ public class SpendingGroupRepo : ISpendingGroupRepo var group = await context.SpendingGroups .Include(x => x.ChangeRecords) + .Include(x => x.SpendingPlans) .FirstOrDefaultAsync(x => x.Id == search.Id || x.Name == search.Name); @@ -72,7 +73,11 @@ public class SpendingGroupRepo : ISpendingGroupRepo } } - return await query.Include(x => x.ChangeRecords).Select(x => x.ToDto()).ToListAsync(); + return await query + .Include(x => x.ChangeRecords) + .Include(x => x.SpendingPlans) + .Select(x => x.ToDto()) + .ToListAsync(); } public async Task Update(SpendingGroupDto spendingGroup) diff --git a/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs b/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs index d74490f..9432073 100644 --- a/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs +++ b/back/Infrastructure/Support/Mappers/SpendingGroupMapper.cs @@ -11,7 +11,8 @@ public static class SpendingGroupMapper Id = group.Id, Name = group.Name, UserId = group.UserId, - ChangeRecords = group.ChangeRecords?.Select(x => x.ToDto()).ToList() ?? [] + ChangeRecords = group.ChangeRecords?.Select(x => x.ToDto()).ToList() ?? [], + SpendingPlans = group.SpendingPlans?.Select(x => x.ToDto()).ToList() ?? [] }; public static SpendingGroup ToModel(this SpendingGroupDto group) => new() -- 2.25.1