From 2183073421ab5e3cde3432dfdac3e65c1e41211b Mon Sep 17 00:00:00 2001 From: lq1405 <2769838458@qq.com> Date: Sun, 13 Oct 2024 17:04:47 +0800 Subject: [PATCH] ceshi --- .gitattributes | 63 ++ .gitignore | 363 +++++++++ Dockerfile | 31 + LMS.Common/Attributes/DescriptionAttribute.cs | 13 + LMS.Common/Attributes/ResultAttribute.cs | 13 + LMS.Common/Enum/MachineEnum.cs | 31 + LMS.Common/Enum/PermissionEnum.cs | 41 + LMS.Common/Enum/PromptEnum.cs | 49 ++ LMS.Common/Enum/ResponseCodeEnum.cs | 198 +++++ LMS.Common/Enum/ResponseString.cs | 42 ++ LMS.Common/Enum/UserStatus.cs | 31 + LMS.Common/LMS.Common.csproj | 9 + LMS.Common/RSAKey/AESGenerate.cs | 37 + LMS.Common/RSAKey/ComplexKeyObfuscator.cs | 99 +++ LMS.Common/RSAKey/RSAKeyGenerateModel.cs | 19 + LMS.Common/RSAKey/RsaKeyPairGenerator.cs | 131 ++++ LMS.DAO/ApplicationDbContext.cs | 46 ++ LMS.DAO/LMS.DAO.csproj | 21 + LMS.DAO/MachineDAO/MachineBasicDao.cs | 43 ++ LMS.DAO/MyDbcontextDesignFactory.cs | 26 + LMS.DAO/PermissionDAO/PermissionBasicDao.cs | 117 +++ LMS.DAO/PermissionDAO/PermissionTypeDao.cs | 55 ++ LMS.DAO/RoleDAO/RoleBasicDao.cs | 28 + LMS.DAO/UserDAO/UserBasicDAO.cs | 29 + LMS.Repository/DB/ApiEndpoints.cs | 22 + LMS.Repository/DB/Machine.cs | 62 ++ LMS.Repository/DB/Permission.cs | 82 ++ LMS.Repository/DB/PermissionType.cs | 56 ++ LMS.Repository/DB/Prompt.cs | 71 ++ LMS.Repository/DB/PromptType.cs | 61 ++ LMS.Repository/DB/RefreshTokens.cs | 46 ++ LMS.Repository/DB/Role.cs | 18 + LMS.Repository/DB/RsaKeys.cs | 57 ++ LMS.Repository/DB/User.cs | 69 ++ LMS.Repository/DB/UserRoles.cs | 11 + LMS.Repository/DTO/CollectionResponse.cs | 19 + LMS.Repository/DTO/MachineDetailDto.cs | 14 + LMS.Repository/DTO/MachineDto.cs | 30 + LMS.Repository/DTO/RoleDto.cs | 21 + LMS.Repository/DTO/SoftwareDao.cs | 10 + .../DTO/UserDto/UserAgentInfoDto.cs | 44 ++ LMS.Repository/DTO/UserDto/UserBaseDto.cs | 18 + .../DTO/UserDto/UserCollectionDto.cs | 17 + LMS.Repository/DTO/UserDto/UserDto.cs | 40 + LMS.Repository/Forward/OpenAI.cs | 27 + LMS.Repository/LMS.Repository.csproj | 21 + LMS.Repository/Machine/MachineModel.cs | 42 ++ LMS.Repository/Promission/PermissionModel.cs | 47 ++ .../Promission/PermissionTypeModel.cs | 35 + LMS.Repository/RSAKey/RSAKeyGenerateModel.cs | 19 + LMS.Repository/Role/RoleModel.cs | 17 + LMS.Repository/User/LoginModel.cs | 31 + LMS.Repository/User/PublicKeyModel.cs | 19 + LMS.Repository/User/RefreshTokenModel.cs | 10 + LMS.Repository/User/RegisterModel.cs | 18 + LMS.Repository/User/UpdatedUserModel.cs | 14 + LMS.Repository/User/UserPrivateOptions.cs | 12 + LMS.Tools/Extensions/BeijingTimeExtension.cs | 15 + LMS.Tools/Extensions/ConvertExtension.cs | 44 ++ LMS.Tools/Extensions/EnumExtensions.cs | 74 ++ LMS.Tools/LMS.Tools.csproj | 13 + LMS.service/.dockerignore | 30 + LMS.service/.gitattributes | 63 ++ LMS.service/.gitignore | 364 +++++++++ LMS.service/APIResponseModel.cs | 174 +++++ LMS.service/Configuration/AddCorsConifg.cs | 19 + .../Configuration/AuthenticationExtensions.cs | 28 + LMS.service/Configuration/AutoMapperConfig.cs | 31 + LMS.service/Configuration/JWTSetting.cs | 10 + .../Configuration/RsaConfigurattions.cs | 66 ++ .../Configuration/ServiceConfiguration.cs | 39 + .../Configuration/SwaggerConfiguration.cs | 31 + LMS.service/Controllers/MachineController.cs | 150 ++++ .../Controllers/PermissionController.cs | 134 ++++ LMS.service/Controllers/RoleController.cs | 107 +++ LMS.service/Controllers/SoftWareController.cs | 15 + LMS.service/Controllers/UserController.cs | 225 ++++++ .../20240912114032_inital.Designer.cs | 299 ++++++++ .../Data/Migrations/20240912114032_inital.cs | 265 +++++++ .../ApplicationDbContextModelSnapshot.cs | 300 ++++++++ LMS.service/Dockerfile | 31 + .../Middleware/DynamicPermissionMiddleware.cs | 65 ++ LMS.service/LMS.service.csproj | 43 ++ LMS.service/LMS.service.http | 6 + LMS.service/LMS.service.sln | 49 ++ LMS.service/Program.cs | 104 +++ LMS.service/Service/MachineService.cs | 712 ++++++++++++++++++ .../PermissionService/PermissionService.cs | 460 +++++++++++ .../PremissionValidationService.cs | 64 ++ .../Service/RoleService/RoleService.cs | 296 ++++++++ .../Service/UserService/LoginService.cs | 278 +++++++ .../Service/UserService/SecurityService.cs | 230 ++++++ .../Service/UserService/UserService.cs | 482 ++++++++++++ LMS.service/appsettings.Development.json | 8 + LMS.service/appsettings.json | 9 + docker.txt | 1 + 96 files changed, 7949 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LMS.Common/Attributes/DescriptionAttribute.cs create mode 100644 LMS.Common/Attributes/ResultAttribute.cs create mode 100644 LMS.Common/Enum/MachineEnum.cs create mode 100644 LMS.Common/Enum/PermissionEnum.cs create mode 100644 LMS.Common/Enum/PromptEnum.cs create mode 100644 LMS.Common/Enum/ResponseCodeEnum.cs create mode 100644 LMS.Common/Enum/ResponseString.cs create mode 100644 LMS.Common/Enum/UserStatus.cs create mode 100644 LMS.Common/LMS.Common.csproj create mode 100644 LMS.Common/RSAKey/AESGenerate.cs create mode 100644 LMS.Common/RSAKey/ComplexKeyObfuscator.cs create mode 100644 LMS.Common/RSAKey/RSAKeyGenerateModel.cs create mode 100644 LMS.Common/RSAKey/RsaKeyPairGenerator.cs create mode 100644 LMS.DAO/ApplicationDbContext.cs create mode 100644 LMS.DAO/LMS.DAO.csproj create mode 100644 LMS.DAO/MachineDAO/MachineBasicDao.cs create mode 100644 LMS.DAO/MyDbcontextDesignFactory.cs create mode 100644 LMS.DAO/PermissionDAO/PermissionBasicDao.cs create mode 100644 LMS.DAO/PermissionDAO/PermissionTypeDao.cs create mode 100644 LMS.DAO/RoleDAO/RoleBasicDao.cs create mode 100644 LMS.DAO/UserDAO/UserBasicDAO.cs create mode 100644 LMS.Repository/DB/ApiEndpoints.cs create mode 100644 LMS.Repository/DB/Machine.cs create mode 100644 LMS.Repository/DB/Permission.cs create mode 100644 LMS.Repository/DB/PermissionType.cs create mode 100644 LMS.Repository/DB/Prompt.cs create mode 100644 LMS.Repository/DB/PromptType.cs create mode 100644 LMS.Repository/DB/RefreshTokens.cs create mode 100644 LMS.Repository/DB/Role.cs create mode 100644 LMS.Repository/DB/RsaKeys.cs create mode 100644 LMS.Repository/DB/User.cs create mode 100644 LMS.Repository/DB/UserRoles.cs create mode 100644 LMS.Repository/DTO/CollectionResponse.cs create mode 100644 LMS.Repository/DTO/MachineDetailDto.cs create mode 100644 LMS.Repository/DTO/MachineDto.cs create mode 100644 LMS.Repository/DTO/RoleDto.cs create mode 100644 LMS.Repository/DTO/SoftwareDao.cs create mode 100644 LMS.Repository/DTO/UserDto/UserAgentInfoDto.cs create mode 100644 LMS.Repository/DTO/UserDto/UserBaseDto.cs create mode 100644 LMS.Repository/DTO/UserDto/UserCollectionDto.cs create mode 100644 LMS.Repository/DTO/UserDto/UserDto.cs create mode 100644 LMS.Repository/Forward/OpenAI.cs create mode 100644 LMS.Repository/LMS.Repository.csproj create mode 100644 LMS.Repository/Machine/MachineModel.cs create mode 100644 LMS.Repository/Promission/PermissionModel.cs create mode 100644 LMS.Repository/Promission/PermissionTypeModel.cs create mode 100644 LMS.Repository/RSAKey/RSAKeyGenerateModel.cs create mode 100644 LMS.Repository/Role/RoleModel.cs create mode 100644 LMS.Repository/User/LoginModel.cs create mode 100644 LMS.Repository/User/PublicKeyModel.cs create mode 100644 LMS.Repository/User/RefreshTokenModel.cs create mode 100644 LMS.Repository/User/RegisterModel.cs create mode 100644 LMS.Repository/User/UpdatedUserModel.cs create mode 100644 LMS.Repository/User/UserPrivateOptions.cs create mode 100644 LMS.Tools/Extensions/BeijingTimeExtension.cs create mode 100644 LMS.Tools/Extensions/ConvertExtension.cs create mode 100644 LMS.Tools/Extensions/EnumExtensions.cs create mode 100644 LMS.Tools/LMS.Tools.csproj create mode 100644 LMS.service/.dockerignore create mode 100644 LMS.service/.gitattributes create mode 100644 LMS.service/.gitignore create mode 100644 LMS.service/APIResponseModel.cs create mode 100644 LMS.service/Configuration/AddCorsConifg.cs create mode 100644 LMS.service/Configuration/AuthenticationExtensions.cs create mode 100644 LMS.service/Configuration/AutoMapperConfig.cs create mode 100644 LMS.service/Configuration/JWTSetting.cs create mode 100644 LMS.service/Configuration/RsaConfigurattions.cs create mode 100644 LMS.service/Configuration/ServiceConfiguration.cs create mode 100644 LMS.service/Configuration/SwaggerConfiguration.cs create mode 100644 LMS.service/Controllers/MachineController.cs create mode 100644 LMS.service/Controllers/PermissionController.cs create mode 100644 LMS.service/Controllers/RoleController.cs create mode 100644 LMS.service/Controllers/SoftWareController.cs create mode 100644 LMS.service/Controllers/UserController.cs create mode 100644 LMS.service/Data/Migrations/20240912114032_inital.Designer.cs create mode 100644 LMS.service/Data/Migrations/20240912114032_inital.cs create mode 100644 LMS.service/Data/Migrations/ApplicationDbContextModelSnapshot.cs create mode 100644 LMS.service/Dockerfile create mode 100644 LMS.service/Extensions/Middleware/DynamicPermissionMiddleware.cs create mode 100644 LMS.service/LMS.service.csproj create mode 100644 LMS.service/LMS.service.http create mode 100644 LMS.service/LMS.service.sln create mode 100644 LMS.service/Program.cs create mode 100644 LMS.service/Service/MachineService.cs create mode 100644 LMS.service/Service/PermissionService/PermissionService.cs create mode 100644 LMS.service/Service/PermissionService/PremissionValidationService.cs create mode 100644 LMS.service/Service/RoleService/RoleService.cs create mode 100644 LMS.service/Service/UserService/LoginService.cs create mode 100644 LMS.service/Service/UserService/SecurityService.cs create mode 100644 LMS.service/Service/UserService/UserService.cs create mode 100644 LMS.service/appsettings.Development.json create mode 100644 LMS.service/appsettings.json create mode 100644 docker.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# 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/ +[Oo]ut/ +[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 Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# 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 +*.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 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/ + +# 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 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..018a754 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER app +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +# 复制所有的项目文件 +COPY ["LMS.service/LMS.service.csproj", "LMS.service/"] +COPY ["LMS.Common/LMS.Common.csproj", "LMS.Common/"] +COPY ["LMS.DAO/LMS.DAO.csproj", "LMS.DAO/"] +COPY ["LMS.Repository/LMS.Repository.csproj", "LMS.Repository/"] +COPY ["LMS.Tools/LMS.Tools.csproj", "LMS.Tools/"] + +RUN dotnet restore "LMS.service/LMS.service.csproj" +COPY . . +WORKDIR "/src/LMS.service" +RUN dotnet build "LMS.service.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "LMS.service.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "LMS.service.dll"] \ No newline at end of file diff --git a/LMS.Common/Attributes/DescriptionAttribute.cs b/LMS.Common/Attributes/DescriptionAttribute.cs new file mode 100644 index 0000000..f73aebf --- /dev/null +++ b/LMS.Common/Attributes/DescriptionAttribute.cs @@ -0,0 +1,13 @@ +锘縩amespace LMS.Common.Attributes +{ + [AttributeUsage(AttributeTargets.Field)] + public class DescriptionAttribute : Attribute + { + public string Description { get; set; } + + public DescriptionAttribute(string description) + { + Description = description; + } + } +} diff --git a/LMS.Common/Attributes/ResultAttribute.cs b/LMS.Common/Attributes/ResultAttribute.cs new file mode 100644 index 0000000..6ce017f --- /dev/null +++ b/LMS.Common/Attributes/ResultAttribute.cs @@ -0,0 +1,13 @@ +锘縩amespace LMS.Common.Attributes +{ + [AttributeUsage(AttributeTargets.Field)] + public class ResultAttribute : Attribute + { + public string Result { get; set; } + + public ResultAttribute(string result) + { + Result = result; + } + } +} diff --git a/LMS.Common/Enum/MachineEnum.cs b/LMS.Common/Enum/MachineEnum.cs new file mode 100644 index 0000000..72b0f17 --- /dev/null +++ b/LMS.Common/Enum/MachineEnum.cs @@ -0,0 +1,31 @@ +锘縰sing LMS.Common.Attributes; + +namespace LMS.Common.Enums +{ + public class MachineEnum + { + /// + /// 鏈哄櫒鐮佷娇鐢ㄧ姸鎬 + /// + public enum MachineUseStatus + { + [Description("璇曠敤")] + Trial = 0, + + [Description("姘镐箙")] + Permanent = 1 + } + + /// + /// 鏈哄櫒鐮佺姸鎬 + /// + public enum MachineStatus + { + [Description("婵娲")] + Active = 1, + + [Description("鍐荤粨")] + Frozen = 0 + } + } +} diff --git a/LMS.Common/Enum/PermissionEnum.cs b/LMS.Common/Enum/PermissionEnum.cs new file mode 100644 index 0000000..9ab842e --- /dev/null +++ b/LMS.Common/Enum/PermissionEnum.cs @@ -0,0 +1,41 @@ +锘 + +using LMS.Common.Attributes; + +namespace LMS.Common.Enums +{ + public class PermissionEnum + { + public enum PermissionType + { + [Description("鐢ㄦ埛鏉冮檺")] + User = 1, + + [Description("鏈哄櫒鐮佹潈闄")] + Machine = 2, + + [Description("瑙掕壊鏉冮檺")] + Role = 3, + + [Description("鎻愮ず璇嶇被鍨")] + Prompt = 4, + + [Description("鏉冮檺绫诲瀷鏉冮檺")] + PermissionType = 5, + + [Description("鏉冮檺鏉冮檺")] + Permission = 6, + + [Description("鍏朵粬鏉冮檺")] + Custom = 100 + + } + + public enum PType + { + User = 1, + Machine = 2, + Role = 3, + } + } +} diff --git a/LMS.Common/Enum/PromptEnum.cs b/LMS.Common/Enum/PromptEnum.cs new file mode 100644 index 0000000..2e66584 --- /dev/null +++ b/LMS.Common/Enum/PromptEnum.cs @@ -0,0 +1,49 @@ +锘縩amespace LMS.Common.Enums +{ + public class PromptEnum + { + public enum PromptType + { + /// + /// 寮澶存彁绀鸿瘝 + /// + StartPrompt = 0, + } + + public class PromptStatus + { + /// + /// 鎻愮ず璇嶇姸鎬侊細鍚敤 + /// + public const string Enable = "enable"; + /// + /// + ///鎻愮ず璇嶇姸鎬侊細绂佺敤 + /// + public const string Disable = "disable"; + } + + public class PromptTypeStatus + { + /// + /// 鎻愮ず璇嶇被鍨嬬姸鎬侊細鍚敤 + /// + public const string Enable = "enable"; + /// + /// + ///鎻愮ず璇嶇被鍨嬬姸鎬侊細绂佺敤 + /// + public const string Disable = "disable"; + + /// + /// 鎵鏈夋湁鏁堢殑鎻愮ず璇嶇被鍨嬬姸鎬 + /// + public static readonly HashSet ValidStatuses = new HashSet + { + Enable, + Disable + }; + } + } + +} diff --git a/LMS.Common/Enum/ResponseCodeEnum.cs b/LMS.Common/Enum/ResponseCodeEnum.cs new file mode 100644 index 0000000..3c8ac6e --- /dev/null +++ b/LMS.Common/Enum/ResponseCodeEnum.cs @@ -0,0 +1,198 @@ +锘縰sing LMS.Common.Attributes; + +namespace LMS.Common.Enums +{ + public class ResponseCodeEnum + { + public enum ResponseCode + { + #region 鎴愬姛 + + [Result("璇锋眰鎴愬姛")] + [Description("璇锋眰鎴愬姛")] + Success = 1, + + #endregion + + #region User 鎿嶄綔澶辫触 + + [Result(ResponseString.UndefinedLoginType)] + [Description(ResponseString.UndefinedLoginType)] + UndefinedLoginType = 1000, + + [Result(ResponseString.UserNotFound)] + [Description("鏈壘鍒板搴旂敤鎴峰悕鐨勮处鍙")] + FindUserByNameFail = 1001, + + [Result(ResponseString.UserPasswordError)] + [Description("鐢ㄦ埛瀵嗙爜閿欒")] + UserPasswordFail = 1002, + + [Result(ResponseString.IdDateNotFound)] + [Description("鎸囧畾ID鐨勭敤鎴蜂笉瀛樺湪")] + FindUserByIdFail = 1003, + + [Result(ResponseString.UserStatusError)] + [Description("鐢ㄦ埛鐨勭姸鎬侀敊璇紝鍙兘鐘舵佷负姝e父鐨勭敤鎴峰彲浠ョ櫥褰")] + UserStatusError = 1004, + + + [Result("鎸囧畾閭鐨勭敤鎴峰凡缁忓瓨鍦")] + [Description("鎸囧畾閭鐨勭敤鎴峰凡缁忓瓨鍦")] + UserEmailExist = 1005, + + [Result("鎸囧畾鎵嬫満鍙风殑鐢ㄦ埛宸茬粡瀛樺湪")] + [Description("鎸囧畾鎵嬫満鍙风殑鐢ㄦ埛宸茬粡瀛樺湪")] + UserPhoneExist = 1006, + + [Result("鎸囧畾閭鎴栨墜鏈哄彿鐨勭敤鎴峰凡缁忓瓨鍦")] + [Description("鎸囧畾閭鎴栨墜鏈哄彿鐨勭敤鎴峰凡缁忓瓨鍦")] + UserEmailOrPhoneExist = 1007, + + [Result("鎸囧畾鐨勭敤鎴峰悕宸插瓨鍦")] + [Description("鎸囧畾鐨勭敤鎴峰悕宸插瓨鍦")] + UasrNameExist = 1008, + + [Result("鎸囧畾鐨勭敤鎴锋樀绉板凡瀛樺湪")] + [Description("鎸囧畾鐨勭敤鎴锋樀绉板凡瀛樺湪")] + UserNickNameExist = 1009, + + [Result(ResponseString.UserIsLockedOut)] + [Description(ResponseString.UserIsLockedOut)] + UserIsLockedOut = 1010, + + [Result(ResponseString.UserLoginOut)] + [Description("鐢ㄦ埛鐨勫埛鏂颁护鐗屾棤鏁堬紝鍙兘鏄簩娆$櫥褰曪紝鎴栬呮槸鐧诲綍鍒版湡锛岃閲嶆柊鐧诲綍")] + RefreshTokenInvalid = 1011, + + [Result("鐢ㄦ埛娉ㄥ唽澶辫触")] + [Description("鐢ㄦ埛娉ㄥ唽澶辫触")] + UserRegisterFial = 1012, + + [Result("鐢ㄦ埛涓嶆槸VIP鐢ㄦ埛")] + [Description("鐢ㄦ埛涓嶆槸VIP鐢ㄦ埛")] + UserNotVip = 1013, + + [Result("鏃犳晥鐨勯個璇风爜")] + [Description("鏃犳晥鐨勯個璇风爜")] + InvalidAffiliateCode = 1014, + + #endregion + + #region Machine 鎿嶄綔澶辫触 + + [Result(ResponseString.MachineStatusNotFoundOrStatusIsNot)] + [Description(ResponseString.MachineStatusNotFoundOrStatusIsNot)] + MachineNotFound = 2001, + + [Result(ResponseString.IdDateNotFound)] + [Description("鎸囧畾ID鐨勬満鍣ㄧ爜涓嶅瓨鍦")] + FindMachineByIdFail = 2002, + + [Result(ResponseString.DataExist)] + [Description("鎸囧畾鏈哄櫒鐮佸凡缁忓瓨鍦")] + MachineAlreadyExist = 2003, + + #endregion + + #region PermissionType 鎿嶄綔澶辫触 + + [Result(ResponseString.IdDateNotFound)] + [Description("鎸囧畾ID鐨勬潈闄愬垎绫绘病鏈夋壘鍒")] + FindPermissionTypeByIdFail = 3001, + + [Result(ResponseString.DataExist)] + [Description("鎸囧畾ID鐨勬潈闄愬悕绉板拰缂栫爜宸茬粡瀛樺湪")] + PermissionTypeExist = 3002, + + + #endregion + + #region 鏉冮檺鎿嶄綔澶辫触 + + [Result(ResponseString.NotPermissionAction)] + [Description(ResponseString.NotPermissionAction)] + NotPermissionAction = 4001, + + [Result(ResponseString.DataExist)] + [Description("鎸囧畾鐢ㄦ埛ID鍜屾潈闄愬垎绫籌D宸插瓨鍦紝涓嶅彲鏂板")] + PermissionExist = 4002, + + [Result(ResponseString.IdDateNotFound)] + [Description("鎸囧畾ID鐨勬潈闄愭病鏈夋壘鍒")] + FindPermissionByIdFail = 4003, + + [Result("鏉冮檺缂栫爜宸插瓨鍦紝璇锋鏌")] + [Description("鏉冮檺缂栫爜宸插瓨鍦紝璇锋鏌")] + PermissionCodeExist = 4004, + + #endregion + + #region 绯荤粺鎶ラ敊 + + [Result(ResponseString.SystemError)] + [Description(ResponseString.SystemError)] + SystemError = 5000, + + [Result(ResponseString.ParameterError)] + [Description(ResponseString.ParameterError)] + ParameterError = 5001, + + [Result(ResponseString.InvalidOptions)] + [Description(ResponseString.InvalidOptions)] + InvalidOptions = 5002, + #endregion + + #region 鎻愮ず璇嶆搷浣滃け璐 + + [Result(ResponseString.DataExist)] + [Description("鎸囧畾鐨勬彁绀鸿瘝宸茬粡瀛樺湪")] + PromptStringExist = 6001, + + [Result(ResponseString.IdDateNotFound)] + [Description("鎸囧畾ID鐨勬彁绀鸿瘝娌℃湁鎵惧埌")] + FindPromptStringFail = 6002, + + #endregion + + #region 鎻愮ず璇嶇被鍨嬫搷浣滃け璐 + + [Result(ResponseString.DataExist)] + [Description("鎸囧畾鐨勬彁绀鸿瘝鐨勫悕绉版垨鑰呮槸缂栫爜宸插瓨鍦")] + PromptTypeExist = 7001, + + [Result(ResponseString.IdDateNotFound)] + [Description("鎸囧畾ID鐨勬彁绀鸿瘝绫诲瀷娌℃湁鎵惧埌")] + FindPromptTypeFail = 7002, + #endregion + + #region 鏂囨杞彂澶辫触 + + [Result(ResponseString.ForwardFail)] + [Description("鏂囨杞彂澶辫触")] + ForwardWordFail = 8001, + + #endregion + + #region 瑙掕壊鎿嶄綔澶辫触 + + [Result(ResponseString.DataExist)] + [Description("鎸囧畾鐨勮鑹插悕绉板凡缁忓瓨鍦")] + RoleNameExist = 9001, + + [Result(ResponseString.IdDateNotFound)] + [Description("鎸囧畾ID鐨勮鑹蹭笉瀛樺湪")] + FindRoleByIdFail = 9002, + + [Result(ResponseString.DataNotExist)] + [Description("鎸囧畾缂栫爜鐨勮鑹蹭笉瀛樺湪")] + FindRoleByCodeFail = 9003, + + [Result(ResponseString.HasRelationship)] + [Description("鎸囧畾瑙掕壊宸茬粡缁戝畾浜嗙敤鎴凤紝鏃犳硶鍒犻櫎")] + RoleHasUser = 9004, + + #endregion + } + } +} diff --git a/LMS.Common/Enum/ResponseString.cs b/LMS.Common/Enum/ResponseString.cs new file mode 100644 index 0000000..69e8048 --- /dev/null +++ b/LMS.Common/Enum/ResponseString.cs @@ -0,0 +1,42 @@ +锘縩amespace LMS.Common.Enums +{ + public static class ResponseString + { + #region 鐢ㄦ埛鐩稿叧 + public const string UndefinedLoginType = "鏈煡鐨勭櫥褰曠被鍨"; + + public const string UserNotFound = "鐢ㄦ埛鏈壘鍒帮紝璇峰厛娉ㄥ唽"; + + public const string UserIsLockedOut = "鐢ㄦ埛宸茶閿佸畾锛岃鑱旂郴绠$悊鍛"; + + public const string UserPasswordError = "妫鏌ヨ处鍙峰拰瀵嗙爜鏄惁姝g‘"; + + public const string UserLoginOut = "鐢ㄦ埛宸查鍑猴紝璇烽噸鏂扮櫥褰"; + + #endregion + + public const string UserLogin = "妫鏌ヨ处鍙峰拰瀵嗙爜鏄惁姝g‘"; + public const string UserEmailNotFound = "鏈壘鍒板搴旂敤鎴峰悕/閭鐨勮处鍙"; + public const string UserStatusError = "鐢ㄦ埛鐨勭姸鎬侀敊璇"; + + public const string MachineStatusNotFoundOrStatusIsNot = "鏈壘鍒板搴旂殑鏈哄櫒鐮佹垨鑰呮槸鏈哄櫒鐮佸凡杩囨湡锛岃鑱旂郴寮鍙戞垨瀹㈡湇"; + + public const string SystemError = "绯荤粺閿欒"; + + public const string ParameterError = "鍙傛暟閿欒"; + + public const string InvalidOptions = "鏃犳晥鐨勬搷浣"; + + public const string IdDateNotFound = "鎸囧畾ID鐨勬暟鎹病鏈夋壘鍒"; + + public const string DataExist = "鏁版嵁宸插瓨鍦紝涓嶅彲鏂板"; + + public const string HasRelationship = "鏁版嵁瀛樺湪鍏宠仈锛岃鍏堝垹闄ゅ叧鑱"; + + public const string DataNotExist = "鏁版嵁涓嶅瓨鍦紝璇锋鏌"; + + public const string NotPermissionAction = "娌℃湁鎵ц璇ユ搷浣滅殑鏉冮檺"; + + public const string ForwardFail = "杞彂澶辫触"; + } +} diff --git a/LMS.Common/Enum/UserStatus.cs b/LMS.Common/Enum/UserStatus.cs new file mode 100644 index 0000000..89f10f9 --- /dev/null +++ b/LMS.Common/Enum/UserStatus.cs @@ -0,0 +1,31 @@ +锘縰sing LMS.Common.Attributes; + +namespace LMS.Common.Enums +{ + public class UserStatus + { + public enum UserStatusEnum + { + [Description("姝e父")] + Normal = 1, + + [Description("绂佺敤")] + Disable = 0, + + [Description("鍒犻櫎")] + Delete = 2, + + [Description("榛戝悕鍗")] + BlackList = 3, + + [Description("鏈縺娲")] + NotActive = 4, + + [Description("杩濊")] + Violation = 5, + + [Description("鏈煡")] + Unknown = 6 + } + } +} diff --git a/LMS.Common/LMS.Common.csproj b/LMS.Common/LMS.Common.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/LMS.Common/LMS.Common.csproj @@ -0,0 +1,9 @@ +锘 + + + net8.0 + enable + enable + + + diff --git a/LMS.Common/RSAKey/AESGenerate.cs b/LMS.Common/RSAKey/AESGenerate.cs new file mode 100644 index 0000000..d3d6d35 --- /dev/null +++ b/LMS.Common/RSAKey/AESGenerate.cs @@ -0,0 +1,37 @@ +锘縰sing System.Security.Cryptography; +using System.Text; + +namespace LMS.Common.RSAKey +{ + public static class AESGenerate + { + + public static string Encrypt(string plainText, byte[] key, byte[] iv) + { + using Aes aesAlg = Aes.Create(); + aesAlg.Key = key; + aesAlg.IV = iv; // 浣跨敤鍏ㄩ浂鐨処V,瀹為檯浣跨敤鏃跺簲璇ヤ娇鐢ㄩ殢鏈篒V + + ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); + + byte[] encrypted = encryptor.TransformFinalBlock(Encoding.UTF8.GetBytes(plainText), 0, plainText.Length); + + return Convert.ToBase64String(encrypted); + } + + public static string Decrypt(string cipherText, byte[] key, byte[] iv) + { + using Aes aesAlg = Aes.Create(); + aesAlg.Key = key; + aesAlg.IV = iv; // 浣跨敤鍏ㄩ浂鐨処V,闇瑕佷笌鍔犲瘑鏃朵娇鐢ㄧ殑IV涓鑷 + + ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); + + byte[] cipher = Convert.FromBase64String(cipherText); + byte[] decrypted = decryptor.TransformFinalBlock(cipher, 0, cipher.Length); + + return Encoding.UTF8.GetString(decrypted); + } + + } +} diff --git a/LMS.Common/RSAKey/ComplexKeyObfuscator.cs b/LMS.Common/RSAKey/ComplexKeyObfuscator.cs new file mode 100644 index 0000000..7ef4e01 --- /dev/null +++ b/LMS.Common/RSAKey/ComplexKeyObfuscator.cs @@ -0,0 +1,99 @@ +锘縰sing System.Security.Cryptography; + +namespace LMS.Common.RSAKey +{ + /// + /// 杩欐槸涓涓贩娣嗗櫒锛岀敤浜庡瀵嗛挜杩涜娣锋穯锛屼互澧炲姞鐮磋В闅惧害 + /// + public static class ComplexKeyObfuscator + { + private const int SaltSize = 16; + + /// + /// 娣锋穯鏂规硶 + /// + /// + /// + /// + public static byte[] Obfuscate(byte[] key) + { + if (key == null || key.Length == 0) + throw new ArgumentException("Key cannot be null or empty"); + + byte[] salt = GenerateRandomSalt(); + byte[] obfuscatedKey = new byte[key.Length]; + Array.Copy(key, obfuscatedKey, key.Length); + + // 澶氬眰娣锋穯鎿嶄綔 + for (int i = 0; i < obfuscatedKey.Length; i++) + { + // 寮傛垨鎿嶄綔 + obfuscatedKey[i] ^= salt[i % salt.Length]; + obfuscatedKey[i] ^= (byte)((i * 17) % 256); // 浣跨敤璐ㄦ暟17 + obfuscatedKey[i] ^= (byte)((obfuscatedKey.Length - i) % 256); + + // 浣嶇Щ鎿嶄綔 + obfuscatedKey[i] = (byte)(((obfuscatedKey[i] << 3) | (obfuscatedKey[i] >> 5)) & 0xFF); + + // 鍩轰簬浣嶇疆鐨勭疆鎹 + int newPos = (i * 7 + 5) % obfuscatedKey.Length; // 浣跨敤璐ㄦ暟7 + byte temp = obfuscatedKey[i]; + obfuscatedKey[i] = obfuscatedKey[newPos]; + obfuscatedKey[newPos] = temp; + } + + // 灏唖alt鍜屾贩娣嗗悗鐨勫瘑閽ュ悎骞 + byte[] result = new byte[salt.Length + obfuscatedKey.Length]; + Array.Copy(salt, 0, result, 0, salt.Length); + Array.Copy(obfuscatedKey, 0, result, salt.Length, obfuscatedKey.Length); + + return result; + } + + /// + /// 瑙e喅娣锋穯鏂规硶 + /// + /// + /// + /// + public static byte[] Deobfuscate(byte[] obfuscatedData) + { + if (obfuscatedData == null || obfuscatedData.Length <= SaltSize) + throw new ArgumentException("Invalid obfuscated data"); + + byte[] salt = new byte[SaltSize]; + byte[] obfuscatedKey = new byte[obfuscatedData.Length - SaltSize]; + + Array.Copy(obfuscatedData, 0, salt, 0, SaltSize); + Array.Copy(obfuscatedData, SaltSize, obfuscatedKey, 0, obfuscatedKey.Length); + + // 鍙嶅悜澶氬眰娣锋穯鎿嶄綔 + for (int i = obfuscatedKey.Length - 1; i >= 0; i--) + { + // 鍙嶅悜缃崲 + int newPos = (i * 7 + 5) % obfuscatedKey.Length; + byte temp = obfuscatedKey[i]; + obfuscatedKey[i] = obfuscatedKey[newPos]; + obfuscatedKey[newPos] = temp; + + // 鍙嶅悜浣嶇Щ鎿嶄綔 + obfuscatedKey[i] = (byte)(((obfuscatedKey[i] >> 3) | (obfuscatedKey[i] << 5)) & 0xFF); + + // 鍙嶅悜寮傛垨鎿嶄綔 + obfuscatedKey[i] ^= (byte)((obfuscatedKey.Length - i) % 256); + obfuscatedKey[i] ^= (byte)((i * 17) % 256); + obfuscatedKey[i] ^= salt[i % salt.Length]; + } + + return obfuscatedKey; + } + + private static byte[] GenerateRandomSalt() + { + using var rng = RandomNumberGenerator.Create(); + byte[] salt = new byte[SaltSize]; + rng.GetBytes(salt); + return salt; + } + } +} diff --git a/LMS.Common/RSAKey/RSAKeyGenerateModel.cs b/LMS.Common/RSAKey/RSAKeyGenerateModel.cs new file mode 100644 index 0000000..792a209 --- /dev/null +++ b/LMS.Common/RSAKey/RSAKeyGenerateModel.cs @@ -0,0 +1,19 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LMS.Common.RSAKey +{ + public class RSAKeyGenerateModel + { + public string PublicKey { get; set; } + + public string EncryptedPrivateKey { get; set; } + + public string EncryptedKey { get; set; } + + public string EncryptionIV { get; set; } + } +} diff --git a/LMS.Common/RSAKey/RsaKeyPairGenerator.cs b/LMS.Common/RSAKey/RsaKeyPairGenerator.cs new file mode 100644 index 0000000..564ddb0 --- /dev/null +++ b/LMS.Common/RSAKey/RsaKeyPairGenerator.cs @@ -0,0 +1,131 @@ +锘縰sing LMS.Common.RSAKey; +using System.Security.Cryptography; +using System.Text; +namespace LMS.Common.RSAKey +{ + public static class RsaKeyPairGenerator + { + + /// + /// 鍒濆鍖栦竴涓猂SA瀵嗛挜瀵 + /// + /// + public static RSAKeyGenerateModel InitRsaKey() + { + using RSACryptoServiceProvider rsa = new(2048); + string publicKeyPem = ExportPublicKeyToPem(rsa); + string privateKeyPem = ExportPrivateKeyToPem(rsa); + + // 闅忔満鐢熸垚涓涓狝ES瀵嗛挜 + byte[] aesKey = GenerateRandomAesKey(32); + byte[] aesIv = GenerateRandomAesKey(16); + + // 閫氳繃闅忔満鐨勫瘑閽ュ紑濮婣ES鍔犲瘑绉侀挜 + string encryptPrivateKey = AESGenerate.Encrypt(privateKeyPem, aesKey, aesIv); + + return new RSAKeyGenerateModel + { + PublicKey = publicKeyPem, + EncryptedPrivateKey = encryptPrivateKey, + EncryptedKey = Convert.ToBase64String(ComplexKeyObfuscator.Obfuscate(aesKey)), + EncryptionIV = Convert.ToBase64String(ComplexKeyObfuscator.Obfuscate(aesIv)) + }; + } + + private static byte[] GenerateRandomAesKey(int bits) + { + using var randomNumberGenerator = RandomNumberGenerator.Create(); + var randomBytes = new byte[bits]; // 256 bits + randomNumberGenerator.GetBytes(randomBytes); + return randomBytes; + } + + public static void GenerateRsaKeyPair() + { + using RSACryptoServiceProvider rsa = new(2048); + + string publicKeyPem = ExportPublicKeyToPem(rsa); + string privateKeyPem = ExportPrivateKeyToPem(rsa); + + Console.WriteLine("鍏挜 (PEM)锛歕n" + publicKeyPem); + Console.WriteLine("绉侀挜 (PEM)锛歕n" + privateKeyPem); + + Console.WriteLine(); + string original = "Hello, RSA!"; + var encrypted = Encrypt(publicKeyPem, original); + Console.WriteLine("Encrypted: " + encrypted); + + Console.WriteLine(); + var decrypted = Decrypt(privateKeyPem, encrypted); + Console.WriteLine("Decrypted: " + decrypted); + + var signData = SignData(privateKeyPem, original); + Console.WriteLine("SignData: " + signData); + + var isVerify = VerifySignData(publicKeyPem, original, signData); + Console.WriteLine("VerifySign: " + isVerify); + } + + private static string ExportPublicKeyToPem(RSA rsa) + { + var publicKey = rsa.ExportSubjectPublicKeyInfo(); + var base64 = Convert.ToBase64String(publicKey); + return $"-----BEGIN PUBLIC KEY-----\n{InsertNewLines(base64)}\n-----END PUBLIC KEY-----"; + } + + private static string ExportPrivateKeyToPem(RSA rsa) + { + var privateKey = rsa.ExportPkcs8PrivateKey(); + var base64 = Convert.ToBase64String(privateKey); + return $"-----BEGIN PRIVATE KEY-----\n{InsertNewLines(base64)}\n-----END PRIVATE KEY-----"; + } + + private static string InsertNewLines(string input) + { + return string.Join("\n", Enumerable.Range(0, input.Length / 64 + 1) + .Select(i => input.Substring(i * 64, Math.Min(64, input.Length - i * 64)))); + } + + public static string Encrypt(string publicKeyPem, string data) + { + using (var rsa = RSA.Create()) + { + rsa.ImportFromPem(publicKeyPem); + byte[] encryptedData = rsa.Encrypt(Encoding.UTF8.GetBytes(data), RSAEncryptionPadding.OaepSHA256); + return Convert.ToBase64String(encryptedData); + } + } + + public static string Decrypt(string privateKeyPem, string data) + { + using (var rsa = RSA.Create()) + { + rsa.ImportFromPem(privateKeyPem); + byte[] decryptedData = rsa.Decrypt(Convert.FromBase64String(data), RSAEncryptionPadding.OaepSHA256); + return Encoding.UTF8.GetString(decryptedData); + } + } + + public static string SignData(string privateKeyPem, string data) + { + using (var rsa = RSA.Create()) + { + rsa.ImportFromPem(privateKeyPem); + var inputBytes = Encoding.UTF8.GetBytes(data); + var resultBytes = rsa.SignData(inputBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + return Convert.ToBase64String(resultBytes); + } + } + + public static bool VerifySignData(string publicKeyPem, string data, string sign) + { + using (var rsa = RSA.Create()) + { + rsa.ImportFromPem(publicKeyPem); + var dataBytes = Encoding.UTF8.GetBytes(data); + var signBytes = Convert.FromBase64String(sign); + return rsa.VerifyData(dataBytes, signBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } + } + } +} diff --git a/LMS.DAO/ApplicationDbContext.cs b/LMS.DAO/ApplicationDbContext.cs new file mode 100644 index 0000000..565791f --- /dev/null +++ b/LMS.DAO/ApplicationDbContext.cs @@ -0,0 +1,46 @@ +锘 + +using LMS.Repository.DB; +using LMS.Repository.Models.DB; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using System.Text.Json; + +namespace LMS.DAO +{ + public class ApplicationDbContext : IdentityDbContext + { + public ApplicationDbContext(DbContextOptions options) : base(options) { } + + //public DbSet Prompt { get; set; } + + //public DbSet PromptType { get; set; } + + public DbSet Permission { get; set; } + + public DbSet PermissionType { get; set; } + + public DbSet Machine { get; set; } + + public DbSet RefreshTokens { get; set; } + + public DbSet ApiEndpoints { get; set; } + + public DbSet RsaKeys { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity() + .Property(a => a.RequiredPermissionIds) + .HasConversion( + v => string.IsNullOrEmpty(JsonSerializer.Serialize(v, (JsonSerializerOptions?)null)) + ? "[]" // 濡傛灉搴忓垪鍖栫粨鏋滀负绌猴紝鍒欏瓨鍌ㄧ┖鏁扮粍 + : JsonSerializer.Serialize(v, (JsonSerializerOptions?)null), + v => string.IsNullOrEmpty(v) || v == "[]" + ? new List() // 濡傛灉瀛樺偍鐨勬槸绌哄瓧绗︿覆鎴栫┖鏁扮粍锛屽垯杩斿洖绌哄垪琛 + : JsonSerializer.Deserialize>(v, (JsonSerializerOptions?)null) ?? new List() + ); + } + } +} diff --git a/LMS.DAO/LMS.DAO.csproj b/LMS.DAO/LMS.DAO.csproj new file mode 100644 index 0000000..c3d9456 --- /dev/null +++ b/LMS.DAO/LMS.DAO.csproj @@ -0,0 +1,21 @@ +锘 + + + net8.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/LMS.DAO/MachineDAO/MachineBasicDao.cs b/LMS.DAO/MachineDAO/MachineBasicDao.cs new file mode 100644 index 0000000..2f6ff0d --- /dev/null +++ b/LMS.DAO/MachineDAO/MachineBasicDao.cs @@ -0,0 +1,43 @@ +锘縰sing Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LMS.DAO.MachineDAO +{ + public class MachineBasicDao(ApplicationDbContext context) + { + private readonly ApplicationDbContext _context = context; + + /// + /// 妫鏌ユ満鍣ㄧ爜鏄惁瀛樺湪锛堟満鍣ㄧ爜锛屼笉鏄搴旂殑ID锛 + /// + /// + /// + public async Task CheckMachineByMachineId(string? machineId) + { + if (string.IsNullOrWhiteSpace(machineId)) + { + return false; + } + return await _context.Machine.AnyAsync(x => x.MachineId == machineId); + } + + + /// + /// 妫鏌ユ満鍣ㄧ爜鏄惁瀛樺湪锛堟満鍣ㄧ爜锛屽搴旂殑ID锛 + /// + /// + /// + public async Task CheckMachineById(string Id) + { + if (string.IsNullOrWhiteSpace(Id)) + { + return false; + } + return await _context.Machine.AnyAsync(x => x.Id == Id); + } + } +} diff --git a/LMS.DAO/MyDbcontextDesignFactory.cs b/LMS.DAO/MyDbcontextDesignFactory.cs new file mode 100644 index 0000000..307d16b --- /dev/null +++ b/LMS.DAO/MyDbcontextDesignFactory.cs @@ -0,0 +1,26 @@ +锘縰sing Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +namespace LMS.DAO +{ + public class MyDbcontextDesignFactory : IDesignTimeDbContextFactory + { + //public MyDbcontextDesignFactory CreateDbContext(string[] args) + //{ + // DbContextOptionsBuilder optionsBuilder = new DbContextOptionsBuilder(); + // string str = Environment.GetEnvironmentVariable("CONNECTION_STRING"); + // optionsBuilder.UseMySql(str, ServerVersion.Parse("8.0.18-mysql")); + // ApplicationDbContext db = new ApplicationDbContext(optionsBuilder.Options); + // return db; + //} + + public ApplicationDbContext CreateDbContext(string[] args) + { + DbContextOptionsBuilder optionsBuilder = new DbContextOptionsBuilder(); + string str = "server=123.129.219.240;port=14080;user=luo;password=Luoqiang1405;database=LMS_TEST"; + optionsBuilder.UseMySql(str, ServerVersion.Parse("8.0.18-mysql")); + ApplicationDbContext db = new ApplicationDbContext(optionsBuilder.Options); + return db; + } + } +} diff --git a/LMS.DAO/PermissionDAO/PermissionBasicDao.cs b/LMS.DAO/PermissionDAO/PermissionBasicDao.cs new file mode 100644 index 0000000..4493494 --- /dev/null +++ b/LMS.DAO/PermissionDAO/PermissionBasicDao.cs @@ -0,0 +1,117 @@ +锘縰sing Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Primitives; +using OneOf; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static LMS.Common.Enums.PermissionEnum; + +namespace LMS.DAO.PermissionDAO +{ + public class PermissionBasicDao(ApplicationDbContext context) + { + private readonly ApplicationDbContext _context = context; + + /// + /// 鍒ゆ柇鎸囧畾鐨処D鐨勬潈闄愭槸涓嶆槸瀛樺湪 + /// + /// + /// + public async Task CheckPermissionExistById(string id) + { + if (string.IsNullOrWhiteSpace(id)) + { + return false; + } + return await _context.Permission.AnyAsync(x => x.Id == id); + } + + /// + /// 鍒ゆ柇鏉冮檺鐨凱ermissionCode鏄笉鏄瓨鍦 + /// 閫氳繃鍒ゆ柇鏄笉鏄湁浼犻扞D , 鍒ゆ柇鏄笉鏄渶瑕佹帓闄ゅ綋鍓嶇殑鏁版嵁 + /// + /// + /// + /// + public async Task CheckPermissionCodeExist(string permissionCode, string id = null) + { + if (string.IsNullOrWhiteSpace(permissionCode)) + { + throw new ArgumentNullException(nameof(permissionCode)); + } + + if (string.IsNullOrEmpty(id)) + { + return await _context.Permission.AnyAsync(x => x.PermissionCode == permissionCode); + } + else + { + return await _context.Permission.AnyAsync(x => x.PermissionCode == permissionCode && x.Id != id); + } + } + + /// + /// 妫鏌ュ搴斿垎绫荤殑鏉冮檺鏄笉鏄瓨鍦 + /// + /// + /// + /// 鍐嶄慨鏀圭殑鏃跺欏繀浼犺繖涓硷紝鐢ㄤ簬鍒ゆ柇瀵瑰簲鐨勫叧鑱擨D鏄笉鏄湁鍏朵粬鐨勫硷紝鏈夌殑璇濅笉鑳戒慨鏀 + /// + /// + public async Task CheckPermissionByTypeAndId(PType type, OneOf id, string pid = null) + { + if (id.IsT0) + { + if (type != PType.Machine) + { + throw new Exception("璇锋鏌ュ弬鏁扮殑瀵瑰簲鍏崇郴"); + } + if (!string.IsNullOrEmpty(pid)) + { + return await _context.Permission.AnyAsync(x => x.MachineId == id.AsT0 && x.Type == type && x.Id != pid); + } + else + { + return await _context.Permission.AnyAsync(x => x.MachineId == id.AsT0 && x.Type == type); + } + } + else if (id.IsT1) + { + if (type == PType.Machine) + { + throw new Exception("璇锋鏌ュ弬鏁扮殑瀵瑰簲鍏崇郴"); + } + if (id.AsT1 == null) + { + return false; + } + if (!string.IsNullOrEmpty(pid)) + { + return type switch + { + PType.User => await _context.Permission.AnyAsync(x => x.UserId == id.AsT1 && x.Type == type && x.Id != pid), + PType.Role => await _context.Permission.AnyAsync(x => x.RoleId == id.AsT1 && x.Type == type && x.Id != pid), + PType.Machine => throw new Exception("璇锋鏌ュ弬鏁扮殑瀵瑰簲鍏崇郴"), + _ => throw new Exception("鍙傛暟绫诲瀷閿欒"), + }; + } + else + { + return type switch + { + PType.User => await _context.Permission.AnyAsync(x => x.UserId == id.AsT1 && x.Type == type), + PType.Role => await _context.Permission.AnyAsync(x => x.RoleId == id.AsT1 && x.Type == type), + PType.Machine => throw new Exception("璇锋鏌ュ弬鏁扮殑瀵瑰簲鍏崇郴"), + _ => throw new Exception("鍙傛暟绫诲瀷閿欒"), + }; + } + } + else + { + throw new Exception("鍙傛暟绫诲瀷閿欒"); + } + } + } +} diff --git a/LMS.DAO/PermissionDAO/PermissionTypeDao.cs b/LMS.DAO/PermissionDAO/PermissionTypeDao.cs new file mode 100644 index 0000000..937a27f --- /dev/null +++ b/LMS.DAO/PermissionDAO/PermissionTypeDao.cs @@ -0,0 +1,55 @@ +锘縰sing Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace LMS.DAO.PermissionDAO +{ + public class PermissionTypeDao(ApplicationDbContext context) + { + private readonly ApplicationDbContext _context = context; + + /// + /// 鍒ゆ柇鏉冮檺绫诲瀷ID鏁扮粍涓殑鏁版嵁鏄笉鏄兘瀛樺湪鏁版嵁搴撲腑 + /// + /// + /// + public async Task CheckPermissionTypeIdsExist(List ids) + { + foreach (var id in ids) + { + if (!await _context.PermissionType.AnyAsync(x => x.Id == id)) + { + return false; + } + } + return true; + } + + /// + /// 妫鏌ユ潈闄愮被鍨嬬殑Code鏄笉鏄瓨鍦 + /// + /// + /// + /// + /// + public async Task CheckPermissionTypeCodeExist(string Code, string Id = null) + { + if (string.IsNullOrWhiteSpace(Code)) + { + throw new ArgumentNullException(nameof(Code)); + } + if (string.IsNullOrWhiteSpace(Id)) + { + return await _context.PermissionType.AnyAsync(x => x.Code == Code); + } + else + { + return await _context.PermissionType.AnyAsync(x => x.Code == Code && x.Id != Id); + } + } + } +} diff --git a/LMS.DAO/RoleDAO/RoleBasicDao.cs b/LMS.DAO/RoleDAO/RoleBasicDao.cs new file mode 100644 index 0000000..cc69761 --- /dev/null +++ b/LMS.DAO/RoleDAO/RoleBasicDao.cs @@ -0,0 +1,28 @@ +锘縰sing LMS.Repository.Models.DB; +using Microsoft.AspNetCore.Identity; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LMS.DAO.RoleDAO +{ + public class RoleBasicDao(ApplicationDbContext context, RoleManager roleManager) + { + private readonly ApplicationDbContext _context = context; + private readonly RoleManager _roleManager = roleManager; + + /// + /// 妫鏌ヨ鑹叉槸涓嶆槸瀛樺湪 + /// + /// + /// + public async Task CheckRoleExistById(long? roleId) + { + if (roleId == null) + return false; + return await _roleManager.FindByIdAsync(roleId.ToString()) != null; + } + } +} diff --git a/LMS.DAO/UserDAO/UserBasicDAO.cs b/LMS.DAO/UserDAO/UserBasicDAO.cs new file mode 100644 index 0000000..f8cdea4 --- /dev/null +++ b/LMS.DAO/UserDAO/UserBasicDAO.cs @@ -0,0 +1,29 @@ +锘縰sing LMS.Repository.Models.DB; +using Microsoft.AspNetCore.Identity; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LMS.DAO.UserDAO +{ + public class UserBasicDao(UserManager userManager) + { + private readonly UserManager _userManager = userManager; + /// + /// 妫鏌ョ敤鎴锋槸鍚﹀瓨鍦,閫氳繃鐢ㄦ埛ID + /// + /// + /// + public async Task CheckUserExistsByID(long? userId) + { + if (userId == null) + { + return false; + } + return await _userManager.FindByIdAsync(userId.ToString()) != null; + } + } + +} diff --git a/LMS.Repository/DB/ApiEndpoints.cs b/LMS.Repository/DB/ApiEndpoints.cs new file mode 100644 index 0000000..52212f6 --- /dev/null +++ b/LMS.Repository/DB/ApiEndpoints.cs @@ -0,0 +1,22 @@ +锘縰sing LMS.Tools.Extensions; + +namespace LMS.Repository.Models.DB +{ + public class ApiEndpoints + { + + public string Id { get; set; } + + public string HttpMethod { get; set; } + + public string Path { get; set; } + + public DateTime CreateTime { get; set; } = BeijingTimeExtension.GetBeijingTime(); + + public List RequiredPermissionIds { get; set; } + + public long CreatedId { get; set; } + + public long UpdatedId { get; set; } + } +} diff --git a/LMS.Repository/DB/Machine.cs b/LMS.Repository/DB/Machine.cs new file mode 100644 index 0000000..c010ac1 --- /dev/null +++ b/LMS.Repository/DB/Machine.cs @@ -0,0 +1,62 @@ +锘縰sing static LMS.Common.Enums.MachineEnum; + +namespace LMS.Repository.Models.DB +{ + public class Machine + { + /// + /// ID 涓婚敭 + /// + public string Id { get; set; } + + /// + /// 鏈哄櫒ID + /// + public string MachineId { get; set; } + + /// + /// 鍒涘缓鏃堕棿 + /// + public DateTime CreateTime { get; set; } + + /// + /// 鏇存柊鏃堕棿 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 鍋滅敤鏃堕棿 + /// + public DateTime? DeactivationTime { get; set; } + + /// + /// 鐘舵侊紙鏄惁鍙敤锛 + /// + public MachineStatus Status { get; set; } + + /// + /// 鍒涘缓鑰匢D + /// + public long CreateId { get; set; } + + /// + /// 鏇存柊鑰匢D + /// + public long UpdateId { get; set; } + + /// + /// 浣跨敤鐘舵(璇曠敤锛屾案涔) + /// + public MachineUseStatus UseStatus { get; set; } + + /// + /// 澶囨敞 + /// + public string? Remark { get; set; } + + /// + /// 鎵灞炵敤鎴稩D + /// + public required long UserID { get; set; } + } +} diff --git a/LMS.Repository/DB/Permission.cs b/LMS.Repository/DB/Permission.cs new file mode 100644 index 0000000..1409a9a --- /dev/null +++ b/LMS.Repository/DB/Permission.cs @@ -0,0 +1,82 @@ +锘縰sing LMS.Common.Enums; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json; +using static LMS.Common.Enums.PermissionEnum; + +namespace LMS.Repository.Models.DB +{ + /// + /// 鎵鏈夌殑鏉冮檺闆嗗悎 + /// + public class Permission + { + /// + /// 鏉冮檺鐨処D + /// + public string Id { get; set; } + + /// + /// 鐢ㄦ埛鐨処D + /// + public long? UserId { get; set; } + + /// + /// 鏈哄櫒鐮佺殑ID锛堟槸ID锛屼笉鏄満鍣ㄧ爜锛 + /// + public string? MachineId { get; set; } + + /// + /// 瑙掕壊ID + /// + public long? RoleId { get; set; } + + /// + /// 鏉冮檺绫诲瀷鐨処D锛堝瓙鏉冮檺锛 + /// + [Column(TypeName = "json")] + public string PermissionTypeIds { get; set; } + + /// + /// 鏉冮檺瀵瑰簲鐨凜ode + /// + public string PermissionCode { get; set; } + + /// + /// 鏉冮檺绫诲瀷 + /// + public PType Type { get; set; } + + /// + /// 鍒涘缓浜篒D + /// + public long CreateUserId { get; set; } + + /// + /// 鏇存柊浜篒D + /// + public long UpdateUserId { get; set; } + + /// + /// 鍒涘缓鏃堕棿 + /// + public DateTime CreateTime { get; set; } + + /// + /// 鏇存柊鏃堕棿 + /// + public DateTime UpdateTime { get; set; } + + + /// + /// 澶囨敞 + /// + public string Remark { get; set; } + + [NotMapped] + public List PermissionTypeIdsJson + { + get => JsonSerializer.Deserialize>(PermissionTypeIds) ?? []; + set => PermissionTypeIds = JsonSerializer.Serialize(value); + } + } +} diff --git a/LMS.Repository/DB/PermissionType.cs b/LMS.Repository/DB/PermissionType.cs new file mode 100644 index 0000000..f777d9c --- /dev/null +++ b/LMS.Repository/DB/PermissionType.cs @@ -0,0 +1,56 @@ +锘縰sing LMS.Common.Enums; +using static LMS.Common.Enums.PermissionEnum; + +namespace LMS.Repository.Models.DB +{ + /// + /// 瀛樻斁鏉冮檺鐨勮〃 + /// + public class PermissionType + { + /// + /// 鏉冮檺ID + /// + public string Id { get; set; } + + /// + /// 鏉冮檺鐨勫悕瀛 + /// + public string Name { get; set; } + + /// + /// 琛ㄧず鏉冮檺鐨勪唬鐮 + /// + public string Code { get; set; } + + /// + /// 鏉冮檺鐨勫垎绫伙紙user鍜宮achine锛 + /// + public PermissionEnum.PermissionType Type { get; set; } + + /// + /// 鍒涘缓鑰匢D + /// + public long CreateUserId { get; set; } + + /// + /// 鍒涘缓鏃堕棿 + /// + public DateTime CreateTime { get; set; } + + /// + /// 鏇存柊鑰匢D + /// + public long UpdateUserId { get; set; } + + /// + /// 鏇存柊鏉冮檺鐨勬椂闂 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 鏉冮檺鐨勬弿杩 + /// + public string? Remark { get; set; } + } +} diff --git a/LMS.Repository/DB/Prompt.cs b/LMS.Repository/DB/Prompt.cs new file mode 100644 index 0000000..afaa291 --- /dev/null +++ b/LMS.Repository/DB/Prompt.cs @@ -0,0 +1,71 @@ +锘縩amespace LMS.Repository.Models.DB +{ + public class Prompt + { + /// + /// ID + /// + public string Id { get; set; } + + /// + /// 鎻愮ず璇嶉璁剧殑鍚嶇О + /// + public string Name { get; set; } + + /// + /// 鎻愮ず璇嶇殑绫诲瀷ID + /// + public string PromptTypeId { get; set; } + + /// + /// 鎻愮ず璇嶇殑绫诲瀷缂栫爜 + /// + public string PromptTypeCode { get; set; } + + /// + /// 鎻愮ず璇嶉璁惧瓧绗︿覆 + /// + public string PromptString { get; set; } + + /// + /// 褰撳墠鐗堟湰 + /// + public int Version { get; set; } + + /// + /// 鐘舵 + /// + public string Status { get; set; } + + /// + /// 鎻愮ず璇嶇殑鎻忚堪 + /// + public string? Description { get; set; } + + /// + /// 鍒涘缓鑰 + /// + public string CreateUserId { get; set; } + + /// + /// 鍒涘缓鏃堕棿 + /// + public DateTime CreateTime { get; set; } + + /// + /// 鏇存柊鑰 + /// + public string UpdateUserId { get; set; } + + /// + /// 鏇存柊鏃堕棿 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 澶囨敞 + /// + public string? Remark { get; set; } + + } +} diff --git a/LMS.Repository/DB/PromptType.cs b/LMS.Repository/DB/PromptType.cs new file mode 100644 index 0000000..f204e63 --- /dev/null +++ b/LMS.Repository/DB/PromptType.cs @@ -0,0 +1,61 @@ +锘縩amespace LMS.Repository.Models.DB +{ + /// + /// 鎻愮ず璇嶇被鍨嬬殑搴 + /// + public class PromptType + { + /// + /// ID + /// + public string Id { get; set; } + + /// + /// 鎻愮ず璇嶇被鍨嬬紪鐮 + /// + public string Code { get; set; } + + /// + /// 鎻愮ず璇嶇被鍨嬪悕绉 + /// + public string Name { get; set; } + + /// + /// 鎻愮ず璇嶇被鍨嬫弿杩 + /// + public string? Remark { get; set; } + + /// + /// 鎻愮ず璇嶇被鍨嬬姸鎬 + /// + public string Status { get; set; } + + /// + /// 鏄惁鍒犻櫎 + /// + public bool IsDeleted { get; set; } + + /// + /// 鍒涘缓鐢ㄦ埛ID + /// + public string CreateUserId { get; set; } + + /// + /// 鍒涘缓鏃堕棿 + /// + public DateTime CreateTime { get; set; } + + /// + /// 鏇存柊鑰匢D + /// + public string UpdateUserId { get; set; } + + /// + /// 鏇存柊鏃堕棿 + /// + public DateTime UpdateTime { get; set; } + + + + } +} diff --git a/LMS.Repository/DB/RefreshTokens.cs b/LMS.Repository/DB/RefreshTokens.cs new file mode 100644 index 0000000..bba94e8 --- /dev/null +++ b/LMS.Repository/DB/RefreshTokens.cs @@ -0,0 +1,46 @@ +锘縩amespace LMS.Repository.Models.DB +{ + public class RefreshTokens + { + /// + /// 涓婚敭 ID + /// + public required string Id { get; set; } + + /// + /// 鐢ㄦ埛ID + /// + public required long UserId { get; set; } + + /// + /// Token + /// + public required string Token { get; set; } + + /// + /// 鍒版湡鏃堕棿 + /// + public DateTime Expiration { get; set; } + + /// + /// 鏄笉鏄け鏁 + /// + public Boolean Revoked { get; set; } + + /// + /// 鍒涘缓鏃堕棿 + /// + public DateTime CreatedTime { get; set; } + + /// + /// 涓婃鏍¢獙IP + /// + public required string LastCheckIp { get; set; } + + /// + /// 璁惧淇℃伅锛屾祻瑙堝櫒淇℃伅绛 + /// + public required string DeviceInfo { get; set; } + + } +} diff --git a/LMS.Repository/DB/Role.cs b/LMS.Repository/DB/Role.cs new file mode 100644 index 0000000..b4b1f52 --- /dev/null +++ b/LMS.Repository/DB/Role.cs @@ -0,0 +1,18 @@ +锘縰sing LMS.Tools.Extensions; +using Microsoft.AspNetCore.Identity; + +namespace LMS.Repository.Models.DB +{ + public class Role : IdentityRole + { + public long CreatedUserId { get; set; } + + public long UpdatedUserId { get; set; } + + public DateTime CreatedTime { get; set; } + + public DateTime UpdatedTime { get; set; } + + public string? Remark { get; set; } = string.Empty; + } +} diff --git a/LMS.Repository/DB/RsaKeys.cs b/LMS.Repository/DB/RsaKeys.cs new file mode 100644 index 0000000..a57bdd9 --- /dev/null +++ b/LMS.Repository/DB/RsaKeys.cs @@ -0,0 +1,57 @@ +锘縰sing LMS.Tools.Extensions; + +namespace LMS.Repository.DB +{ + public class RsaKeys + { + /// + /// ID 涓婚敭 + /// + public required string Id { get; set; } + + /// + /// 浣跨敤娆℃暟 + /// + public int UseCount { get; set; } + + /// + /// 鍏挜 + /// + public required string PublicKey { get; set; } + + /// + /// 鍔犲瘑鍚庣殑绉侀挜 + /// + public required string EncryptedPrivateKey { get; set; } + + /// + /// 鍔犲瘑绉侀挜鐨勯殢鏈哄瓧绗︿覆 + /// + public required string EncryptionKey { get; set; } // 鍔犲瘑绉侀挜鐨勯殢鏈哄瓧绗︿覆 + + /// + /// 鍔犲瘑绉侀挜鐨勯殢鏈篒V + /// + public required string EncryptionIV { get; set; } // 鍔犲瘑绉侀挜鐨勯殢鏈哄瓧绗︿覆 + + /// + /// Key鐨勭増鏈 + /// + public int KeyVersion { get; set; } + + /// + /// 鍒涘缓鏃堕棿 + /// + public DateTime CreatedTime { get; set; } = BeijingTimeExtension.GetBeijingTime(); + + /// + /// 涓婃浣跨敤鐨勬椂闂 + /// + public DateTime? LastUsed { get; set; } + + /// + /// 鍒版湡鏃堕棿 + /// + public DateTime ExpirationTime { get; set; } + } +} diff --git a/LMS.Repository/DB/User.cs b/LMS.Repository/DB/User.cs new file mode 100644 index 0000000..9ecffd5 --- /dev/null +++ b/LMS.Repository/DB/User.cs @@ -0,0 +1,69 @@ +锘縰sing LMS.Repository.User; +using Microsoft.AspNetCore.Identity; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json; + +namespace LMS.Repository.Models.DB +{ + public class User : IdentityUser + { + [MaxLength(50)] + public string NickName { get; set; } + + /// + /// 鍏佽浣跨敤鐨勬満鍣ㄧ爜鏁伴噺 + /// + public long AllDeviceCount { get; set; } = 1; + + /// + /// 浠g悊鍒嗘垚姣斾緥 + /// + public double AgentPercent { get; set; } = 0.00; + + /// + /// 鍏嶈垂淇敼鏈哄櫒鐮佹鏁 + /// + public long FreeCount { get; set; } = 0; + + /// + /// 鐢ㄦ埛鐨勪竴浜涚畝鍗曠殑鎿嶄綔 + /// + [Column(TypeName = "json")] + public string? Options { get; set; } = "{}"; + + [Column(TypeName = "datetime")] + public DateTime CreatedDate { get; set; } = DateTime.Now; + + [Column(TypeName = "datetime")] + public DateTime UpdatedDate { get; set; } = DateTime.Now; + + [Column(TypeName = "datetime")] + public DateTime LastLoginDate { get; set; } = DateTime.Now; + + public string? LastLoginIp { get; set; } = ""; + + public string? LastLoginDevice { get; set; } = ""; + + /// + /// 涓婄骇ID + /// + public long? ParentId { get; set; } + + /// + /// 鎺ㄥ箍鐮 + /// + public string AffiliateCode { get; set; } = string.Empty; + + /// + /// 瀹為檯浣跨敤鐨凮ptions + /// + [NotMapped] + public UserPrivateOptions OptionsJson + { + get => JsonSerializer.Deserialize(Options ?? "{}") ?? new UserPrivateOptions(); + set => Options = JsonSerializer.Serialize(value); + } + + } +} diff --git a/LMS.Repository/DB/UserRoles.cs b/LMS.Repository/DB/UserRoles.cs new file mode 100644 index 0000000..33ee785 --- /dev/null +++ b/LMS.Repository/DB/UserRoles.cs @@ -0,0 +1,11 @@ +锘縩amespace LMS.Repository.Models.DB +{ + public class UserRoles + { + + public required string RoleId { get; set; } + + public required string UserId { get; set; } + + } +} diff --git a/LMS.Repository/DTO/CollectionResponse.cs b/LMS.Repository/DTO/CollectionResponse.cs new file mode 100644 index 0000000..51d76a8 --- /dev/null +++ b/LMS.Repository/DTO/CollectionResponse.cs @@ -0,0 +1,19 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LMS.Repository.DTO +{ + public class CollectionResponse + { + + public int Current { get; set; } + + public int Total { get; set; } + + public List Collection { get; set; } = new List(); + + } +} diff --git a/LMS.Repository/DTO/MachineDetailDto.cs b/LMS.Repository/DTO/MachineDetailDto.cs new file mode 100644 index 0000000..5f99351 --- /dev/null +++ b/LMS.Repository/DTO/MachineDetailDto.cs @@ -0,0 +1,14 @@ +锘縰sing LMS.Repository.DTO.UserDto; +using LMS.Repository.Models.DB; + +namespace LMS.Repository.DTO +{ + public class MachineDetailDto : Machine + { + public UserBaseDto? CreatedUser { get; set; } + + public UserBaseDto? UpdatedUser { get; set; } + + public UserBaseDto? OwnUser { get; set; } + } +} diff --git a/LMS.Repository/DTO/MachineDto.cs b/LMS.Repository/DTO/MachineDto.cs new file mode 100644 index 0000000..a7b7d43 --- /dev/null +++ b/LMS.Repository/DTO/MachineDto.cs @@ -0,0 +1,30 @@ +锘縰sing static LMS.Common.Enums.MachineEnum; + +namespace LMS.Repository.DTO.MachineResponse +{ + public class MachineDto + { + public class MachineStatusResponse + { + /// + /// 鏈哄櫒鐘舵 + /// + public MachineStatus Status { get; set; } + + /// + /// 鍒版湡鏃堕棿 + /// + public DateTime? DeactivationTime { get; set; } + + /// + /// 鏈哄櫒鐮両D + /// + public string Id { get; set; } + + /// + /// 鏈哄櫒鐮 + /// + public string MachineId { get; set; } + } + } +} diff --git a/LMS.Repository/DTO/RoleDto.cs b/LMS.Repository/DTO/RoleDto.cs new file mode 100644 index 0000000..cbb4a04 --- /dev/null +++ b/LMS.Repository/DTO/RoleDto.cs @@ -0,0 +1,21 @@ +锘縰sing LMS.Repository.DTO.UserDto; + +namespace LMS.Repository.DTO +{ + public class RoleDto + { + public long Id { get; set; } + + public string Name { get; set; } = string.Empty; + + public UserBaseDto? CreatedUser { get; set; } + + public UserBaseDto? UpdeatedUser { get; set; } + + public DateTime CreatedTime { get; set; } + + public DateTime UpdatedTime { get; set; } + + public string Remark { get; set; } = string.Empty; + } +} diff --git a/LMS.Repository/DTO/SoftwareDao.cs b/LMS.Repository/DTO/SoftwareDao.cs new file mode 100644 index 0000000..c81e765 --- /dev/null +++ b/LMS.Repository/DTO/SoftwareDao.cs @@ -0,0 +1,10 @@ +锘縩amespace LMS.Repository.DTO; + +public class SoftwareDao +{ + public string Version { get; set; } + + public string Author { get; set; } + + public string Description { get; set; } +} diff --git a/LMS.Repository/DTO/UserDto/UserAgentInfoDto.cs b/LMS.Repository/DTO/UserDto/UserAgentInfoDto.cs new file mode 100644 index 0000000..ebdffbb --- /dev/null +++ b/LMS.Repository/DTO/UserDto/UserAgentInfoDto.cs @@ -0,0 +1,44 @@ +锘縩amespace LMS.Repository.DTO.UserDto; + +public class UserAgentInfoDto +{ + /// + /// 鐢ㄦ埛ID + /// + public long UserId { get; set; } + + /// + /// 鐢ㄦ埛鍚嶇О + /// + public string UserName { get; set; } + + /// + /// 鐢ㄦ埛鏄电О + /// + public string NickName { get; set; } + + /// + /// 鐢ㄦ埛鐨勯個璇风爜 + /// + public string AffiliateCode { get; set; } + + /// + /// 鐢ㄦ埛鐨勪唬鐞嗗垎鎴愭瘮渚 + /// + public double AgentPercent { get; set; } + + /// + /// 閭璇蜂汉鏁 + /// + public int AffiliateNumber { get; set; } + + /// + /// 閭璇风殑VIP浜烘暟 + /// + public int AffiliateVIPNumber { get; set; } + + /// + /// 閭璇疯幏鍙栫殑閲戦 + /// + public double AffiliateMoney { get; set; } +} diff --git a/LMS.Repository/DTO/UserDto/UserBaseDto.cs b/LMS.Repository/DTO/UserDto/UserBaseDto.cs new file mode 100644 index 0000000..9bfd3e4 --- /dev/null +++ b/LMS.Repository/DTO/UserDto/UserBaseDto.cs @@ -0,0 +1,18 @@ +锘縰sing LMS.Repository.User; + +namespace LMS.Repository.DTO.UserDto; + +public class UserBaseDto +{ + public long Id { get; set; } + + public string NickName { get; set; } = string.Empty; + + public string UserName { get; set; } = string.Empty; + + public string Email { get; set; } = string.Empty; + + public string PhoneNumber { get; set; } = string.Empty; + + public string Avatar { get; set; } = string.Empty; +} diff --git a/LMS.Repository/DTO/UserDto/UserCollectionDto.cs b/LMS.Repository/DTO/UserDto/UserCollectionDto.cs new file mode 100644 index 0000000..e7d0f7a --- /dev/null +++ b/LMS.Repository/DTO/UserDto/UserCollectionDto.cs @@ -0,0 +1,17 @@ +锘縩amespace LMS.Repository.DTO.UserDto +{ + public class UserCollectionDto : UserBaseDto + { + public List RoleNames { get; set; } = []; + + public DateTime CreatedDate { get; set; } + + public DateTime LastLoginDate { get; set; } + + public long ParentId { get; set; } + + public string LastLoginIp { get; set; } = string.Empty; + + public string LastLoginDevice { get; set; } = string.Empty; + } +} diff --git a/LMS.Repository/DTO/UserDto/UserDto.cs b/LMS.Repository/DTO/UserDto/UserDto.cs new file mode 100644 index 0000000..9beff27 --- /dev/null +++ b/LMS.Repository/DTO/UserDto/UserDto.cs @@ -0,0 +1,40 @@ +锘縰sing LMS.Repository.User; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json; + +namespace LMS.Repository.DTO.UserDto +{ + public class UserDto : UserBaseDto + { + public List RoleNames { get; set; } = []; + + /// + /// 鍏佽浣跨敤鐨勬満鍣ㄧ爜鏁伴噺 + /// + public long AllDeviceCount { get; set; } = 1; + + /// + /// 浠g悊鍒嗘垚姣斾緥 + /// + public double AgentPercent { get; set; } = 0.00; + + /// + /// 鍏嶈垂淇敼鏈哄櫒鐮佹鏁 + /// + public long FreeCount { get; set; } = 0; + + public DateTime CreatedDate { get; set; } + + /// + /// 閭璇风爜 + /// + public string AffiliateCode { get; set; } = string.Empty; + + public long ParentId { get; set; } + + /// + /// 鏀句竴浜涙搷浣滀俊鎭 涓嶈兘棰戠箒鐨勪慨鏀规暟鎹簱锛屼娇鐢ㄧ殑json鏍煎紡瀛樻斁 + /// + public UserPrivateOptions Options { get; set; } = new UserPrivateOptions(); + } +} diff --git a/LMS.Repository/Forward/OpenAI.cs b/LMS.Repository/Forward/OpenAI.cs new file mode 100644 index 0000000..92772c4 --- /dev/null +++ b/LMS.Repository/Forward/OpenAI.cs @@ -0,0 +1,27 @@ +锘縩amespace LMS.Repository.Model +{ + public class OpenAI + { + public class RequestMessage + { + /// + /// 璇锋眰鐨勮鑹 + /// + public string role { get; set; } + + /// + /// 璇锋眰鐨勬秷鎭 + /// + public string content { get; set; } + } + + /// + /// 璇锋眰澶辫触锛岃繑鍥為敊璇俊鎭 + /// + public class ErrorResponse + { + public string Error { get; set; } + public string Message { get; set; } + } + } +} diff --git a/LMS.Repository/LMS.Repository.csproj b/LMS.Repository/LMS.Repository.csproj new file mode 100644 index 0000000..7bdbfa6 --- /dev/null +++ b/LMS.Repository/LMS.Repository.csproj @@ -0,0 +1,21 @@ +锘 + + + net8.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/LMS.Repository/Machine/MachineModel.cs b/LMS.Repository/Machine/MachineModel.cs new file mode 100644 index 0000000..6f2fe58 --- /dev/null +++ b/LMS.Repository/Machine/MachineModel.cs @@ -0,0 +1,42 @@ +锘縰sing System.ComponentModel.DataAnnotations; +using static LMS.Common.Enums.MachineEnum; + +namespace LMS.Repository.Models.Machine +{ + public class MachineModel + { + /// + /// 鏈哄櫒ID + /// + [Required] + public string MachineId { get; set; } + + /// + /// 鍋滅敤鏃堕棿 + /// + public DateTime? DeactivationTime { get; set; } + + + /// + /// 浣跨敤鐘舵(璇曠敤锛屾案涔) + /// + [Required] + public MachineUseStatus UseStatus { get; set; } + + /// + /// 褰撳墠鏈哄櫒鐨勭姸鎬侊紙婵娲伙紝鍐荤粨锛 + /// + [Required] + public MachineStatus Status { get; set; } + + /// + /// 澶囨敞 + /// + public string? Remark { get; set; } + + /// + /// 鎵灞炵敤鎴稩D + /// + public long? UserId { get; set; } + } +} diff --git a/LMS.Repository/Promission/PermissionModel.cs b/LMS.Repository/Promission/PermissionModel.cs new file mode 100644 index 0000000..11d2f42 --- /dev/null +++ b/LMS.Repository/Promission/PermissionModel.cs @@ -0,0 +1,47 @@ +锘縰sing System.ComponentModel.DataAnnotations; +using static LMS.Common.Enums.PermissionEnum; + +namespace LMS.Repository.RequestModel.Permission +{ + public class PermissionModel + { + + /// + /// 瑙掕壊ID + /// + public long? RoleId { get; set; } + + /// + /// 鐢ㄦ埛鐨処D + /// + public long? UserId { get; set; } + + /// + /// 鏈哄櫒鐮佺殑ID锛堟槸ID锛屼笉鏄満鍣ㄧ爜锛 + /// + public string? MachineId { get; set; } + + /// + /// 鏉冮檺绫诲瀷鐨処D锛堝瓙鏉冮檺锛 + /// + [Required] + public List PermissionTypeIds { get; set; } + + /// + /// 鏉冮檺瀵瑰簲鐨凜ode + /// + [Required] + public string PermissionCode { get; set; } + + /// + /// 鏉冮檺绫诲瀷 + /// + [Required] + public PType Type { get; set; } + + /// + /// 澶囨敞 + /// + public string? Remark { get; set; } + } +} diff --git a/LMS.Repository/Promission/PermissionTypeModel.cs b/LMS.Repository/Promission/PermissionTypeModel.cs new file mode 100644 index 0000000..38744d7 --- /dev/null +++ b/LMS.Repository/Promission/PermissionTypeModel.cs @@ -0,0 +1,35 @@ +锘縰sing static LMS.Common.Enums.PermissionEnum; +using System.ComponentModel.DataAnnotations; + +namespace LMS.Repository.Models.Promission +{ + public class PermissionTypeModel + { + + + /// + /// 鏉冮檺鐨勫悕瀛 + /// + [Required] + public string Name { get; set; } + + /// + /// 鏉冮檺鐨勫垎绫伙紙user鍜宮achine锛 + /// + [Required] + public PermissionType Type { get; set; } + + /// + /// 琛ㄧず鏉冮檺鐨勪唬鐮 + /// + [Required] + public string Code { get; set; } + + + /// + /// 鏉冮檺鐨勬弿杩 + /// + public string? Remark { get; set; } + + } +} diff --git a/LMS.Repository/RSAKey/RSAKeyGenerateModel.cs b/LMS.Repository/RSAKey/RSAKeyGenerateModel.cs new file mode 100644 index 0000000..4d722b7 --- /dev/null +++ b/LMS.Repository/RSAKey/RSAKeyGenerateModel.cs @@ -0,0 +1,19 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LMS.Repository.RSAKey +{ + public class RSAKeyGenerateModel + { + public string PublicKey { get; set; } + + public string EncryptedPrivateKey { get; set; } + + public string EncryptedKey { get; set; } + + public string EncryptionIV { get; set; } + } +} diff --git a/LMS.Repository/Role/RoleModel.cs b/LMS.Repository/Role/RoleModel.cs new file mode 100644 index 0000000..1b1ab65 --- /dev/null +++ b/LMS.Repository/Role/RoleModel.cs @@ -0,0 +1,17 @@ +锘縰sing System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LMS.Repository.Role +{ + public class RoleModel + { + [Required] + public string Name { get; set; } + + public string Remark { get; set; } = string.Empty; + } +} diff --git a/LMS.Repository/User/LoginModel.cs b/LMS.Repository/User/LoginModel.cs new file mode 100644 index 0000000..fbd9cd4 --- /dev/null +++ b/LMS.Repository/User/LoginModel.cs @@ -0,0 +1,31 @@ +锘縩amespace LMS.Repository.Models.User +{ + /// + /// 鐢ㄦ埛鐧诲綍鐨勭被鍨 + /// + public enum LoginType + { + Username = 1, Email = 0, + PhoneNumber = 2, + } + + /// + /// 鐢ㄦ埛鐧诲綍鐨勫疄浣撶被 + /// + public class LoginModel + { + public string? UserName { get; set; } + + public string? Email { get; set; } + + public LoginType LoginType { get; set; } = 0; + + public required string Password { get; set; } + + public required string TokenId { get; set; } + + public bool RememberMe { get; set; } = false; + + public required string DeviceInfo { get; set; } + } +} diff --git a/LMS.Repository/User/PublicKeyModel.cs b/LMS.Repository/User/PublicKeyModel.cs new file mode 100644 index 0000000..bafb65b --- /dev/null +++ b/LMS.Repository/User/PublicKeyModel.cs @@ -0,0 +1,19 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LMS.Repository.User +{ + public class PublicKeyModel + { + public required string ClientPublicKey { get; set; } + } + + public class PublicKeyResponse + { + public required string Token { get; set; } + public required string PublicKey { get; set; } + } +} diff --git a/LMS.Repository/User/RefreshTokenModel.cs b/LMS.Repository/User/RefreshTokenModel.cs new file mode 100644 index 0000000..8d0dca0 --- /dev/null +++ b/LMS.Repository/User/RefreshTokenModel.cs @@ -0,0 +1,10 @@ +锘縩amespace LMS.Repository.Models.User +{ + public class RefreshTokenModel + { + public string refreshToken { get; set; } + public required long UserId { get; set; } + + public required string DeviceInfo { get; set; } + } +} diff --git a/LMS.Repository/User/RegisterModel.cs b/LMS.Repository/User/RegisterModel.cs new file mode 100644 index 0000000..281ba9f --- /dev/null +++ b/LMS.Repository/User/RegisterModel.cs @@ -0,0 +1,18 @@ +锘縰sing System.ComponentModel.DataAnnotations; + +namespace LMS.Repository.Models.User +{ + public class RegisterModel + { + [Required] + public required string UserName { get; set; } + public string? Email { get; set; } + [Required] + public required string Password { get; set; } + [Required] + public required string TokenId { get; set; } + + [Required] + public required string AffiliateCode { get; set; } + } +} diff --git a/LMS.Repository/User/UpdatedUserModel.cs b/LMS.Repository/User/UpdatedUserModel.cs new file mode 100644 index 0000000..b5ef95b --- /dev/null +++ b/LMS.Repository/User/UpdatedUserModel.cs @@ -0,0 +1,14 @@ +锘縩amespace LMS.Repository.User +{ + public class UpdatedUserModel + { + public double? AgentPercent { get; set; } + public int? AllDeviceCount { get; set; } + public string? Email { get; set; } + public int? FreeCount { get; set; } + public string? NickName { get; set; } + public string? UserName { get; set; } + public string? PhoneNumber { get; set; } + public List? RoleNames { get; set; } + } +} \ No newline at end of file diff --git a/LMS.Repository/User/UserPrivateOptions.cs b/LMS.Repository/User/UserPrivateOptions.cs new file mode 100644 index 0000000..d832439 --- /dev/null +++ b/LMS.Repository/User/UserPrivateOptions.cs @@ -0,0 +1,12 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LMS.Repository.User; + +public class UserPrivateOptions +{ + +} diff --git a/LMS.Tools/Extensions/BeijingTimeExtension.cs b/LMS.Tools/Extensions/BeijingTimeExtension.cs new file mode 100644 index 0000000..9622d95 --- /dev/null +++ b/LMS.Tools/Extensions/BeijingTimeExtension.cs @@ -0,0 +1,15 @@ +锘縩amespace LMS.Tools.Extensions +{ + public class BeijingTimeExtension + { + /// + /// 鑾峰彇鍖椾含鏃堕棿锛屽皢鏃跺尯杞崲涓哄寳浜椂闂 + /// + /// + public static DateTime GetBeijingTime() + { + return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, + TimeZoneInfo.FindSystemTimeZoneById("China Standard Time")); + } + } +} diff --git a/LMS.Tools/Extensions/ConvertExtension.cs b/LMS.Tools/Extensions/ConvertExtension.cs new file mode 100644 index 0000000..cce56c3 --- /dev/null +++ b/LMS.Tools/Extensions/ConvertExtension.cs @@ -0,0 +1,44 @@ +锘縩amespace LMS.Tools.Extensions +{ + public class ConvertExtension + { + /// + /// 灏嗗瓧绗︿覆杞崲涓簂ong锛岄粯璁ゆ垨鑰呮槸杞崲閿欒杩斿洖0 + /// + /// + /// + public static long ObjectToLong(object obj) + { + if (obj == null) + return 0; + + if (obj is long longValue) + return longValue; + + if (obj is int intValue) + return intValue; + + if (obj is string strValue) + { + if (long.TryParse(strValue, out long result)) + return result; + } + + // 澶勭悊鍏朵粬鏁板肩被鍨 + if (obj is IConvertible convertible) + { + try + { + return convertible.ToInt64(System.Globalization.CultureInfo.InvariantCulture); + } + catch + { + // 杞崲澶辫触锛岃繑鍥0 + return 0; + } + } + + return 0; // 榛樿杩斿洖0 + } + } +} diff --git a/LMS.Tools/Extensions/EnumExtensions.cs b/LMS.Tools/Extensions/EnumExtensions.cs new file mode 100644 index 0000000..03bab85 --- /dev/null +++ b/LMS.Tools/Extensions/EnumExtensions.cs @@ -0,0 +1,74 @@ +锘縰sing LMS.Common.Attributes; +using static LMS.Common.Enums.PermissionEnum; + +namespace LMS.Tools +{ + public static class EnumExtensions + { + /// + /// 鍒ゆ柇鏄惁涓烘湁鏁堢殑鏉冮檺绫诲瀷 + /// + /// + /// + public static bool IsValidPermissionType(Type enumType, object value) + { + if (enumType == null || !enumType.IsEnum) + { + return false; + } + + if (value == null) + { + return false; + } + + // 澶勭悊鏁存暟绫诲瀷 + if (value is int intValue) + { + return Enum.IsDefined(enumType, intValue); + } + + // 澶勭悊瀛楃涓茬被鍨 + if (value is string stringValue) + { + return Enum.TryParse(enumType, stringValue, true, out _); + } + + // 濡傛灉涓嶆槸鏁存暟鎴栧瓧绗︿覆锛屽皾璇曡浆鎹负鏋氫妇搴曞眰绫诲瀷 + try + { + var underlyingType = Enum.GetUnderlyingType(enumType); + var convertedValue = Convert.ChangeType(value, underlyingType); + return Enum.IsDefined(enumType, convertedValue); + } + catch + { + return false; + } + } + + /// + /// 鑾峰彇瀵瑰簲鐨勬灇涓剧殑鎻忚堪 + /// + /// + /// + public static string GetDescription(this Enum value) + { + var field = value.GetType().GetField(value.ToString()); + var attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute; + return attribute == null ? value.ToString() : attribute.Description; + } + + /// + /// 鑾峰彇瀵瑰簲鐨勬灇涓剧殑缁撴灉 + /// + /// + /// + public static string GetResult(this Enum value) + { + var field = value.GetType().GetField(value.ToString()); + var attribute = Attribute.GetCustomAttribute(field, typeof(ResultAttribute)) as ResultAttribute; + return attribute == null ? value.ToString() : attribute.Result; + } + } +} diff --git a/LMS.Tools/LMS.Tools.csproj b/LMS.Tools/LMS.Tools.csproj new file mode 100644 index 0000000..1808db5 --- /dev/null +++ b/LMS.Tools/LMS.Tools.csproj @@ -0,0 +1,13 @@ +锘 + + + net8.0 + enable + enable + + + + + + + diff --git a/LMS.service/.dockerignore b/LMS.service/.dockerignore new file mode 100644 index 0000000..fe1152b --- /dev/null +++ b/LMS.service/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/LMS.service/.gitattributes b/LMS.service/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/LMS.service/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/LMS.service/.gitignore b/LMS.service/.gitignore new file mode 100644 index 0000000..7aff47a --- /dev/null +++ b/LMS.service/.gitignore @@ -0,0 +1,364 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# 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/ +[Oo]ut/ +[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 Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# 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 +*.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 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/ + +# 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 +/Properties/launchSettings.json diff --git a/LMS.service/APIResponseModel.cs b/LMS.service/APIResponseModel.cs new file mode 100644 index 0000000..5819cda --- /dev/null +++ b/LMS.service/APIResponseModel.cs @@ -0,0 +1,174 @@ +锘縰sing LMS.Tools; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace LMS.service +{ + public class APIResponseModel + { + public APIResponseModel() + { + Code = (int)ResponseCode.Success; + Message = string.Empty; + Data = default(T); + } + + /// + /// 杩斿洖鐨勭爜 0锛1 + /// + public int Code { get; set; } + + /// + /// 杩斿洖鐨勪俊鎭紝鎴愬姛鎴栬呭け璐ョ殑淇℃伅 + /// + public string? Message { get; set; } + + /// + /// 杩斿洖鐨勬暟鎹紝鍙互鏄换浣曠被鍨 + /// + public object? Data { get; set; } + + /// + /// 鍒涘缓杩斿洖娑堟伅 + /// + /// 杩斿洖鐮 + /// 杩斿洖鏁版嵁 + /// 杩斿洖娑堟伅 + /// + public static APIResponseModel CreateResponseModel(ResponseCode code, T data) + { + return new APIResponseModel + { + Code = (int)code, + Message = code.GetResult(), + Data = data + }; + } + + /// + /// 鍒涘缓姝e父鐨勮繑鍥炴暟鎹 + /// + /// 杩斿洖鐨勬暟鎹 + /// 杩斿洖鎴愬姛鐨勬秷鎭 + /// + public static APIResponseModel CreateSuccessResponseModel(ResponseCode code, T data) + { + return new APIResponseModel + { + Code = (int)code, + Message = code.GetResult(), + Data = data + }; + } + + /// + /// 鍒涘缓涓涓繑鍥炴垚鍔熺殑娑堟伅 + /// + /// + /// + /// + public static APIResponseModel CreateSuccessResponseModel(T data, string message) + { + return new APIResponseModel + { + Code = (int)ResponseCode.Success, + Message = message, + Data = data + }; + } + + /// + /// 鍒涘缓涓涓繑鍥炴垚鍔熺殑娑堟伅 + /// + /// + /// + public static APIResponseModel CreateSuccessResponseModel(T data) + { + return new APIResponseModel + { + Code = (int)ResponseCode.Success, + Message = "璇锋眰鎴愬姛", + Data = data + }; + } + + + /// + /// 鍒涘缓姝e父鐨勮繑鍥炴暟鎹 + /// + /// 杩斿洖鐨勬暟鎹 + /// + public static APIResponseModel CreateSuccessResponseModel(ResponseCode code) + { + return new APIResponseModel + { + Code = (int)code, + Message = code.GetResult(), + Data = null + }; + } + + /// + /// 杩斿洖閿欒娑堟伅 + /// + /// 閿欒鐨勭爜 + /// 杩斿洖鐨勬暟鎹 + /// + public static APIResponseModel CreateErrorResponseModel(ResponseCode code, T data) + { + return new APIResponseModel + { + Code = (int)code, + Message = code.GetResult(), + Data = data + }; + } + + /// + /// 杩斿洖閿欒娑堟伅 + /// + /// 閿欒鐨勭爜 + /// 杩斿洖鐨勬暟鎹 + /// 杩斿洖鐨勯敊璇秷鎭 + /// + public static APIResponseModel CreateErrorResponseModel(ResponseCode code, T data, string message) + { + return new APIResponseModel + { + Code = (int)code, + Message = message, + Data = data + }; + } + + /// + /// 鍒涘缓涓涓敊璇殑杩斿洖鏁版嵁锛屽彧鏈夐敊璇秷鎭 + /// + /// 閿欒鐨勭爜 + /// + public static APIResponseModel CreateErrorResponseModel(ResponseCode code) + { + return new APIResponseModel + { + Code = (int)code, + Message = code.GetResult(), + Data = null + }; + } + + /// + /// 鍒涘缓涓涓敊璇殑杩斿洖鏁版嵁锛屽彧鏈夐敊璇秷鎭 + /// + /// 閿欒鐨勭爜 + /// 閿欒娑堟伅鎻愮ず + /// + public static APIResponseModel CreateErrorResponseModel(ResponseCode code, string message) + { + return new APIResponseModel + { + Code = (int)code, + Message = message, + Data = null + }; + } + } +} diff --git a/LMS.service/Configuration/AddCorsConifg.cs b/LMS.service/Configuration/AddCorsConifg.cs new file mode 100644 index 0000000..b00c20c --- /dev/null +++ b/LMS.service/Configuration/AddCorsConifg.cs @@ -0,0 +1,19 @@ +锘縩amespace LMS.service.Configuration +{ + public static class AddCorsConifg + { + public static void AddCorsServices(this IServiceCollection services) + { + services.AddCors(options => + { + options.AddPolicy("AllowAll", + builder => + { + builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + }); + }); + } + } +} diff --git a/LMS.service/Configuration/AuthenticationExtensions.cs b/LMS.service/Configuration/AuthenticationExtensions.cs new file mode 100644 index 0000000..c7ab974 --- /dev/null +++ b/LMS.service/Configuration/AuthenticationExtensions.cs @@ -0,0 +1,28 @@ +锘縰sing Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using System.Text; + +namespace Lai_server.Configuration +{ + public static class AuthenticationExtensions + { + public static void AddJWTAuthentication(this IServiceCollection services) + { + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(x => + { + string secKeyEnv = Environment.GetEnvironmentVariable("SecKey"); + byte[] keyBytes = Encoding.UTF8.GetBytes(secKeyEnv); + var secKey = new SymmetricSecurityKey(keyBytes); + x.TokenValidationParameters = new() + { + ValidateIssuer = false, + ValidateAudience = false, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + IssuerSigningKey = secKey + }; + }); + } + } +} diff --git a/LMS.service/Configuration/AutoMapperConfig.cs b/LMS.service/Configuration/AutoMapperConfig.cs new file mode 100644 index 0000000..414d8b6 --- /dev/null +++ b/LMS.service/Configuration/AutoMapperConfig.cs @@ -0,0 +1,31 @@ +锘縰sing AutoMapper; +using LMS.Repository.DTO; +using LMS.Repository.DTO.UserDto; +using LMS.Repository.Models.DB; +using LMS.Repository.Models.Machine; +using LMS.Repository.Models.Promission; +using LMS.Repository.RequestModel.Permission; +using static LMS.Repository.DTO.MachineResponse.MachineDto; + +namespace Lai_server.Configuration +{ + public class AutoMapperConfig : Profile + { + public AutoMapperConfig() + { + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + } + } +} diff --git a/LMS.service/Configuration/JWTSetting.cs b/LMS.service/Configuration/JWTSetting.cs new file mode 100644 index 0000000..37942c7 --- /dev/null +++ b/LMS.service/Configuration/JWTSetting.cs @@ -0,0 +1,10 @@ +锘縩amespace Lai_server.Configuration +{ + public class JWTSetting + { + public string SecKey { get; set; } + + public int ExpireMinutes { get; set; } + + } +} diff --git a/LMS.service/Configuration/RsaConfigurattions.cs b/LMS.service/Configuration/RsaConfigurattions.cs new file mode 100644 index 0000000..ffcf458 --- /dev/null +++ b/LMS.service/Configuration/RsaConfigurattions.cs @@ -0,0 +1,66 @@ +锘 +using LMS.Common.RSAKey; +using LMS.DAO; +using LMS.Repository.DB; +using LMS.Tools.Extensions; +using Microsoft.EntityFrameworkCore; + +namespace LMS.service.Configuration +{ + public class RsaConfigurattions(IServiceProvider serviceProvider) : IHostedService + { + private readonly IServiceProvider _serviceProvider = serviceProvider; + + public async Task StartAsync(CancellationToken cancellationToken) + { + using var scope = _serviceProvider.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken); + try + { + int count = await dbContext.RsaKeys.CountAsync(cancellationToken: cancellationToken); + + // 鍏堝垹闄ゆ棤鏁堢殑RSA鏁版嵁 + List rsaKeys = await dbContext.RsaKeys.Where(x => x.UseCount > 50 || x.ExpirationTime < BeijingTimeExtension.GetBeijingTime()).ToListAsync(cancellationToken: cancellationToken); + if (rsaKeys.Count > 0) + { + dbContext.RsaKeys.RemoveRange(rsaKeys); + } + + // 鏂板 淇濊瘉鏁版嵁搴撲腑鏈夋寚瀹氭暟閲忕殑RSA鏁版嵁 + + int AddCount = 20 - (count - rsaKeys.Count); + for (int i = 0; i < AddCount; i++) + { + RSAKeyGenerateModel rSAKeyGenerate = RsaKeyPairGenerator.InitRsaKey(); + + await dbContext.RsaKeys.AddAsync(new RsaKeys + { + PublicKey = rSAKeyGenerate.PublicKey, + EncryptedPrivateKey = rSAKeyGenerate.EncryptedPrivateKey, + EncryptionKey = rSAKeyGenerate.EncryptedKey, + EncryptionIV = rSAKeyGenerate.EncryptionIV, + UseCount = 0, + ExpirationTime = BeijingTimeExtension.GetBeijingTime().AddDays(10), + Id = Guid.NewGuid().ToString(), + }, cancellationToken); + } + + await dbContext.SaveChangesAsync(cancellationToken); + await transaction.CommitAsync(cancellationToken); + } + catch (Exception ex) + { + await transaction.RollbackAsync(cancellationToken); + // 鏍规嵁闇瑕佸鐞嗗紓甯革紝渚嬪缁堟搴旂敤绋嬪簭 + Console.WriteLine(ex.Message); + Environment.Exit(1); + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } +} diff --git a/LMS.service/Configuration/ServiceConfiguration.cs b/LMS.service/Configuration/ServiceConfiguration.cs new file mode 100644 index 0000000..e60bf00 --- /dev/null +++ b/LMS.service/Configuration/ServiceConfiguration.cs @@ -0,0 +1,39 @@ +锘縰sing LMS.DAO.MachineDAO; +using LMS.DAO.PermissionDAO; +using LMS.DAO.RoleDAO; +using LMS.DAO.UserDAO; +using LMS.service.Configuration; +using LMS.service.Service; +using LMS.service.Service.PermissionService; +using LMS.service.Service.RoleService; +using LMS.service.Service.UserService; + +namespace Lai_server.Configuration +{ + public static class ServiceConfiguration + { + public static void AddServices(this IServiceCollection services) + { + // 娉ㄥ叆Service + services.AddScoped(); + + // 娉ㄥ叆DDL + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + + // 娉ㄥ叆 DAO + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + } + } +} diff --git a/LMS.service/Configuration/SwaggerConfiguration.cs b/LMS.service/Configuration/SwaggerConfiguration.cs new file mode 100644 index 0000000..2493097 --- /dev/null +++ b/LMS.service/Configuration/SwaggerConfiguration.cs @@ -0,0 +1,31 @@ +锘縰sing Microsoft.OpenApi.Models; + +namespace Lai_server.Configuration +{ + public static class SwaggerConfiguration + { + public static void AddSwaggerService(this IServiceCollection services) + { + services.AddSwaggerGen(c => + { + var scheme = new OpenApiSecurityScheme() + { + Description = "Authorization header. \r\nExample: 'Bearer 12345abcdef'", + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Authorization" + }, + Scheme = "oauth2", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey, + }; + c.AddSecurityDefinition("Authorization", scheme); + var requirement = new OpenApiSecurityRequirement(); + requirement[scheme] = new List(); + c.AddSecurityRequirement(requirement); + }); + } + } +} diff --git a/LMS.service/Controllers/MachineController.cs b/LMS.service/Controllers/MachineController.cs new file mode 100644 index 0000000..07e0d78 --- /dev/null +++ b/LMS.service/Controllers/MachineController.cs @@ -0,0 +1,150 @@ +锘縰sing LMS.Repository.DTO; +using LMS.Repository.Models.DB; +using LMS.Repository.Models.Machine; +using LMS.service.Service; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.ComponentModel.DataAnnotations; +using static LMS.Common.Enums.MachineEnum; +using static LMS.Common.Enums.ResponseCodeEnum; +using static LMS.Repository.DTO.MachineResponse.MachineDto; + +namespace LMS.service.Controllers +{ + [Route("lms/[controller]/[action]")] + [ApiController] + public class MachineController(MachineService machineService) : ControllerBase + { + private readonly MachineService _machineService = machineService; + + #region 鑾峰彇鎸囧畾鏈哄櫒鐮佺殑鐘舵 + /// + /// 鑾峰彇鎸囧畾鏈哄櫒鐮佺殑鐘舵 + /// + /// + /// + [HttpGet("{machineId}")] + public async Task>> GetMachineStatus(string machineId) + { + return await _machineService.GetMachineStatus(machineId); + } + + #endregion + + #region 淇敼鏈哄櫒鐮佹暟鎹 + /// + /// 淇敼褰撳墠鏈哄櫒鐮 + /// + /// + /// + [HttpPost("{id}")] + [Authorize] + public async Task>> ModifyMachine(string id, [FromBody] MachineModel request) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _machineService.ModifyMachine(id, request, userId); + } + + #endregion + + #region 娣诲姞鏈哄櫒鐮 + /// + /// 娣诲姞鏈哄櫒鐮 + /// + /// + /// + [HttpPost] + [Authorize] + public async Task>> AddMachine([FromBody] MachineModel request) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _machineService.AddMachine(request, userId); + } + + #endregion + + #region 鑾峰彇鏈哄櫒鐮佸垪琛 + + /// + /// 閫氳繃鏌ヨ鏉′欢鑾峰彇鏈哄櫒鐮佸垪琛 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet] + [Authorize] + public async Task>>> QueryMachineCollection([Required] int page, [Required] int pageSize, string? machineId, string? createdUserName, MachineStatus? status, MachineUseStatus? useStatus, string? remark, string? ownUserName) + { + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _machineService.QueryMachineCollection(page, pageSize, machineId, createdUserName, status, useStatus, remark, ownUserName, requestUserId); + } + + #endregion + + #region 鑾峰彇鎸囧畾鐨勬満鍣ㄧ爜璇︽儏 + + [HttpGet("{Id}")] + [Authorize] + public async Task>> GetMachineDetail(string Id) + { + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _machineService.GetMachineDetail(Id, userId); + } + + #endregion + + #region 灏嗘満鍣ㄧ爜鍋滅敤 + + [HttpPost("{Id}")] + [Authorize] + public async Task>> DeactivateMachine(string Id) + { + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _machineService.DeactivateMachine(Id, userId); + } + + #endregion + + #region 灏嗘満鍣ㄧ爜鍗囩骇涓烘案涔呬娇鐢 + + [HttpPost("{Id}")] + [Authorize] + public async Task>> UpgradeMachine(string Id) + { + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _machineService.UpgradeMachine(Id, userId); + } + + #endregion + + #region 鍒犻櫎鏈哄櫒鐮 + /// + /// 鍒犻櫎鏈哄櫒鐮 + /// + /// + /// + [HttpDelete("{id}")] + [Authorize] + public async Task>> DeleteMachine(string id) + { + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _machineService.DeleteMachine(id, userId); + } + #endregion + } +} diff --git a/LMS.service/Controllers/PermissionController.cs b/LMS.service/Controllers/PermissionController.cs new file mode 100644 index 0000000..c8ad1a1 --- /dev/null +++ b/LMS.service/Controllers/PermissionController.cs @@ -0,0 +1,134 @@ +锘縰sing LMS.Repository.Models.Promission; +using LMS.Repository.RequestModel.Permission; +using LMS.service; +using LMS.service.Service.PermissionService; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace Lai_server.Controllers +{ + [Route("lms/[controller]/[action]")] + [ApiController] + public class PermissionController : ControllerBase + { + private readonly PermissionService _permissionService; + public PermissionController(PermissionService permissionService) + { + _permissionService = permissionService; + } + + #region 鏉冮檺绫诲瀷 + + /// + /// 鍒涘缓鏉冮檺绫诲瀷 + /// + /// + /// + [HttpPost] + [Authorize] + public async Task>> AddPermissionType([FromBody] PermissionTypeModel permissionType) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _permissionService.AddPermissionType(permissionType, userId); + } + + + /// + /// 鏂板鏉冮檺绫诲瀷 + /// + /// + /// + [HttpPost("{permissionId}")] + [Authorize] + public async Task>> ModifyPermissionType(string permissionId, [FromBody] PermissionTypeModel permissionType) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _permissionService.ModifyPermissionType(permissionId, permissionType, userId); + } + + /// + /// 鍒犻櫎鏉冮檺绫诲瀷 + /// + /// + /// + /// + [HttpDelete("{id}/{relation}")] + [Authorize] + public async Task>> DeletePermissionType(string id, bool relation) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _permissionService.DeletePermissionType(id, relation, userId); + } + + #endregion + + #region 鏉冮檺 + + /// + /// 娣诲姞瀵瑰簲鐨勬潈闄 + /// + /// + /// + [HttpPost] + [Authorize] + public async Task>> AddPermission([FromBody] PermissionModel model) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _permissionService.AddPermission(model, userId); + } + + ///// + ///// 淇敼鏉冮檺 + ///// + ///// + ///// + [HttpPost("{id}")] + [Authorize] + public async Task>> ModfiyPermission(string id, [FromBody] PermissionModel request) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _permissionService.ModfiyPermission(id, request, userId); + } + + /// + /// 鍒犻櫎鏉冮檺 + /// + /// + /// + [HttpDelete("{id}")] + [Authorize] + public async Task>> DeletePermission(string Id) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _permissionService.DeletePermission(Id, userId); + } + + #endregion + } +} diff --git a/LMS.service/Controllers/RoleController.cs b/LMS.service/Controllers/RoleController.cs new file mode 100644 index 0000000..63670d1 --- /dev/null +++ b/LMS.service/Controllers/RoleController.cs @@ -0,0 +1,107 @@ +锘縰sing LMS.Repository.DTO; +using LMS.Repository.Models.DB; +using LMS.Repository.Role; +using LMS.service.Service.RoleService; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using System.ComponentModel.DataAnnotations; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace LMS.service.Controllers +{ + [Route("lms/[controller]/[action]")] + public class RoleController(RoleService roleService) : ControllerBase + { + private readonly RoleService _roleService = roleService; + + /// + /// 鑾峰彇瑙掕壊鍒楄〃鏁版嵁 + /// + /// + /// + /// + /// + /// + [HttpGet] + [Authorize] + public async Task>>> QueryRoleCollection([Required] int page, [Required] int pageSize, string? roleName, long? roleId) + { + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _roleService.QueryRoleCollection(page, pageSize, roleName, roleId, userId); + } + + /// + /// 鑾峰彇鎵鏈夌殑瑙掕壊鐨勬暟鎹紝瀵瑰簲鐨凬ame锛岀敤浣滀笅鎷夋鐨勬暟鎹 + /// + /// + [HttpGet] + [Authorize] + public async Task>>> QueryRoleOption() + { + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _roleService.QueryRoleOption(userId); + } + + /// + /// 閫氳繃鎸囧畾Id鑾峰彇瑙掕壊 + /// + /// + /// + [HttpGet("{Id}")] + [Authorize] + public async Task>> QueryRoleById(long Id) + { + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _roleService.QueryRoleById(Id, userId); + } + + /// + /// 娣诲姞瑙掕壊 + /// + /// + [HttpPost] + [Authorize] + public async Task>> AddRole([FromBody] RoleModel model) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _roleService.AddRole(model, userId); + } + + /// + /// 淇敼瑙掕壊 + /// + /// + /// + /// + [HttpPost("{id}")] + [Authorize] + public async Task>> UpdateRole(long id, [FromBody] RoleModel model) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _roleService.UpdateRole(id, model, userId); + } + + /// + /// 鍒犻櫎瑙掕壊 + /// + /// + /// + [HttpDelete("{id}")] + [Authorize] + public async Task>> DeleteRole(long id) + { + return await _roleService.DeleteRole(id); + } + + } +} diff --git a/LMS.service/Controllers/SoftWareController.cs b/LMS.service/Controllers/SoftWareController.cs new file mode 100644 index 0000000..18c30c0 --- /dev/null +++ b/LMS.service/Controllers/SoftWareController.cs @@ -0,0 +1,15 @@ +锘縰sing Microsoft.AspNetCore.Mvc; +using System.Reflection; + +namespace LMS.service.Controllers +{ + [Route("lms/[controller]/[action]")] + public class SoftWareController : Controller + { + //public Index() + //{ + // var version = Assembly.GetExecutingAssembly().GetName().Version; + // Console.WriteLine($"Version: {version}"); + //} + } +} diff --git a/LMS.service/Controllers/UserController.cs b/LMS.service/Controllers/UserController.cs new file mode 100644 index 0000000..701791c --- /dev/null +++ b/LMS.service/Controllers/UserController.cs @@ -0,0 +1,225 @@ +锘縰sing LMS.DAO; +using LMS.Repository.DTO; +using LMS.Repository.DTO.UserDto; +using LMS.Repository.Models.DB; +using LMS.Repository.Models.User; +using LMS.Repository.User; +using LMS.service.Service.UserService; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Concurrent; +using System.ComponentModel.DataAnnotations; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace LMS.service.Controllers +{ + [Route("lms/[controller]/[action]")] + public class UserController(ApplicationDbContext context, UserManager userManager, LoginService loginService, SecurityService securityService, UserService userService) : ControllerBase + { + private readonly ApplicationDbContext _context = context; + private readonly UserManager _userManager = userManager; + public readonly LoginService _loginService = loginService; + private readonly SecurityService _securityService = securityService; + private readonly UserService _userService = userService; + + /// + /// 瀛樺偍瀵嗛挜鐨 ConcurrentDictionary + /// + private static readonly ConcurrentDictionary _keyStore = new(); + + #region 鑾峰彇鍏挜 + /// + /// 鑾峰彇鍏挜 + /// + /// + [HttpGet] + public async Task>> GetPublicKey() + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + return await _securityService.GetPublicKey(_keyStore); + } + + #endregion + + #region 鐧诲綍 + /// + /// 鐢ㄦ埛鐧诲綍鎺ュ彛 + /// + /// + /// + [HttpPost] + public async Task>> Login([FromBody] LoginModel login) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + + string? ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (string.IsNullOrEmpty(ip)) + { + ip = HttpContext.Connection.RemoteIpAddress?.ToString(); + } + + APIResponseModel apiResponse = await _loginService.UserLogin(login, ip, _keyStore); + if (apiResponse.Code != 1) + { + return apiResponse; + } + + // 杩欎釜娣诲姞Cokkie + + var cookieOptions = new CookieOptions + { + HttpOnly = true, + Secure = false, // 濡傛灉浣跨敤 HTTPS + SameSite = SameSiteMode.None, + Expires = DateTime.UtcNow.AddDays(7), + }; + Response.Cookies.Append("refreshToken", ((LoginService.LoginResponse)apiResponse.Data).RefreshToken, cookieOptions); + return apiResponse; + } + + #endregion + + #region 娉ㄥ唽 + + /// + /// 鐢ㄦ埛娉ㄥ唽鎺ュ彛 + /// + /// + /// + [HttpPost] + public async Task>> Register([FromBody] RegisterModel model) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _userService.Register(model, _keyStore); + + + } + + #endregion + + #region 鍒锋柊token + + [HttpPost] + public async Task>> RefreshToken([FromBody] RefreshTokenModel model) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + string? ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (string.IsNullOrEmpty(ip)) + { + ip = HttpContext.Connection.RemoteIpAddress?.ToString(); + } + // 鑾峰彇Cookie 鐨勫 + string? refreshToken = model.refreshToken; + if (string.IsNullOrWhiteSpace(refreshToken)) + { + //Response.StatusCode = StatusCodes.Status401Unauthorized; + //await Response.WriteAsync("Access denied, RefreshToken is null"); + // 杩斿洖鏉冮檺閿欒 + return APIResponseModel.CreateErrorResponseModel(ResponseCode.RefreshTokenInvalid); + } + else + { + return await _loginService.RefreshToken(model, ip, refreshToken); + } + } + #endregion + + #region 閫鍑虹櫥褰 + + #endregion + + #region 鑾峰彇鐢ㄦ埛淇℃伅 + + /// + /// 鑾峰彇鐢ㄦ埛淇℃伅 + /// + /// + /// + [HttpGet("{id}")] + [Authorize] + public async Task>> GetUserInfo(long id) + { + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _userService.GetUserInfo(id, requestUserId); + } + + #endregion + + #region 鑾峰彇鐢ㄦ埛鍒楄〃 + + /// + /// 鑾峰彇鐢ㄦ埛鍒楄〃锛屽彲浠ヤ娇鐢ㄦ煡璇㈡潯浠 userName, userId, nickName, phoneNumber, email, roleNames + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet] + [Authorize] + public async Task>>> QueryUserCollection([Required] int page, [Required] int pageSize, string userName, long? userId, string nickName, string phoneNumber, string email, string[] roleNames, long? parentId) + { + long reuqertUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _userService.QueryUserCollection(page, pageSize, userName, userId, nickName, phoneNumber, email, roleNames, parentId, reuqertUserId); + } + + #endregion + + #region 淇敼鐢ㄦ埛淇℃伅 + + [HttpPost("{id}")] + [Authorize] + public async Task>> UpdatedUser(long id, [FromBody] UpdatedUserModel userModel) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _userService.UpdatedUser(id, userModel, requestUserId); + } + #endregion + + #region 灏嗙敤鎴峰崌绾т负浠g悊 + + [HttpPost] + [Authorize] + public async Task>> EnableAgent() + { + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _loginService.EnableAgent(requestUserId); + } + #endregion + + #region 鑾峰彇鐢ㄦ埛鐨勪唬鐞嗕俊鎭 + + [HttpGet] + [Authorize] + public async Task>> GetUserAgentInfo() + { + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _userService.GetUserAgentInfo(requestUserId); + } + + #endregion + } +} diff --git a/LMS.service/Data/Migrations/20240912114032_inital.Designer.cs b/LMS.service/Data/Migrations/20240912114032_inital.Designer.cs new file mode 100644 index 0000000..df3dce9 --- /dev/null +++ b/LMS.service/Data/Migrations/20240912114032_inital.Designer.cs @@ -0,0 +1,299 @@ +锘// +using LMS.DAO; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LMS.service.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240912114032_inital")] + partial class inital + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("LMS.service.Models.UserModels.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("LMS.service.Models.UserModels.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("CreatedDate") + .HasColumnType("datetime"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("LastLoginDate") + .HasColumnType("datetime"); + + b.Property("LastLoginDevice") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastLoginIp") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NickName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("LMS.service.Models.UserModels.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("LMS.service.Models.UserModels.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("LMS.service.Models.UserModels.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("LMS.service.Models.UserModels.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LMS.service.Models.UserModels.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("LMS.service.Models.UserModels.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LMS.service/Data/Migrations/20240912114032_inital.cs b/LMS.service/Data/Migrations/20240912114032_inital.cs new file mode 100644 index 0000000..76449e8 --- /dev/null +++ b/LMS.service/Data/Migrations/20240912114032_inital.cs @@ -0,0 +1,265 @@ +锘縰sing System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LMS.service.Data.Migrations +{ + /// + public partial class inital : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + NickName = table.Column(type: "varchar(50)", maxLength: 50, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedDate = table.Column(type: "datetime", nullable: false), + UpdatedDate = table.Column(type: "datetime", nullable: false), + LastLoginDate = table.Column(type: "datetime", nullable: false), + LastLoginIp = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LastLoginDevice = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "bigint", nullable: false), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "bigint", nullable: false), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "bigint", nullable: false), + RoleId = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "bigint", nullable: false), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/LMS.service/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/LMS.service/Data/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000..7be2459 --- /dev/null +++ b/LMS.service/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,300 @@ +锘// +using System; +using LMS.DAO; +using LMS.service.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace LMS.service.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("LMS.service.Models.UserModels.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("LMS.service.Models.UserModels.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("CreatedDate") + .HasColumnType("datetime"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("LastLoginDate") + .HasColumnType("datetime"); + + b.Property("LastLoginDevice") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastLoginIp") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NickName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UpdatedDate") + .HasColumnType("datetime"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("LMS.service.Models.UserModels.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("LMS.service.Models.UserModels.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("LMS.service.Models.UserModels.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("LMS.service.Models.UserModels.Role", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LMS.service.Models.UserModels.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("LMS.service.Models.UserModels.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LMS.service/Dockerfile b/LMS.service/Dockerfile new file mode 100644 index 0000000..018a754 --- /dev/null +++ b/LMS.service/Dockerfile @@ -0,0 +1,31 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER app +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +# 复制所有的项目文件 +COPY ["LMS.service/LMS.service.csproj", "LMS.service/"] +COPY ["LMS.Common/LMS.Common.csproj", "LMS.Common/"] +COPY ["LMS.DAO/LMS.DAO.csproj", "LMS.DAO/"] +COPY ["LMS.Repository/LMS.Repository.csproj", "LMS.Repository/"] +COPY ["LMS.Tools/LMS.Tools.csproj", "LMS.Tools/"] + +RUN dotnet restore "LMS.service/LMS.service.csproj" +COPY . . +WORKDIR "/src/LMS.service" +RUN dotnet build "LMS.service.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "LMS.service.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "LMS.service.dll"] \ No newline at end of file diff --git a/LMS.service/Extensions/Middleware/DynamicPermissionMiddleware.cs b/LMS.service/Extensions/Middleware/DynamicPermissionMiddleware.cs new file mode 100644 index 0000000..b224d03 --- /dev/null +++ b/LMS.service/Extensions/Middleware/DynamicPermissionMiddleware.cs @@ -0,0 +1,65 @@ +锘縰sing LMS.service.Service.PermissionService; +using Microsoft.AspNetCore.Routing; +using System.Security.Claims; + +namespace LMS.service.Extensions.Middleware +{ + public class DynamicPermissionMiddleware + { + private readonly RequestDelegate _next; + + public DynamicPermissionMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context, PremissionValidationService _premissionValidationServices) + { + var endpoint = context.GetEndpoint(); + var userId = GetUserIdFromContext(context); // 浠嶫WT token鎴杝ession涓幏鍙栫敤鎴稩D + if (userId == -1) // 鍒ゆ柇鐢ㄦ埛ID鏄惁鏈夋晥 + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + await context.Response.WriteAsync("鐢ㄦ埛鍙傛暟鏍¢獙閿欒"); + return; + } + + if (endpoint != null) + { + var httpMethod = context.Request.Method; + var path = (endpoint as RouteEndpoint)?.RoutePattern.RawText; + + if (await _premissionValidationServices.HasPermissionForEndpoint(userId, httpMethod, path)) + { + await _next(context); + } + else + { + context.Response.StatusCode = StatusCodes.Status403Forbidden; + await context.Response.WriteAsync("Access denied"); + } + } + else + { + await _next(context); + } + } + + private long GetUserIdFromContext(HttpContext context) + { + var userIdClaim = context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier); + var userId = userIdClaim?.Value; + if (!string.IsNullOrWhiteSpace(userId)) + { + // 鍒ゆ柇userId鏄惁涓烘暟瀛 + if (!long.TryParse(userId, out var result)) + { + return -1; + } + context.Items["UserId"] = userId; + return Convert.ToInt64(userIdClaim?.Value); + } + return 0; + } + } +} diff --git a/LMS.service/LMS.service.csproj b/LMS.service/LMS.service.csproj new file mode 100644 index 0000000..5bed246 --- /dev/null +++ b/LMS.service/LMS.service.csproj @@ -0,0 +1,43 @@ + + + + net8.0 + enable + enable + ed64fb6f-9c93-43d0-b418-61f507f28420 + Linux + . + 1.0.0 + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + diff --git a/LMS.service/LMS.service.http b/LMS.service/LMS.service.http new file mode 100644 index 0000000..60db1ca --- /dev/null +++ b/LMS.service/LMS.service.http @@ -0,0 +1,6 @@ +@LMS.service_HostAddress = http://localhost:5181 + +GET {{LMS.service_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/LMS.service/LMS.service.sln b/LMS.service/LMS.service.sln new file mode 100644 index 0000000..c6e0f0e --- /dev/null +++ b/LMS.service/LMS.service.sln @@ -0,0 +1,49 @@ +锘 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34622.214 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LMS.service", "LMS.service.csproj", "{E6EDD426-2283-40EA-93EA-2C718FBD66CB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LMS.Tools", "..\LMS.Tools\LMS.Tools.csproj", "{2551EC61-E4DA-4DF8-BCCC-2D0456D61424}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LMS.Common", "..\LMS.Common\LMS.Common.csproj", "{AB8D7E1E-2AA9-45C1-9C9A-A549C9FCA231}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LMS.Repository", "..\LMS.Repository\LMS.Repository.csproj", "{ABE14686-E9D8-4FCD-9D1F-767EC82D5538}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LMS.DAO", "..\LMS.DAO\LMS.DAO.csproj", "{CC297416-0545-4416-9E9A-EA738DA931B9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E6EDD426-2283-40EA-93EA-2C718FBD66CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6EDD426-2283-40EA-93EA-2C718FBD66CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6EDD426-2283-40EA-93EA-2C718FBD66CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6EDD426-2283-40EA-93EA-2C718FBD66CB}.Release|Any CPU.Build.0 = Release|Any CPU + {2551EC61-E4DA-4DF8-BCCC-2D0456D61424}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2551EC61-E4DA-4DF8-BCCC-2D0456D61424}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2551EC61-E4DA-4DF8-BCCC-2D0456D61424}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2551EC61-E4DA-4DF8-BCCC-2D0456D61424}.Release|Any CPU.Build.0 = Release|Any CPU + {AB8D7E1E-2AA9-45C1-9C9A-A549C9FCA231}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB8D7E1E-2AA9-45C1-9C9A-A549C9FCA231}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB8D7E1E-2AA9-45C1-9C9A-A549C9FCA231}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB8D7E1E-2AA9-45C1-9C9A-A549C9FCA231}.Release|Any CPU.Build.0 = Release|Any CPU + {ABE14686-E9D8-4FCD-9D1F-767EC82D5538}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ABE14686-E9D8-4FCD-9D1F-767EC82D5538}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ABE14686-E9D8-4FCD-9D1F-767EC82D5538}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ABE14686-E9D8-4FCD-9D1F-767EC82D5538}.Release|Any CPU.Build.0 = Release|Any CPU + {CC297416-0545-4416-9E9A-EA738DA931B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC297416-0545-4416-9E9A-EA738DA931B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC297416-0545-4416-9E9A-EA738DA931B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC297416-0545-4416-9E9A-EA738DA931B9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BD24726A-3AE2-4762-8EC5-1528B1A863E0} + EndGlobalSection +EndGlobal diff --git a/LMS.service/Program.cs b/LMS.service/Program.cs new file mode 100644 index 0000000..6e49b1a --- /dev/null +++ b/LMS.service/Program.cs @@ -0,0 +1,104 @@ +using Lai_server.Configuration; +using LMS.DAO; +using LMS.Repository.Models.DB; +using LMS.service.Configuration; +using LMS.service.Extensions.Middleware; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); + +builder.Services.AddAuthentication(); + +// 添加跨域 +builder.Services.AddCorsServices(); + +// 配置注入自定义服务方法 +builder.Services.AddServices(); + +builder.Services.ConfigureApplicationCookie(options => +{ + options.Cookie.HttpOnly = true; + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; + options.Cookie.SameSite = SameSiteMode.None; +}); + +//配置JWT +builder.Services.AddJWTAuthentication(); +builder.Services.AddAutoMapper(typeof(AutoMapperConfig)); + +builder.Services.AddDbContext(options => +{ + string connectionString = $"server={Environment.GetEnvironmentVariable("DATABASE_SERVER")};port={Environment.GetEnvironmentVariable("DATABASE_PORT")};user={Environment.GetEnvironmentVariable("DATABASE_USER")};password={Environment.GetEnvironmentVariable("DATABASE_PASSWORD")};database={Environment.GetEnvironmentVariable("DATABASE_NAME")};ConvertZeroDateTime=True;SslMode={Environment.GetEnvironmentVariable("DATABASE_SSL_MODE") ?? "None"};"; + options.UseMySql(connectionString, ServerVersion.Parse("8.0.18-mysql")); +}); + +builder.Services.AddIdentityCore(options => +{ + options.SignIn.RequireConfirmedAccount = true; //已有账号才能登录 + options.SignIn.RequireConfirmedEmail = true; // + options.Password.RequireDigit = true; // 数据库中至少有一个数字 + options.Password.RequireLowercase = true; // 数据库中至少有一个小写字母 + options.Password.RequireUppercase = true; // 数据库中至少有一个大写字母 + options.Password.RequireNonAlphanumeric = true; // 数据库中至少有一个特殊字符 + options.Password.RequiredLength = 8; // 密码长度最少8位 + + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); // 锁定时间 + options.Lockout.MaxFailedAccessAttempts = 10; // 尝试次数 + options.Lockout.AllowedForNewUsers = true; // 新用户是否可以锁定 + + options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"; // 用户名允许的字符 + options.User.RequireUniqueEmail = true; // 允许重复邮箱 + //options.User. +}); + +var idBuilder = new IdentityBuilder(typeof(User), typeof(Role), builder.Services); +idBuilder.AddEntityFrameworkStores() + .AddDefaultTokenProviders() + .AddUserManager>() + .AddRoleManager>(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +// 注入Swagger +builder.Services.AddSwaggerService(); + +builder.Services.AddHostedService(); + + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); + app.UseWhen(context => context.Request.Path.Value == "/", builder => + builder.Run(context => + { + context.Response.Redirect("/swagger"); + return Task.CompletedTask; + })); +} + +app.UseHttpsRedirection(); +app.UseRouting(); +app.UseCors("AllowAll"); +app.UseAuthentication(); +app.UseAuthorization(); +app.MapControllers(); + +app.UseMiddleware(); +app.UseEndpoints(endpoints => +{ + _ = endpoints.MapControllers(); +}); + + +app.Run(); diff --git a/LMS.service/Service/MachineService.cs b/LMS.service/Service/MachineService.cs new file mode 100644 index 0000000..c39e9c5 --- /dev/null +++ b/LMS.service/Service/MachineService.cs @@ -0,0 +1,712 @@ +锘縰sing AutoMapper; +using LMS.DAO; +using LMS.Repository.DTO; +using LMS.Repository.DTO.UserDto; +using LMS.Repository.Models.DB; +using LMS.Repository.Models.Machine; +using LMS.Tools; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using static LMS.Common.Enums.MachineEnum; +using static LMS.Common.Enums.ResponseCodeEnum; +using static LMS.Repository.DTO.MachineResponse.MachineDto; +using Machine = LMS.Repository.Models.DB.Machine; + +namespace LMS.service.Service +{ + public class MachineService + { + private readonly ApplicationDbContext _context; + private readonly UserManager _userManager; + private readonly IMapper _mapper; + + public MachineService(ApplicationDbContext context, IMapper mapper, UserManager userManager) + { + _context = context; + _mapper = mapper; + _userManager = userManager; + } + + #region Private Methods + + /// + /// 鍒ゆ柇褰撳墠鏈哄櫒鐮佹墍灞炵殑鐢ㄦ埛鏄笉鏄彲浠ユ坊鍔犳案涔呮満鍣ㄧ爜 + /// 妫鏌ョ敤鎴锋槸涓嶆槸VIP鐢ㄦ埛 + /// 妫鏌ュ綋鍓嶇敤鎴锋槸涓嶆槸宸茬粡鎷ユ湁浜嗘渶澶х殑鏁伴噺鐨勬満鍣ㄧ爜 + /// 妫鏌ュ綋鍓嶇敤鎴锋槸涓嶆槸杩樻湁鍏嶈垂鏇存崲娆℃暟 + /// + /// + /// + /// + private async Task> CanAddPermanentMachine(User ownerUser, long userId, string? id, bool checkVip) + { + // 鍒ゆ柇鏄笉鏄疺IP + if (checkVip && !await _userManager.IsInRoleAsync(ownerUser, "Vip User")) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserNotVip); + } + + // 鍒ゆ柇鏄笉鏄凡缁忔湁姝e湪浣跨敤鐨勬満鍣ㄧ爜 + List usingMachine = await _context.Machine.Where(x => x.UserID == userId && x.Status == MachineStatus.Active && (x.DeactivationTime == null || x.DeactivationTime >= BeijingTimeExtension.GetBeijingTime()) && (id == null || x.Id != id)).ToListAsync(); + + // 鍒ゆ柇鏄笉鏄秴杩囦簡鐢ㄦ埛鍏佽鐨勬渶澶ф満鍣ㄧ爜鏁伴噺 + if ((usingMachine.Count + 1) > ownerUser.AllDeviceCount) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "褰撳墠鐢ㄦ埛姝e湪缁戝畾鐨勬満鍣ㄧ爜鏁伴噺瓒呰繃鏈澶ф暟閲"); + } + + // 鍒ゆ柇鍏嶈垂鏇存崲娆℃暟 + if (ownerUser.FreeCount <= 0) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "褰撳墠鐢ㄦ埛鏇存崲鏈哄櫒鐮佺殑娆℃暟宸茬敤瀹"); + } + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + + #endregion + + #region 娣诲姞鏈哄櫒鐮 + /// + /// 娣诲姞鏈哄櫒鐮 + /// + /// + /// + /// + internal async Task>> AddMachine(MachineModel request, long reqId) + { + using var transaction = _context.Database.BeginTransaction(); + try + { + // 鏂板 + // 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁鏈夋柊澧炲拰绠$悊鏈哄櫒鐮佺殑鏉冮檺 + //if (!await _context.Permission.AnyAsync(x => x.UserId == reqId && (x.PermissionCode == SubPermissionType.ManageMachine || x.PermissionCode == SubPermissionType.AddMachine))) + //{ + // return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + //} + + // 璇锋眰鏂规硶鐨勭敤鎴 + User? user = await _userManager.FindByIdAsync(reqId.ToString()); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + // 鍒ゆ柇褰撳墠鏈哄櫒鐮佹槸鍚﹀凡缁忓瓨鍦 + if (await _context.Machine.AnyAsync(x => x.MachineId == request.MachineId)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.MachineAlreadyExist); + } + + if (request.Status == MachineStatus.Frozen) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "鏂板鏈哄櫒鐮佷笉鑳界洿鎺ュ仠鐢"); + } + + if (request.UserId == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + // 鍒ゆ柇OwnerUserId鏄笉鏄繕鑳芥坊鍔 + User? ownerUser = await _userManager.FindByIdAsync(request.UserId.ToString()); + if (ownerUser == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + // 鍒ゆ柇鏄笉鏄鐞嗗憳锛屼笉鏄鐞嗗憳锛屽彧鑳芥坊鍔犺嚜宸辩殑鏈哄櫒鐮 + bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(user, "Admin") || await _userManager.IsInRoleAsync(user, "Super Admin"); + if (!isAdminOrSuperAdmin && ownerUser.Id != reqId) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + if (!EnumExtensions.IsValidPermissionType(typeof(MachineUseStatus), request.UseStatus)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "鏈哄櫒鐮佷娇鐢ㄧ姸鎬佷笉姝g‘"); + } + if (!EnumExtensions.IsValidPermissionType(typeof(MachineStatus), request.Status)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "鏈哄櫒鐮佺姸鎬佷笉姝g‘"); + } + + // 鍒ゆ柇鏄笉鏄疺IP锛屼笉鏄疺IP鐨勮瘽锛屽垽鏂箣涓嶆槸宸茬粡瀛樺湪鏈哄櫒鐮 + if (!await _userManager.IsInRoleAsync(ownerUser, "VIP User")) + { + if (await _context.Machine.AnyAsync(x => x.UserID == ownerUser.Id)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "鏅氱敤鎴峰彧鑳芥坊鍔犱竴涓満鍣ㄧ爜"); + } + } + + if (request.UseStatus == MachineUseStatus.Trial) + { + var checkRes = await CanAddPermanentMachine(ownerUser, (long)request.UserId, null, false); + if (checkRes.Code != 1) + { + return checkRes; + } + // 蹇呬紶鍋滅敤鏃堕棿 + if (request.DeactivationTime == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "璇曠敤鏈哄櫒鐮佸繀椤讳紶鍏ュ仠鐢ㄦ椂闂"); + } + request.DeactivationTime = request.DeactivationTime?.AddHours(8); + + // 鍒ゆ柇鍋滅敤鏃堕棿鏄笉鏄皯浜庡綋鍓嶇殑 + if (request.DeactivationTime < BeijingTimeExtension.GetBeijingTime()) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "鍒版湡鏃堕棿涓嶈兘灏忎簬褰撳墠鏃堕棿"); + } + Console.WriteLine(BeijingTimeExtension.GetBeijingTime().ToString()); + int s = (request.DeactivationTime - BeijingTimeExtension.GetBeijingTime()).Value.Days; + // 鍒ゆ柇褰撳墠鏃堕棿鍜岀幇鍦ㄧ殑鏃堕棿宸ぇ浜庝笁澶╋紝鎶ラ敊 + if ((request.DeactivationTime - BeijingTimeExtension.GetBeijingTime()).Value.Days >= 3) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "鍒版湡鏃堕棿涓嶈兘瓒呰繃涓夊ぉ"); + } + // 鍏堜慨鏀圭敤鎴风殑鍏嶈垂鏇存崲娆℃暟 + ownerUser.FreeCount -= 1; + _context.Users.Update(ownerUser); + } + else + { + var checkRes = await CanAddPermanentMachine(ownerUser, (long)request.UserId, null, true); + if (checkRes.Code != 1) + { + return checkRes; + } + // 鍏堜慨鏀圭敤鎴风殑鍏嶈垂鏇存崲娆℃暟 + ownerUser.FreeCount -= 1; + _context.Users.Update(ownerUser); + request.DeactivationTime = null; + } + + //寮濮嬫柊澧 + Machine machine = _mapper.Map(request); + machine.Id = Guid.NewGuid().ToString(); + machine.CreateId = reqId; + machine.UpdateId = reqId; + machine.CreateTime = BeijingTimeExtension.GetBeijingTime(); + machine.UpdateTime = BeijingTimeExtension.GetBeijingTime(); + + await _context.Machine.AddAsync(machine); + + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + #endregion + + #region 鍒犻櫎鏈哄櫒鐮 + /// + /// 鍒犻櫎鏈哄櫒鐮 + /// + /// + /// + /// + internal async Task>> DeleteMachine(string id, long userId) + { + try + { + Machine? machine = await _context.Machine.FirstOrDefaultAsync(x => x.Id == id); + if (machine == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindMachineByIdFail); + } + + // 寮濮嬪垹闄 + _context.Machine.Remove(machine); + await _context.SaveChangesAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + + #region 鑾峰彇鏈哄櫒鐮佺姸鎬 + + /// + /// 鑾峰彇鏈哄櫒鐘舵侊紙鏄惁鍋滅敤鍜屾埅鑷虫椂闂达級 + /// + /// + /// + /// + internal async Task>> GetMachineStatus(string machineId) + { + try + { + // 鑾峰彇瀵瑰簲鐨刴achine + var machine = await _context.Machine.FirstOrDefaultAsync(x => + x.MachineId == machineId && + ((x.DeactivationTime == null && x.UseStatus == MachineUseStatus.Permanent && x.Status == MachineStatus.Active) + || (x.DeactivationTime != null && x.UseStatus == MachineUseStatus.Trial && DateTime.Now < x.DeactivationTime && x.Status == MachineStatus.Active))); + + if (machine == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.MachineNotFound); + } + + MachineStatusResponse machineStatus = _mapper.Map(machine); + + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success, machineStatus); + } + catch (Exception e) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + + #region 淇敼鏈哄櫒鐮佹暟鎹 + /// + /// 鏂板/淇敼鏈哄櫒鐘舵 + /// + /// 璇锋眰浣 + /// 璇锋眰ID + /// + /// + internal async Task>> ModifyMachine(string machineId, MachineModel request, long reqId) + { + using var transaction = _context.Database.BeginTransaction(); + try + { + // 淇敼 + // 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁鏈変慨鏀瑰拰绠$悊鏈哄櫒鐮佺殑鏉冮檺 + //if (!await _context.Permission.AnyAsync(x => x.UserId == reqId && (x.PermissionCode == SubPermissionType.ManageMachine || x.PermissionCode == SubPermissionType.ModifyMachine))) + //{ + // return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + //} + // 鍒ゆ柇浼犲叆鐨剈serId鏄惁瀛樺湪 + User? user = await _userManager.FindByIdAsync(reqId.ToString()); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + // 鍒ゆ柇褰撳墠ID鏄笉鏄瓨鍦 + var machine = await _context.Machine.FirstOrDefaultAsync(x => x.Id == machineId); + if (machine == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindMachineByIdFail); + } + + bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(user, "Admin") || await _userManager.IsInRoleAsync(user, "Super Admin"); + if (!isAdminOrSuperAdmin) + { + if (reqId != machine.UserID || request.MachineId != machine.MachineId) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + if (machine.MachineId != request.MachineId) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "鏈哄櫒鐮佷笉鑳戒慨鏀"); + } + } + + // 鍒ゆ柇褰撳墠淇敼鍚庣殑鏈哄櫒鐮佹槸鍚﹀凡缁忓瓨鍦紙涓嶅寘鍚嚜宸憋級 + if (await _context.Machine.AnyAsync(x => x.MachineId == request.MachineId && x.Id != machineId)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.MachineAlreadyExist); + } + + // 灏嗘爣鍑嗘椂闂磋浆鎹负鍖椾含鏃堕棿 + request.DeactivationTime = request.DeactivationTime?.AddHours(8); + + // 鍒ゆ柇鏄笉鏄案涔呮敼璇曠敤 + if (machine.UseStatus == MachineUseStatus.Permanent && request.UseStatus == MachineUseStatus.Trial) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "鏈哄櫒鐮佷笉鑳芥案涔呮敼璇曠敤"); + } + + if (!isAdminOrSuperAdmin) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + // 璇曠敤锛岄櫎浜嗙鐞嗗憳涔嬪涓嶈兘淇敼 + if (request.UseStatus == MachineUseStatus.Trial) + { + // 鍒ゆ柇缁撴潫鏃堕棿鏄笉鏄皯浜庡綋鍓嶇殑 + if (request.DeactivationTime != null && request.DeactivationTime < BeijingTimeExtension.GetBeijingTime()) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "鍒版湡鏃堕棿涓嶈兘灏忎簬褰撳墠鏃堕棿"); + } + + // 鍒ゆ柇褰撳墠鏃堕棿鍜岀幇鍦ㄧ殑鏃堕棿宸ぇ浜庝笁澶╋紝鎶ラ敊 + if (request.DeactivationTime != null && (request.DeactivationTime - BeijingTimeExtension.GetBeijingTime()).Value.Days > 3) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "鍒版湡鏃堕棿涓嶈兘瓒呰繃涓夊ぉ"); + } + } + else if (request.UseStatus == MachineUseStatus.Permanent) + { + request.DeactivationTime = null; + User? ownerUser = await _userManager.FindByIdAsync(machine.UserID.ToString()); + if (ownerUser == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + // 鍒ゆ柇鏄笉鏄厖璇曠敤鍗囩骇鍒版案涔咃紝杩欒竟瑕佹坊鍔犱竴浜涘垽鏂 + if (machine.UseStatus == MachineUseStatus.Trial) + { + var checkRes = await CanAddPermanentMachine(ownerUser, machine.UserID, machine.Id, true); + if (checkRes.Code != 1) + { + return checkRes; + } + // 鍏堜慨鏀圭敤鎴风殑鍏嶈垂鏇存崲娆℃暟 + ownerUser.FreeCount -= 1; + await _userManager.UpdateAsync(ownerUser); + } + + // 鍒ゆ柇鏄笉鏄噸鏂版縺娲 + if (request.Status == MachineStatus.Active && machine.Status == MachineStatus.Frozen) + { + var checkRes = await CanAddPermanentMachine(ownerUser, machine.UserID, machine.Id, true); + if (checkRes.Code != 1) + { + return checkRes; + } + // 鍏堜慨鏀圭敤鎴风殑鍏嶈垂鏇存崲娆℃暟 + ownerUser.FreeCount -= 1; + await _userManager.UpdateAsync(ownerUser); + } + } + + // 寮濮嬩慨鏀 + machine.UpdateId = reqId; + machine.UpdateTime = BeijingTimeExtension.GetBeijingTime(); + if (request.DeactivationTime != null) + { + machine.DeactivationTime = request.DeactivationTime; + } + if (request.Status == MachineStatus.Active && request.UseStatus == MachineUseStatus.Permanent) + { + machine.DeactivationTime = null; + } + machine.UseStatus = request.UseStatus; + machine.MachineId = request.MachineId; + machine.Status = request.Status; + machine.Remark = request.Remark; + _context.Machine.Update(machine); + + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + + #region 鏌ヨ鏈哄櫒鐮佸垪琛 + /// + /// 鏌ヨ鏈哄櫒鐮佸垪琛 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + internal async Task>>> QueryMachineCollection(int page, int pageSize, string? machineId, string? createdUserName, MachineStatus? status, MachineUseStatus? useStatus, string? remark, string? ownUserName, long requestUserId) + { + try + { + User? user = await _userManager.FindByIdAsync(requestUserId.ToString()); + if (user == null) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + bool isSuperAdmin = await _userManager.IsInRoleAsync(user, "Super Admin"); + bool isAdmin = await _userManager.IsInRoleAsync(user, "Admin"); + + IQueryable query = _context.Machine; + + if (isAdmin) + { + List superAdminUserIds = ((List)await _userManager.GetUsersInRoleAsync("Super Admin")).Select(x => x.Id).ToList(); + + //.Result.Select(x => x.Id).ToList(); + query = query.Where(x => !superAdminUserIds.Contains(x.UserID)); + } + else if (!isSuperAdmin) + { + query = query.Where(x => x.UserID == requestUserId); + } + // 娣诲姞鍏朵粬鐨勬煡璇㈡潯浠 + if (!string.IsNullOrWhiteSpace(machineId)) + { + query = query.Where(x => x.MachineId == machineId); + } + // 绠$悊鍛樺拰瓒呯骇绠$悊鍛樺彲浠ヤ娇鐢ㄨ瀛楁鏌ヨ鎵鏈夊垱寤鸿呯殑鏈哄櫒鐮 + if (!string.IsNullOrWhiteSpace(createdUserName) && (isAdmin || isSuperAdmin)) + { + List queryUserId = (await _userManager.Users.Where(x => x.UserName.Contains(createdUserName)).ToListAsync()).Select(x => x.Id).ToList(); + query = query.Where(x => queryUserId.Contains(x.CreateId)); + } + // 鏅氱敤鎴峰彧鑳芥煡鎵捐嚜宸卞垱寤虹殑鏈哄櫒鐮 + else if (!string.IsNullOrWhiteSpace(createdUserName)) + { + query = query.Where(x => x.CreateId == user.Id); + } + + if (status != null) + { + query = query.Where(x => x.Status == status); + } + if (useStatus != null) + { + query = query.Where(x => x.UseStatus == useStatus); + } + if (!string.IsNullOrWhiteSpace(remark)) + { + query = query.Where(x => x.Remark.Contains(remark)); + } + + // 绠$悊鍛樺拰瓒呯骇绠$悊鍛樺彲浠ヤ娇鐢ㄨ瀛楁鏌ヨ鎵鏈夌殑鏈哄櫒鐮佺殑鎷ユ湁鑰 + if (!string.IsNullOrWhiteSpace(ownUserName) && (isAdmin || isSuperAdmin)) + { + List queryUserId = (await _userManager.Users.Where(x => x.UserName.Contains(ownUserName)).ToListAsync()).Select(x => x.Id).ToList(); + query = query.Where(x => queryUserId.Contains(x.UserID)); + } + // 鏅氱敤鎴峰彧鑳芥煡鎵捐嚜宸辨嫢鏈夌殑鏈哄櫒鐮 + else if (!string.IsNullOrWhiteSpace(ownUserName)) + { + query = query.Where(x => x.UserID == user.Id); + } + + int total = await query.CountAsync(); + + // 闄嶅簭锛屽彇鎸囧畾鐨勬潯鏁扮殑鏁版嵁 + List machines = await query.OrderByDescending(x => x.CreateTime).Skip((page - 1) * pageSize).Take(pageSize).ToListAsync(); + + return APIResponseModel>.CreateSuccessResponseModel(ResponseCode.Success, new CollectionResponse + { + Total = total, + Collection = machines, + Current = page + }); + } + catch (Exception e) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + + #region 灏嗘満鍣ㄧ爜鍗囩骇涓烘案涔 + /// + /// 灏嗘寚瀹氱殑鏈哄櫒鐮佸崌绾т负姘镐箙浣跨敤 + /// + /// + /// + /// + /// + internal async Task>> UpgradeMachine(string id, long requestUserId) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + User? requestUser = await _userManager.FindByIdAsync(requestUserId.ToString()); + if (requestUser == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + Machine? machine = await _context.Machine.FirstOrDefaultAsync(x => x.Id == id); + if (machine == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindMachineByIdFail); + } + + bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(requestUser, "Admin") || await _userManager.IsInRoleAsync(requestUser, "Super Admin"); + + if (!isAdminOrSuperAdmin && machine.UserID != requestUserId) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + User? ownerUser = await _userManager.FindByIdAsync(machine.UserID.ToString()); + if (ownerUser == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + // 鍒ゆ柇褰撳墠鐢ㄦ埛鏄笉鏄彲浠ュ崌绾ф案涔 + // 鍒ゆ柇鐢ㄦ埛鏄笉鏄疺IP + if (!await _userManager.IsInRoleAsync(ownerUser, "Vip User")) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserNotVip); + } + + // 鍒ゆ柇鏄笉鏄凡缁忔湁姝e湪浣跨敤鐨勬満鍣ㄧ爜 + List usingMachine = await _context.Machine.Where(x => x.UserID == machine.UserID && x.Status == MachineStatus.Active && (x.DeactivationTime == null || x.DeactivationTime >= BeijingTimeExtension.GetBeijingTime()) && x.Id != machine.Id).ToListAsync(); + + // 鍒ゆ柇鏄笉鏄秴杩囦簡鐢ㄦ埛鍏佽鐨勬渶澶ф満鍣ㄧ爜鏁伴噺 + if ((usingMachine.Count + 1) > ownerUser.AllDeviceCount) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "褰撳墠鐢ㄦ埛姝e湪缁戝畾鐨勬満鍣ㄧ爜鏁伴噺瓒呰繃鏈澶ф暟閲"); + } + + // 鍒ゆ柇鍏嶈垂鏇存崲娆℃暟 + if (ownerUser.FreeCount <= 0) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "褰撳墠鐢ㄦ埛鏇存崲鏈哄櫒鐮佺殑娆℃暟宸茬敤瀹"); + } + + // 鍒ゆ柇褰撳墠鏈哄櫒鐮佹槸鍚﹀凡缁忔槸姘镐箙浣跨敤 + if (machine.Status == MachineStatus.Active && machine.UseStatus == MachineUseStatus.Permanent) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "褰撳墠鏈哄櫒鐮佸凡鏄案涔"); + } + + // 鍏堜慨鏀圭敤鎴风殑鍏嶈垂鏇存崲娆℃暟 + ownerUser.FreeCount -= 1; + await _userManager.UpdateAsync(ownerUser); + + machine.UpdateTime = BeijingTimeExtension.GetBeijingTime(); + machine.Status = MachineStatus.Active; + machine.UseStatus = MachineUseStatus.Permanent; + machine.DeactivationTime = null; + _context.Machine.Update(machine); + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + + #region 灏嗘満鍣ㄧ爜鍋滅敤 + /// + /// 涓閿仠鐢ㄦ満鍣ㄧ爜锛屽彧鏈夌鐞嗗憳鍜岃秴绾х鐞嗗憳鍙互鎿嶄綔锛屾櫘閫氱敤鎴峰彧鑳藉仠鐢ㄨ嚜宸辩殑鏈哄櫒鐮 + /// + /// + /// + /// + internal async Task>> DeactivateMachine(string id, long requestUserId) + { + try + { + User? user = await _userManager.FindByIdAsync(requestUserId.ToString()); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(user, "Admin") || await _userManager.IsInRoleAsync(user, "Super Admin"); + + Machine? machine = await _context.Machine.FirstOrDefaultAsync(x => x.Id == id); + if (machine == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindMachineByIdFail); + } + + if (!isAdminOrSuperAdmin && machine.UserID != requestUserId) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + // 鍒ゆ柇鏄笉鏄凡缁忓仠鐢 + if (machine.Status == MachineStatus.Frozen) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "璇ユ満鍣ㄧ爜宸茬粡琚喕缁擄紝涓嶈兘閲嶅鎿嶄綔"); + } + + // 寮濮嬪喕缁 + machine.Status = MachineStatus.Frozen; + if (machine.DeactivationTime == null) + { + machine.DeactivationTime = BeijingTimeExtension.GetBeijingTime(); + } + machine.UpdateTime = BeijingTimeExtension.GetBeijingTime(); + machine.UpdateId = requestUserId; + _context.Update(machine); + await _context.SaveChangesAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + + #region 鑾峰彇鎸囧畾鐨勬満鍣ㄧ爜璇︽儏 + /// + /// 鑾峰彇鎸囧畾鐨勬満鍣ㄧ爜璇︽儏 + /// + /// + /// + /// + /// + internal async Task>> GetMachineDetail(string id, long userId) + { + try + { + User? user = await _userManager.FindByIdAsync(userId.ToString()); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + Machine? machine = await _context.Machine.FirstOrDefaultAsync(x => x.Id == id); + if (machine == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindMachineByIdFail); + } + + bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(user, "Admin") || await _userManager.IsInRoleAsync(user, "Super Admin"); + if (!isAdminOrSuperAdmin && machine.UserID != userId) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + MachineDetailDto machineDetail = _mapper.Map(machine); + User? createdUser = await _userManager.FindByIdAsync(machine.CreateId.ToString()); + User? updatedUser = await _userManager.FindByIdAsync(machine.UpdateId.ToString()); + User? ownUser = await _userManager.FindByIdAsync(machine.UserID.ToString()); + machineDetail.CreatedUser = _mapper.Map(createdUser); + machineDetail.UpdatedUser = _mapper.Map(createdUser); + machineDetail.OwnUser = _mapper.Map(createdUser); + + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success, machineDetail); + } + catch (Exception e) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + } +} diff --git a/LMS.service/Service/PermissionService/PermissionService.cs b/LMS.service/Service/PermissionService/PermissionService.cs new file mode 100644 index 0000000..85811cf --- /dev/null +++ b/LMS.service/Service/PermissionService/PermissionService.cs @@ -0,0 +1,460 @@ +锘縰sing AutoMapper; +using LMS.Common.Enums; +using LMS.DAO; +using LMS.DAO.MachineDAO; +using LMS.DAO.PermissionDAO; +using LMS.DAO.RoleDAO; +using LMS.DAO.UserDAO; +using LMS.Repository.Models.DB; +using LMS.Repository.Models.Promission; +using LMS.Repository.RequestModel.Permission; +using LMS.service.Data; +using LMS.Tools; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using MySqlConnector; +using OneOf; +using static LMS.Common.Enums.PermissionEnum; +using static LMS.Common.Enums.ResponseCodeEnum; +using PermissionType = LMS.Repository.Models.DB.PermissionType; + +namespace LMS.service.Service.PermissionService +{ + public class PermissionService + { + private readonly ApplicationDbContext _context; + private readonly IMapper _mapper; + private readonly UserManager _userManager; + private readonly UserBasicDao _userBasicDAO; + private readonly RoleBasicDao _roleBasicDAO; + private readonly MachineBasicDao _machineBasicDAO; + private readonly PermissionBasicDao _permissionBasicDAO; + private readonly PermissionTypeDao _permissionTypeDao; + + public PermissionService(ApplicationDbContext context, IMapper mapper, UserManager userManager, UserBasicDao userBasicDAO, RoleBasicDao roleBasicDAO, MachineBasicDao machineBasicDAO, PermissionBasicDao permissionBasicDao, PermissionTypeDao permissionTypeDao) + { + _context = context; + _mapper = mapper; + _userManager = userManager; + _userBasicDAO = userBasicDAO; + _roleBasicDAO = roleBasicDAO; + _machineBasicDAO = machineBasicDAO; + _permissionBasicDAO = permissionBasicDao; + _permissionTypeDao = permissionTypeDao; + } + + #region 鏉冮檺绫诲瀷 + + /// + /// 娣诲姞鏉冮檺绫诲瀷鏁版嵁 + /// + /// + /// + public async Task>> AddPermissionType(PermissionTypeModel permissionTypeReq, long requestUserId) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + // 娣诲姞鏁版嵁 + PermissionType permissionType = _mapper.Map(permissionTypeReq); + permissionType.Id = Guid.NewGuid().ToString(); + + // 鍒ゆ柇瀵瑰簲鐨勭被鍨嬫槸涓嶆槸婊¤冻鏉′欢 + if (!EnumExtensions.IsValidPermissionType(typeof(PermissionEnum.PermissionType), (int)permissionTypeReq.Type)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + + // 鍒ゆ柇鍚嶇О锛宑ode鏄笉鏄瓨鍦紝瀛樺湪鎶ラ敊 + if (await _context.PermissionType.AnyAsync(x => x.Name == permissionType.Name || x.Code == permissionType.Code)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.PermissionTypeExist); + } + + //// 鍒ゆ柇褰撳墠鐢ㄦ埛鏄笉鏄湁鏂板鏉冮檺鐨勬潈闄 + //if (!await _context.Permission.AnyAsync(x => x.UserId == permissionType.CreateUserId && (x.PermissionCode == SubPermissionType.AddAndDeletePermission || x.PermissionCode == SubPermissionType.ManagePermission))) + //{ + // return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + //} + + permissionType.CreateUserId = requestUserId; + permissionType.UpdateUserId = requestUserId; + permissionType.UpdateTime = BeijingTimeExtension.GetBeijingTime(); + permissionType.CreateTime = BeijingTimeExtension.GetBeijingTime(); + // 寮濮嬫柊澧 + await _context.PermissionType.AddAsync(permissionType); + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + + /// + /// 淇敼鏉冮檺绫诲瀷鏁版嵁 + /// + /// + /// + /// + /// + public async Task>> ModifyPermissionType(string permissionId, PermissionTypeModel model, long requestUserId) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + // 淇敼鏁版嵁 + PermissionType? permissionType = await _context.PermissionType.FirstOrDefaultAsync(x => x.Id == permissionId); + if (permissionType == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindPermissionTypeByIdFail); + } + permissionType.Name = model.Name; + permissionType.Code = model.Code; + permissionType.Type = model.Type; + permissionType.UpdateTime = DateTime.Now; + + //// 鍒ゆ柇褰撳墠鐢ㄦ埛鏄笉鏄湁淇敼鍜岀鐞嗘潈闄愮殑鏉冮檺 + //if (!await _context.Permission.AnyAsync(x => x.UserId == model.UpdateUserId && (x.PermissionCode == model.ModifyPermission || x.PermissionCode == SubPermissionType.ManagePermission))) + //{ + // return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + //} + + // 鍒ゆ柇瀵瑰簲鐨勭被鍨嬫槸涓嶆槸婊¤冻鏉′欢 + if (!EnumExtensions.IsValidPermissionType(typeof(PermissionEnum.PermissionType), (int)model.Type)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + + // 鍒ゆ柇瀵瑰簲鐨凜Ode鍜孨ame鏄笉鏄瓨鍦紝瀛樺湪鎶ラ敊锛堜笉鍖呭惈褰撳墠ID鐨勶級 + if (await _context.PermissionType.AnyAsync(x => x.Name == permissionType.Name && x.Code == permissionType.Code && x.Id != permissionType.Id)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.PermissionTypeExist); + } + + permissionType.UpdateUserId = requestUserId; + permissionType.Remark = model.Remark; + + _context.PermissionType.Update(permissionType); + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + transaction.Rollback(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + + /// + /// 鍒犻櫎鏉冮檺绫诲瀷鏁版嵁 + /// + /// + /// + /// + internal async Task>> DeletePermissionType(string id, bool relation, long requestUserId) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + PermissionType? permissionType = await _context.PermissionType.FirstOrDefaultAsync(x => x.Id == id); + // 鍒ゆ柇褰撳墠鏉冮檺绫诲瀷鏄笉鏄瓨鍦 + if (permissionType == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindPermissionTypeByIdFail); + } + + //// 鍒ゆ柇褰撳墠鐢ㄦ埛鏄笉鏄湁鍒犻櫎鏉冮檺鐨勬潈闄 + //if (!await _context.Permission.AnyAsync(x => x.UserId == permissionTypeRequest.UserId && (x.PermissionCode == SubPermissionType.AddAndDeletePermission || x.PermissionCode == SubPermissionType.ManagePermission))) + //{ + // return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + //} + + // 鍒ゆ柇褰撳墠鏉冮檺绫诲瀷鏄笉鏄湁鏉冮檺琚娇鐢 + var permissions = await _context.Permission + .FromSqlRaw("SELECT * FROM Permission WHERE JSON_CONTAINS(PermissionTypeIds, JSON_ARRAY(@id))", + new MySqlParameter("@id", id)) + .ToListAsync(); + foreach (var item in permissions) + { + // 鍒ゆ柇鏄笉鏄湁锛屽垽鏂槸涓嶆槸瑕佸垹闄ゅ搴旂殑鍏宠仈鏉冮檺 + if (relation) + { + List permissionTypeIds = item.PermissionTypeIdsJson; + await Console.Out.WriteLineAsync("1"); + // 鍒犻櫎瀵瑰簲鐨勬潈闄愮被鍨 + permissionTypeIds.Remove(permissionType.Id); + if (permissionTypeIds.Count == 0) + { + // 鍒犻櫎 + _context.Permission.Remove(item); + } + else + { + // 鏇存柊 + item.PermissionTypeIdsJson = permissionTypeIds; + _context.Permission.Update(item); + } + } + else + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.PermissionTypeExist, "褰撳墠鏉冮檺鏈夊叧鑱旂殑瀛愭潈闄愶紝涓嶈兘鐩存帴鍒犻櫎"); + } + } + + // 鍒犻櫎鏉冮檺绫诲瀷 + _context.PermissionType.Remove(permissionType); + + // 鎻愪氦 + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + + #region 鏉冮檺 + + /// + /// 妫娴嬩笉鍚岀殑绫诲瀷锛屽垽鏂搴旂殑ID鐨勬暟鎹槸涓嶆槸鏈変紶鍏 + /// + /// + /// + private async Task CheckPermissionTypeAndId(PermissionModel model) + { + switch (model.Type) + { + case PType.User: + if (model.MachineId != null || model.RoleId != null) + { + return false; + } + if (!await _userBasicDAO.CheckUserExistsByID(model.UserId)) + { + return false; + } + break; + case PType.Role: + if (model.MachineId != null || model.UserId != null) + { + return false; + } + if (!await _roleBasicDAO.CheckRoleExistById(model.RoleId)) + { + return false; + } + break; + case PType.Machine: + if (model.UserId != null || model.RoleId != null) + { + return false; + } + if (!await _machineBasicDAO.CheckMachineByMachineId(model.MachineId)) + { + return false; + } + break; + default: + return false; + } + return true; + } + + /// + /// 妫娴嬩笉鍚岀殑绫诲瀷锛岃幏鍙栧搴旂殑ID鏁版嵁 + /// + /// + /// + /// + private static OneOf GetPermissionRelationIdByType(PermissionModel model) + { + return model.Type switch + { + PType.User => model.UserId, + PType.Role => model.RoleId, + PType.Machine => model.MachineId, + _ => throw new NotImplementedException(), + }; + } + + /// + /// 鏂板缓鏉冮檺 + /// + /// + /// + /// + internal async Task>> AddPermission(PermissionModel model, long requestUserId) + { + try + { + // 鍒ゆ柇Type鏄笉鏄瓨鍦 + if (!EnumExtensions.IsValidPermissionType(typeof(PType), model.Type)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + + + // 妫鏌ュ搴旂殑鍒嗙被瀵瑰簲鐨処D鏄笉鏄瓨鍦 + if (!await CheckPermissionTypeAndId(model)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + + // 鑾峰彇瀵瑰簲鐨処D + OneOf relationId = GetPermissionRelationIdByType(model); + + // 鍒ゆ柇瀵瑰簲鐨勭敤鎴稩D锛岃鑹睮D锛屾満鍣↖D鏄笉鏄瓨鍦ㄧ浉瀵瑰簲鐨勬潈闄愶紝瀛樺湪鐨勮瘽锛屼笉璁告柊澧烇紝鍙兘淇敼 + if (await _permissionBasicDAO.CheckPermissionByTypeAndId(model.Type, relationId)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.PermissionExist); + } + + // 鍒ゆ柇浼犲叆鐨刾ermissionTypeId鏄笉鏄兘瀛樺湪 + if (!await _permissionTypeDao.CheckPermissionTypeIdsExist(model.PermissionTypeIds)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindPermissionTypeByIdFail); + } + + // 鍒ゆ柇PermissionCode鏄笉鏄噸澶 + if (await _permissionBasicDAO.CheckPermissionCodeExist(model.PermissionCode)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.PermissionCodeExist); + } + + // 寮濮嬫柊澧 + Permission permission = _mapper.Map(model); + permission.Id = Guid.NewGuid().ToString(); + permission.UpdateTime = BeijingTimeExtension.GetBeijingTime(); + permission.CreateTime = BeijingTimeExtension.GetBeijingTime(); + permission.CreateUserId = requestUserId; + permission.UpdateUserId = requestUserId; + permission.PermissionTypeIdsJson = model.PermissionTypeIds; + + await _context.Permission.AddAsync(permission); + await _context.SaveChangesAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + /// + /// 淇敼鏉冮檺 + /// + /// + /// + /// + internal async Task>> ModfiyPermission(string id, PermissionModel model, long requsetUserId) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + Permission? permission = await _context.Permission.FirstOrDefaultAsync(x => x.Id == id); + if (permission == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindPermissionByIdFail); + } + + // 鍒ゆ柇Type鏄笉鏄瓨鍦 + if (!EnumExtensions.IsValidPermissionType(typeof(PType), model.Type)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + + + if (!await CheckPermissionTypeAndId(model)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + + // 鑾峰彇瀵瑰簲鐨処D + OneOf relationId = GetPermissionRelationIdByType(model); + + // 鍒ゆ柇瀵瑰簲鐨勫叧鑱擨D鏄笉鏄繕鏈夊叾浠栫殑鏉冮檺锛屾湁鐨勮瘽涓嶈兘淇敼 + if (await _permissionBasicDAO.CheckPermissionByTypeAndId(model.Type, relationId)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.PermissionExist); + } + + // 鍒ゆ柇浼犲叆鐨刾ermissionTypeIDs鏄笉鏄瓨鍦 + if (!await _permissionTypeDao.CheckPermissionTypeIdsExist(model.PermissionTypeIds)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindPermissionTypeByIdFail); + } + + // 鍒ゆ柇permissionCode鏄笉鏄噸澶 + if (await _permissionBasicDAO.CheckPermissionCodeExist(model.PermissionCode, id)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.PermissionExist); + } + + // 寮濮嬩慨鏀 + permission.UserId = model.UserId; + permission.RoleId = model.RoleId; + permission.MachineId = model.MachineId; + permission.UpdateUserId = requsetUserId; + // 鍒ゆ柇鏄笉鏄湁浼犳潈闄恈ode锛屾病鏈夎鑾峰彇permissionType鐨刢ode + permission.PermissionCode = model.PermissionCode; + + permission.UpdateTime = BeijingTimeExtension.GetBeijingTime(); + _context.Permission.Update(permission); + + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + + /// + /// 鍒犻櫎鏉冮檺 + /// + /// + /// + /// + internal async Task>> DeletePermission(string Id, long requestUserId) + { + try + { + // 鍒ゆ柇褰撳墠璇锋眰鐨処D鏄笉鏄瓨鍦 + var permission = await _context.Permission.FirstOrDefaultAsync(x => x.Id == Id); + if (permission == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindPermissionByIdFail); + } + + // 鍒犻櫎鏉冮檺 + _context.Permission.Remove(permission); + await _context.SaveChangesAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success); + } + catch (Exception e) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + } +} diff --git a/LMS.service/Service/PermissionService/PremissionValidationService.cs b/LMS.service/Service/PermissionService/PremissionValidationService.cs new file mode 100644 index 0000000..9e75b63 --- /dev/null +++ b/LMS.service/Service/PermissionService/PremissionValidationService.cs @@ -0,0 +1,64 @@ +锘 +using LMS.DAO; +using LMS.service.Data; +using Microsoft.EntityFrameworkCore; + +namespace LMS.service.Service.PermissionService +{ + public class PremissionValidationService + { + private readonly ApplicationDbContext _context; + + public PremissionValidationService(ApplicationDbContext context) + { + _context = context; + } + + /// + /// 鑾峰彇鐢ㄦ埛鐨勬潈闄 + /// + /// + /// + private async Task> GetUserPermissions(int userId) + { + return null; + //return await _context.UserRoles + // .Where(ur => ur.UserId == userId) + // .SelectMany(ur => ur.Role.RolePermissions.Select(rp => rp.PermissionId)) + // .Distinct() + // .ToListAsync(); + } + + /// + /// 鑾峰彇鎺ュ彛闇瑕佺殑鏉冮檺 + /// + /// + /// + /// + private async Task GetRequiredPermissionForEndpoint(string httpMethod, string path) + { + return null; + //var endpoint = await _context.ApiEndpoints + // .FirstOrDefaultAsync(e => e.HttpMethod == httpMethod && e.Path == path); + + //return endpoint?.RequiredPermissionId; + } + + + /// + /// 鍒ゆ柇鐢ㄦ埛鏉冮檺鏄笉鏄瓨鍦 + /// + /// + /// + /// + /// + internal async Task HasPermissionForEndpoint(long userId, string httpMethod, object path) + { + return true; + //var userPermissions = await GetUserPermissions(userId); + //var requiredPermission = await GetRequiredPermissionForEndpoint(httpMethod, path); + + //return userPermissions.Contains(requiredPermission); + } + } +} diff --git a/LMS.service/Service/RoleService/RoleService.cs b/LMS.service/Service/RoleService/RoleService.cs new file mode 100644 index 0000000..25227df --- /dev/null +++ b/LMS.service/Service/RoleService/RoleService.cs @@ -0,0 +1,296 @@ +锘縰sing AutoMapper; +using LMS.DAO; +using LMS.Repository.DTO; +using LMS.Repository.DTO.UserDto; +using LMS.Repository.Models.DB; +using LMS.Repository.Role; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace LMS.service.Service.RoleService +{ + public class RoleService(RoleManager roleManager, ApplicationDbContext dbContext, UserManager userManager, IMapper mapper) + { + private readonly RoleManager _roleManager = roleManager; + private readonly ApplicationDbContext _context = dbContext; + private readonly UserManager _userManager = userManager; + private readonly IMapper _mapper = mapper; + + /// + /// 鍒涘缓瑙掕壊鏁版嵁 + /// + /// + /// + /// + public async Task>> AddRole(RoleModel model, long userId) + { + try + { + if (string.IsNullOrWhiteSpace(model.Name)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "瑙掕壊鍚嶄笉鑳戒负绌"); + } + + if (await _roleManager.RoleExistsAsync(model.Name)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.RoleNameExist); + } + + await _roleManager.CreateAsync(new Role + { + Name = model.Name, + CreatedUserId = userId, + UpdatedUserId = userId, + CreatedTime = BeijingTimeExtension.GetBeijingTime(), + UpdatedTime = BeijingTimeExtension.GetBeijingTime(), + Remark = model.Remark + }); + return APIResponseModel.CreateSuccessResponseModel("娣诲姞瑙掕壊鎴愬姛"); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + + /// + /// 淇敼瑙掕壊鏁版嵁 + /// + /// + /// + /// + /// + /// + internal async Task>> UpdateRole(long id, RoleModel model, long userId) + { + try + { + Role? role = _roleManager.FindByIdAsync(id.ToString()).Result; + if (role == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindRoleByIdFail); + } + + // 鍒ゆ柇code鏄笉鏄瓨鍦紝涓嶆姤閿欒嚜宸 + if (await _roleManager.Roles.FirstOrDefaultAsync(x => x.Name == model.Name && x.Id != id) != null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.RoleNameExist); + } + + // 淇敼 + role.Name = model.Name; + role.Remark = model.Remark; + role.UpdatedUserId = userId; + role.UpdatedTime = BeijingTimeExtension.GetBeijingTime(); + await _roleManager.UpdateAsync(role); + return APIResponseModel.CreateSuccessResponseModel("淇敼瑙掕壊鎴愬姛"); + + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + /// + /// 鍒犻櫎瑙掕壊鏁版嵁 + /// + /// + /// + /// + internal async Task>> DeleteRole(long id) + { + try + { + Role? role = await _roleManager.FindByIdAsync(id.ToString()); + if (role == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindRoleByIdFail); + } + if (await _context.UserRoles.AnyAsync(x => x.RoleId == id)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.RoleHasUser); + } + + await _roleManager.DeleteAsync(role); + return APIResponseModel.CreateSuccessResponseModel("鍒犻櫎瑙掕壊鎴愬姛"); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + + /// + /// 鏌ヨ瑙掕壊鏁版嵁锛岃繑鍥為泦鍚 + /// + /// + /// + /// + /// + /// + /// + /// + internal async Task>>> QueryRoleCollection(int page, int pageSize, string? roleName, long? roleId, long requestUserId) + { + roleName = roleName == "null" ? null : roleName; + roleId = roleId == null ? null : roleId; + try + { + // 鍒ゆ柇褰撳墠鐢ㄦ埛鐨勮鑹 + User? user = await _userManager.FindByIdAsync(requestUserId.ToString()); + if (user == null) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + // TODO + //if (!await _userManager.IsInRoleAsync(user, "Super Admin")) + //{ + // return APIResponseModel>.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + //} + + // 鏌ヨ鏁版嵁锛屽垽鏂槸涓嶆槸浼犲叆浜唕oleName鍜宺oleId锛屽鏋滀紶鍏ヤ簡灏辨煡璇紝濡傛灉娌℃湁灏辨煡璇㈠叏閮 + IQueryable rolesQueryable = _roleManager.Roles; + if (!string.IsNullOrWhiteSpace(roleName)) + { + rolesQueryable = rolesQueryable.Where(x => !string.IsNullOrEmpty(x.Name) && x.Name.Contains(roleName)); + } + + if (roleId != null) + { + rolesQueryable = rolesQueryable.Where(x => x.Id == roleId); + } + // 灏嗘彃鍙欏嚭鏉ョ殑鏁版嵁閫氳繃ID闄嶅簭鎺掑簭 + rolesQueryable = rolesQueryable.OrderByDescending(x => x.Id); + + // 鏌ヨ鎬绘暟 + int total = await rolesQueryable.CountAsync(); + + // 鍒嗛〉 + rolesQueryable = rolesQueryable.Skip((page - 1) * pageSize).Take(pageSize); + + + List roles = await rolesQueryable.ToListAsync(); + + List roleDtos = roles.Select(x => new RoleDto + { + Id = x.Id, + Name = x.Name ?? string.Empty, + CreatedTime = x.CreatedTime, + UpdatedTime = x.UpdatedTime, + Remark = x.Remark ?? string.Empty, + }).ToList(); + + for (int i = 0; i < roleDtos.Count; i++) + { + long createdUserId = roles[i].CreatedUserId; + long updatedUserId = roles[i].UpdatedUserId; + User? createdUser = await _userManager.FindByIdAsync(createdUserId.ToString()); + User? updatedUser = await _userManager.FindByIdAsync(updatedUserId.ToString()); + roleDtos[i].CreatedUser = _mapper.Map(createdUser); + roleDtos[i].UpdeatedUser = _mapper.Map(updatedUser); + } + + CollectionResponse collectionResponse = new() + { + Total = total, + Collection = roleDtos, + Current = page, + }; + + return APIResponseModel>.CreateSuccessResponseModel(collectionResponse); + } + catch (Exception ex) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + /// + /// 鏌ヨ瑙掕壊鏁版嵁锛岄氳繃瑙掕壊ID + /// + /// + /// + /// + public async Task>> QueryRoleById(long id, long userId) + { + try + { + User? user = await _userManager.FindByIdAsync(userId.ToString()); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + // 鍒ゆ柇鏉冮檺 + + // 寮濮嬫煡璇 + Role? role = await _roleManager.FindByIdAsync(id.ToString()); + if (role == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindRoleByIdFail); + } + RoleDto roleDto = new() + { + Id = role.Id, + Name = role.Name ?? string.Empty, + CreatedTime = role.CreatedTime, + UpdatedTime = role.UpdatedTime, + Remark = role.Remark ?? string.Empty + }; + + long createdUserId = role.CreatedUserId; + long updatedUserId = role.UpdatedUserId; + User? createdUser = await _userManager.FindByIdAsync(createdUserId.ToString()); + User? updatedUser = await _userManager.FindByIdAsync(updatedUserId.ToString()); + roleDto.CreatedUser = _mapper.Map(createdUser); + roleDto.UpdeatedUser = _mapper.Map(updatedUser); + + // 鍒ゆ柇鏄笉鏄秴绾х鐞嗗憳 + if (roleDto.UpdeatedUser != null && roleDto.CreatedUser != null && !await _userManager.IsInRoleAsync(user, "Super Admin")) + { + roleDto.UpdeatedUser.PhoneNumber = "***********"; + roleDto.CreatedUser.PhoneNumber = "***********"; + roleDto.UpdeatedUser.Email = "***********"; + roleDto.CreatedUser.Email = "***********"; + } + return APIResponseModel.CreateSuccessResponseModel(roleDto); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + /// + /// 鏌ヨ瑙掕壊鏁版嵁锛岃繑鍥炰笅鎷夋鏁版嵁锛屽彧瑕丯ame + /// + /// + /// + /// + public async Task>>> QueryRoleOption(long userId) + { + try + { + User? user = await _userManager.FindByIdAsync(userId.ToString()); + if (user == null) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + // 寮濮嬫煡璇 + List roles = await _roleManager.Roles.Where(x => !string.IsNullOrWhiteSpace(x.Name)).Select(x => x.Name ?? string.Empty).ToListAsync(); + return APIResponseModel>.CreateSuccessResponseModel(roles); + } + catch (Exception ex) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + } +} diff --git a/LMS.service/Service/UserService/LoginService.cs b/LMS.service/Service/UserService/LoginService.cs new file mode 100644 index 0000000..75b34e4 --- /dev/null +++ b/LMS.service/Service/UserService/LoginService.cs @@ -0,0 +1,278 @@ +锘 +using LMS.Common.RSAKey; +using LMS.DAO; +using LMS.Repository.Models.DB; +using LMS.Repository.Models.User; +using LMS.service.Data; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using System.Collections.Concurrent; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Security.Cryptography.Xml; +using System.Text; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace LMS.service.Service.UserService +{ + public class LoginService + { + private readonly UserManager _userManager; + private readonly ApplicationDbContext _context; + private readonly SecurityService _securityService; + + public LoginService(UserManager userManager, ApplicationDbContext context, SecurityService securityService) + { + _userManager = userManager; + _context = context; + _securityService = securityService; + } + + public class LoginResponse + { + public string Token { get; set; } + public string UserName { get; set; } + public long Id { get; set; } + public string NickName { get; set; } + public string RefreshToken { get; set; } + } + + /// + /// 鐢熸垚JWT + /// + /// + /// + /// + public string GenerateJWT(User user) + { + var claims = new List(); + claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())); + claims.Add(new Claim(ClaimTypes.Name, user.UserName)); + // 鑾峰彇瑙掕壊锛堢洰鍓嶅彧鏈夌鐞嗗憳锛 + claims.Add(new Claim(ClaimTypes.Role, "AdminUser")); + //foreach (string role in roles) + //{ + // claims.Add(new Claim(ClaimTypes.Role, role)); + //} + //key鏄鏌ョ鍚嶇殑瀵嗛挜 + if (Environment.GetEnvironmentVariable("SecKey") == null) + { + throw new Exception("SecKey is not set in environment variables"); + } + + string key = Environment.GetEnvironmentVariable("SecKey"); + + + //璁剧疆Token鐨勮繃鏈熸椂闂 + DateTime expires = DateTime.Now.AddHours(1); + byte[] secBytes = Encoding.UTF8.GetBytes(key); + var secKey = new SymmetricSecurityKey(secBytes); + + //鐢熸垚鏁板瓧绛惧悕 + var credentials = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256Signature); + + //鐢熸垚JWT涓璗oken + var tokenDescriptor = new JwtSecurityToken(claims: claims, + expires: expires, signingCredentials: credentials); + string jwt = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor); + return jwt; + } + + /// + /// 鐢ㄦ埛鐧诲綍鐨勬湇鍔″眰鏂规硶 + /// + /// + /// + public async Task> UserLogin(LoginModel model, string ip, ConcurrentDictionary _keyStore) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + if (!_keyStore.TryRemove(model.TokenId, out var keyInfo)) // 娌℃湁鎵惧埌瀵瑰簲鐨勫叕閽 + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserPasswordFail); + } + + if (BeijingTimeExtension.GetBeijingTime() > keyInfo.Expiry) // 鍏挜瓒呮椂 + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserPasswordFail); + } + + var rsaKeyId = keyInfo.Key; + var privateKey = _securityService.DecryptWithAES(rsaKeyId); + + string decryptedPassword = RsaKeyPairGenerator.Decrypt(privateKey, model.Password); + + User? user = null; + if ((int)(model.LoginType) == 1) //鐢ㄦ埛鍚嶅瘑鐮佺櫥褰 + { + if (model.UserName == null) + { + throw new ArgumentNullException("UserName"); + } + user = await _userManager.FindByNameAsync(model.UserName); + } + else if ((int)(model.LoginType) == 0) //閭鐧诲綍 + { + if (model.Email == null) + { + throw new ArgumentNullException("Email"); + } + user = await _userManager.FindByEmailAsync(model.Email); + } + else + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UndefinedLoginType); + } + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByNameFail); + } + + if (await _userManager.IsLockedOutAsync(user)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserIsLockedOut); + } + + var result = await _userManager.CheckPasswordAsync(user, decryptedPassword); + if (!result) + { + // 澶辫触璁℃暟鍣ㄥ姞1 + await _userManager.AccessFailedAsync(user); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserPasswordFail); + } + // 瀵嗙爜姝g‘锛岃繑鍥瀟oken + string jwt = GenerateJWT(user); + + // 鐢熸垚涓涓埛鏂癟oken + //var refreshToken = Guid.NewGuid().ToString(); + var securityService = new SecurityService(_context); + RefreshTokens refreshTokens = await securityService.CreateRefreshTokenByUserId(user.Id, ip, model.DeviceInfo); + await _context.RefreshTokens.AddAsync(refreshTokens); + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + + + return APIResponseModel.CreateSuccessResponseModel(new LoginResponse() + { + Token = jwt, + UserName = user.UserName, + Id = user.Id, + NickName = user.NickName, + RefreshToken = refreshTokens.Token + }); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + /// + /// 鍒锋柊token鐨勬柟娉 + /// + /// + /// + internal async Task>> RefreshToken(RefreshTokenModel model, string ip, string refreshToken) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + Boolean isValid = await _securityService.ValidateRefreshToken(refreshToken, model.UserId, model.DeviceInfo, ip); + if (!isValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.RefreshTokenInvalid); + } + + // 寮濮嬬敓鎴恡oken + User? user = await _userManager.FindByIdAsync(model.UserId.ToString()); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + // 杩欒竟淇敼ip + + string jwt = GenerateJWT(user); + return APIResponseModel.CreateSuccessResponseModel(jwt); + + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #region 灏嗙敤鎴疯缃负浠g悊 + /// + /// 灏嗙敤鎴疯缃负浠g悊锛岃鍋氫竴绯诲垪妫鏌 + /// + /// + /// + /// + internal async Task>> EnableAgent(long requestUserId) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + User? user = await _userManager.FindByIdAsync(requestUserId.ToString()); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + // 鍒ゆ柇褰撳墠鐢ㄦ埛鏄笉鏄疺IP锛屽彧鏈塚IP鎵嶈兘鎴愪负浠g悊 + if (!await _userManager.IsInRoleAsync(user, "VIP User")) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "褰撳墠鐢ㄦ埛涓嶆槸VIP锛屼笉鑳芥垚涓轰唬鐞"); + } + + // 妫鏌ユ槸鍚﹀凡缁忔槸浠g悊 + if (await _userManager.IsInRoleAsync(user, "Agent User")) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidOptions, "褰撳墠鐢ㄦ埛宸叉槸浠g悊锛屼笉鑳介噸澶嶆搷浣"); + } + // 鐢熸垚鍏綅閭璇风爜锛屽苟涓斾笉鑳介噸澶 + string affiliateCode = ""; + do + { + affiliateCode = new Random().Next(100000, 999999).ToString("D6"); + } while (await _context.Users.AnyAsync(u => u.AffiliateCode == affiliateCode)); + + // 淇敼鍒嗘垚浣嶉粯璁 + user.AgentPercent = 0.50; + // 寮濮嬩慨鏀规暟鎹簱 + Role? role = await _context.Roles.FirstOrDefaultAsync(r => r.Name == "Agent User"); + if (role == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindRoleByIdFail); + } + await _context.UserRoles.AddAsync(new IdentityUserRole + { + RoleId = role.Id, + UserId = user.Id + }); + + if (string.IsNullOrWhiteSpace(user.AffiliateCode)) + { + user.AffiliateCode = affiliateCode; + } + await _userManager.UpdateAsync(user); + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return APIResponseModel.CreateSuccessResponseModel("鐢ㄦ埛宸叉垚鍔熷崌绾т负浠g悊"); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + #endregion + } +} diff --git a/LMS.service/Service/UserService/SecurityService.cs b/LMS.service/Service/UserService/SecurityService.cs new file mode 100644 index 0000000..f1de082 --- /dev/null +++ b/LMS.service/Service/UserService/SecurityService.cs @@ -0,0 +1,230 @@ +锘縰sing LMS.Common.RSAKey; +using LMS.DAO; +using LMS.Repository.DB; +using LMS.Repository.Models.DB; +using LMS.Repository.User; +using LMS.service.Data; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System.Collections.Concurrent; +using System.Security.Cryptography; +using System.Text; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace LMS.service.Service.UserService +{ + public class SecurityService + { + private readonly ApplicationDbContext _context; + + public SecurityService(ApplicationDbContext context) + { + _context = context; + } + + #region 鍒锋柊浠ょ墝 + + /// + /// 鐢熸垚鍒锋柊浠ょ墝 + /// + /// + public static string GenerateRefreshToken() + { + var randomNumber = new byte[32]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(randomNumber); + return Convert.ToBase64String(randomNumber); + } + } + + /// + /// 璁剧疆浠ょ墝鐨勫埛鏂版椂闂 + /// + /// + public static DateTime GetRefreshTokenExpiryTime() + { + return BeijingTimeExtension.GetBeijingTime().AddDays(7); // 璁剧疆鍒锋柊浠ょ墝7澶╁悗杩囨湡 + } + + /// + /// 楠岃瘉鍒锋柊浠ょ墝鏄惁姝g‘ + /// + /// 鍒锋柊token + /// 鐢ㄦ埛ID + /// 璁惧淇℃伅 + /// + public async Task ValidateRefreshToken(string token, long userId, string deviceInfo, string ip) + { + RefreshTokens? refreshTokens = await _context.RefreshTokens.FirstOrDefaultAsync(x => x.Token == token && x.UserId == userId); + if (refreshTokens == null) + { + return false; // 鐢ㄦ埛瀵瑰簲鐨勪护鐗屼笉瀛樺湪 + } + if (BeijingTimeExtension.GetBeijingTime() > refreshTokens.Expiration) + { + _context.Remove(refreshTokens); + return false; // 鍒锋柊浠ょ墝杩囨湡 + } + if (refreshTokens.DeviceInfo != deviceInfo) + { + _context.Remove(refreshTokens); // 涓嶅悓鐨勮澶囩櫥褰 + return false; + } + // 鍒锋柊浠ょ墝閫氳繃锛屼慨鏀瑰綋鍓峣p + refreshTokens.LastCheckIp = ip; + await _context.SaveChangesAsync(); + return true; + } + + /// + /// 缁勫悎涓涓埛鏂颁护鐗岀殑瀹炰綋绫 鐢ㄤ簬瀛樺偍鍒版暟鎹簱 + /// + /// + public async Task CreateRefreshTokenByUserId(long userId, string ip, string deviceInfo) + { + // 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁宸茬粡鏈変簡鍒锋柊浠ょ墝锛屽鏋滄湁鍒欏垹闄 + var oldRefreshToken = await _context.RefreshTokens.FirstOrDefaultAsync(x => x.UserId == userId); + if (oldRefreshToken != null) + { + _context.RefreshTokens.Remove(oldRefreshToken); + } + // 鍒涘缓鏂扮殑鍒锋柊浠ょ墝 + var refreshToken = GenerateRefreshToken(); + var expiryTime = GetRefreshTokenExpiryTime(); + + // 娣诲姞鍒锋柊浠ょ墝鍒版暟鎹簱 + RefreshTokens refreshTokens = new() + { + Id = Guid.NewGuid().ToString(), + UserId = userId, + Token = refreshToken, + Expiration = expiryTime, + Revoked = false, + CreatedTime = BeijingTimeExtension.GetBeijingTime(), + LastCheckIp = ip, + DeviceInfo = deviceInfo + }; + return refreshTokens; + } + + #endregion + + #region 鐧诲綍瀵嗙爜瀵嗛挜鐢熸垚鍜岃В瀵 + + /// + /// 鐢熸垚鍏挜 + /// + /// + /// + /// + internal async Task> GetPublicKey(ConcurrentDictionary _keyStore) + { + try + { + RsaKeys? rsa = await _context.RsaKeys.OrderBy(x => x.UseCount).FirstOrDefaultAsync(); + if (rsa == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, "No RSA key found."); + } + + // 鏇存柊浣跨敤娆℃暟 + rsa.UseCount++; + _context.Update(rsa); + await _context.SaveChangesAsync(); + + string token = Guid.NewGuid().ToString(); + + // 杩欒竟妫鏌ヤ笅鎵鏈夎秴鏃剁殑Key锛屽垹鎺 + foreach (var item in _keyStore) + { + if (item.Value.Expiry < BeijingTimeExtension.GetBeijingTime()) + { + _keyStore.TryRemove(item.Key, out _); + } + } + + _keyStore[token] = (rsa.Id, BeijingTimeExtension.GetBeijingTime().AddSeconds(30)); + + PublicKeyResponse publicKeyResponse = new() + { + Token = token, + PublicKey = rsa.PublicKey + }; + return APIResponseModel.CreateSuccessResponseModel(publicKeyResponse); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + private const int NonceSize = 12; + private const int TagSize = 16; + + /// + /// 瑙g爜鍔犲瘑鍚庣殑瀵嗙爜 + /// + /// + /// + /// + //public string DecryptWithAES(string cipherText, byte[] key) + //{ + // if (key.Length != 32) + // { + // throw new ArgumentException("Invalid key length. Expected 32 bytes.", nameof(key)); + // } + + // byte[] cipherData = Convert.FromBase64String(cipherText); + + // if (cipherData.Length < NonceSize + TagSize) + // { + // throw new ArgumentException("Cipher text is too short", nameof(cipherText)); + // } + + // byte[] nonce = new byte[NonceSize]; + // byte[] cipherTextWithTag = new byte[cipherData.Length - NonceSize]; + // Array.Copy(cipherData, nonce, NonceSize); + // Array.Copy(cipherData, NonceSize, cipherTextWithTag, 0, cipherTextWithTag.Length); + + // using var aes = new AesGcm(key, TagSize); + + // byte[] plaintext = new byte[cipherTextWithTag.Length - TagSize]; + + // aes.Decrypt(nonce, cipherTextWithTag, null, plaintext, cipherTextWithTag.AsSpan(cipherTextWithTag.Length - TagSize)); + + // return Encoding.UTF8.GetString(plaintext); + + //} + + /// + /// 瑙e瘑瀵嗛挜 + /// + /// + /// + /// + /// + public string DecryptWithAES(string rasKeyId) + { + try + { + RsaKeys? rsa = _context.RsaKeys.FirstOrDefault(x => x.Id == rasKeyId) ?? throw new Exception("鏈壘鍒板搴旂殑RSA瀵嗛挜"); + + string secertIV = rsa.EncryptionIV; + string secertKey = rsa.EncryptionKey; + + byte[] key = ComplexKeyObfuscator.Deobfuscate(Convert.FromBase64String(secertKey)); + byte[] iv = ComplexKeyObfuscator.Deobfuscate(Convert.FromBase64String(secertIV)); + + string privateKey = AESGenerate.Decrypt(rsa.EncryptedPrivateKey, key, iv); + return privateKey; + } + catch (Exception ex) + { + throw new Exception("瀵嗙爜瑙e瘑澶辫触"); + } + } + #endregion + } +} diff --git a/LMS.service/Service/UserService/UserService.cs b/LMS.service/Service/UserService/UserService.cs new file mode 100644 index 0000000..3d25c02 --- /dev/null +++ b/LMS.service/Service/UserService/UserService.cs @@ -0,0 +1,482 @@ +锘縰sing LMS.Common.RSAKey; +using LMS.DAO; +using LMS.Repository.DTO; +using LMS.Repository.DTO.UserDto; +using LMS.Repository.Models.DB; +using LMS.Repository.Models.User; +using LMS.Repository.User; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System.Collections.Concurrent; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace LMS.service.Service.UserService +{ + public class UserService(UserManager userManager, RoleManager roleManager, ApplicationDbContext context, SecurityService securityService) + { + private readonly UserManager _userManager = userManager; + private readonly RoleManager _roleManager = roleManager; + private readonly ApplicationDbContext _context = context; + private readonly SecurityService _securityService = securityService; + + #region 鑾峰彇鐢ㄦ埛淇℃伅 + /// + /// 鑾峰彇鐢ㄦ埛淇℃伅鐨勬柟娉 + /// + /// + /// + /// + internal async Task>> GetUserInfo(long userId, long requestUserId) + { + try + { + User? user = await _userManager.FindByIdAsync(userId.ToString()); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + if (userId != requestUserId) + { + // 杩欒竟鍗曠嫭鐨勫垽鏂槸鍚︽湁鏉冮檺 + User? requertUser = await _userManager.FindByIdAsync(requestUserId.ToString()); + if (requertUser == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(requertUser, "Super Admin") || await _userManager.IsInRoleAsync(requertUser, "Admin"); + + // 鍒ゆ柇瑙掕壊 + if (!isAdminOrSuperAdmin) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + } + + List? roles = [.. (await _userManager.GetRolesAsync(user))]; + + UserDto userDto = new() + { + Id = user.Id, + UserName = user.UserName ?? string.Empty, + NickName = user.NickName, + Email = user.Email ?? string.Empty, + PhoneNumber = user.PhoneNumber ?? string.Empty, + Avatar = string.Empty, + RoleNames = roles, + CreatedDate = user.CreatedDate, + AllDeviceCount = user.AllDeviceCount, + FreeCount = user.FreeCount, + Options = user.OptionsJson, + AgentPercent = user.AgentPercent, + AffiliateCode = user.AffiliateCode, + ParentId = user.ParentId ?? 0 + }; + return APIResponseModel.CreateSuccessResponseModel(userDto); + } + catch (Exception e) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + + #region 鏌ヨ鐢ㄦ埛鍒楄〃 + + internal async Task>>> QueryUserCollection(int page, int pageSize, string userName, long? userId, string nickName, string phoneNumber, string email, string[] roleNames, long? parentId, long reuqertUserId) + { + try + { + User? user = await _userManager.FindByIdAsync(reuqertUserId.ToString()); + if (user == null) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(user, "Super Admin") || await _userManager.IsInRoleAsync(user, "Admin"); + bool isSuperAdmin = await _userManager.IsInRoleAsync(user, "Super Admin"); + bool isAgent = !isAdminOrSuperAdmin && await _userManager.IsInRoleAsync(user, "Agent User"); + if (!isAdminOrSuperAdmin && !isAgent) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + List roleIds = []; + List userIds = []; + // 鍏堟煡璇㈡寚瀹氱殑瑙掕壊鍚嶅瓧瀵瑰簲鐨勭敤鎴凤紝鍐嶅鐢ㄦ埛杩涜绛涢 + if (roleNames != null && roleNames.Length > 0) + { + for (int i = 0; i < roleNames.Length; i++) + { + Role? role = await _roleManager.FindByNameAsync(roleNames[i]); + if (role == null) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.FindRoleByIdFail); + } + roleIds.Add(role.Id); + } + // 鎵惧埌鍜岃鑹睮D瀵瑰簲鐨勭敤鎴稩D + userIds = await _context.UserRoles.Where(x => roleIds.Contains(x.RoleId)).Select(x => x.UserId).Distinct().ToListAsync(); + } + + + // 寮濮嬫煡璇㈡暟鎹 + IQueryable? query = _userManager.Users; + + if (isAgent) + { + query = query.Where(x => x.ParentId == user.Id); + } + // 鍒ゆ柇鏄笉鏄鐞嗗憳 + IList superUsers = await _userManager.GetUsersInRoleAsync("Super Admin"); + List superUserIds = superUsers.Select(x => x.Id).ToList(); + // 榛樿鎶婅嚜宸辨帓闄 + //query = query.Where(x => x.Id != reuqertUserId); + if (!isSuperAdmin) + { + // 涓嶆槸鑽夐浮绠$悊鍛橈紝灏辨妸瓒呯骇绠$悊鍛樻帓闄 + query = query.Where(x => reuqertUserId == x.Id || (!superUserIds.Contains(x.ParentId ?? 0) && !superUserIds.Contains(x.Id))); + } + + // 娣诲姞鏌ヨ鏉′欢 + if (!string.IsNullOrWhiteSpace(userName)) + { + query = query.Where(x => !string.IsNullOrWhiteSpace(x.UserName) && x.UserName.Contains(userName)); + } + + if (!string.IsNullOrWhiteSpace(nickName)) + { + query = query.Where(x => !string.IsNullOrWhiteSpace(x.NickName) && x.NickName.Contains(nickName)); + } + + if (userId != null) + { + query = query.Where(x => x.Id == userId); + } + + if (parentId != null) + { + query = query.Where(x => x.ParentId == parentId); + } + + if (!string.IsNullOrWhiteSpace(phoneNumber)) + { + query = query.Where(x => !string.IsNullOrWhiteSpace(x.PhoneNumber) && x.PhoneNumber.Contains(phoneNumber)); + } + + if (!string.IsNullOrWhiteSpace(email)) + { + query = query.Where(x => !string.IsNullOrWhiteSpace(x.Email) && x.Email.Contains(email)); + } + + if (roleIds.Count > 0) + { + query = query.Where(x => userIds.Contains(x.Id)); + } + + // 閫氳繃ID闄嶅簭 + query = query.OrderByDescending(x => x.Id); + + // 鏌ヨ鎬绘暟 + int total = await query.CountAsync(); + + // 鍒嗛〉 + query = query.Skip((page - 1) * pageSize).Take(pageSize); + + List? users = await query.ToListAsync(); + + List? userCollections = users.Select(x => + new UserCollectionDto + { + Id = x.Id, + UserName = x.UserName ?? string.Empty, + NickName = x.NickName, + Email = x.Email ?? string.Empty, + PhoneNumber = x.PhoneNumber ?? string.Empty, + CreatedDate = x.CreatedDate, + LastLoginDate = x.LastLoginDate, + LastLoginIp = x.LastLoginIp, + LastLoginDevice = x.LastLoginDevice, + ParentId = x.ParentId ?? 0 + }).ToList(); + for (int i = 0; i < userCollections.Count; i++) + { + List? roles = [.. (await _userManager.GetRolesAsync(users[i]))]; + userCollections[i].RoleNames = roles; + if (!isAdminOrSuperAdmin) + { + userCollections[i].PhoneNumber = "***********"; + userCollections[i].Email = "***********"; + } + } + + return APIResponseModel>.CreateSuccessResponseModel(new CollectionResponse + { + Total = total, + Collection = userCollections, + Current = page, + }); + } + catch (Exception e) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + + #endregion + + #region 淇敼鐢ㄦ埛淇℃伅 + /// + /// 淇敼鐢ㄦ埛淇℃伅 + /// + /// + /// + /// + /// + /// + public async Task>> UpdatedUser(long id, UpdatedUserModel userModel, long requestUserId) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + User? user = await _userManager.FindByIdAsync(id.ToString()); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + // 妫鏌ュ綋鍓嶇敤鎴风殑鏉冮檺 + User? requestUser = await _userManager.FindByIdAsync(requestUserId.ToString()); + if (requestUser == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(requestUser, "Super Admin") || await _userManager.IsInRoleAsync(requestUser, "Admin"); + if (requestUserId != id && !isAdminOrSuperAdmin) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + // 寮濮嬩慨鏀圭敤鎴蜂俊鎭 + if (userModel.AgentPercent != null) + { + user.AgentPercent = userModel.AgentPercent.Value; + } + if (userModel.AllDeviceCount != null) + { + user.AllDeviceCount = userModel.AllDeviceCount.Value; + } + if (userModel.FreeCount != null) + { + user.FreeCount = userModel.FreeCount.Value; + } + if (userModel.NickName != null) + { + user.NickName = userModel.NickName; + } + if (userModel.UserName != null) + { + user.UserName = userModel.UserName; + user.NormalizedUserName = userModel.UserName.ToUpper(); + } + if (userModel.Email != null) + { + user.Email = userModel.Email; + user.NormalizedEmail = userModel.Email.ToUpper(); + } + if (userModel.PhoneNumber != null) + { + user.PhoneNumber = userModel.PhoneNumber; + } + + // 鍙湁绠$悊鍛樻墠鑳戒慨鏀硅鑹 + if (isAdminOrSuperAdmin) + { + // 寮濮嬪垹闄や箣鍓嶇殑缁戝畾鍏崇郴 + List> userRoles = await _context.UserRoles.Where(x => x.UserId == id).ToListAsync(); + _context.UserRoles.RemoveRange(userRoles); + + // 鍒ゆ柇鏄笉鏄墍鏈夌殑瑙掕壊鍚嶉兘瀛樺湪 + if (userModel.RoleNames != null && userModel.RoleNames.Count > 0) + { + // 灏嗕箣鍓嶇殑缁戝畾鍏崇郴鍏ㄩ儴鍒犳帀锛岄噸鏂扮粦瀹 + List roleIds = []; + for (int i = 0; i < userModel.RoleNames.Count; i++) + { + Role? role = await _roleManager.FindByNameAsync(userModel.RoleNames[i]); + if (role == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindRoleByIdFail); + } + roleIds.Add(role.Id); + } + + // 寮濮嬫柊澧 + List> userRole = roleIds.Select(x => new IdentityUserRole + { + UserId = id, + RoleId = x + }).ToList(); + + await _context.UserRoles.AddRangeAsync(userRole); + } + } + _context.Update(user); + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return APIResponseModel.CreateSuccessResponseModel("淇敼鎴愬姛"); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + + #region 鑾峰彇鐢ㄦ埛浠g悊淇℃伅 + /// + /// 鑾峰彇鐢ㄦ埛浠g悊淇℃伅 + /// + /// + /// + internal async Task>> GetUserAgentInfo(long requestUserId) + { + try + { + User? user = await _userManager.FindByIdAsync(requestUserId.ToString()); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(user, "Super Admin") || await _userManager.IsInRoleAsync(user, "Admin"); + + // 鍒ゆ柇褰撳墠鐢ㄦ埛鏄笉鏄唬鐞 + if (!isAdminOrSuperAdmin && !await _userManager.IsInRoleAsync(user, "Agent User")) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + // 鏌ヨ鎵鏈夌敤鎴凤紝鍒ゆ柇浠栫殑parentId鏄笉鏄綋鍓嶇敤鎴 + int allUserCount = await _context.Users.Where(x => x.ParentId == requestUserId).CountAsync(); + // 鏌ヨ鎵鏈夊湪VIP User瑙掕壊涓殑鐢ㄦ埛 + Role? role = await _roleManager.FindByNameAsync("VIP User"); + if (role == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindRoleByIdFail); + } + int vipUserCount = await _context.UserRoles + .Where(ur => ur.RoleId == role.Id) + .Join(_context.Users, + ur => ur.UserId, + u => u.Id, + (ur, u) => u) + .CountAsync(u => u.ParentId == requestUserId); + + UserAgentInfoDto userAgentInfoDto = new() + { + UserId = user.Id, + UserName = user.UserName ?? string.Empty, + NickName = user.NickName, + AffiliateCode = user.AffiliateCode, + AgentPercent = user.AgentPercent, + AffiliateNumber = allUserCount, + AffiliateVIPNumber = vipUserCount, + AffiliateMoney = 0 + }; + return APIResponseModel.CreateSuccessResponseModel(userAgentInfoDto); + } + catch (Exception e) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + + #region 鐢ㄦ埛娉ㄥ唽 + + /// + /// 鐢ㄦ埛娉ㄥ唽 + /// + /// + /// + /// + internal async Task>> Register(RegisterModel model, ConcurrentDictionary _keyStore) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + if (!_keyStore.TryRemove(model.TokenId, out var keyInfo)) // 娌℃湁鎵惧埌瀵瑰簲鐨勫叕閽 + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserPasswordFail); + } + + if (BeijingTimeExtension.GetBeijingTime() > keyInfo.Expiry) // 鍏挜瓒呮椂 + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserPasswordFail); + } + + // 鍒ゆ柇閭璇风爜鏄笉鏄瓨鍦 + if (string.IsNullOrWhiteSpace(model.AffiliateCode)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "閭璇风爜蹇呭~"); + } + User? affiliateUser = await _userManager.Users.FirstOrDefaultAsync(x => x.AffiliateCode == model.AffiliateCode); + if (affiliateUser == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidAffiliateCode); + } + + var rsaKeyId = keyInfo.Key; + var privateKey = _securityService.DecryptWithAES(rsaKeyId); + + string decryptedPassword = RsaKeyPairGenerator.Decrypt(privateKey, model.Password); + + var user = new User { UserName = model.UserName, Email = model.Email ?? string.Empty, NickName = model.UserName }; + + var result = await _userManager.CreateAsync(user, decryptedPassword); + if (!result.Succeeded) + { + foreach (var s in result.Errors) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserRegisterFial, s.Description); + } + } + try + { + await _userManager.AddToRoleAsync(user, "Simple User"); + // 缁欑敤鎴疯缃垵濮嬪 + user.AllDeviceCount = 1; + user.AgentPercent = 0.50; + user.FreeCount = 10; + user.ParentId = affiliateUser.Id; + } + catch (Exception e) + { + // 濡傛灉娣诲姞瑙掕壊澶辫触锛屽垹闄ょ敤鎴 + await _userManager.DeleteAsync(user); + throw; + } + await _userManager.UpdateAsync(user); + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + return APIResponseModel.CreateSuccessResponseModel(ResponseCode.Success, "User Register Success"); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + + #endregion + } +} diff --git a/LMS.service/appsettings.Development.json b/LMS.service/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/LMS.service/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/LMS.service/appsettings.json b/LMS.service/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/LMS.service/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docker.txt b/docker.txt new file mode 100644 index 0000000..8b6afdd --- /dev/null +++ b/docker.txt @@ -0,0 +1 @@ +docker build -t lms-service . \ No newline at end of file